Search in sources :

Example 6 with TrackOutput

use of androidx.media3.extractor.TrackOutput in project ExoPlayer by google.

the class FragmentedMp4Extractor method outputPendingMetadataSamples.

private void outputPendingMetadataSamples(long sampleTimeUs) {
    while (!pendingMetadataSampleInfos.isEmpty()) {
        MetadataSampleInfo sampleInfo = pendingMetadataSampleInfos.removeFirst();
        pendingMetadataSampleBytes -= sampleInfo.size;
        long metadataTimeUs = sampleTimeUs + sampleInfo.presentationTimeDeltaUs;
        if (timestampAdjuster != null) {
            metadataTimeUs = timestampAdjuster.adjustSampleTimestamp(metadataTimeUs);
        }
        for (TrackOutput emsgTrackOutput : emsgTrackOutputs) {
            emsgTrackOutput.sampleMetadata(metadataTimeUs, C.BUFFER_FLAG_KEY_FRAME, sampleInfo.size, pendingMetadataSampleBytes, null);
        }
    }
}
Also used : TrackOutput(com.google.android.exoplayer2.extractor.TrackOutput)

Example 7 with TrackOutput

use of androidx.media3.extractor.TrackOutput in project ExoPlayer by google.

the class FragmentedMp4Extractor method readSample.

/**
 * Attempts to read the next sample in the current mdat atom. The read sample may be output or
 * skipped.
 *
 * <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 partially read in the case that an exception is thrown. In
 * this case the method can be called again to read the remainder of the sample.
 *
 * @param input The {@link ExtractorInput} from which to read data.
 * @return Whether a sample was read. The read sample may have been output or skipped. False
 *     indicates that there are no samples left to read in the current mdat.
 * @throws IOException If an error occurs reading from the input.
 */
private boolean readSample(ExtractorInput input) throws IOException {
    @Nullable TrackBundle trackBundle = currentTrackBundle;
    if (trackBundle == null) {
        trackBundle = getNextTrackBundle(trackBundles);
        if (trackBundle == 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 ParserException.createForMalformedContainer("Offset to end of mdat was negative.", /* cause= */
                null);
            }
            input.skipFully(bytesToSkip);
            enterReadingAtomHeaderState();
            return false;
        }
        long nextDataPosition = trackBundle.getCurrentSampleOffset();
        // 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);
        currentTrackBundle = trackBundle;
    }
    if (parserState == STATE_READING_SAMPLE_START) {
        sampleSize = trackBundle.getCurrentSampleSize();
        if (trackBundle.currentSampleIndex < trackBundle.firstSampleToOutputIndex) {
            input.skipFully(sampleSize);
            trackBundle.skipSampleEncryptionData();
            if (!trackBundle.next()) {
                currentTrackBundle = null;
            }
            parserState = STATE_READING_SAMPLE_START;
            return true;
        }
        if (trackBundle.moovSampleTable.track.sampleTransformation == Track.TRANSFORMATION_CEA608_CDAT) {
            sampleSize -= Atom.HEADER_SIZE;
            input.skipFully(Atom.HEADER_SIZE);
        }
        if (MimeTypes.AUDIO_AC4.equals(trackBundle.moovSampleTable.track.format.sampleMimeType)) {
            // AC4 samples need to be prefixed with a clear sample header.
            sampleBytesWritten = trackBundle.outputSampleEncryptionData(sampleSize, Ac4Util.SAMPLE_HEADER_SIZE);
            Ac4Util.getAc4SampleHeader(sampleSize, scratch);
            trackBundle.output.sampleData(scratch, Ac4Util.SAMPLE_HEADER_SIZE);
            sampleBytesWritten += Ac4Util.SAMPLE_HEADER_SIZE;
        } else {
            sampleBytesWritten = trackBundle.outputSampleEncryptionData(sampleSize, /* clearHeaderSize= */
            0);
        }
        sampleSize += sampleBytesWritten;
        parserState = STATE_READING_SAMPLE_CONTINUE;
        sampleCurrentNalBytesRemaining = 0;
    }
    Track track = trackBundle.moovSampleTable.track;
    TrackOutput output = trackBundle.output;
    long sampleTimeUs = trackBundle.getCurrentSamplePresentationTimeUs();
    if (timestampAdjuster != null) {
        sampleTimeUs = timestampAdjuster.adjustSampleTimestamp(sampleTimeUs);
    }
    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.getData();
        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);
                int nalLengthInt = nalPrefix.readInt();
                if (nalLengthInt < 1) {
                    throw ParserException.createForMalformedContainer("Invalid NAL length", /* cause= */
                    null);
                }
                sampleCurrentNalBytesRemaining = nalLengthInt - 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 = ceaTrackOutputs.length > 0 && 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.getData(), 0, sampleCurrentNalBytesRemaining);
                    output.sampleData(nalBuffer, sampleCurrentNalBytesRemaining);
                    writtenBytes = sampleCurrentNalBytesRemaining;
                    // Unescape and process the SEI NAL unit.
                    int unescapedLength = NalUnitUtil.unescapeStream(nalBuffer.getData(), 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(sampleTimeUs, nalBuffer, ceaTrackOutputs);
                } 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;
        }
    }
    @C.BufferFlags int sampleFlags = trackBundle.getCurrentSampleFlags();
    // Encryption data.
    @Nullable TrackOutput.CryptoData cryptoData = null;
    @Nullable TrackEncryptionBox encryptionBox = trackBundle.getEncryptionBoxIfEncrypted();
    if (encryptionBox != null) {
        cryptoData = encryptionBox.cryptoData;
    }
    output.sampleMetadata(sampleTimeUs, sampleFlags, sampleSize, 0, cryptoData);
    // After we have the sampleTimeUs, we can commit all the pending metadata samples
    outputPendingMetadataSamples(sampleTimeUs);
    if (!trackBundle.next()) {
        currentTrackBundle = null;
    }
    parserState = STATE_READING_SAMPLE_START;
    return true;
}
Also used : Nullable(androidx.annotation.Nullable) TrackOutput(com.google.android.exoplayer2.extractor.TrackOutput)

