Search in sources :

Example 1 with ExoPlaybackException

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

the class MediaCodecRenderer method drmNeedsCodecReinitialization.

/**
 * Returns whether it's necessary to re-initialize the codec to handle a DRM change. If {@code
 * false} is returned then either {@code oldSession == newSession} (i.e., there was no change), or
 * it's possible to update the existing codec using MediaCrypto.setMediaDrmSession.
 */
private boolean drmNeedsCodecReinitialization(MediaCodecInfo codecInfo, Format newFormat, @Nullable DrmSession oldSession, @Nullable DrmSession newSession) throws ExoPlaybackException {
    if (oldSession == newSession) {
        // No need to re-initialize if the old and new sessions are the same.
        return false;
    }
    if (newSession == null || oldSession == null) {
        // Changing from DRM to no DRM and vice-versa always requires re-initialization.
        return true;
    }
    if (Util.SDK_INT < 23) {
        // required to switch to newSession on older API levels.
        return true;
    }
    if (C.PLAYREADY_UUID.equals(oldSession.getSchemeUuid()) || C.PLAYREADY_UUID.equals(newSession.getSchemeUuid())) {
        // TODO: Add an API check once [Internal ref: b/128835874] is fixed.
        return true;
    }
    @Nullable FrameworkCryptoConfig newCryptoConfig = getFrameworkCryptoConfig(newSession);
    if (newCryptoConfig == null) {
        // the case is to occur, so we re-initialize in this case.
        return true;
    }
    boolean requiresSecureDecoder;
    if (newCryptoConfig.forceAllowInsecureDecoderComponents) {
        requiresSecureDecoder = false;
    } else {
        requiresSecureDecoder = newSession.requiresSecureDecoder(newFormat.sampleMimeType);
    }
    if (!codecInfo.secure && requiresSecureDecoder) {
        // output path.
        return true;
    }
    return false;
}
Also used : FrameworkCryptoConfig(androidx.media3.exoplayer.drm.FrameworkCryptoConfig) Nullable(androidx.annotation.Nullable)

Example 2 with ExoPlaybackException

use of androidx.media3.exoplayer.ExoPlaybackException 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 3 with ExoPlaybackException

use of androidx.media3.exoplayer.ExoPlaybackException 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 4 with ExoPlaybackException

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

the class DefaultTrackSelector method selectOtherTrack.

// Generic track selection methods.
/**
 * Called by {@link #selectAllTracks(MappedTrackInfo, int[][][], int[], Parameters)} to create a
 * {@link ExoTrackSelection} for a renderer whose type is neither video, audio or text.
 *
 * @param trackType The type of the renderer.
 * @param groups The {@link TrackGroupArray} mapped to the renderer.
 * @param formatSupport The {@link Capabilities} for each mapped track, indexed by track group and
 *     track (in that order).
 * @param params The selector's current constraint parameters.
 * @return The {@link ExoTrackSelection} for the renderer, or null if no selection was made.
 * @throws ExoPlaybackException If an error occurs while selecting the tracks.
 */
@Nullable
protected ExoTrackSelection.Definition selectOtherTrack(int trackType, TrackGroupArray groups, @Capabilities int[][] formatSupport, Parameters params) throws ExoPlaybackException {
    @Nullable TrackGroup selectedGroup = null;
    int selectedTrackIndex = 0;
    @Nullable OtherTrackScore selectedTrackScore = null;
    for (int groupIndex = 0; groupIndex < groups.length; groupIndex++) {
        TrackGroup trackGroup = groups.get(groupIndex);
        @Capabilities int[] trackFormatSupport = formatSupport[groupIndex];
        for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
            if (isSupported(trackFormatSupport[trackIndex], params.exceedRendererCapabilitiesIfNecessary)) {
                Format format = trackGroup.getFormat(trackIndex);
                OtherTrackScore trackScore = new OtherTrackScore(format, trackFormatSupport[trackIndex]);
                if (selectedTrackScore == null || trackScore.compareTo(selectedTrackScore) > 0) {
                    selectedGroup = trackGroup;
                    selectedTrackIndex = trackIndex;
                    selectedTrackScore = trackScore;
                }
            }
        }
    }
    return selectedGroup == null ? null : new ExoTrackSelection.Definition(selectedGroup, selectedTrackIndex);
}
Also used : Format(androidx.media3.common.Format) TrackGroup(androidx.media3.common.TrackGroup) RendererCapabilities(androidx.media3.exoplayer.RendererCapabilities) Capabilities(androidx.media3.exoplayer.RendererCapabilities.Capabilities) Nullable(androidx.annotation.Nullable) SuppressLint(android.annotation.SuppressLint) Point(android.graphics.Point) Nullable(androidx.annotation.Nullable)

Example 5 with ExoPlaybackException

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

