use of com.google.android.exoplayer2.extractor.ExtractorInput in project ExoPlayer by google.
the class DefaultEbmlReaderTest method testMasterElementEmpty.
public void testMasterElementEmpty() throws IOException, InterruptedException {
ExtractorInput input = createTestInput(0x18, 0x53, 0x80, 0x67, 0x80);
TestOutput expected = new TestOutput();
expected.startMasterElement(TestOutput.ID_SEGMENT, 5, 0);
expected.endMasterElement(TestOutput.ID_SEGMENT);
assertEvents(input, expected.events);
}
use of com.google.android.exoplayer2.extractor.ExtractorInput in project ExoPlayer by google.
the class VarintReaderTest method testReadVarintFlaky.
private static void testReadVarintFlaky(VarintReader reader, boolean removeMask, byte[] data, int expectedLength, long expectedValue) throws IOException, InterruptedException {
ExtractorInput input = new FakeExtractorInput.Builder().setData(data).setSimulateUnknownLength(true).setSimulateIOErrors(true).setSimulatePartialReads(true).build();
long result = -1;
while (result == -1) {
try {
result = reader.readUnsignedVarint(input, false, removeMask, 8);
if (result == C.RESULT_END_OF_INPUT || result == C.RESULT_MAX_LENGTH_EXCEEDED) {
// Unexpected.
fail();
}
} catch (SimulatedIOException e) {
// Expected.
}
}
assertEquals(expectedLength, input.getPosition());
assertEquals(expectedValue, result);
}
use of com.google.android.exoplayer2.extractor.ExtractorInput in project ExoPlayer by google.
the class VarintReaderTest method testReadVarintExceedsMaximumAllowedLength.
public void testReadVarintExceedsMaximumAllowedLength() throws IOException, InterruptedException {
VarintReader reader = new VarintReader();
ExtractorInput input = new FakeExtractorInput.Builder().setData(DATA_8_BYTE_0).setSimulateUnknownLength(true).build();
long result = reader.readUnsignedVarint(input, false, true, 4);
assertEquals(C.RESULT_MAX_LENGTH_EXCEEDED, result);
}
use of com.google.android.exoplayer2.extractor.ExtractorInput in project ExoPlayer by google.
the class FragmentedMp4Extractor method readAtomHeader.
private boolean readAtomHeader(ExtractorInput input) throws IOException, InterruptedException {
if (atomHeaderBytesRead == 0) {
// Read the standard length atom header.
if (!input.readFully(atomHeader.data, 0, Atom.HEADER_SIZE, true)) {
return false;
}
atomHeaderBytesRead = Atom.HEADER_SIZE;
atomHeader.setPosition(0);
atomSize = atomHeader.readUnsignedInt();
atomType = atomHeader.readInt();
}
if (atomSize == Atom.LONG_SIZE_PREFIX) {
// Read the extended atom size.
int headerBytesRemaining = Atom.LONG_HEADER_SIZE - Atom.HEADER_SIZE;
input.readFully(atomHeader.data, Atom.HEADER_SIZE, headerBytesRemaining);
atomHeaderBytesRead += headerBytesRemaining;
atomSize = atomHeader.readUnsignedLongToLong();
}
if (atomSize < atomHeaderBytesRead) {
throw new ParserException("Atom size less than header length (unsupported).");
}
long atomPosition = input.getPosition() - atomHeaderBytesRead;
if (atomType == Atom.TYPE_moof) {
// The data positions may be updated when parsing the tfhd/trun.
int trackCount = trackBundles.size();
for (int i = 0; i < trackCount; i++) {
TrackFragment fragment = trackBundles.valueAt(i).fragment;
fragment.atomPosition = atomPosition;
fragment.auxiliaryDataPosition = atomPosition;
fragment.dataPosition = atomPosition;
}
}
if (atomType == Atom.TYPE_mdat) {
currentTrackBundle = null;
endOfMdatPosition = atomPosition + atomSize;
if (!haveOutputSeekMap) {
extractorOutput.seekMap(new SeekMap.Unseekable(durationUs));
haveOutputSeekMap = true;
}
parserState = STATE_READING_ENCRYPTION_DATA;
return true;
}
if (shouldParseContainerAtom(atomType)) {
long endPosition = input.getPosition() + atomSize - Atom.HEADER_SIZE;
containerAtoms.add(new ContainerAtom(atomType, endPosition));
if (atomSize == atomHeaderBytesRead) {
processAtomEnded(endPosition);
} else {
// Start reading the first child atom.
enterReadingAtomHeaderState();
}
} else if (shouldParseLeafAtom(atomType)) {
if (atomHeaderBytesRead != Atom.HEADER_SIZE) {
throw new ParserException("Leaf atom defines extended atom size (unsupported).");
}
if (atomSize > Integer.MAX_VALUE) {
throw new ParserException("Leaf atom with length > 2147483647 (unsupported).");
}
atomData = new ParsableByteArray((int) atomSize);
System.arraycopy(atomHeader.data, 0, atomData.data, 0, Atom.HEADER_SIZE);
parserState = STATE_READING_ATOM_PAYLOAD;
} else {
if (atomSize > Integer.MAX_VALUE) {
throw new ParserException("Skipping atom with length > 2147483647 (unsupported).");
}
atomData = null;
parserState = STATE_READING_ATOM_PAYLOAD;
}
return true;
}
use of com.google.android.exoplayer2.extractor.ExtractorInput in project ExoPlayer by google.
the class FragmentedMp4Extractor method readSample.
/**
* Attempts to extract the next sample in the current mdat atom.
* <p>
* If there are no more samples in the current mdat atom then the parser state is transitioned
* to {@link #STATE_READING_ATOM_HEADER} and {@code false} is returned.
* <p>
* It is possible for a sample to be extracted in part in the case that an exception is thrown. In
* this case the method can be called again to extract the remainder of the sample.
*
* @param input The {@link ExtractorInput} from which to read data.
* @return Whether a sample was extracted.
* @throws IOException If an error occurs reading from the input.
* @throws InterruptedException If the thread is interrupted.
*/
private boolean readSample(ExtractorInput input) throws IOException, InterruptedException {
if (parserState == STATE_READING_SAMPLE_START) {
if (currentTrackBundle == null) {
TrackBundle currentTrackBundle = getNextFragmentRun(trackBundles);
if (currentTrackBundle == null) {
// We've run out of samples in the current mdat. Discard any trailing data and prepare to
// read the header of the next atom.
int bytesToSkip = (int) (endOfMdatPosition - input.getPosition());
if (bytesToSkip < 0) {
throw new ParserException("Offset to end of mdat was negative.");
}
input.skipFully(bytesToSkip);
enterReadingAtomHeaderState();
return false;
}
long nextDataPosition = currentTrackBundle.fragment.trunDataPosition[currentTrackBundle.currentTrackRunIndex];
// We skip bytes preceding the next sample to read.
int bytesToSkip = (int) (nextDataPosition - input.getPosition());
if (bytesToSkip < 0) {
// Assume the sample data must be contiguous in the mdat with no preceding data.
Log.w(TAG, "Ignoring negative offset to sample data.");
bytesToSkip = 0;
}
input.skipFully(bytesToSkip);
this.currentTrackBundle = currentTrackBundle;
}
sampleSize = currentTrackBundle.fragment.sampleSizeTable[currentTrackBundle.currentSampleIndex];
if (currentTrackBundle.fragment.definesEncryptionData) {
sampleBytesWritten = appendSampleEncryptionData(currentTrackBundle);
sampleSize += sampleBytesWritten;
} else {
sampleBytesWritten = 0;
}
if (currentTrackBundle.track.sampleTransformation == Track.TRANSFORMATION_CEA608_CDAT) {
sampleSize -= Atom.HEADER_SIZE;
input.skipFully(Atom.HEADER_SIZE);
}
parserState = STATE_READING_SAMPLE_CONTINUE;
sampleCurrentNalBytesRemaining = 0;
}
TrackFragment fragment = currentTrackBundle.fragment;
Track track = currentTrackBundle.track;
TrackOutput output = currentTrackBundle.output;
int sampleIndex = currentTrackBundle.currentSampleIndex;
if (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[] nalPrefixData = nalPrefix.data;
nalPrefixData[0] = 0;
nalPrefixData[1] = 0;
nalPrefixData[2] = 0;
int nalUnitPrefixLength = track.nalUnitLengthFieldLength + 1;
int nalUnitLengthFieldLengthDiff = 4 - 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, and its type.
input.readFully(nalPrefixData, nalUnitLengthFieldLengthDiff, nalUnitPrefixLength);
nalPrefix.setPosition(0);
sampleCurrentNalBytesRemaining = nalPrefix.readUnsignedIntToInt() - 1;
// Write a start code for the current NAL unit.
nalStartCode.setPosition(0);
output.sampleData(nalStartCode, 4);
// Write the NAL unit type byte.
output.sampleData(nalPrefix, 1);
processSeiNalUnitPayload = cea608TrackOutputs != null && NalUnitUtil.isNalUnitSei(track.format.sampleMimeType, nalPrefixData[4]);
sampleBytesWritten += 5;
sampleSize += nalUnitLengthFieldLengthDiff;
} else {
int writtenBytes;
if (processSeiNalUnitPayload) {
// Read and write the payload of the SEI NAL unit.
nalBuffer.reset(sampleCurrentNalBytesRemaining);
input.readFully(nalBuffer.data, 0, sampleCurrentNalBytesRemaining);
output.sampleData(nalBuffer, sampleCurrentNalBytesRemaining);
writtenBytes = sampleCurrentNalBytesRemaining;
// Unescape and process the SEI NAL unit.
int unescapedLength = NalUnitUtil.unescapeStream(nalBuffer.data, nalBuffer.limit());
// If the format is H.265/HEVC the NAL unit header has two bytes so skip one more byte.
nalBuffer.setPosition(MimeTypes.VIDEO_H265.equals(track.format.sampleMimeType) ? 1 : 0);
nalBuffer.setLimit(unescapedLength);
CeaUtil.consume(fragment.getSamplePresentationTime(sampleIndex) * 1000L, nalBuffer, cea608TrackOutputs);
} else {
// Write the payload of the NAL unit.
writtenBytes = output.sampleData(input, sampleCurrentNalBytesRemaining, false);
}
sampleBytesWritten += writtenBytes;
sampleCurrentNalBytesRemaining -= writtenBytes;
}
}
} else {
while (sampleBytesWritten < sampleSize) {
int writtenBytes = output.sampleData(input, sampleSize - sampleBytesWritten, false);
sampleBytesWritten += writtenBytes;
}
}
long sampleTimeUs = fragment.getSamplePresentationTime(sampleIndex) * 1000L;
@C.BufferFlags int sampleFlags = (fragment.definesEncryptionData ? C.BUFFER_FLAG_ENCRYPTED : 0) | (fragment.sampleIsSyncFrameTable[sampleIndex] ? C.BUFFER_FLAG_KEY_FRAME : 0);
int sampleDescriptionIndex = fragment.header.sampleDescriptionIndex;
byte[] encryptionKey = null;
if (fragment.definesEncryptionData) {
encryptionKey = fragment.trackEncryptionBox != null ? fragment.trackEncryptionBox.keyId : track.sampleDescriptionEncryptionBoxes[sampleDescriptionIndex].keyId;
}
if (timestampAdjuster != null) {
sampleTimeUs = timestampAdjuster.adjustSampleTimestamp(sampleTimeUs);
}
output.sampleMetadata(sampleTimeUs, sampleFlags, sampleSize, 0, encryptionKey);
while (!pendingMetadataSampleInfos.isEmpty()) {
MetadataSampleInfo sampleInfo = pendingMetadataSampleInfos.removeFirst();
pendingMetadataSampleBytes -= sampleInfo.size;
eventMessageTrackOutput.sampleMetadata(sampleTimeUs + sampleInfo.presentationTimeDeltaUs, C.BUFFER_FLAG_KEY_FRAME, sampleInfo.size, pendingMetadataSampleBytes, null);
}
currentTrackBundle.currentSampleIndex++;
currentTrackBundle.currentSampleInTrackRun++;
if (currentTrackBundle.currentSampleInTrackRun == fragment.trunLength[currentTrackBundle.currentTrackRunIndex]) {
currentTrackBundle.currentTrackRunIndex++;
currentTrackBundle.currentSampleInTrackRun = 0;
currentTrackBundle = null;
}
parserState = STATE_READING_SAMPLE_START;
return true;
}
Aggregations