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) {
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;

View File

@ -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,

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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,

View File

@ -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(

View File

@ -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<CapturingMediaCodecAdapter> 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;