use of com.google.android.exoplayer2.extractor.Extractor in project ExoPlayer by google.
the class HlsMediaChunk method createExtractor.
private Extractor createExtractor() {
// Select the extractor that will read the chunk.
Extractor extractor;
boolean usingNewExtractor = true;
if (MimeTypes.TEXT_VTT.equals(hlsUrl.format.sampleMimeType) || lastPathSegment.endsWith(WEBVTT_FILE_EXTENSION) || lastPathSegment.endsWith(VTT_FILE_EXTENSION)) {
extractor = new WebvttExtractor(trackFormat.language, timestampAdjuster);
} else if (!needNewExtractor) {
// Only reuse TS and fMP4 extractors.
usingNewExtractor = false;
extractor = previousExtractor;
} else if (lastPathSegment.endsWith(MP4_FILE_EXTENSION) || lastPathSegment.startsWith(M4_FILE_EXTENSION_PREFIX, lastPathSegment.length() - 4)) {
extractor = new FragmentedMp4Extractor(0, timestampAdjuster);
} else {
// MPEG-2 TS segments, but we need a new extractor.
// This flag ensures the change of pid between streams does not affect the sample queues.
@DefaultTsPayloadReaderFactory.Flags int esReaderFactoryFlags = DefaultTsPayloadReaderFactory.FLAG_IGNORE_SPLICE_INFO_STREAM;
if (!muxedCaptionFormats.isEmpty()) {
// The playlist declares closed caption renditions, we should ignore descriptors.
esReaderFactoryFlags |= DefaultTsPayloadReaderFactory.FLAG_OVERRIDE_CAPTION_DESCRIPTORS;
}
String codecs = trackFormat.codecs;
if (!TextUtils.isEmpty(codecs)) {
// explicitly ignore them even if they're declared.
if (!MimeTypes.AUDIO_AAC.equals(MimeTypes.getAudioMediaMimeType(codecs))) {
esReaderFactoryFlags |= DefaultTsPayloadReaderFactory.FLAG_IGNORE_AAC_STREAM;
}
if (!MimeTypes.VIDEO_H264.equals(MimeTypes.getVideoMediaMimeType(codecs))) {
esReaderFactoryFlags |= DefaultTsPayloadReaderFactory.FLAG_IGNORE_H264_STREAM;
}
}
extractor = new TsExtractor(TsExtractor.MODE_HLS, timestampAdjuster, new DefaultTsPayloadReaderFactory(esReaderFactoryFlags, muxedCaptionFormats));
}
if (usingNewExtractor) {
extractor.init(extractorOutput);
}
return extractor;
}
use of com.google.android.exoplayer2.extractor.Extractor in project ExoPlayer by google.
the class TestUtil method consumeTestData.
private static void consumeTestData(Extractor extractor, FakeExtractorInput input, long timeUs, FakeExtractorOutput output, boolean retryFromStartIfLive) throws IOException, InterruptedException {
extractor.seek(input.getPosition(), timeUs);
PositionHolder seekPositionHolder = new PositionHolder();
int readResult = Extractor.RESULT_CONTINUE;
while (readResult != Extractor.RESULT_END_OF_INPUT) {
try {
// Extractor.read should not read seekPositionHolder.position. Set it to a value that's
// likely to cause test failure if a read does occur.
seekPositionHolder.position = Long.MIN_VALUE;
readResult = extractor.read(input, seekPositionHolder);
if (readResult == Extractor.RESULT_SEEK) {
long seekPosition = seekPositionHolder.position;
Assertions.checkState(0 <= seekPosition && seekPosition <= Integer.MAX_VALUE);
input.setPosition((int) seekPosition);
}
} catch (SimulatedIOException e) {
if (!retryFromStartIfLive) {
continue;
}
boolean isOnDemand = input.getLength() != C.LENGTH_UNSET || (output.seekMap != null && output.seekMap.getDurationUs() != C.TIME_UNSET);
if (isOnDemand) {
continue;
}
input.setPosition(0);
for (int i = 0; i < output.numberOfTracks; i++) {
output.trackOutputs.valueAt(i).clear();
}
extractor.seek(0, 0);
}
}
}
use of com.google.android.exoplayer2.extractor.Extractor in project ExoPlayer by google.
the class TestUtil method assertOutput.
/**
* Asserts that {@code extractor} consumes {@code sampleFile} successfully and its output equals
* to a prerecorded output dump file with the name {@code sampleFile} + "{@value
* #DUMP_EXTENSION}". If {@code simulateUnknownLength} is true and {@code sampleFile} + "{@value
* #UNKNOWN_LENGTH_EXTENSION}" exists, it's preferred.
*
* @param extractor The {@link Extractor} to be tested.
* @param sampleFile The path to the input sample.
* @param fileData Content of the input file.
* @param instrumentation To be used to load the sample file.
* @param simulateIOErrors If true simulates IOErrors.
* @param simulateUnknownLength If true simulates unknown input length.
* @param simulatePartialReads If true simulates partial reads.
* @return The {@link FakeExtractorOutput} used in the test.
* @throws IOException If reading from the input fails.
* @throws InterruptedException If interrupted while reading from the input.
*/
public static FakeExtractorOutput assertOutput(Extractor extractor, String sampleFile, byte[] fileData, Instrumentation instrumentation, boolean simulateIOErrors, boolean simulateUnknownLength, boolean simulatePartialReads) throws IOException, InterruptedException {
FakeExtractorInput input = new FakeExtractorInput.Builder().setData(fileData).setSimulateIOErrors(simulateIOErrors).setSimulateUnknownLength(simulateUnknownLength).setSimulatePartialReads(simulatePartialReads).build();
Assert.assertTrue(sniffTestData(extractor, input));
input.resetPeekPosition();
FakeExtractorOutput extractorOutput = consumeTestData(extractor, input, 0, true);
if (simulateUnknownLength && assetExists(instrumentation, sampleFile + UNKNOWN_LENGTH_EXTENSION)) {
extractorOutput.assertOutput(instrumentation, sampleFile + UNKNOWN_LENGTH_EXTENSION);
} else {
extractorOutput.assertOutput(instrumentation, sampleFile + ".0" + DUMP_EXTENSION);
}
SeekMap seekMap = extractorOutput.seekMap;
if (seekMap.isSeekable()) {
long durationUs = seekMap.getDurationUs();
for (int j = 0; j < 4; j++) {
long timeUs = (durationUs * j) / 3;
long position = seekMap.getPosition(timeUs);
input.setPosition((int) position);
for (int i = 0; i < extractorOutput.numberOfTracks; i++) {
extractorOutput.trackOutputs.valueAt(i).clear();
}
consumeTestData(extractor, input, timeUs, extractorOutput, false);
extractorOutput.assertOutput(instrumentation, sampleFile + '.' + j + DUMP_EXTENSION);
}
}
return extractorOutput;
}
use of com.google.android.exoplayer2.extractor.Extractor in project ExoPlayer by google.
the class MatroskaExtractor method writeSampleData.
private void writeSampleData(ExtractorInput input, Track track, int size) throws IOException, InterruptedException {
if (CODEC_ID_SUBRIP.equals(track.codecId)) {
int sizeWithPrefix = SUBRIP_PREFIX.length + size;
if (subripSample.capacity() < sizeWithPrefix) {
// Initialize subripSample to contain the required prefix and have space to hold a subtitle
// twice as long as this one.
subripSample.data = Arrays.copyOf(SUBRIP_PREFIX, sizeWithPrefix + size);
}
input.readFully(subripSample.data, SUBRIP_PREFIX.length, size);
subripSample.setPosition(0);
subripSample.setLimit(sizeWithPrefix);
// the correct end timecode, which we might not have yet.
return;
}
TrackOutput output = track.output;
if (!sampleEncodingHandled) {
if (track.hasContentEncryption) {
// If the sample is encrypted, read its encryption signal byte and set the IV size.
// Clear the encrypted flag.
blockFlags &= ~C.BUFFER_FLAG_ENCRYPTED;
if (!sampleSignalByteRead) {
input.readFully(scratch.data, 0, 1);
sampleBytesRead++;
if ((scratch.data[0] & 0x80) == 0x80) {
throw new ParserException("Extension bit is set in signal byte");
}
sampleSignalByte = scratch.data[0];
sampleSignalByteRead = true;
}
boolean isEncrypted = (sampleSignalByte & 0x01) == 0x01;
if (isEncrypted) {
boolean hasSubsampleEncryption = (sampleSignalByte & 0x02) == 0x02;
blockFlags |= C.BUFFER_FLAG_ENCRYPTED;
if (!sampleInitializationVectorRead) {
input.readFully(encryptionInitializationVector.data, 0, ENCRYPTION_IV_SIZE);
sampleBytesRead += ENCRYPTION_IV_SIZE;
sampleInitializationVectorRead = true;
// Write the signal byte, containing the IV size and the subsample encryption flag.
scratch.data[0] = (byte) (ENCRYPTION_IV_SIZE | (hasSubsampleEncryption ? 0x80 : 0x00));
scratch.setPosition(0);
output.sampleData(scratch, 1);
sampleBytesWritten++;
// Write the IV.
encryptionInitializationVector.setPosition(0);
output.sampleData(encryptionInitializationVector, ENCRYPTION_IV_SIZE);
sampleBytesWritten += ENCRYPTION_IV_SIZE;
}
if (hasSubsampleEncryption) {
if (!samplePartitionCountRead) {
input.readFully(scratch.data, 0, 1);
sampleBytesRead++;
scratch.setPosition(0);
samplePartitionCount = scratch.readUnsignedByte();
samplePartitionCountRead = true;
}
int samplePartitionDataSize = samplePartitionCount * 4;
scratch.reset(samplePartitionDataSize);
input.readFully(scratch.data, 0, samplePartitionDataSize);
sampleBytesRead += samplePartitionDataSize;
short subsampleCount = (short) (1 + (samplePartitionCount / 2));
int subsampleDataSize = 2 + 6 * subsampleCount;
if (encryptionSubsampleDataBuffer == null || encryptionSubsampleDataBuffer.capacity() < subsampleDataSize) {
encryptionSubsampleDataBuffer = ByteBuffer.allocate(subsampleDataSize);
}
encryptionSubsampleDataBuffer.position(0);
encryptionSubsampleDataBuffer.putShort(subsampleCount);
// Loop through the partition offsets and write out the data in the way ExoPlayer
// wants it (ISO 23001-7 Part 7):
// 2 bytes - sub sample count.
// for each sub sample:
// 2 bytes - clear data size.
// 4 bytes - encrypted data size.
int partitionOffset = 0;
for (int i = 0; i < samplePartitionCount; i++) {
int previousPartitionOffset = partitionOffset;
partitionOffset = scratch.readUnsignedIntToInt();
if ((i % 2) == 0) {
encryptionSubsampleDataBuffer.putShort((short) (partitionOffset - previousPartitionOffset));
} else {
encryptionSubsampleDataBuffer.putInt(partitionOffset - previousPartitionOffset);
}
}
int finalPartitionSize = size - sampleBytesRead - partitionOffset;
if ((samplePartitionCount % 2) == 1) {
encryptionSubsampleDataBuffer.putInt(finalPartitionSize);
} else {
encryptionSubsampleDataBuffer.putShort((short) finalPartitionSize);
encryptionSubsampleDataBuffer.putInt(0);
}
encryptionSubsampleData.reset(encryptionSubsampleDataBuffer.array(), subsampleDataSize);
output.sampleData(encryptionSubsampleData, subsampleDataSize);
sampleBytesWritten += subsampleDataSize;
}
}
} else if (track.sampleStrippedBytes != null) {
// If the sample has header stripping, prepare to read/output the stripped bytes first.
sampleStrippedBytes.reset(track.sampleStrippedBytes, track.sampleStrippedBytes.length);
}
sampleEncodingHandled = true;
}
size += sampleStrippedBytes.limit();
if (CODEC_ID_H264.equals(track.codecId) || CODEC_ID_H265.equals(track.codecId)) {
// TODO: Deduplicate with Mp4Extractor.
// Zero the top three bytes of the array that we'll use to decode nal unit lengths, in case
// they're only 1 or 2 bytes long.
byte[] nalLengthData = nalLength.data;
nalLengthData[0] = 0;
nalLengthData[1] = 0;
nalLengthData[2] = 0;
int nalUnitLengthFieldLength = track.nalUnitLengthFieldLength;
int nalUnitLengthFieldLengthDiff = 4 - track.nalUnitLengthFieldLength;
// start codes as we encounter them.
while (sampleBytesRead < size) {
if (sampleCurrentNalBytesRemaining == 0) {
// Read the NAL length so that we know where we find the next one.
readToTarget(input, nalLengthData, nalUnitLengthFieldLengthDiff, nalUnitLengthFieldLength);
nalLength.setPosition(0);
sampleCurrentNalBytesRemaining = nalLength.readUnsignedIntToInt();
// Write a start code for the current NAL unit.
nalStartCode.setPosition(0);
output.sampleData(nalStartCode, 4);
sampleBytesWritten += 4;
} else {
// Write the payload of the NAL unit.
sampleCurrentNalBytesRemaining -= readToOutput(input, output, sampleCurrentNalBytesRemaining);
}
}
} else {
while (sampleBytesRead < size) {
readToOutput(input, output, size - sampleBytesRead);
}
}
if (CODEC_ID_VORBIS.equals(track.codecId)) {
// Vorbis decoder in android MediaCodec [1] expects the last 4 bytes of the sample to be the
// number of samples in the current page. This definition holds good only for Ogg and
// irrelevant for Matroska. So we always set this to -1 (the decoder will ignore this value if
// we set it to -1). The android platform media extractor [2] does the same.
// [1] https://android.googlesource.com/platform/frameworks/av/+/lollipop-release/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp#314
// [2] https://android.googlesource.com/platform/frameworks/av/+/lollipop-release/media/libstagefright/NuMediaExtractor.cpp#474
vorbisNumPageSamples.setPosition(0);
output.sampleData(vorbisNumPageSamples, 4);
sampleBytesWritten += 4;
}
}
use of com.google.android.exoplayer2.extractor.Extractor in project ExoPlayer by google.
the class Mp4Extractor method readSample.
/**
* Attempts to extract the next sample in the current mdat atom for the specified track.
* <p>
* Returns {@link #RESULT_SEEK} if the source should be reloaded from the position in
* {@code positionHolder}.
* <p>
* Returns {@link #RESULT_END_OF_INPUT} if no samples are left. Otherwise, returns
* {@link #RESULT_CONTINUE}.
*
* @param input The {@link ExtractorInput} from which to read data.
* @param positionHolder If {@link #RESULT_SEEK} is returned, this holder is updated to hold the
* position of the required data.
* @return One of the {@code RESULT_*} flags in {@link Extractor}.
* @throws IOException If an error occurs reading from the input.
* @throws InterruptedException If the thread is interrupted.
*/
private int readSample(ExtractorInput input, PositionHolder positionHolder) throws IOException, InterruptedException {
int trackIndex = getTrackIndexOfEarliestCurrentSample();
if (trackIndex == C.INDEX_UNSET) {
return RESULT_END_OF_INPUT;
}
Mp4Track track = tracks[trackIndex];
TrackOutput trackOutput = track.trackOutput;
int sampleIndex = track.sampleIndex;
long position = track.sampleTable.offsets[sampleIndex];
int sampleSize = track.sampleTable.sizes[sampleIndex];
if (track.track.sampleTransformation == Track.TRANSFORMATION_CEA608_CDAT) {
// The sample information is contained in a cdat atom. The header must be discarded for
// committing.
position += Atom.HEADER_SIZE;
sampleSize -= Atom.HEADER_SIZE;
}
long skipAmount = position - input.getPosition() + sampleBytesWritten;
if (skipAmount < 0 || skipAmount >= RELOAD_MINIMUM_SEEK_DISTANCE) {
positionHolder.position = position;
return RESULT_SEEK;
}
input.skipFully((int) skipAmount);
if (track.track.nalUnitLengthFieldLength != 0) {
// Zero the top three bytes of the array that we'll use to decode nal unit lengths, in case
// they're only 1 or 2 bytes long.
byte[] nalLengthData = nalLength.data;
nalLengthData[0] = 0;
nalLengthData[1] = 0;
nalLengthData[2] = 0;
int nalUnitLengthFieldLength = track.track.nalUnitLengthFieldLength;
int nalUnitLengthFieldLengthDiff = 4 - track.track.nalUnitLengthFieldLength;
// start codes as we encounter them.
while (sampleBytesWritten < sampleSize) {
if (sampleCurrentNalBytesRemaining == 0) {
// Read the NAL length so that we know where we find the next one.
input.readFully(nalLength.data, nalUnitLengthFieldLengthDiff, nalUnitLengthFieldLength);
nalLength.setPosition(0);
sampleCurrentNalBytesRemaining = nalLength.readUnsignedIntToInt();
// Write a start code for the current NAL unit.
nalStartCode.setPosition(0);
trackOutput.sampleData(nalStartCode, 4);
sampleBytesWritten += 4;
sampleSize += nalUnitLengthFieldLengthDiff;
} else {
// Write the payload of the NAL unit.
int writtenBytes = trackOutput.sampleData(input, sampleCurrentNalBytesRemaining, false);
sampleBytesWritten += writtenBytes;
sampleCurrentNalBytesRemaining -= writtenBytes;
}
}
} else {
while (sampleBytesWritten < sampleSize) {
int writtenBytes = trackOutput.sampleData(input, sampleSize - sampleBytesWritten, false);
sampleBytesWritten += writtenBytes;
sampleCurrentNalBytesRemaining -= writtenBytes;
}
}
trackOutput.sampleMetadata(track.sampleTable.timestampsUs[sampleIndex], track.sampleTable.flags[sampleIndex], sampleSize, 0, null);
track.sampleIndex++;
sampleBytesWritten = 0;
sampleCurrentNalBytesRemaining = 0;
return RESULT_CONTINUE;
}
Aggregations