the class ExoPlayerTest method mediaPeriodMaybeThrowPrepareError_isNotThrownUntilPlaybackReachedFailingItem.

@Test
public void mediaPeriodMaybeThrowPrepareError_isNotThrownUntilPlaybackReachedFailingItem() throws Exception {
    ExoPlayer player = new TestExoPlayerBuilder(context).build();
    Timeline timeline = new FakeTimeline();
    player.addMediaSource(new FakeMediaSource(timeline, ExoPlayerTestRunner.VIDEO_FORMAT));
    player.addMediaSource(new FakeMediaSource(timeline, ExoPlayerTestRunner.VIDEO_FORMAT) {

        @Override
        protected MediaPeriod createMediaPeriod(MediaPeriodId id, TrackGroupArray trackGroupArray, Allocator allocator, MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher, DrmSessionManager drmSessionManager, DrmSessionEventListener.EventDispatcher drmEventDispatcher, @Nullable TransferListener transferListener) {
            return new FakeMediaPeriod(trackGroupArray, allocator, /* singleSampleTimeUs= */
            0, mediaSourceEventDispatcher, DrmSessionManager.DRM_UNSUPPORTED, drmEventDispatcher, /* deferOnPrepared= */
            true) {

                @Override
                public void maybeThrowPrepareError() throws IOException {
                    throw new IOException();
                }
            };
        }
    });
    player.prepare();
    player.play();
    ExoPlaybackException error = TestPlayerRunHelper.runUntilError(player);
    Object period1Uid = player.getCurrentTimeline().getPeriod(/* periodIndex= */
    1, new Timeline.Period(), /* setIds= */
    true).uid;
    assertThat(error.mediaPeriodId.periodUid).isEqualTo(period1Uid);
    assertThat(player.getCurrentMediaItemIndex()).isEqualTo(1);
}
Also used : TransferListener(androidx.media3.datasource.TransferListener) Allocator(androidx.media3.exoplayer.upstream.Allocator) FakeMediaSource(androidx.media3.test.utils.FakeMediaSource) DrmSessionManager(androidx.media3.exoplayer.drm.DrmSessionManager) TrackGroupArray(androidx.media3.common.TrackGroupArray) FakeMediaPeriod(androidx.media3.test.utils.FakeMediaPeriod) MediaPeriod(androidx.media3.exoplayer.source.MediaPeriod) IOException(java.io.IOException) DrmSessionEventListener(androidx.media3.exoplayer.drm.DrmSessionEventListener) FakeMediaPeriod(androidx.media3.test.utils.FakeMediaPeriod) Timeline(androidx.media3.common.Timeline) NoUidTimeline(androidx.media3.test.utils.NoUidTimeline) FakeTimeline(androidx.media3.test.utils.FakeTimeline) SinglePeriodTimeline(androidx.media3.exoplayer.source.SinglePeriodTimeline) MediaSourceEventListener(androidx.media3.exoplayer.source.MediaSourceEventListener) FakeTimeline(androidx.media3.test.utils.FakeTimeline) MediaPeriodId(androidx.media3.exoplayer.source.MediaSource.MediaPeriodId) FakeMediaPeriod(androidx.media3.test.utils.FakeMediaPeriod) MediaPeriod(androidx.media3.exoplayer.source.MediaPeriod) TestExoPlayerBuilder(androidx.media3.test.utils.TestExoPlayerBuilder) Test(org.junit.Test)

Aggregations

Test (org.junit.Test)50 Nullable (androidx.annotation.Nullable)24 FakeMediaClockRenderer (androidx.media3.test.utils.FakeMediaClockRenderer)24 Format (androidx.media3.common.Format)23 TrackGroupArray (androidx.media3.common.TrackGroupArray)19 Timeline (androidx.media3.common.Timeline)18 IOException (java.io.IOException)16 TestExoPlayerBuilder (androidx.media3.test.utils.TestExoPlayerBuilder)15 FakeMediaSource (androidx.media3.test.utils.FakeMediaSource)14 MediaPeriodId (androidx.media3.exoplayer.source.MediaSource.MediaPeriodId)13 ActionSchedule (androidx.media3.test.utils.ActionSchedule)13 ExoPlayerTestRunner (androidx.media3.test.utils.ExoPlayerTestRunner)13 FakeTimeline (androidx.media3.test.utils.FakeTimeline)13 TrackGroup (androidx.media3.common.TrackGroup)12 RendererCapabilities (androidx.media3.exoplayer.RendererCapabilities)11 Surface (android.view.Surface)9 TracksInfo (androidx.media3.common.TracksInfo)9 SinglePeriodTimeline (androidx.media3.exoplayer.source.SinglePeriodTimeline)9 PlaybackException (androidx.media3.common.PlaybackException)8 Player (androidx.media3.common.Player)8