Add device-specific opt-ins for async MediaCodecAdapter

Some devices just don't work very well with the synchronous
model, but are currently still excluded from our approximate
API 31 check. This change allows to include additional devices
or device groups by passing in the Context to the default
adapter.

It also adopts the workaround added in ebceee08c6
for Fire TV Smart devices that exhibit this issue.

PiperOrigin-RevId: 614642545
This commit is contained in:
tonihei 2024-03-11 06:27:15 -07:00 committed by Copybara-Service
parent 410c0492cc
commit e4a55844d0
7 changed files with 64 additions and 11 deletions

View File

@ -112,7 +112,7 @@ public class DefaultRenderersFactory implements RenderersFactory {
*/ */
public DefaultRenderersFactory(Context context) { public DefaultRenderersFactory(Context context) {
this.context = context; this.context = context;
codecAdapterFactory = new DefaultMediaCodecAdapterFactory(); codecAdapterFactory = new DefaultMediaCodecAdapterFactory(context);
extensionRendererMode = EXTENSION_RENDERER_MODE_OFF; extensionRendererMode = EXTENSION_RENDERER_MODE_OFF;
allowedVideoJoiningTimeMs = DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS; allowedVideoJoiningTimeMs = DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS;
mediaCodecSelector = MediaCodecSelector.DEFAULT; mediaCodecSelector = MediaCodecSelector.DEFAULT;

View File

@ -191,7 +191,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
AudioSink audioSink) { AudioSink audioSink) {
this( this(
context, context,
MediaCodecAdapter.Factory.DEFAULT, MediaCodecAdapter.Factory.getDefault(context),
mediaCodecSelector, mediaCodecSelector,
/* enableDecoderFallback= */ false, /* enableDecoderFallback= */ false,
eventHandler, eventHandler,
@ -219,7 +219,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
AudioSink audioSink) { AudioSink audioSink) {
this( this(
context, context,
MediaCodecAdapter.Factory.DEFAULT, MediaCodecAdapter.Factory.getDefault(context),
mediaCodecSelector, mediaCodecSelector,
enableDecoderFallback, enableDecoderFallback,
eventHandler, eventHandler,

View File

@ -17,8 +17,10 @@ package androidx.media3.exoplayer.mediacodec;
import static java.lang.annotation.ElementType.TYPE_USE; import static java.lang.annotation.ElementType.TYPE_USE;
import android.content.Context;
import android.media.MediaCodec; import android.media.MediaCodec;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import androidx.media3.common.MimeTypes; import androidx.media3.common.MimeTypes;
import androidx.media3.common.util.Log; import androidx.media3.common.util.Log;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
@ -54,12 +56,30 @@ public final class DefaultMediaCodecAdapterFactory implements MediaCodecAdapter.
private static final String TAG = "DMCodecAdapterFactory"; private static final String TAG = "DMCodecAdapterFactory";
@Nullable private final Context context;
private @Mode int asynchronousMode; private @Mode int asynchronousMode;
private boolean asyncCryptoFlagEnabled; private boolean asyncCryptoFlagEnabled;
/**
* @deprecated Use {@link #DefaultMediaCodecAdapterFactory(Context)} instead.
*/
@Deprecated
public DefaultMediaCodecAdapterFactory() { public DefaultMediaCodecAdapterFactory() {
asynchronousMode = MODE_DEFAULT; asynchronousMode = MODE_DEFAULT;
asyncCryptoFlagEnabled = true; 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 { throws IOException {
if (Util.SDK_INT >= 23 if (Util.SDK_INT >= 23
&& (asynchronousMode == MODE_ENABLED && (asynchronousMode == MODE_ENABLED
|| (asynchronousMode == MODE_DEFAULT && Util.SDK_INT >= 31))) { || (asynchronousMode == MODE_DEFAULT && shouldUseAsynchronousAdapterInDefaultMode()))) {
int trackType = MimeTypes.getTrackType(configuration.format.sampleMimeType); int trackType = MimeTypes.getTrackType(configuration.format.sampleMimeType);
Log.i( Log.i(
TAG, TAG,
@ -118,4 +138,19 @@ public final class DefaultMediaCodecAdapterFactory implements MediaCodecAdapter.
} }
return new SynchronousMediaCodecAdapter.Factory().createAdapter(configuration); 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;
}
} }

View File

@ -15,6 +15,7 @@
*/ */
package androidx.media3.exoplayer.mediacodec; package androidx.media3.exoplayer.mediacodec;
import android.content.Context;
import android.media.MediaCodec; import android.media.MediaCodec;
import android.media.MediaCrypto; import android.media.MediaCrypto;
import android.media.MediaFormat; import android.media.MediaFormat;
@ -120,9 +121,23 @@ public interface MediaCodecAdapter {
/** A factory for {@link MediaCodecAdapter} instances. */ /** A factory for {@link MediaCodecAdapter} instances. */
interface Factory { interface Factory {
/** Default factory used in most cases. */ /**
* @deprecated Use {@link #getDefault} instead.
*/
@Deprecated
@SuppressWarnings("deprecation") // Forwarding to deprecated method.
Factory DEFAULT = new DefaultMediaCodecAdapterFactory(); 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. */ /** Creates a {@link MediaCodecAdapter} instance. */
MediaCodecAdapter createAdapter(Configuration configuration) throws IOException; MediaCodecAdapter createAdapter(Configuration configuration) throws IOException;
} }

View File

@ -222,7 +222,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
int maxDroppedFramesToNotify) { int maxDroppedFramesToNotify) {
this( this(
context, context,
MediaCodecAdapter.Factory.DEFAULT, MediaCodecAdapter.Factory.getDefault(context),
mediaCodecSelector, mediaCodecSelector,
allowedJoiningTimeMs, allowedJoiningTimeMs,
/* enableDecoderFallback= */ false, /* enableDecoderFallback= */ false,
@ -256,7 +256,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
int maxDroppedFramesToNotify) { int maxDroppedFramesToNotify) {
this( this(
context, context,
MediaCodecAdapter.Factory.DEFAULT, MediaCodecAdapter.Factory.getDefault(context),
mediaCodecSelector, mediaCodecSelector,
allowedJoiningTimeMs, allowedJoiningTimeMs,
enableDecoderFallback, enableDecoderFallback,

View File

@ -44,6 +44,7 @@ import androidx.media3.exoplayer.drm.DrmSessionManager;
import androidx.media3.exoplayer.source.MediaSource; import androidx.media3.exoplayer.source.MediaSource;
import androidx.media3.exoplayer.upstream.DefaultAllocator; import androidx.media3.exoplayer.upstream.DefaultAllocator;
import androidx.media3.test.utils.FakeSampleStream; import androidx.media3.test.utils.FakeSampleStream;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@ -480,7 +481,7 @@ public class MediaCodecRendererTest {
public TestRenderer() { public TestRenderer() {
super( super(
C.TRACK_TYPE_AUDIO, C.TRACK_TYPE_AUDIO,
MediaCodecAdapter.Factory.DEFAULT, MediaCodecAdapter.Factory.getDefault(ApplicationProvider.getApplicationContext()),
/* mediaCodecSelector= */ (mimeType, requiresSecureDecoder, requiresTunnelingDecoder) -> /* mediaCodecSelector= */ (mimeType, requiresSecureDecoder, requiresTunnelingDecoder) ->
Collections.singletonList( Collections.singletonList(
MediaCodecInfo.newInstance( MediaCodecInfo.newInstance(

View File

@ -81,7 +81,7 @@ public class CapturingRenderersFactory implements RenderersFactory, Dumper.Dumpa
*/ */
public CapturingRenderersFactory(Context context) { public CapturingRenderersFactory(Context context) {
this.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.audioSink = new CapturingAudioSink(new DefaultAudioSink.Builder(context).build());
this.imageOutput = new CapturingImageOutput(); this.imageOutput = new CapturingImageOutput();
this.imageDecoderFactory = ImageDecoder.Factory.DEFAULT; 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 static class Factory implements MediaCodecAdapter.Factory, Dumper.Dumpable {
private final Context context;
private final List<CapturingMediaCodecAdapter> constructedAdapters; private final List<CapturingMediaCodecAdapter> constructedAdapters;
private Factory() { private Factory(Context context) {
this.context = context;
constructedAdapters = new ArrayList<>(); constructedAdapters = new ArrayList<>();
} }
@ -178,7 +180,7 @@ public class CapturingRenderersFactory implements RenderersFactory, Dumper.Dumpa
public MediaCodecAdapter createAdapter(Configuration configuration) throws IOException { public MediaCodecAdapter createAdapter(Configuration configuration) throws IOException {
CapturingMediaCodecAdapter adapter = CapturingMediaCodecAdapter adapter =
new CapturingMediaCodecAdapter( new CapturingMediaCodecAdapter(
MediaCodecAdapter.Factory.DEFAULT.createAdapter(configuration), MediaCodecAdapter.Factory.getDefault(context).createAdapter(configuration),
configuration.codecInfo.name); configuration.codecInfo.name);
constructedAdapters.add(adapter); constructedAdapters.add(adapter);
return adapter; return adapter;