Example 8 with TrackOutput

use of androidx.media3.extractor.TrackOutput in project ExoPlayer by google.

the class OggExtractor method read.

@Override
public int read(ExtractorInput input, PositionHolder seekPosition) throws IOException {
    // Check that init has been called.
    checkStateNotNull(output);
    if (streamReader == null) {
        if (!sniffInternal(input)) {
            throw ParserException.createForMalformedContainer("Failed to determine bitstream type", /* cause= */
            null);
        }
        input.resetPeekPosition();
    }
    if (!streamReaderInitialized) {
        TrackOutput trackOutput = output.track(0, C.TRACK_TYPE_AUDIO);
        output.endTracks();
        streamReader.init(output, trackOutput);
        streamReaderInitialized = true;
    }
    return streamReader.read(input, seekPosition);
}
Also used : TrackOutput(com.google.android.exoplayer2.extractor.TrackOutput)

Example 9 with TrackOutput

use of androidx.media3.extractor.TrackOutput in project ExoPlayer by google.

the class WebvttExtractor method buildTrackOutput.

@RequiresNonNull("output")
private TrackOutput buildTrackOutput(long subsampleOffsetUs) {
    TrackOutput trackOutput = output.track(0, C.TRACK_TYPE_TEXT);
    trackOutput.format(new Format.Builder().setSampleMimeType(MimeTypes.TEXT_VTT).setLanguage(language).setSubsampleOffsetUs(subsampleOffsetUs).build());
    output.endTracks();
    return trackOutput;
}
Also used : Format(com.google.android.exoplayer2.Format) TrackOutput(com.google.android.exoplayer2.extractor.TrackOutput) RequiresNonNull(org.checkerframework.checker.nullness.qual.RequiresNonNull)

Example 10 with TrackOutput

use of androidx.media3.extractor.TrackOutput in project Telegram-FOSS by Telegram-FOSS-Team.

the class SingleSampleMediaChunk method load.

@SuppressWarnings("NonAtomicVolatileUpdate")
@Override
public void load() throws IOException, InterruptedException {
    BaseMediaChunkOutput output = getOutput();
    output.setSampleOffsetUs(0);
    TrackOutput trackOutput = output.track(0, trackType);
    trackOutput.format(sampleFormat);
    try {
        // Create and open the input.
        DataSpec loadDataSpec = dataSpec.subrange(nextLoadPosition);
        long length = dataSource.open(loadDataSpec);
        if (length != C.LENGTH_UNSET) {
            length += nextLoadPosition;
        }
        ExtractorInput extractorInput = new DefaultExtractorInput(dataSource, nextLoadPosition, length);
        // Load the sample data.
        int result = 0;
        while (result != C.RESULT_END_OF_INPUT) {
            nextLoadPosition += result;
            result = trackOutput.sampleData(extractorInput, Integer.MAX_VALUE, true);
        }
        int sampleSize = (int) nextLoadPosition;
        trackOutput.sampleMetadata(startTimeUs, C.BUFFER_FLAG_KEY_FRAME, sampleSize, 0, null);
    } finally {
        Util.closeQuietly(dataSource);
    }
    loadCompleted = true;
}
Also used : ExtractorInput(com.google.android.exoplayer2.extractor.ExtractorInput) DefaultExtractorInput(com.google.android.exoplayer2.extractor.DefaultExtractorInput) DataSpec(com.google.android.exoplayer2.upstream.DataSpec) DefaultExtractorInput(com.google.android.exoplayer2.extractor.DefaultExtractorInput) TrackOutput(com.google.android.exoplayer2.extractor.TrackOutput)

Aggregations

FakeTrackOutput (androidx.media3.test.utils.FakeTrackOutput)70 Test (org.junit.Test)61 SeekMap (androidx.media3.extractor.SeekMap)60 Uri (android.net.Uri)50 TrackOutput (com.google.android.exoplayer2.extractor.TrackOutput)38 FakeExtractorOutput (androidx.media3.test.utils.FakeExtractorOutput)31 TrackOutput (androidx.media3.extractor.TrackOutput)19 Nullable (androidx.annotation.Nullable)13 Format (androidx.media3.common.Format)11 RequiresNonNull (org.checkerframework.checker.nullness.qual.RequiresNonNull)11 Format (com.google.android.exoplayer2.Format)8 FakeExtractorInput (androidx.media3.test.utils.FakeExtractorInput)5 ParserException (com.google.android.exoplayer2.ParserException)5 ParsableByteArray (com.google.android.exoplayer2.util.ParsableByteArray)5 ParsableByteArray (androidx.media3.common.util.ParsableByteArray)4 SeekPoint (androidx.media3.extractor.SeekPoint)4 Metadata (androidx.media3.common.Metadata)3 Cue (androidx.media3.common.text.Cue)3 DefaultExtractorInput (androidx.media3.extractor.DefaultExtractorInput)3 ExtractorInput (androidx.media3.extractor.ExtractorInput)3