diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 3783c6c63d..8ef7be083d 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -102,6 +102,9 @@ ([#5698](https://github.com/google/ExoPlayer/issues/5698), [#5694](https://github.com/google/ExoPlayer/issues/5694)). * Move `PriorityTaskManager` from `DefaultLoadControl` to `SimpleExoPlayer`. +* Remove `MediaCodecSelector.DEFAULT_WITH_FALLBACK`. Apps should instead signal + that fallback should be used by passing `true` as the `enableDecoderFallback` + parameter when instantiating the video renderer. ### 2.9.6 ### diff --git a/library/core/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java index 3d46496eed..0982bc8862 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java @@ -250,6 +250,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys, + /* enableDecoderFallback= */ false, /* assumedMinimumCodecOperatingRate= */ 44100); this.context = context.getApplicationContext(); this.audioSink = audioSink; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java index 3040fc74bb..bbf1134b7f 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java @@ -53,6 +53,7 @@ import java.lang.annotation.RetentionPolicy; import java.nio.ByteBuffer; import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @@ -289,6 +290,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { private final MediaCodecSelector mediaCodecSelector; @Nullable private final DrmSessionManager drmSessionManager; private final boolean playClearSamplesWithoutKeys; + private final boolean enableDecoderFallback; private final float assumedMinimumCodecOperatingRate; private final DecoderInputBuffer buffer; private final DecoderInputBuffer flagsOnlyBuffer; @@ -354,6 +356,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer { * begin in parallel with key acquisition. This parameter specifies whether the renderer is * permitted to play clear regions of encrypted media files before {@code drmSessionManager} * has obtained the keys necessary to decrypt encrypted regions of the media. + * @param enableDecoderFallback Whether to enable fallback to lower-priority decoders if decoder + * initialization fails. This may result in using a decoder that is less efficient or slower + * than the primary decoder. * @param assumedMinimumCodecOperatingRate A codec operating rate that all codecs instantiated by * this renderer are assumed to meet implicitly (i.e. without the operating rate being set * explicitly using {@link MediaFormat#KEY_OPERATING_RATE}). @@ -363,11 +368,13 @@ public abstract class MediaCodecRenderer extends BaseRenderer { MediaCodecSelector mediaCodecSelector, @Nullable DrmSessionManager drmSessionManager, boolean playClearSamplesWithoutKeys, + boolean enableDecoderFallback, float assumedMinimumCodecOperatingRate) { super(trackType); this.mediaCodecSelector = Assertions.checkNotNull(mediaCodecSelector); this.drmSessionManager = drmSessionManager; this.playClearSamplesWithoutKeys = playClearSamplesWithoutKeys; + this.enableDecoderFallback = enableDecoderFallback; this.assumedMinimumCodecOperatingRate = assumedMinimumCodecOperatingRate; buffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DISABLED); flagsOnlyBuffer = DecoderInputBuffer.newFlagsOnlyInstance(); @@ -735,8 +742,14 @@ public abstract class MediaCodecRenderer extends BaseRenderer { throws DecoderInitializationException { if (availableCodecInfos == null) { try { - availableCodecInfos = - new ArrayDeque<>(getAvailableCodecInfos(mediaCryptoRequiresSecureDecoder)); + List allAvailableCodecInfos = + getAvailableCodecInfos(mediaCryptoRequiresSecureDecoder); + if (enableDecoderFallback) { + availableCodecInfos = new ArrayDeque<>(allAvailableCodecInfos); + } else { + availableCodecInfos = + new ArrayDeque<>(Collections.singletonList(allAvailableCodecInfos.get(0))); + } preferredDecoderInitializationException = null; } catch (DecoderQueryException e) { throw new DecoderInitializationException( diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecSelector.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecSelector.java index 496d015b58..a5cbb77342 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecSelector.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecSelector.java @@ -18,7 +18,6 @@ package com.google.android.exoplayer2.mediacodec; import android.media.MediaCodec; import androidx.annotation.Nullable; import com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryException; -import java.util.Collections; import java.util.List; /** @@ -31,32 +30,6 @@ public interface MediaCodecSelector { * the given format. */ MediaCodecSelector DEFAULT = - new MediaCodecSelector() { - @Override - public List getDecoderInfos(String mimeType, boolean requiresSecureDecoder) - throws DecoderQueryException { - List decoderInfos = - MediaCodecUtil.getDecoderInfos(mimeType, requiresSecureDecoder); - return decoderInfos.isEmpty() - ? Collections.emptyList() - : Collections.singletonList(decoderInfos.get(0)); - } - - @Override - public @Nullable MediaCodecInfo getPassthroughDecoderInfo() throws DecoderQueryException { - return MediaCodecUtil.getPassthroughDecoderInfo(); - } - }; - - /** - * A {@link MediaCodecSelector} that returns a list of decoders in priority order, allowing - * fallback to less preferred decoders if initialization fails. - * - *

Note: if a hardware-accelerated video decoder fails to initialize, this selector may provide - * a software video decoder to use as a fallback. Using software decoding can be inefficient, and - * the decoder may be too slow to keep up with the playback position. - */ - MediaCodecSelector DEFAULT_WITH_FALLBACK = new MediaCodecSelector() { @Override public List getDecoderInfos(String mimeType, boolean requiresSecureDecoder) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java index 9c16fa6380..f26bc29aec 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java @@ -210,16 +210,64 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { * @param maxDroppedFramesToNotify The maximum number of frames that can be dropped between * invocations of {@link VideoRendererEventListener#onDroppedFrames(int, long)}. */ - public MediaCodecVideoRenderer(Context context, MediaCodecSelector mediaCodecSelector, + public MediaCodecVideoRenderer( + Context context, + MediaCodecSelector mediaCodecSelector, long allowedJoiningTimeMs, @Nullable DrmSessionManager drmSessionManager, - boolean playClearSamplesWithoutKeys, @Nullable Handler eventHandler, - @Nullable VideoRendererEventListener eventListener, int maxDroppedFramesToNotify) { + boolean playClearSamplesWithoutKeys, + @Nullable Handler eventHandler, + @Nullable VideoRendererEventListener eventListener, + int maxDroppedFramesToNotify) { + this( + context, + mediaCodecSelector, + allowedJoiningTimeMs, + drmSessionManager, + playClearSamplesWithoutKeys, + /* enableDecoderFallback= */ false, + eventHandler, + eventListener, + maxDroppedFramesToNotify); + } + + /** + * @param context A context. + * @param mediaCodecSelector A decoder selector. + * @param allowedJoiningTimeMs The maximum duration in milliseconds for which this video renderer + * can attempt to seamlessly join an ongoing playback. + * @param drmSessionManager For use with encrypted content. May be null if support for encrypted + * content is not required. + * @param playClearSamplesWithoutKeys Encrypted media may contain clear (un-encrypted) regions. + * For example a media file may start with a short clear region so as to allow playback to + * begin in parallel with key acquisition. This parameter specifies whether the renderer is + * permitted to play clear regions of encrypted media files before {@code drmSessionManager} + * has obtained the keys necessary to decrypt encrypted regions of the media. + * @param enableDecoderFallback Whether to enable fallback to lower-priority decoders if decoder + * initialization fails. This may result in using a decoder that is slower/less efficient than + * the primary decoder. + * @param eventHandler A handler to use when delivering events to {@code eventListener}. May be + * null if delivery of events is not required. + * @param eventListener A listener of events. May be null if delivery of events is not required. + * @param maxDroppedFramesToNotify The maximum number of frames that can be dropped between + * invocations of {@link VideoRendererEventListener#onDroppedFrames(int, long)}. + */ + public MediaCodecVideoRenderer( + Context context, + MediaCodecSelector mediaCodecSelector, + long allowedJoiningTimeMs, + @Nullable DrmSessionManager drmSessionManager, + boolean playClearSamplesWithoutKeys, + boolean enableDecoderFallback, + @Nullable Handler eventHandler, + @Nullable VideoRendererEventListener eventListener, + int maxDroppedFramesToNotify) { super( C.TRACK_TYPE_VIDEO, mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys, + enableDecoderFallback, /* assumedMinimumCodecOperatingRate= */ 30); this.allowedJoiningTimeMs = allowedJoiningTimeMs; this.maxDroppedFramesToNotify = maxDroppedFramesToNotify;