HDR: Input ColorInfo to the FrameProcessor.
This allows the GlEffectsFrameProcessor to later handle HLG and PQ differently, or limited and full color range differently. No functional change intended in this CL. PiperOrigin-RevId: 466070764
This commit is contained in:
parent
579f25802a
commit
536d42c865
@ -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.
|
||||
*
|
||||
* <p>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 {
|
||||
|
||||
|
@ -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<Effect> effects,
|
||||
DebugViewProvider debugViewProvider,
|
||||
boolean useHdr)
|
||||
ColorInfo colorInfo)
|
||||
throws FrameProcessingException;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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<Long> 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<Integer, Integer> outputSize =
|
||||
|
@ -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<Effect> 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<Effect> 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<GlTextureProcessor> 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<GlTextureProcessor> 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();
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
*
|
||||
* <p>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<GlMatrixTransformation> 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<GlMatrixTransformation> 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,
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user