Search in sources :

Example 1 with FormatHolder

use of androidx.media3.exoplayer.FormatHolder in project media by androidx.

the class MediaCodecRenderer method feedInputBuffer.

/**
 * @return Whether it may be possible to feed more input data.
 * @throws ExoPlaybackException If an error occurs feeding the input buffer.
 */
private boolean feedInputBuffer() throws ExoPlaybackException {
    if (codec == null || codecDrainState == DRAIN_STATE_WAIT_END_OF_STREAM || inputStreamEnded) {
        return false;
    }
    if (codecDrainState == DRAIN_STATE_NONE && shouldReinitCodec()) {
        drainAndReinitializeCodec();
    }
    if (inputIndex < 0) {
        inputIndex = codec.dequeueInputBufferIndex();
        if (inputIndex < 0) {
            return false;
        }
        buffer.data = codec.getInputBuffer(inputIndex);
        buffer.clear();
    }
    if (codecDrainState == DRAIN_STATE_SIGNAL_END_OF_STREAM) {
        // that it outputs any remaining buffers before we release it.
        if (codecNeedsEosPropagation) {
        // Do nothing.
        } else {
            codecReceivedEos = true;
            codec.queueInputBuffer(inputIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
            resetInputBuffer();
        }
        codecDrainState = DRAIN_STATE_WAIT_END_OF_STREAM;
        return false;
    }
    if (codecNeedsAdaptationWorkaroundBuffer) {
        codecNeedsAdaptationWorkaroundBuffer = false;
        buffer.data.put(ADAPTATION_WORKAROUND_BUFFER);
        codec.queueInputBuffer(inputIndex, 0, ADAPTATION_WORKAROUND_BUFFER.length, 0, 0);
        resetInputBuffer();
        codecReceivedBuffers = true;
        return true;
    }
    // the start of the buffer that also contains the first frame in the new format.
    if (codecReconfigurationState == RECONFIGURATION_STATE_WRITE_PENDING) {
        for (int i = 0; i < codecInputFormat.initializationData.size(); i++) {
            byte[] data = codecInputFormat.initializationData.get(i);
            buffer.data.put(data);
        }
        codecReconfigurationState = RECONFIGURATION_STATE_QUEUE_PENDING;
    }
    int adaptiveReconfigurationBytes = buffer.data.position();
    FormatHolder formatHolder = getFormatHolder();
    @SampleStream.ReadDataResult int result;
    try {
        result = readSource(formatHolder, buffer, /* readFlags= */
        0);
    } catch (InsufficientCapacityException e) {
        onCodecError(e);
        // Skip the sample that's too large by reading it without its data. Then flush the codec so
        // that rendering will resume from the next key frame.
        readSourceOmittingSampleData(/* readFlags= */
        0);
        flushCodec();
        return true;
    }
    if (hasReadStreamToEnd()) {
        // Notify output queue of the last buffer's timestamp.
        lastBufferInStreamPresentationTimeUs = largestQueuedPresentationTimeUs;
    }
    if (result == C.RESULT_NOTHING_READ) {
        return false;
    }
    if (result == C.RESULT_FORMAT_READ) {
        if (codecReconfigurationState == RECONFIGURATION_STATE_QUEUE_PENDING) {
            // We received two formats in a row. Clear the current buffer of any reconfiguration data
            // associated with the first format.
            buffer.clear();
            codecReconfigurationState = RECONFIGURATION_STATE_WRITE_PENDING;
        }
        onInputFormatChanged(formatHolder);
        return true;
    }
    // We've read a buffer.
    if (buffer.isEndOfStream()) {
        if (codecReconfigurationState == RECONFIGURATION_STATE_QUEUE_PENDING) {
            // We received a new format immediately before the end of the stream. We need to clear
            // the corresponding reconfiguration data from the current buffer, but re-write it into
            // a subsequent buffer if there are any (for example, if the user seeks backwards).
            buffer.clear();
            codecReconfigurationState = RECONFIGURATION_STATE_WRITE_PENDING;
        }
        inputStreamEnded = true;
        if (!codecReceivedBuffers) {
            processEndOfStream();
            return false;
        }
        try {
            if (codecNeedsEosPropagation) {
            // Do nothing.
            } else {
                codecReceivedEos = true;
                codec.queueInputBuffer(inputIndex, /* offset= */
                0, /* size= */
                0, /* presentationTimeUs= */
                0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                resetInputBuffer();
            }
        } catch (CryptoException e) {
            throw createRendererException(e, inputFormat, Util.getErrorCodeForMediaDrmErrorCode(e.getErrorCode()));
        }
        return false;
    }
    // sample that's too large to be held in one of the decoder's input buffers.
    if (!codecReceivedBuffers && !buffer.isKeyFrame()) {
        buffer.clear();
        if (codecReconfigurationState == RECONFIGURATION_STATE_QUEUE_PENDING) {
            // The buffer we just cleared contained reconfiguration data. We need to re-write this data
            // into a subsequent buffer (if there is one).
            codecReconfigurationState = RECONFIGURATION_STATE_WRITE_PENDING;
        }
        return true;
    }
    boolean bufferEncrypted = buffer.isEncrypted();
    if (bufferEncrypted) {
        buffer.cryptoInfo.increaseClearDataFirstSubSampleBy(adaptiveReconfigurationBytes);
    }
    if (codecNeedsDiscardToSpsWorkaround && !bufferEncrypted) {
        NalUnitUtil.discardToSps(buffer.data);
        if (buffer.data.position() == 0) {
            return true;
        }
        codecNeedsDiscardToSpsWorkaround = false;
    }
    long presentationTimeUs = buffer.timeUs;
    if (c2Mp3TimestampTracker != null) {
        presentationTimeUs = c2Mp3TimestampTracker.updateAndGetPresentationTimeUs(inputFormat, buffer);
        // When draining the C2 MP3 decoder it produces an extra non-empty buffer with a timestamp
        // after all queued input buffer timestamps (unlike other decoders, which generally propagate
        // the input timestamps to output buffers 1:1). To detect the end of the stream when this
        // buffer is dequeued we override the largest queued timestamp accordingly.
        largestQueuedPresentationTimeUs = max(largestQueuedPresentationTimeUs, c2Mp3TimestampTracker.getLastOutputBufferPresentationTimeUs(inputFormat));
    }
    if (buffer.isDecodeOnly()) {
        decodeOnlyPresentationTimestamps.add(presentationTimeUs);
    }
    if (waitingForFirstSampleInFormat) {
        formatQueue.add(presentationTimeUs, inputFormat);
        waitingForFirstSampleInFormat = false;
    }
    largestQueuedPresentationTimeUs = max(largestQueuedPresentationTimeUs, presentationTimeUs);
    buffer.flip();
    if (buffer.hasSupplementalData()) {
        handleInputBufferSupplementalData(buffer);
    }
    onQueueInputBuffer(buffer);
    try {
        if (bufferEncrypted) {
            codec.queueSecureInputBuffer(inputIndex, /* offset= */
            0, buffer.cryptoInfo, presentationTimeUs, /* flags= */
            0);
        } else {
            codec.queueInputBuffer(inputIndex, /* offset= */
            0, buffer.data.limit(), presentationTimeUs, /* flags= */
            0);
        }
    } catch (CryptoException e) {
        throw createRendererException(e, inputFormat, Util.getErrorCodeForMediaDrmErrorCode(e.getErrorCode()));
    }
    resetInputBuffer();
    codecReceivedBuffers = true;
    codecReconfigurationState = RECONFIGURATION_STATE_NONE;
    decoderCounters.queuedInputBufferCount++;
    return true;
}
Also used : FormatHolder(androidx.media3.exoplayer.FormatHolder) ReadDataResult(androidx.media3.exoplayer.source.SampleStream.ReadDataResult) InsufficientCapacityException(androidx.media3.decoder.DecoderInputBuffer.InsufficientCapacityException) MediaCryptoException(android.media.MediaCryptoException) CryptoException(android.media.MediaCodec.CryptoException)

Example 2 with FormatHolder

use of androidx.media3.exoplayer.FormatHolder in project media by androidx.

the class MediaCodecRenderer method bypassRead.

private void bypassRead() throws ExoPlaybackException {
    checkState(!inputStreamEnded);
    FormatHolder formatHolder = getFormatHolder();
    bypassSampleBuffer.clear();
    while (true) {
        bypassSampleBuffer.clear();
        @ReadDataResult int result = readSource(formatHolder, bypassSampleBuffer, /* readFlags= */
        0);
        switch(result) {
            case C.RESULT_FORMAT_READ:
                onInputFormatChanged(formatHolder);
                return;
            case C.RESULT_NOTHING_READ:
                return;
            case C.RESULT_BUFFER_READ:
                if (bypassSampleBuffer.isEndOfStream()) {
                    inputStreamEnded = true;
                    return;
                }
                if (waitingForFirstSampleInFormat) {
                    // This is the first buffer in a new format, the output format must be updated.
                    outputFormat = checkNotNull(inputFormat);
                    onOutputFormatChanged(outputFormat, /* mediaFormat= */
                    null);
                    waitingForFirstSampleInFormat = false;
                }
                // Try to append the buffer to the batch buffer.
                bypassSampleBuffer.flip();
                if (!bypassBatchBuffer.append(bypassSampleBuffer)) {
                    bypassSampleBufferPending = true;
                    return;
                }
                break;
            default:
                throw new IllegalStateException();
        }
    }
}
Also used : ReadDataResult(androidx.media3.exoplayer.source.SampleStream.ReadDataResult) FormatHolder(androidx.media3.exoplayer.FormatHolder)

Example 3 with FormatHolder

use of androidx.media3.exoplayer.FormatHolder in project media by androidx.

the class MetadataRenderer method readMetadata.

private void readMetadata() {
    if (!inputStreamEnded && pendingMetadata == null) {
        buffer.clear();
        FormatHolder formatHolder = getFormatHolder();
        @ReadDataResult int result = readSource(formatHolder, buffer, /* readFlags= */
        0);
        if (result == C.RESULT_BUFFER_READ) {
            if (buffer.isEndOfStream()) {
                inputStreamEnded = true;
            } else {
                buffer.subsampleOffsetUs = subsampleOffsetUs;
                buffer.flip();
                @Nullable Metadata metadata = castNonNull(decoder).decode(buffer);
                if (metadata != null) {
                    List<Metadata.Entry> entries = new ArrayList<>(metadata.length());
                    decodeWrappedMetadata(metadata, entries);
                    if (!entries.isEmpty()) {
                        Metadata expandedMetadata = new Metadata(entries);
                        pendingMetadata = expandedMetadata;
                        pendingMetadataTimestampUs = buffer.timeUs;
                    }
                }
            }
        } else if (result == C.RESULT_FORMAT_READ) {
            subsampleOffsetUs = Assertions.checkNotNull(formatHolder.format).subsampleOffsetUs;
        }
    }
}
Also used : ReadDataResult(androidx.media3.exoplayer.source.SampleStream.ReadDataResult) FormatHolder(androidx.media3.exoplayer.FormatHolder) Metadata(androidx.media3.common.Metadata) ArrayList(java.util.ArrayList) Nullable(androidx.annotation.Nullable)

Example 4 with FormatHolder

use of androidx.media3.exoplayer.FormatHolder in project media by androidx.

the class EventSampleStreamTest method seekToUsThenUpdateStreamContinueToReadFromSeekPosition.

/**
 * Tests that {@link EventSampleStream#updateEventStream(EventStream, boolean)} will update the
 * underlying event stream, but keep the timestamp the stream has seek to, so the next {@link
 * EventSampleStream#readData(FormatHolder, DecoderInputBuffer, int)} call will return sample data
 * from the seek position.
 */
@Test
public void seekToUsThenUpdateStreamContinueToReadFromSeekPosition() {
    long presentationTimeUs1 = 1000000;
    long presentationTimeUs2 = 2000000;
    long presentationTimeUs3 = 3000000;
    EventMessage eventMessage1 = newEventMessageWithId(1);
    EventMessage eventMessage2 = newEventMessageWithId(2);
    EventMessage eventMessage3 = newEventMessageWithId(3);
    EventStream eventStream1 = new EventStream(SCHEME_ID, VALUE, TIME_SCALE, new long[] { presentationTimeUs1, presentationTimeUs2 }, new EventMessage[] { eventMessage1, eventMessage2 });
    EventStream eventStream2 = new EventStream(SCHEME_ID, VALUE, TIME_SCALE, new long[] { presentationTimeUs1, presentationTimeUs2, presentationTimeUs3 }, new EventMessage[] { eventMessage1, eventMessage2, eventMessage3 });
    EventSampleStream sampleStream = new EventSampleStream(eventStream1, FORMAT, true);
    // first read - read format
    readData(sampleStream);
    sampleStream.seekToUs(presentationTimeUs2);
    sampleStream.updateEventStream(eventStream2, true);
    int result = readData(sampleStream);
    assertThat(result).isEqualTo(C.RESULT_BUFFER_READ);
    assertThat(inputBuffer.data.array()).isEqualTo(getEncodedMessage(eventMessage2));
}
Also used : EventMessage(androidx.media3.extractor.metadata.emsg.EventMessage) EventStream(androidx.media3.exoplayer.dash.manifest.EventStream) Test(org.junit.Test)

Example 5 with FormatHolder

use of androidx.media3.exoplayer.FormatHolder in project media by androidx.

the class EventSampleStreamTest method setUp.

@Before
public void setUp() {
    formatHolder = new FormatHolder();
    inputBuffer = new MetadataInputBuffer();
    eventMessageEncoder = new EventMessageEncoder();
}
Also used : MetadataInputBuffer(androidx.media3.extractor.metadata.MetadataInputBuffer) FormatHolder(androidx.media3.exoplayer.FormatHolder) EventMessageEncoder(androidx.media3.extractor.metadata.emsg.EventMessageEncoder) Before(org.junit.Before)

Aggregations

FormatHolder (androidx.media3.exoplayer.FormatHolder)16 Test (org.junit.Test)13 ReadDataResult (androidx.media3.exoplayer.source.SampleStream.ReadDataResult)11 Format (androidx.media3.common.Format)9 EventStream (androidx.media3.exoplayer.dash.manifest.EventStream)9 EventMessage (androidx.media3.extractor.metadata.emsg.EventMessage)8 Nullable (androidx.annotation.Nullable)7 DecoderReuseEvaluation (androidx.media3.exoplayer.DecoderReuseEvaluation)5 DecoderInputBuffer (androidx.media3.decoder.DecoderInputBuffer)4 DrmSession (androidx.media3.exoplayer.drm.DrmSession)4 CallSuper (androidx.annotation.CallSuper)2 DecoderException (androidx.media3.decoder.DecoderException)2 ExoTrackSelection (androidx.media3.exoplayer.trackselection.ExoTrackSelection)2 FixedTrackSelection (androidx.media3.exoplayer.trackselection.FixedTrackSelection)2 Before (org.junit.Before)2 CryptoException (android.media.MediaCodec.CryptoException)1 MediaCryptoException (android.media.MediaCryptoException)1 MediaFormat (android.media.MediaFormat)1 DrmInitData (androidx.media3.common.DrmInitData)1 Metadata (androidx.media3.common.Metadata)1