diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DecoderReuseEvaluation.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DecoderReuseEvaluation.java index 95771c00ce8..1a49dbdbe07 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DecoderReuseEvaluation.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DecoderReuseEvaluation.java @@ -137,6 +137,9 @@ public final class DecoderReuseEvaluation { /** The audio bypass mode is possible. */ public static final int DISCARD_REASON_AUDIO_BYPASS_POSSIBLE = 1 << 15; + /** The codec is changing. */ + public static final int DISCARD_REASON_CODEC_CHANGED = 1 << 16; + /** The name of the decoder. */ public final String decoderName; 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 97cd968143f..4db0b78a1f9 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 @@ -19,6 +19,7 @@ import static androidx.media3.exoplayer.DecoderReuseEvaluation.DISCARD_REASON_AUDIO_CHANNEL_COUNT_CHANGED; import static androidx.media3.exoplayer.DecoderReuseEvaluation.DISCARD_REASON_AUDIO_ENCODING_CHANGED; import static androidx.media3.exoplayer.DecoderReuseEvaluation.DISCARD_REASON_AUDIO_SAMPLE_RATE_CHANGED; +import static androidx.media3.exoplayer.DecoderReuseEvaluation.DISCARD_REASON_CODEC_CHANGED; import static androidx.media3.exoplayer.DecoderReuseEvaluation.DISCARD_REASON_INITIALIZATION_DATA_CHANGED; import static androidx.media3.exoplayer.DecoderReuseEvaluation.DISCARD_REASON_MIME_TYPE_CHANGED; import static androidx.media3.exoplayer.DecoderReuseEvaluation.DISCARD_REASON_VIDEO_COLOR_INFO_CHANGED; @@ -439,6 +440,9 @@ public DecoderReuseEvaluation canReuseCodec(Format oldFormat, Format newFormat) if (!Objects.equals(oldFormat.sampleMimeType, newFormat.sampleMimeType)) { discardReasons |= DISCARD_REASON_MIME_TYPE_CHANGED; } + if (!Objects.equals(oldFormat.codecs, newFormat.codecs)) { + discardReasons |= DISCARD_REASON_CODEC_CHANGED; + } if (isVideo) { if (oldFormat.rotationDegrees != newFormat.rotationDegrees) { @@ -516,6 +520,18 @@ public DecoderReuseEvaluation canReuseCodec(Format oldFormat, Format newFormat) } } + // For eac3, eac3-joc and ac4 formats, adaptation is possible without reconfiguration or + // flushing. + if (discardReasons == 0 && (MimeTypes.AUDIO_E_AC3_JOC.equals(mimeType) + || MimeTypes.AUDIO_E_AC3.equals(mimeType) || MimeTypes.AUDIO_AC4.equals(mimeType))) { + return new DecoderReuseEvaluation( + name, + oldFormat, + newFormat, + REUSE_RESULT_YES_WITHOUT_RECONFIGURATION, + /* discardReasons= */ 0); + } + if (!oldFormat.initializationDataEquals(newFormat)) { discardReasons |= DISCARD_REASON_INITIALIZATION_DATA_CHANGED; } diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/mediacodec/MediaCodecInfoTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/mediacodec/MediaCodecInfoTest.java index ded29cb8f5c..e0df47b03e9 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/mediacodec/MediaCodecInfoTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/mediacodec/MediaCodecInfoTest.java @@ -16,6 +16,9 @@ package androidx.media3.exoplayer.mediacodec; import static androidx.media3.common.MimeTypes.AUDIO_AAC; +import static androidx.media3.common.MimeTypes.AUDIO_AC4; +import static androidx.media3.common.MimeTypes.AUDIO_E_AC3; +import static androidx.media3.common.MimeTypes.AUDIO_E_AC3_JOC; import static androidx.media3.common.MimeTypes.VIDEO_AV1; import static androidx.media3.common.MimeTypes.VIDEO_H264; import static androidx.media3.exoplayer.DecoderReuseEvaluation.DISCARD_REASON_AUDIO_CHANNEL_COUNT_CHANGED; @@ -74,6 +77,30 @@ public final class MediaCodecInfoTest { .setInitializationData(ImmutableList.of(new byte[] {4, 4, 1, 0, 0})) .build(); + private static final Format FORMAT_EAC3 = + new Format.Builder() + .setSampleMimeType(AUDIO_E_AC3) + .setChannelCount(6) + .setSampleRate(48000) + .setAverageBitrate(5000) + .build(); + + private static final Format FORMAT_EAC3JOC = + new Format.Builder() + .setSampleMimeType(AUDIO_E_AC3_JOC) + .setChannelCount(12) + .setSampleRate(48000) + .setAverageBitrate(5000) + .build(); + + private static final Format FORMAT_AC4 = + new Format.Builder() + .setSampleMimeType(AUDIO_AC4) + .setChannelCount(21) + .setSampleRate(48000) + .setAverageBitrate(5000) + .build(); + @Test public void canReuseCodec_withDifferentMimeType_returnsNo() { MediaCodecInfo codecInfo = buildH264CodecInfo(/* adaptive= */ true); @@ -318,6 +345,63 @@ public void canReuseCodec_differentVideoCrop_returnsNo() { DISCARD_REASON_WORKAROUND)); } + @Test + public void canReuseCodec_eac3_returnsYesWithoutReconfiguration() { + MediaCodecInfo codecInfo = buildEac3CodecInfo(); + + Format variantFormat = + FORMAT_EAC3 + .buildUpon() + .setInitializationData(ImmutableList.of(new byte[] {0})) + .build(); + assertThat(codecInfo.canReuseCodec(FORMAT_EAC3, variantFormat)) + .isEqualTo( + new DecoderReuseEvaluation( + codecInfo.name, + FORMAT_EAC3, + variantFormat, + DecoderReuseEvaluation.REUSE_RESULT_YES_WITHOUT_RECONFIGURATION, + /* discardReasons= */ 0)); + } + + @Test + public void canReuseCodec_eac3joc_returnsYesWithoutReconfiguration() { + MediaCodecInfo codecInfo = buildEac3JocCodecInfo(); + + Format variantFormat = + FORMAT_EAC3JOC + .buildUpon() + .setInitializationData(ImmutableList.of(new byte[] {0})) + .build(); + assertThat(codecInfo.canReuseCodec(FORMAT_EAC3JOC, variantFormat)) + .isEqualTo( + new DecoderReuseEvaluation( + codecInfo.name, + FORMAT_EAC3JOC, + variantFormat, + DecoderReuseEvaluation.REUSE_RESULT_YES_WITHOUT_RECONFIGURATION, + /* discardReasons= */ 0)); + } + + @Test + public void canReuseCodec_ac4_returnsYesWithoutReconfiguration() { + MediaCodecInfo codecInfo = buildAc4CodecInfo(); + + Format variantFormat = + FORMAT_AC4 + .buildUpon() + .setInitializationData(ImmutableList.of(new byte[] {0})) + .build(); + assertThat(codecInfo.canReuseCodec(FORMAT_AC4, variantFormat)) + .isEqualTo( + new DecoderReuseEvaluation( + codecInfo.name, + FORMAT_AC4, + variantFormat, + DecoderReuseEvaluation.REUSE_RESULT_YES_WITHOUT_RECONFIGURATION, + /* discardReasons= */ 0)); + } + private static MediaCodecInfo buildH264CodecInfo(boolean adaptive) { return new MediaCodecInfo( "h264", @@ -348,6 +432,51 @@ private static MediaCodecInfo buildAacCodecInfo() { /* detachedSurfaceSupported= */ false); } + private static MediaCodecInfo buildEac3CodecInfo() { + return new MediaCodecInfo( + "eac3joc", + AUDIO_E_AC3, + AUDIO_E_AC3, + /* capabilities= */ null, + /* hardwareAccelerated= */ false, + /* softwareOnly= */ true, + /* vendor= */ true, + /* adaptive= */ false, + /* tunneling= */ false, + /* secure= */ false, + /* detachedSurfaceSupported= */ false); + } + + private static MediaCodecInfo buildEac3JocCodecInfo() { + return new MediaCodecInfo( + "eac3joc", + AUDIO_E_AC3_JOC, + AUDIO_E_AC3_JOC, + /* capabilities= */ null, + /* hardwareAccelerated= */ false, + /* softwareOnly= */ true, + /* vendor= */ true, + /* adaptive= */ false, + /* tunneling= */ false, + /* secure= */ false, + /* detachedSurfaceSupported= */ false); + } + + private static MediaCodecInfo buildAc4CodecInfo() { + return new MediaCodecInfo( + "ac4", + AUDIO_AC4, + AUDIO_AC4, + /* capabilities= */ null, + /* hardwareAccelerated= */ false, + /* softwareOnly= */ true, + /* vendor= */ true, + /* adaptive= */ false, + /* tunneling= */ false, + /* secure= */ false, + /* detachedSurfaceSupported= */ false); + } + private static ColorInfo buildHdrColorInfo(@C.ColorSpace int colorSpace) { return new ColorInfo.Builder() .setColorSpace(colorSpace)