diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultCodec.java b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultCodec.java index c8331510a9..99ec6ece72 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultCodec.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultCodec.java @@ -16,6 +16,7 @@ package androidx.media3.transformer; +import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Assertions.checkState; import static androidx.media3.common.util.Assertions.checkStateNotNull; @@ -27,7 +28,9 @@ import android.media.MediaCodec.BufferInfo; import android.media.MediaCrypto; import android.media.MediaFormat; import android.view.Surface; +import androidx.annotation.DoNotInline; import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; import androidx.annotation.VisibleForTesting; import androidx.media3.common.C; import androidx.media3.common.ColorInfo; @@ -36,6 +39,7 @@ import androidx.media3.common.MimeTypes; import androidx.media3.common.util.MediaFormatUtil; import androidx.media3.common.util.TraceUtil; import androidx.media3.common.util.UnstableApi; +import androidx.media3.common.util.Util; import androidx.media3.decoder.DecoderInputBuffer; import com.google.common.base.Ascii; import com.google.common.collect.ImmutableList; @@ -56,12 +60,8 @@ public final class DefaultCodec implements Codec { private final MediaFormat configurationMediaFormat; private final Format configurationFormat; - /** The expected {@link ColorInfo} output from the codec. */ - @Nullable private final ColorInfo configuredOutputColor; - private final MediaCodec mediaCodec; @Nullable private final Surface inputSurface; - private final boolean decoderNeedsFrameDroppingWorkaround; private @MonotonicNonNull Format outputFormat; @@ -103,8 +103,18 @@ public final class DefaultCodec implements Codec { @Nullable MediaCodec mediaCodec = null; @Nullable Surface inputSurface = null; try { + boolean requestedHdrToneMapping = + Util.SDK_INT >= 29 && Api29.isSdrToneMappingEnabled(configurationMediaFormat); mediaCodec = MediaCodec.createByCodecName(mediaCodecName); configureCodec(mediaCodec, configurationMediaFormat, isDecoder, outputSurface); + if (SDK_INT >= 29 && requestedHdrToneMapping) { + // The MediaCodec input format reflects whether tone-mapping is possible after configure(). + // See + // https://developer.android.com/reference/android/media/MediaFormat#KEY_COLOR_TRANSFER_REQUEST. + checkArgument( + Api29.isSdrToneMappingEnabled(mediaCodec.getInputFormat()), + "Tone-mapping requested but not supported by the decoder"); + } if (isVideo && !isDecoder) { inputSurface = mediaCodec.createInputSurface(); } @@ -121,12 +131,6 @@ public final class DefaultCodec implements Codec { e, configurationMediaFormat, isVideo, isDecoder, mediaCodecName); } this.mediaCodec = mediaCodec; - boolean toneMapRequested = - SDK_INT >= 31 - && isDecoder - && (configurationMediaFormat.getInteger(MediaFormat.KEY_COLOR_TRANSFER_REQUEST, 0) - == MediaFormat.COLOR_TRANSFER_SDR_VIDEO); - configuredOutputColor = toneMapRequested ? null : configurationFormat.colorInfo; this.inputSurface = inputSurface; decoderNeedsFrameDroppingWorkaround = decoderNeedsFrameDroppingWorkaround(context); } @@ -283,11 +287,7 @@ public final class DefaultCodec implements Codec { */ @Override public String getName() { - if (SDK_INT >= 29) { - return mediaCodec.getCanonicalName(); - } - - return mediaCodec.getName(); + return SDK_INT >= 29 ? Api29.getCanonicalName(mediaCodec) : mediaCodec.getName(); } @VisibleForTesting @@ -320,15 +320,19 @@ public final class DefaultCodec implements Codec { if (outputBufferIndex < 0) { if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { outputFormat = convertToFormat(mediaCodec.getOutputFormat()); - if (!areColorTransfersEqual(configuredOutputColor, outputFormat.colorInfo)) { + boolean isToneMappingEnabled = + SDK_INT >= 29 && Api29.isSdrToneMappingEnabled(configurationMediaFormat); + ColorInfo expectedColorInfo = + isToneMappingEnabled ? ColorInfo.SDR_BT709_LIMITED : configurationFormat.colorInfo; + if (!areColorTransfersEqual(expectedColorInfo, outputFormat.colorInfo)) { // TODO(b/237674316): These exceptions throw when the container ColorInfo doesn't match // the video ColorInfo. Instead of throwing when seeing unexpected ColorInfos, consider // reconfiguring downstream components (ex. FrameProcessor and encoder) when different // ColorInfo values are output. throw createTransformationException( new IllegalStateException( - "Codec output color format does not match configured color format. Configured: " - + configuredOutputColor + "Codec output color format does not match configured color format. Expected: " + + expectedColorInfo + ". Actual: " + outputFormat.colorInfo)); } @@ -480,4 +484,21 @@ public final class DefaultCodec implements Codec { return SDK_INT < 29 || context.getApplicationContext().getApplicationInfo().targetSdkVersion < 29; } + + @RequiresApi(29) + private static final class Api29 { + @DoNotInline + public static String getCanonicalName(MediaCodec mediaCodec) { + return mediaCodec.getCanonicalName(); + } + + @DoNotInline + public static boolean isSdrToneMappingEnabled(MediaFormat mediaFormat) { + // MediaFormat.getInteger(String, int) was added in API 29 but applying a color transfer + // request is only possible from API 31. + return SDK_INT >= 31 + && mediaFormat.getInteger(MediaFormat.KEY_COLOR_TRANSFER_REQUEST, /* defaultValue= */ 0) + == MediaFormat.COLOR_TRANSFER_SDR_VIDEO; + } + } }