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 70bad2569d4..03710387a17 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 @@ -296,6 +296,7 @@ public MediaCodecAudioRenderer( AudioSink audioSink, @Nullable LoudnessCodecController loudnessCodecController) { super( + context.getApplicationContext(), C.TRACK_TYPE_AUDIO, codecAdapterFactory, mediaCodecSelector, @@ -357,13 +358,13 @@ public String getName() { // Check whether the first decoder supports the format. This is the preferred decoder for the // format's MIME type, according to the MediaCodecSelector. MediaCodecInfo decoderInfo = decoderInfos.get(0); - boolean isFormatSupported = decoderInfo.isFormatSupported(format); + boolean isFormatSupported = decoderInfo.isFormatSupported(context, format); boolean isPreferredDecoder = true; if (!isFormatSupported) { // Check whether any of the other decoders support the format. for (int i = 1; i < decoderInfos.size(); i++) { MediaCodecInfo otherDecoderInfo = decoderInfos.get(i); - if (otherDecoderInfo.isFormatSupported(format)) { + if (otherDecoderInfo.isFormatSupported(context, format)) { decoderInfo = otherDecoderInfo; isFormatSupported = true; isPreferredDecoder = false; @@ -415,6 +416,7 @@ protected List getDecoderInfos( MediaCodecSelector mediaCodecSelector, Format format, boolean requiresSecureDecoder) throws DecoderQueryException { return MediaCodecUtil.getDecoderInfosSortedByFormatSupport( + context, getDecoderInfos(mediaCodecSelector, format, requiresSecureDecoder, audioSink), format); } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecInfo.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecInfo.java index ed59010a365..fa6f13872f8 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecInfo.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecInfo.java @@ -33,6 +33,7 @@ import static androidx.media3.exoplayer.mediacodec.MediaCodecPerformancePointCoverageProvider.COVERAGE_RESULT_YES; import static androidx.media3.exoplayer.mediacodec.MediaCodecUtil.createCodecProfileLevel; +import android.content.Context; import android.graphics.Point; import android.media.MediaCodec; import android.media.MediaCodecInfo.AudioCapabilities; @@ -55,6 +56,8 @@ import androidx.media3.common.util.Util; import androidx.media3.exoplayer.DecoderReuseEvaluation; import androidx.media3.exoplayer.DecoderReuseEvaluation.DecoderDiscardReasons; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; /** Information about a {@link MediaCodec} for a given MIME type. */ @@ -270,12 +273,14 @@ public int getMaxSupportedInstances() { * @return Whether the decoder may support decoding the given {@code format}. * @throws MediaCodecUtil.DecoderQueryException Thrown if an error occurs while querying decoders. */ - public boolean isFormatSupported(Format format) throws MediaCodecUtil.DecoderQueryException { + public boolean isFormatSupported(Context context, Format format) + throws MediaCodecUtil.DecoderQueryException { if (!isSampleMimeTypeSupported(format)) { return false; } - if (!isCodecProfileAndLevelSupported(format, /* checkPerformanceCapabilities= */ true)) { + if (!isCodecProfileAndLevelSupported( + context, format, /* checkPerformanceCapabilities= */ true)) { return false; } @@ -302,9 +307,10 @@ public boolean isFormatSupported(Format format) throws MediaCodecUtil.DecoderQue * @param format The input media format. * @return Whether the decoder may functionally support decoding the given {@code format}. */ - public boolean isFormatFunctionallySupported(Format format) { + public boolean isFormatFunctionallySupported(Context context, Format format) { return isSampleMimeTypeSupported(format) - && isCodecProfileAndLevelSupported(format, /* checkPerformanceCapabilities= */ false) + && isCodecProfileAndLevelSupported( + context, format, /* checkPerformanceCapabilities= */ false) && isCompressedAudioBitDepthSupported(format); } @@ -314,7 +320,7 @@ private boolean isSampleMimeTypeSupported(Format format) { } private boolean isCodecProfileAndLevelSupported( - Format format, boolean checkPerformanceCapabilities) { + Context context, Format format, boolean checkPerformanceCapabilities) { Pair codecProfileAndLevel = MediaCodecUtil.getCodecProfileAndLevel(format); if (format.sampleMimeType != null && format.sampleMimeType.equals(MimeTypes.VIDEO_MV_HEVC)) { String normalizedCodecMimeType = MimeTypes.normalizeMimeType(codecMimeType); @@ -372,7 +378,7 @@ private boolean isCodecProfileAndLevelSupported( if (mimeType.equals(MimeTypes.AUDIO_AC4) && profileLevels.length == 0) { // Some older devices don't report profile levels for AC-4. Estimate them using other data // in the codec capabilities. - profileLevels = estimateLegacyAc4ProfileLevels(capabilities); + profileLevels = estimateLegacyAc4ProfileLevels(context, capabilities); } if (SDK_INT <= 23 && MimeTypes.VIDEO_VP9.equals(mimeType) && profileLevels.length == 0) { // Some older devices don't report profile levels for VP9. Estimate them using other data in @@ -838,8 +844,8 @@ private static int getMaxSupportedInstancesV23(CodecCapabilities capabilities) { * known. * @return The estimated {@link CodecProfileLevel CodecProfileLevels} for the decoder. */ - private static CodecProfileLevel[] estimateLegacyAc4ProfileLevels( - @Nullable CodecCapabilities capabilities) { + private CodecProfileLevel[] estimateLegacyAc4ProfileLevels( + Context context, @Nullable CodecCapabilities capabilities) { int maxInChannelCount = 2; if (capabilities != null) { @Nullable AudioCapabilities audioCapabilities = capabilities.getAudioCapabilities(); @@ -853,13 +859,20 @@ private static CodecProfileLevel[] estimateLegacyAc4ProfileLevels( level = CodecProfileLevel.AC4Level4; } - return new CodecProfileLevel[] { - createCodecProfileLevel(CodecProfileLevel.AC4Profile00, level), - createCodecProfileLevel(CodecProfileLevel.AC4Profile10, level), - createCodecProfileLevel(CodecProfileLevel.AC4Profile11, level), - createCodecProfileLevel(CodecProfileLevel.AC4Profile21, level), - createCodecProfileLevel(CodecProfileLevel.AC4Profile22, level) - }; + List codecProfileLevel = new ArrayList<>(); + // All MediaCodec AC-4 decoders are expected to support AC4Profile21 + codecProfileLevel.add(createCodecProfileLevel(CodecProfileLevel.AC4Profile21, level)); + + // Automotive platform MediaCodec AC-4 decoders are not expected to support AC4Profile22 and + // deprecated profiles AC4Profile00, AC4Profile10 and AC4Profile11. + if (!Util.isAutomotive(context)) { + codecProfileLevel.add(createCodecProfileLevel(CodecProfileLevel.AC4Profile00, level)); + codecProfileLevel.add(createCodecProfileLevel(CodecProfileLevel.AC4Profile10, level)); + codecProfileLevel.add(createCodecProfileLevel(CodecProfileLevel.AC4Profile11, level)); + codecProfileLevel.add(createCodecProfileLevel(CodecProfileLevel.AC4Profile22, level)); + } + + return codecProfileLevel.toArray(new CodecProfileLevel[0]); } /** diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java index bedcb0350b8..2c6d6ab0034 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java @@ -34,6 +34,7 @@ import static java.lang.annotation.ElementType.TYPE_USE; import android.annotation.TargetApi; +import android.content.Context; import android.media.MediaCodec; import android.media.MediaCodec.CodecException; import android.media.MediaCrypto; @@ -403,6 +404,8 @@ private static String buildCustomDiagnosticInfo(int errorCode) { private boolean hasSkippedFlushAndWaitingForQueueInputBuffer; private long skippedFlushOffsetUs; + private final Context context; + /** * @param trackType The {@link C.TrackType track type} that the renderer handles. * @param codecAdapterFactory A factory for {@link MediaCodecAdapter} instances. @@ -415,12 +418,14 @@ private static String buildCustomDiagnosticInfo(int errorCode) { * explicitly using {@link MediaFormat#KEY_OPERATING_RATE}). */ public MediaCodecRenderer( + Context context, @C.TrackType int trackType, MediaCodecAdapter.Factory codecAdapterFactory, MediaCodecSelector mediaCodecSelector, boolean enableDecoderFallback, float assumedMinimumCodecOperatingRate) { super(trackType); + this.context = context; this.codecAdapterFactory = codecAdapterFactory; this.mediaCodecSelector = checkNotNull(mediaCodecSelector); this.enableDecoderFallback = enableDecoderFallback; @@ -1296,7 +1301,7 @@ private void initCodec(MediaCodecInfo codecInfo, @Nullable MediaCrypto crypto) t } codecInitializedTimestamp = getClock().elapsedRealtime(); - if (!codecInfo.isFormatSupported(inputFormat)) { + if (!codecInfo.isFormatSupported(context, inputFormat)) { Log.w( TAG, Util.formatInvariant( diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecUtil.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecUtil.java index f33b585285c..9981be2ed36 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecUtil.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecUtil.java @@ -20,6 +20,7 @@ import static java.lang.Math.max; import android.annotation.SuppressLint; +import android.content.Context; import android.media.MediaCodecInfo.CodecCapabilities; import android.media.MediaCodecInfo.CodecProfileLevel; import android.media.MediaCodecList; @@ -256,10 +257,11 @@ public static List getAlternativeDecoderInfos( */ @CheckResult public static List getDecoderInfosSortedByFormatSupport( - List decoderInfos, Format format) { + Context context, List decoderInfos, Format format) { decoderInfos = new ArrayList<>(decoderInfos); sortByScore( - decoderInfos, decoderInfo -> decoderInfo.isFormatFunctionallySupported(format) ? 1 : 0); + decoderInfos, + decoderInfo -> decoderInfo.isFormatFunctionallySupported(context, format) ? 1 : 0); return decoderInfos; } @@ -269,13 +271,13 @@ public static List getDecoderInfosSortedByFormatSupport( */ @CheckResult public static List getDecoderInfosSortedByFullFormatSupport( - List decoderInfos, Format format) { + Context context, List decoderInfos, Format format) { decoderInfos = new ArrayList<>(decoderInfos); sortByScore( decoderInfos, decoderInfo -> { try { - return decoderInfo.isFormatSupported(format) ? 1 : 0; + return decoderInfo.isFormatSupported(context, format) ? 1 : 0; } catch (DecoderQueryException e) { return -1; } 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 9ea57d727b1..c0e03b9b13c 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 @@ -562,6 +562,7 @@ public MediaCodecVideoRenderer( */ protected MediaCodecVideoRenderer(Builder builder) { super( + builder.context.getApplicationContext(), C.TRACK_TYPE_VIDEO, builder.codecAdapterFactory, builder.mediaCodecSelector, @@ -697,13 +698,13 @@ public String getName() { // Check whether the first decoder supports the format. This is the preferred decoder for the // format's MIME type, according to the MediaCodecSelector. MediaCodecInfo decoderInfo = decoderInfos.get(0); - boolean isFormatSupported = decoderInfo.isFormatSupported(format); + boolean isFormatSupported = decoderInfo.isFormatSupported(context, format); boolean isPreferredDecoder = true; if (!isFormatSupported) { // Check whether any of the other decoders support the format. for (int i = 1; i < decoderInfos.size(); i++) { MediaCodecInfo otherDecoderInfo = decoderInfos.get(i); - if (otherDecoderInfo.isFormatSupported(format)) { + if (otherDecoderInfo.isFormatSupported(context, format)) { decoderInfo = otherDecoderInfo; isFormatSupported = true; isPreferredDecoder = false; @@ -743,9 +744,10 @@ public String getName() { /* requiresTunnelingDecoder= */ true); if (!tunnelingDecoderInfos.isEmpty()) { MediaCodecInfo tunnelingDecoderInfo = - MediaCodecUtil.getDecoderInfosSortedByFormatSupport(tunnelingDecoderInfos, format) + MediaCodecUtil.getDecoderInfosSortedByFormatSupport( + context, tunnelingDecoderInfos, format) .get(0); - if (tunnelingDecoderInfo.isFormatSupported(format) + if (tunnelingDecoderInfo.isFormatSupported(context, format) && tunnelingDecoderInfo.isSeamlessAdaptationSupported(format)) { tunnelingSupport = TUNNELING_SUPPORTED; } @@ -764,7 +766,7 @@ public String getName() { protected List getDecoderInfos( MediaCodecSelector mediaCodecSelector, Format format, boolean requiresSecureDecoder) throws DecoderQueryException { - return MediaCodecUtil.getDecoderInfosSortedByFormatSupport( + return MediaCodecUtil.getDecoderInfosSortedByFormatSupport(context, getDecoderInfos(context, mediaCodecSelector, format, requiresSecureDecoder, tunneling), format); }