Search in sources :

Example 6 with ExtractorInput

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);
}
Also used : ExtractorInput(com.google.android.exoplayer2.extractor.ExtractorInput) FakeExtractorInput(com.google.android.exoplayer2.testutil.FakeExtractorInput)

Example 7 with ExtractorInput

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);
}
Also used : FakeExtractorInput(com.google.android.exoplayer2.testutil.FakeExtractorInput) ExtractorInput(com.google.android.exoplayer2.extractor.ExtractorInput) FakeExtractorInput(com.google.android.exoplayer2.testutil.FakeExtractorInput) SimulatedIOException(com.google.android.exoplayer2.testutil.FakeExtractorInput.SimulatedIOException)

Example 8 with ExtractorInput

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);
}
Also used : FakeExtractorInput(com.google.android.exoplayer2.testutil.FakeExtractorInput) ExtractorInput(com.google.android.exoplayer2.extractor.ExtractorInput) FakeExtractorInput(com.google.android.exoplayer2.testutil.FakeExtractorInput)

Example 9 with ExtractorInput

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;
}
Also used : ParsableByteArray(com.google.android.exoplayer2.util.ParsableByteArray) ParserException(com.google.android.exoplayer2.ParserException) ContainerAtom(com.google.android.exoplayer2.extractor.mp4.Atom.ContainerAtom) SeekMap(com.google.android.exoplayer2.extractor.SeekMap)

Example 10 with ExtractorInput

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;
}
Also used : ParserException(com.google.android.exoplayer2.ParserException) TrackOutput(com.google.android.exoplayer2.extractor.TrackOutput)

Aggregations

ExtractorInput (com.google.android.exoplayer2.extractor.ExtractorInput)20 FakeExtractorInput (com.google.android.exoplayer2.testutil.FakeExtractorInput)19 ParsableByteArray (com.google.android.exoplayer2.util.ParsableByteArray)12 ParserException (com.google.android.exoplayer2.ParserException)8 DefaultExtractorInput (com.google.android.exoplayer2.extractor.DefaultExtractorInput)5 DataSpec (com.google.android.exoplayer2.upstream.DataSpec)5 TrackOutput (com.google.android.exoplayer2.extractor.TrackOutput)4 SeekMap (com.google.android.exoplayer2.extractor.SeekMap)3 Format (com.google.android.exoplayer2.Format)2 Extractor (com.google.android.exoplayer2.extractor.Extractor)2 ContainerAtom (com.google.android.exoplayer2.extractor.mp4.Atom.ContainerAtom)2 EOFException (java.io.EOFException)2 LeafAtom (com.google.android.exoplayer2.extractor.mp4.Atom.LeafAtom)1 VorbisSetup (com.google.android.exoplayer2.extractor.ogg.VorbisReader.VorbisSetup)1 TrackIdGenerator (com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator)1 Metadata (com.google.android.exoplayer2.metadata.Metadata)1 PrivFrame (com.google.android.exoplayer2.metadata.id3.PrivFrame)1 SimulatedIOException (com.google.android.exoplayer2.testutil.FakeExtractorInput.SimulatedIOException)1 FlacStreamInfo (com.google.android.exoplayer2.util.FlacStreamInfo)1 ParsableBitArray (com.google.android.exoplayer2.util.ParsableBitArray)1