diff --git a/libraries/common/src/main/java/androidx/media3/common/ColorInfo.java b/libraries/common/src/main/java/androidx/media3/common/ColorInfo.java index a4b871a64f..b1ee267326 100644 --- a/libraries/common/src/main/java/androidx/media3/common/ColorInfo.java +++ b/libraries/common/src/main/java/androidx/media3/common/ColorInfo.java @@ -28,7 +28,12 @@ import java.lang.annotation.Target; import java.util.Arrays; import org.checkerframework.dataflow.qual.Pure; -/** Stores color info. */ +/** + * Stores color info. + * + *

When a {@code null} {@code ColorInfo} instance is used, this often represents a generic {@link + * #SDR_BT709_LIMITED} instance. + */ @UnstableApi public final class ColorInfo implements Bundleable { diff --git a/libraries/common/src/main/java/androidx/media3/common/FrameProcessor.java b/libraries/common/src/main/java/androidx/media3/common/FrameProcessor.java index 491545f933..c52bbb922e 100644 --- a/libraries/common/src/main/java/androidx/media3/common/FrameProcessor.java +++ b/libraries/common/src/main/java/androidx/media3/common/FrameProcessor.java @@ -46,17 +46,17 @@ public interface FrameProcessor { * @param listener A {@link Listener}. * @param effects The {@link Effect} instances to apply to each frame. * @param debugViewProvider A {@link DebugViewProvider}. - * @param useHdr Whether to process the input as an HDR signal. + * @param colorInfo The {@link ColorInfo} for input and output frames. * @return A new instance. * @throws FrameProcessingException If a problem occurs while creating the {@link * FrameProcessor}. */ FrameProcessor create( Context context, - FrameProcessor.Listener listener, + Listener listener, List effects, DebugViewProvider debugViewProvider, - boolean useHdr) + ColorInfo colorInfo) throws FrameProcessingException; } diff --git a/libraries/effect/src/androidTest/java/androidx/media3/effect/GlEffectsFrameProcessorPixelTest.java b/libraries/effect/src/androidTest/java/androidx/media3/effect/GlEffectsFrameProcessorPixelTest.java index f36270b2cd..5ccb2491fb 100644 --- a/libraries/effect/src/androidTest/java/androidx/media3/effect/GlEffectsFrameProcessorPixelTest.java +++ b/libraries/effect/src/androidTest/java/androidx/media3/effect/GlEffectsFrameProcessorPixelTest.java @@ -34,6 +34,7 @@ import android.media.MediaExtractor; import android.media.MediaFormat; import android.util.Pair; import androidx.annotation.Nullable; +import androidx.media3.common.ColorInfo; import androidx.media3.common.DebugViewProvider; import androidx.media3.common.Effect; import androidx.media3.common.FrameInfo; @@ -388,7 +389,7 @@ public final class GlEffectsFrameProcessorPixelTest { }, effects, DebugViewProvider.NONE, - /* useHdr= */ false)); + ColorInfo.SDR_BT709_LIMITED)); glEffectsFrameProcessor.setInputFrameInfo( new FrameInfo(inputWidth, inputHeight, pixelWidthHeightRatio, /* streamOffsetUs= */ 0)); glEffectsFrameProcessor.registerInputFrame(); diff --git a/libraries/effect/src/main/java/androidx/media3/effect/FinalMatrixTransformationProcessorWrapper.java b/libraries/effect/src/main/java/androidx/media3/effect/FinalMatrixTransformationProcessorWrapper.java index 77801f3774..a114e7dd47 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/FinalMatrixTransformationProcessorWrapper.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/FinalMatrixTransformationProcessorWrapper.java @@ -33,6 +33,7 @@ import androidx.annotation.GuardedBy; import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; import androidx.media3.common.C; +import androidx.media3.common.ColorInfo; import androidx.media3.common.DebugViewProvider; import androidx.media3.common.FrameProcessingException; import androidx.media3.common.FrameProcessor; @@ -69,7 +70,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; private final DebugViewProvider debugViewProvider; private final FrameProcessor.Listener frameProcessorListener; private final boolean sampleFromExternalTexture; - private final boolean useHdr; + private final ColorInfo colorInfo; private final float[] textureTransformMatrix; private final Queue streamOffsetUsQueue; @@ -91,8 +92,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @Nullable private EGLSurface outputEglSurface; - // TODO(b/227624622): Instead of inputting useHdr, input ColorInfo to handle HLG and PQ - // differently. public FinalMatrixTransformationProcessorWrapper( Context context, EGLDisplay eglDisplay, @@ -101,7 +100,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; FrameProcessor.Listener frameProcessorListener, DebugViewProvider debugViewProvider, boolean sampleFromExternalTexture, - boolean useHdr) { + ColorInfo colorInfo) { this.context = context; this.matrixTransformations = matrixTransformations; this.eglDisplay = eglDisplay; @@ -109,7 +108,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; this.debugViewProvider = debugViewProvider; this.frameProcessorListener = frameProcessorListener; this.sampleFromExternalTexture = sampleFromExternalTexture; - this.useHdr = useHdr; + this.colorInfo = colorInfo; textureTransformMatrix = new float[16]; Matrix.setIdentityM(textureTransformMatrix, /* smOffset= */ 0); @@ -215,7 +214,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; SurfaceInfo outputSurfaceInfo = this.outputSurfaceInfo; @Nullable EGLSurface outputEglSurface = this.outputEglSurface; if (outputEglSurface == null) { - if (useHdr) { + boolean colorInfoIsHdr = ColorInfo.isHdr(colorInfo); + if (colorInfoIsHdr) { outputEglSurface = GlUtil.getEglSurfaceRgba1010102(eglDisplay, outputSurfaceInfo.surface); } else { outputEglSurface = GlUtil.getEglSurface(eglDisplay, outputSurfaceInfo.surface); @@ -227,7 +227,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; outputSurfaceInfo.width, outputSurfaceInfo.height); if (debugSurfaceView != null && !Util.areEqual(this.debugSurfaceView, debugSurfaceView)) { debugSurfaceViewWrapper = - new SurfaceViewWrapper(eglDisplay, eglContext, useHdr, debugSurfaceView); + new SurfaceViewWrapper(eglDisplay, eglContext, colorInfoIsHdr, debugSurfaceView); } this.debugSurfaceView = debugSurfaceView; } @@ -266,7 +266,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; context, matrixTransformationListBuilder.build(), sampleFromExternalTexture, - useHdr, + colorInfo, /* outputOpticalColors= */ true); matrixTransformationProcessor.setTextureTransformMatrix(textureTransformMatrix); Pair outputSize = diff --git a/libraries/effect/src/main/java/androidx/media3/effect/GlEffectsFrameProcessor.java b/libraries/effect/src/main/java/androidx/media3/effect/GlEffectsFrameProcessor.java index 7de1b31357..ed41ab2837 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/GlEffectsFrameProcessor.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/GlEffectsFrameProcessor.java @@ -29,6 +29,7 @@ import android.view.Surface; import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; import androidx.media3.common.C; +import androidx.media3.common.ColorInfo; import androidx.media3.common.DebugViewProvider; import androidx.media3.common.Effect; import androidx.media3.common.FrameInfo; @@ -68,7 +69,7 @@ public final class GlEffectsFrameProcessor implements FrameProcessor { FrameProcessor.Listener listener, List effects, DebugViewProvider debugViewProvider, - boolean useHdr) + ColorInfo colorInfo) throws FrameProcessingException { ExecutorService singleThreadExecutorService = Util.newSingleThreadExecutor(THREAD_NAME); @@ -81,7 +82,7 @@ public final class GlEffectsFrameProcessor implements FrameProcessor { listener, effects, debugViewProvider, - useHdr, + colorInfo, singleThreadExecutorService)); try { @@ -111,11 +112,14 @@ public final class GlEffectsFrameProcessor implements FrameProcessor { FrameProcessor.Listener listener, List effects, DebugViewProvider debugViewProvider, - boolean useHdr, + ColorInfo colorInfo, ExecutorService singleThreadExecutorService) throws GlUtil.GlException, FrameProcessingException { checkState(Thread.currentThread().getName().equals(THREAD_NAME)); + // TODO(b/237674316): Delay initialization of things requiring the colorInfo, to + // configure based on the color info from the decoder output media format instead. + boolean useHdr = ColorInfo.isHdr(colorInfo); EGLDisplay eglDisplay = GlUtil.createEglDisplay(); EGLContext eglContext = useHdr @@ -133,7 +137,7 @@ public final class GlEffectsFrameProcessor implements FrameProcessor { ImmutableList textureProcessors = getGlTextureProcessorsForGlEffects( - context, effects, eglDisplay, eglContext, listener, debugViewProvider, useHdr); + context, effects, eglDisplay, eglContext, listener, debugViewProvider, colorInfo); FrameProcessingTaskExecutor frameProcessingTaskExecutor = new FrameProcessingTaskExecutor(singleThreadExecutorService, listener); chainTextureProcessorsWithListeners(textureProcessors, frameProcessingTaskExecutor, listener); @@ -164,7 +168,7 @@ public final class GlEffectsFrameProcessor implements FrameProcessor { EGLContext eglContext, FrameProcessor.Listener listener, DebugViewProvider debugViewProvider, - boolean useHdr) + ColorInfo colorInfo) throws FrameProcessingException { ImmutableList.Builder textureProcessorListBuilder = new ImmutableList.Builder<>(); @@ -187,12 +191,13 @@ public final class GlEffectsFrameProcessor implements FrameProcessor { context, matrixTransformations, sampleFromExternalTexture, - useHdr, + colorInfo, /* outputOpticalColors= */ false)); matrixTransformationListBuilder = new ImmutableList.Builder<>(); sampleFromExternalTexture = false; } - textureProcessorListBuilder.add(glEffect.toGlTextureProcessor(context, useHdr)); + textureProcessorListBuilder.add( + glEffect.toGlTextureProcessor(context, ColorInfo.isHdr(colorInfo))); } textureProcessorListBuilder.add( new FinalMatrixTransformationProcessorWrapper( @@ -203,7 +208,7 @@ public final class GlEffectsFrameProcessor implements FrameProcessor { listener, debugViewProvider, sampleFromExternalTexture, - useHdr)); + colorInfo)); return textureProcessorListBuilder.build(); } diff --git a/libraries/effect/src/main/java/androidx/media3/effect/MatrixTransformationProcessor.java b/libraries/effect/src/main/java/androidx/media3/effect/MatrixTransformationProcessor.java index eb206ba12a..cb13d68851 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/MatrixTransformationProcessor.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/MatrixTransformationProcessor.java @@ -21,6 +21,7 @@ import android.content.Context; import android.opengl.GLES20; import android.opengl.Matrix; import android.util.Pair; +import androidx.media3.common.ColorInfo; import androidx.media3.common.FrameProcessingException; import androidx.media3.common.util.GlProgram; import androidx.media3.common.util.GlUtil; @@ -112,11 +113,13 @@ import java.util.Arrays; Context context, boolean useHdr, MatrixTransformation matrixTransformation) throws FrameProcessingException { this( - context, + createGlProgram( + context, + /* inputOpticalColorsFromExternalTexture= */ false, + useHdr, + /* outputOpticalColors= */ false), ImmutableList.of(matrixTransformation), - /* sampleFromExternalTexture= */ false, - useHdr, - /* outputOpticalColors= */ false); + useHdr); } /** @@ -133,43 +136,89 @@ import java.util.Arrays; Context context, boolean useHdr, GlMatrixTransformation matrixTransformation) throws FrameProcessingException { this( - context, + createGlProgram( + context, + /* inputOpticalColorsFromExternalTexture= */ false, + useHdr, + /* outputOpticalColors= */ false), ImmutableList.of(matrixTransformation), - /* sampleFromExternalTexture= */ false, - useHdr, - /* outputOpticalColors= */ false); + useHdr); } /** * Creates a new instance. * + *

