diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java index 7dbf06c86d..6c28504521 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java @@ -112,7 +112,7 @@ public class DefaultRenderersFactory implements RenderersFactory { */ public DefaultRenderersFactory(Context context) { this.context = context; - codecAdapterFactory = new DefaultMediaCodecAdapterFactory(); + codecAdapterFactory = new DefaultMediaCodecAdapterFactory(context); extensionRendererMode = EXTENSION_RENDERER_MODE_OFF; allowedVideoJoiningTimeMs = DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS; mediaCodecSelector = MediaCodecSelector.DEFAULT; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/MediaCodecAudioRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/MediaCodecAudioRenderer.java index 0c0765ea49..ddf7164cd1 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/MediaCodecAudioRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/MediaCodecAudioRenderer.java @@ -191,7 +191,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media AudioSink audioSink) { this( context, - MediaCodecAdapter.Factory.DEFAULT, + MediaCodecAdapter.Factory.getDefault(context), mediaCodecSelector, /* enableDecoderFallback= */ false, eventHandler, @@ -219,7 +219,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media AudioSink audioSink) { this( context, - MediaCodecAdapter.Factory.DEFAULT, + MediaCodecAdapter.Factory.getDefault(context), mediaCodecSelector, enableDecoderFallback, eventHandler, diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/DefaultMediaCodecAdapterFactory.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/DefaultMediaCodecAdapterFactory.java index 48ffcc87a0..6699c424cc 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/DefaultMediaCodecAdapterFactory.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/DefaultMediaCodecAdapterFactory.java @@ -17,8 +17,10 @@ package androidx.media3.exoplayer.mediacodec; import static java.lang.annotation.ElementType.TYPE_USE; +import android.content.Context; import android.media.MediaCodec; import androidx.annotation.IntDef; +import androidx.annotation.Nullable; import androidx.media3.common.MimeTypes; import androidx.media3.common.util.Log; import androidx.media3.common.util.UnstableApi; @@ -54,12 +56,30 @@ public final class DefaultMediaCodecAdapterFactory implements MediaCodecAdapter. private static final String TAG = "DMCodecAdapterFactory"; + @Nullable private final Context context; + private @Mode int asynchronousMode; private boolean asyncCryptoFlagEnabled; + /** + * @deprecated Use {@link #DefaultMediaCodecAdapterFactory(Context)} instead. + */ + @Deprecated public DefaultMediaCodecAdapterFactory() { asynchronousMode = MODE_DEFAULT; asyncCryptoFlagEnabled = true; + context = null; + } + + /** + * Creates the default media codec adapter factory. + * + * @param context A {@link Context}. + */ + public DefaultMediaCodecAdapterFactory(Context context) { + this.context = context; + asynchronousMode = MODE_DEFAULT; + asyncCryptoFlagEnabled = true; } /** @@ -105,7 +125,7 @@ public final class DefaultMediaCodecAdapterFactory implements MediaCodecAdapter. throws IOException { if (Util.SDK_INT >= 23 && (asynchronousMode == MODE_ENABLED - || (asynchronousMode == MODE_DEFAULT && Util.SDK_INT >= 31))) { + || (asynchronousMode == MODE_DEFAULT && shouldUseAsynchronousAdapterInDefaultMode()))) { int trackType = MimeTypes.getTrackType(configuration.format.sampleMimeType); Log.i( TAG, @@ -118,4 +138,19 @@ public final class DefaultMediaCodecAdapterFactory implements MediaCodecAdapter. } return new SynchronousMediaCodecAdapter.Factory().createAdapter(configuration); } + + private boolean shouldUseAsynchronousAdapterInDefaultMode() { + if (Util.SDK_INT >= 31) { + // Asynchronous codec interactions started to be reliable for all devices on API 31+. + return true; + } + // Allow additional devices that work reliably with the asynchronous adapter and show + // performance problems when not using it. + if (context != null + && Util.SDK_INT >= 28 + && context.getPackageManager().hasSystemFeature("com.amazon.hardware.tv_screen")) { + return true; + } + return false; + } } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecAdapter.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecAdapter.java index 5eb6848320..1adc7b19f5 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecAdapter.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecAdapter.java @@ -15,6 +15,7 @@ */ package androidx.media3.exoplayer.mediacodec; +import android.content.Context; import android.media.MediaCodec; import android.media.MediaCrypto; import android.media.MediaFormat; @@ -120,9 +121,23 @@ public interface MediaCodecAdapter { /** A factory for {@link MediaCodecAdapter} instances. */ interface Factory { - /** Default factory used in most cases. */ + /** + * @deprecated Use {@link #getDefault} instead. + */ + @Deprecated + @SuppressWarnings("deprecation") // Forwarding to deprecated method. Factory DEFAULT = new DefaultMediaCodecAdapterFactory(); + /** + * Returns the default factory that should be used in most cases. + * + * @param context A {@link Context}. + * @return The default factory. + */ + static Factory getDefault(Context context) { + return new DefaultMediaCodecAdapterFactory(context); + } + /** Creates a {@link MediaCodecAdapter} instance. */ MediaCodecAdapter createAdapter(Configuration configuration) throws IOException; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java index 518954d123..1e5d13909e 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java @@ -222,7 +222,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer int maxDroppedFramesToNotify) { this( context, - MediaCodecAdapter.Factory.DEFAULT, + MediaCodecAdapter.Factory.getDefault(context), mediaCodecSelector, allowedJoiningTimeMs, /* enableDecoderFallback= */ false, @@ -256,7 +256,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer int maxDroppedFramesToNotify) { this( context, - MediaCodecAdapter.Factory.DEFAULT, + MediaCodecAdapter.Factory.getDefault(context), mediaCodecSelector, allowedJoiningTimeMs, enableDecoderFallback, diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/mediacodec/MediaCodecRendererTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/mediacodec/MediaCodecRendererTest.java index 157bbd7bbc..45c0f177bc 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/mediacodec/MediaCodecRendererTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/mediacodec/MediaCodecRendererTest.java @@ -44,6 +44,7 @@ import androidx.media3.exoplayer.drm.DrmSessionManager; import androidx.media3.exoplayer.source.MediaSource; import androidx.media3.exoplayer.upstream.DefaultAllocator; import androidx.media3.test.utils.FakeSampleStream; +import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.common.collect.ImmutableList; import java.nio.ByteBuffer; @@ -480,7 +481,7 @@ public class MediaCodecRendererTest { public TestRenderer() { super( C.TRACK_TYPE_AUDIO, - MediaCodecAdapter.Factory.DEFAULT, + MediaCodecAdapter.Factory.getDefault(ApplicationProvider.getApplicationContext()), /* mediaCodecSelector= */ (mimeType, requiresSecureDecoder, requiresTunnelingDecoder) -> Collections.singletonList( MediaCodecInfo.newInstance( diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/CapturingRenderersFactory.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/CapturingRenderersFactory.java index d11aa70432..515a94974b 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/CapturingRenderersFactory.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/CapturingRenderersFactory.java @@ -81,7 +81,7 @@ public class CapturingRenderersFactory implements RenderersFactory, Dumper.Dumpa */ public CapturingRenderersFactory(Context context) { this.context = context; - this.mediaCodecAdapterFactory = new CapturingMediaCodecAdapter.Factory(); + this.mediaCodecAdapterFactory = new CapturingMediaCodecAdapter.Factory(context); this.audioSink = new CapturingAudioSink(new DefaultAudioSink.Builder(context).build()); this.imageOutput = new CapturingImageOutput(); this.imageDecoderFactory = ImageDecoder.Factory.DEFAULT; @@ -168,9 +168,11 @@ public class CapturingRenderersFactory implements RenderersFactory, Dumper.Dumpa private static class Factory implements MediaCodecAdapter.Factory, Dumper.Dumpable { + private final Context context; private final List constructedAdapters; - private Factory() { + private Factory(Context context) { + this.context = context; constructedAdapters = new ArrayList<>(); } @@ -178,7 +180,7 @@ public class CapturingRenderersFactory implements RenderersFactory, Dumper.Dumpa public MediaCodecAdapter createAdapter(Configuration configuration) throws IOException { CapturingMediaCodecAdapter adapter = new CapturingMediaCodecAdapter( - MediaCodecAdapter.Factory.DEFAULT.createAdapter(configuration), + MediaCodecAdapter.Factory.getDefault(context).createAdapter(configuration), configuration.codecInfo.name); constructedAdapters.add(adapter); return adapter;