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 59b6edeb9f..87f0ab1200 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 @@ -299,16 +299,28 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_SUBTYPE); } List decoderInfos = - getDecoderInfos(mediaCodecSelector, format, /* requiresSecureDecoder= */ false); + getDecoderInfos(mediaCodecSelector, format, /* requiresSecureDecoder= */ false, audioSink); if (decoderInfos.isEmpty()) { return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_SUBTYPE); } if (!supportsFormatDrm) { return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_DRM); } - // Check capabilities for the first decoder in the list, which takes priority. + // 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); + 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)) { + decoderInfo = otherDecoderInfo; + isFormatSupported = true; + break; + } + } + } @AdaptiveSupport int adaptiveSupport = isFormatSupported && decoderInfo.isSeamlessAdaptationSupported(format) @@ -323,6 +335,32 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media protected List getDecoderInfos( MediaCodecSelector mediaCodecSelector, Format format, boolean requiresSecureDecoder) throws DecoderQueryException { + return MediaCodecUtil.getDecoderInfosSortedByFormatSupport( + getDecoderInfos(mediaCodecSelector, format, requiresSecureDecoder, audioSink), format); + } + + /** + * Returns a list of decoders that can decode media in the specified format, in the priority order + * specified by the {@link MediaCodecSelector}. Note that since the {@link MediaCodecSelector} + * only has access to {@link Format#sampleMimeType}, the list is not ordered to account for + * whether each decoder supports the details of the format (e.g., taking into account the format's + * profile, level, channel count and so on). {@link + * MediaCodecUtil#getDecoderInfosSortedByFormatSupport} can be used to further sort the list into + * an order where decoders that fully support the format come first. + * + * @param mediaCodecSelector The decoder selector. + * @param format The {@link Format} for which a decoder is required. + * @param requiresSecureDecoder Whether a secure decoder is required. + * @param audioSink The {@link AudioSink} to which audio will be output. + * @return A list of {@link MediaCodecInfo}s corresponding to decoders. May be empty. + * @throws DecoderQueryException Thrown if there was an error querying decoders. + */ + private static List getDecoderInfos( + MediaCodecSelector mediaCodecSelector, + Format format, + boolean requiresSecureDecoder, + AudioSink audioSink) + throws DecoderQueryException { @Nullable String mimeType = format.sampleMimeType; if (mimeType == null) { return Collections.emptyList(); @@ -337,7 +375,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media List decoderInfos = mediaCodecSelector.getDecoderInfos( mimeType, requiresSecureDecoder, /* requiresTunnelingDecoder= */ false); - decoderInfos = MediaCodecUtil.getDecoderInfosSortedByFormatSupport(decoderInfos, format); if (MimeTypes.AUDIO_E_AC3_JOC.equals(mimeType)) { // E-AC3 decoders can decode JOC streams, but in 2-D rather than 3-D. List decoderInfosWithEac3 = new ArrayList<>(decoderInfos); 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 325657ddff..74de91fcfa 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 @@ -373,9 +373,21 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { if (!supportsFormatDrm(format)) { return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_DRM); } - // Check capabilities for the first decoder in the list, which takes priority. + // 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); + 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)) { + decoderInfo = otherDecoderInfo; + isFormatSupported = true; + break; + } + } + } @AdaptiveSupport int adaptiveSupport = decoderInfo.isSeamlessAdaptationSupported(format) @@ -390,7 +402,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { requiresSecureDecryption, /* requiresTunnelingDecoder= */ true); if (!tunnelingDecoderInfos.isEmpty()) { - MediaCodecInfo tunnelingDecoderInfo = tunnelingDecoderInfos.get(0); + MediaCodecInfo tunnelingDecoderInfo = + MediaCodecUtil.getDecoderInfosSortedByFormatSupport(tunnelingDecoderInfos, format) + .get(0); if (tunnelingDecoderInfo.isFormatSupported(format) && tunnelingDecoderInfo.isSeamlessAdaptationSupported(format)) { tunnelingSupport = TUNNELING_SUPPORTED; @@ -406,9 +420,26 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { protected List getDecoderInfos( MediaCodecSelector mediaCodecSelector, Format format, boolean requiresSecureDecoder) throws DecoderQueryException { - return getDecoderInfos(mediaCodecSelector, format, requiresSecureDecoder, tunneling); + return MediaCodecUtil.getDecoderInfosSortedByFormatSupport( + getDecoderInfos(mediaCodecSelector, format, requiresSecureDecoder, tunneling), format); } + /** + * Returns a list of decoders that can decode media in the specified format, in the priority order + * specified by the {@link MediaCodecSelector}. Note that since the {@link MediaCodecSelector} + * only has access to {@link Format#sampleMimeType}, the list is not ordered to account for + * whether each decoder supports the details of the format (e.g., taking into account the format's + * profile, level, resolution and so on). {@link + * MediaCodecUtil#getDecoderInfosSortedByFormatSupport} can be used to further sort the list into + * an order where decoders that fully support the format come first. + * + * @param mediaCodecSelector The decoder selector. + * @param format The {@link Format} for which a decoder is required. + * @param requiresSecureDecoder Whether a secure decoder is required. + * @param requiresTunnelingDecoder Whether a tunneling decoder is required. + * @return A list of {@link MediaCodecInfo}s corresponding to decoders. May be empty. + * @throws DecoderQueryException Thrown if there was an error querying decoders. + */ private static List getDecoderInfos( MediaCodecSelector mediaCodecSelector, Format format, @@ -422,7 +453,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { List decoderInfos = mediaCodecSelector.getDecoderInfos( mimeType, requiresSecureDecoder, requiresTunnelingDecoder); - decoderInfos = MediaCodecUtil.getDecoderInfosSortedByFormatSupport(decoderInfos, format); if (MimeTypes.VIDEO_DOLBY_VISION.equals(mimeType)) { // Fall back to H.264/AVC or H.265/HEVC for the relevant DV profiles. This can't be done for // profile CodecProfileLevel.DolbyVisionProfileDvheStn and profile