Able to convert optical {@link ColorInfo} inputs and outputs to and from the intermediate + * {@link GlTextureProcessor} colors of linear RGB BT.2020 for HDR, and gamma RGB BT.709 for SDR. + * * @param context The {@link Context}. * @param matrixTransformations The {@link GlMatrixTransformation GlMatrixTransformations} to * apply to each frame in order. - * @param sampleFromExternalTexture Whether the input will be provided using an external texture. - * If {@code true}, the caller should use {@link #setTextureTransformMatrix(float[])} to - * provide the transformation matrix associated with the external texture. - * @param useHdr Whether to process the input as an HDR signal. Using HDR requires the {@code - * EXT_YUV_target} OpenGL extension. - * @param outputOpticalColors If {@code true} and {@code useHdr} is also {@code true}, outputs a - * non-linear optical, or display light colors, possibly by applying the EOTF (Electro-optical - * transfer function). Otherwise, outputs linear electrical colors. + * @param inputOpticalColorsFromExternalTexture Whether optical color input will be provided using + * an external texture. If {@code true}, the caller should use {@link + * #setTextureTransformMatrix(float[])} to provide the transformation matrix associated with + * the external texture. + * @param opticalColorInfo The optical {@link ColorInfo}, only used to transform between color + * spaces and transfers, when {@code inputOpticalColorsFromExternalTexture} or {@code + * outputOpticalColors} are {@code true}. If it {@link ColorInfo#isHdr(ColorInfo)}, + * intermediate {@link GlTextureProcessor} colors will be in linear RGB BT.2020. Otherwise, + * these colors will be in gamma RGB BT.709. + * @param outputOpticalColors If {@code true}, outputs {@code opticalColorInfo}. If {@code false}, + * outputs intermediate colors of linear RGB BT.2020 if {@code opticalColorInfo} {@link + * ColorInfo#isHdr(ColorInfo)}, and gamma RGB BT.709 otherwise. * @throws FrameProcessingException If a problem occurs while reading shader files or an OpenGL * operation fails or is unsupported. */ public MatrixTransformationProcessor( Context context, ImmutableList matrixTransformations, - boolean sampleFromExternalTexture, - boolean useHdr, + boolean inputOpticalColorsFromExternalTexture, + ColorInfo opticalColorInfo, boolean outputOpticalColors) throws FrameProcessingException { - super(useHdr); - if (sampleFromExternalTexture && useHdr && !GlUtil.isYuvTargetExtensionSupported()) { + this( + createGlProgram( + context, + inputOpticalColorsFromExternalTexture, + ColorInfo.isHdr(opticalColorInfo), + outputOpticalColors), + matrixTransformations, + ColorInfo.isHdr(opticalColorInfo)); + if (!ColorInfo.isHdr(opticalColorInfo) || !inputOpticalColorsFromExternalTexture) { + return; + } + // TODO(b/227624622): Implement YUV to RGB conversions in COLOR_RANGE_LIMITED as well, using + // opticalColorInfo.colorRange to select between them. + + // In HDR editing mode the decoder output is sampled in YUV. + if (!GlUtil.isYuvTargetExtensionSupported()) { throw new FrameProcessingException( "The EXT_YUV_target extension is required for HDR editing input."); } + glProgram.setFloatsUniform("uColorTransform", MATRIX_YUV_TO_BT2020_COLOR_TRANSFORM); + // TODO(b/227624622): Implement PQ and gamma TFs, and use an @IntDef to select between HLG, + // PQ, and gamma, coming from opticalColorInfo.colorTransfer. + // Applying the OETF will output a linear signal. Not applying the OETF will output an optical + // signal. + glProgram.setFloatUniform("uApplyHlgOetf", outputOpticalColors ? 0.0f : 1.0f); + } + + /** + * Creates a new instance. + * + * @param glProgram The {@link GlProgram}. + * @param matrixTransformations The {@link GlMatrixTransformation GlMatrixTransformations} to + * apply to each frame in order. + * @param useHdr Whether to process the input as an HDR signal. Using HDR requires the {@code + * EXT_YUV_target} OpenGL extension. + */ + private MatrixTransformationProcessor( + GlProgram glProgram, + ImmutableList matrixTransformations, + boolean useHdr) { + super(useHdr); + this.glProgram = glProgram; this.matrixTransformations = matrixTransformations; transformationMatrixCache = new float[matrixTransformations.size()][16]; @@ -177,42 +226,44 @@ import java.util.Arrays; tempResultMatrix = new float[16]; Matrix.setIdentityM(compositeTransformationMatrix, /* smOffset= */ 0); visiblePolygon = NDC_SQUARE; + } + + private static GlProgram createGlProgram( + Context context, + boolean inputOpticalColorsFromExternalTexture, + boolean useHdr, + boolean outputOpticalColors) + throws FrameProcessingException { String vertexShaderFilePath; String fragmentShaderFilePath; - if (sampleFromExternalTexture) { - vertexShaderFilePath = - useHdr ? VERTEX_SHADER_TRANSFORMATION_ES3_PATH : VERTEX_SHADER_TRANSFORMATION_PATH; - fragmentShaderFilePath = - useHdr ? FRAGMENT_SHADER_COPY_EXTERNAL_YUV_ES3_PATH : FRAGMENT_SHADER_COPY_EXTERNAL_PATH; - } else if (outputOpticalColors) { - vertexShaderFilePath = - useHdr ? VERTEX_SHADER_TRANSFORMATION_ES3_PATH : VERTEX_SHADER_TRANSFORMATION_PATH; - fragmentShaderFilePath = - useHdr ? FRAGMENT_SHADER_HLG_EOTF_ES3_PATH : FRAGMENT_SHADER_COPY_PATH; + if (inputOpticalColorsFromExternalTexture) { + if (useHdr) { + vertexShaderFilePath = VERTEX_SHADER_TRANSFORMATION_ES3_PATH; + fragmentShaderFilePath = FRAGMENT_SHADER_COPY_EXTERNAL_YUV_ES3_PATH; + } else { + vertexShaderFilePath = VERTEX_SHADER_TRANSFORMATION_PATH; + fragmentShaderFilePath = FRAGMENT_SHADER_COPY_EXTERNAL_PATH; + } + } else if (outputOpticalColors && useHdr) { + vertexShaderFilePath = VERTEX_SHADER_TRANSFORMATION_ES3_PATH; + fragmentShaderFilePath = FRAGMENT_SHADER_HLG_EOTF_ES3_PATH; } else { vertexShaderFilePath = VERTEX_SHADER_TRANSFORMATION_PATH; fragmentShaderFilePath = FRAGMENT_SHADER_COPY_PATH; } + GlProgram glProgram; try { glProgram = new GlProgram(context, vertexShaderFilePath, fragmentShaderFilePath); } catch (IOException | GlUtil.GlException e) { throw new FrameProcessingException(e); } - if (useHdr && sampleFromExternalTexture) { - // In HDR editing mode the decoder output is sampled in YUV. - glProgram.setFloatsUniform("uColorTransform", MATRIX_YUV_TO_BT2020_COLOR_TRANSFORM); - // TODO(b/227624622): Implement PQ, and use an @IntDef to select between HLG, PQ, and no - // transfer function. - // Applying the OETF will output a linear signal. Not applying the OETF will output an optical - // signal. - glProgram.setFloatUniform("uApplyHlgOetf", outputOpticalColors ? 0.0f : 1.0f); - } float[] identityMatrix = new float[16]; Matrix.setIdentityM(identityMatrix, /* smOffset= */ 0); glProgram.setFloatsUniform("uTexTransformationMatrix", identityMatrix); + return glProgram; } @Override @@ -276,11 +327,11 @@ import java.util.Arrays; visiblePolygon = NDC_SQUARE; for (float[] transformationMatrix : transformationMatrixCache) { Matrix.multiplyMM( - tempResultMatrix, + /* result= */ tempResultMatrix, /* resultOffset= */ 0, - transformationMatrix, + /* lhs= */ transformationMatrix, /* lhsOffset= */ 0, - compositeTransformationMatrix, + /* rhs= */ compositeTransformationMatrix, /* rhsOffset= */ 0); System.arraycopy( /* src= */ tempResultMatrix, diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java b/libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java index 912efd5bff..d536e1add9 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java @@ -32,6 +32,7 @@ import androidx.media3.common.FrameInfo; import androidx.media3.common.FrameProcessingException; import androidx.media3.common.FrameProcessor; import androidx.media3.common.SurfaceInfo; +import androidx.media3.common.util.Log; import androidx.media3.common.util.Util; import androidx.media3.decoder.DecoderInputBuffer; import androidx.media3.effect.Presentation; @@ -141,10 +142,11 @@ import org.checkerframework.dataflow.qual.Pure; }, effectsListBuilder.build(), debugViewProvider, - // HDR is only used if the MediaCodec encoder supports FEATURE_HdrEditing. This - // implies that the OpenGL EXT_YUV_target extension is supported and hence the - // default FrameProcessor, GlEffectsFrameProcessor, also supports HDR. - /* useHdr= */ encoderWrapper.isHdrEditingEnabled()); + // HDR colors are only used if the MediaCodec encoder supports FEATURE_HdrEditing. + // This implies that the OpenGL EXT_YUV_target extension is supported and hence the + // default FrameProcessor, GlEffectsFrameProcessor, also supports HDR. Otherwise, tone + // mapping is applied, which ensures the decoder outputs SDR output for an HDR input. + encoderWrapper.getSupportedInputColor()); } catch (FrameProcessingException e) { throw TransformationException.createForFrameProcessingException( e, TransformationException.ERROR_CODE_FRAME_PROCESSING_FAILED); @@ -154,7 +156,8 @@ import org.checkerframework.dataflow.qual.Pure; decodedWidth, decodedHeight, inputFormat.pixelWidthHeightRatio, streamOffsetUs)); boolean isToneMappingRequired = - ColorInfo.isHdr(inputFormat.colorInfo) && !encoderWrapper.isHdrEditingEnabled(); + ColorInfo.isHdr(inputFormat.colorInfo) + && !ColorInfo.isHdr(encoderWrapper.getSupportedInputColor()); decoder = decoderFactory.createForVideoDecoding( inputFormat, frameProcessor.getInputSurface(), isToneMappingRequired); @@ -317,6 +320,7 @@ import org.checkerframework.dataflow.qual.Pure; */ @VisibleForTesting /* package */ static final class EncoderWrapper { + private static final String TAG = "EncoderWrapper"; private final Codec.EncoderFactory encoderFactory; private final Format inputFormat; @@ -353,11 +357,24 @@ import org.checkerframework.dataflow.qual.Pure; requestedOutputMimeType, inputFormat.colorInfo); } - /** Returns whether the wrapped encoder is expecting HDR input for the HDR editing use case. */ - public boolean isHdrEditingEnabled() { - return transformationRequest.enableHdrEditing - && !transformationRequest.enableRequestSdrToneMapping - && !supportedEncoderNamesForHdrEditing.isEmpty(); + /** Returns the {@link ColorInfo} expected from the input surface. */ + public ColorInfo getSupportedInputColor() { + boolean isHdrEditingEnabled = + transformationRequest.enableHdrEditing + && !transformationRequest.enableRequestSdrToneMapping + && !supportedEncoderNamesForHdrEditing.isEmpty(); + boolean isInputToneMapped = !isHdrEditingEnabled && ColorInfo.isHdr(inputFormat.colorInfo); + if (isInputToneMapped) { + // When tone-mapping HDR to SDR is enabled, assume we get BT.709 to avoid having the encoder + // populate default color info, which depends on the resolution. + // TODO(b/237674316): Get the color info from the decoder output media format instead. + return ColorInfo.SDR_BT709_LIMITED; + } + if (inputFormat.colorInfo == null) { + Log.d(TAG, "colorInfo is null. Defaulting to SDR_BT709_LIMITED."); + return ColorInfo.SDR_BT709_LIMITED; + } + return inputFormat.colorInfo; } @Nullable @@ -382,12 +399,6 @@ import org.checkerframework.dataflow.qual.Pure; outputRotationDegrees = 90; } - boolean isInputToneMapped = ColorInfo.isHdr(inputFormat.colorInfo) && !isHdrEditingEnabled(); - // When tone-mapping HDR to SDR is enabled, assume we get BT.709 to avoid having the encoder - // populate default color info, which depends on the resolution. - // TODO(b/237674316): Get the color info from the decoder output media format instead. - ColorInfo outputColorInfo = - isInputToneMapped ? ColorInfo.SDR_BT709_LIMITED : inputFormat.colorInfo; Format requestedEncoderFormat = new Format.Builder() .setWidth(requestedWidth) @@ -395,14 +406,14 @@ import org.checkerframework.dataflow.qual.Pure; .setRotationDegrees(0) .setFrameRate(inputFormat.frameRate) .setSampleMimeType(requestedOutputMimeType) - .setColorInfo(outputColorInfo) + .setColorInfo(getSupportedInputColor()) .build(); encoder = encoderFactory.createForVideoEncoding(requestedEncoderFormat, allowedOutputMimeTypes); Format encoderSupportedFormat = encoder.getConfigurationFormat(); - if (isHdrEditingEnabled()) { + if (ColorInfo.isHdr(requestedEncoderFormat.colorInfo)) { if (!requestedOutputMimeType.equals(encoderSupportedFormat.sampleMimeType)) { throw createEncodingException( new IllegalStateException("MIME type fallback unsupported with HDR editing"), @@ -413,6 +424,9 @@ import org.checkerframework.dataflow.qual.Pure; encoderSupportedFormat); } } + boolean isInputToneMapped = + ColorInfo.isHdr(inputFormat.colorInfo) + && !ColorInfo.isHdr(requestedEncoderFormat.colorInfo); fallbackListener.onTransformationRequestFinalized( createFallbackTransformationRequest( transformationRequest,