Add support for requesting color transfer to SDR
From Android T onwards `MediaCodec` supports requesting tone-mapping down to SDR. Add an option to request this behavior and document that it isn't supported before T. Also add an option in the demo app to try it out. Tested manually on a prerelease build. PiperOrigin-RevId: 437765325
This commit is contained in:
parent
79db98e733
commit
d2a9419ad3
@ -54,6 +54,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
||||
public static final String SCALE_Y = "scale_y";
|
||||
public static final String ROTATE_DEGREES = "rotate_degrees";
|
||||
public static final String ENABLE_FALLBACK = "enable_fallback";
|
||||
public static final String ENABLE_REQUEST_SDR_TONE_MAPPING = "enable_request_sdr_tone_mapping";
|
||||
public static final String ENABLE_HDR_EDITING = "enable_hdr_editing";
|
||||
private static final String[] INPUT_URIS = {
|
||||
"https://html5demos.com/assets/dizzy.mp4",
|
||||
@ -84,6 +85,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
||||
private @MonotonicNonNull Spinner scaleSpinner;
|
||||
private @MonotonicNonNull Spinner rotateSpinner;
|
||||
private @MonotonicNonNull CheckBox enableFallbackCheckBox;
|
||||
private @MonotonicNonNull CheckBox enableRequestSdrToneMappingCheckBox;
|
||||
private @MonotonicNonNull CheckBox enableHdrEditingCheckBox;
|
||||
private int inputUriPosition;
|
||||
|
||||
@ -150,6 +152,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
||||
rotateAdapter.addAll(SAME_AS_INPUT_OPTION, "0", "10", "45", "60", "90", "180");
|
||||
|
||||
enableFallbackCheckBox = findViewById(R.id.enable_fallback_checkbox);
|
||||
enableRequestSdrToneMappingCheckBox = findViewById(R.id.request_sdr_tone_mapping_checkbox);
|
||||
enableHdrEditingCheckBox = findViewById(R.id.hdr_editing_checkbox);
|
||||
}
|
||||
|
||||
@ -179,6 +182,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
||||
"scaleSpinner",
|
||||
"rotateSpinner",
|
||||
"enableFallbackCheckBox",
|
||||
"enableRequestSdrToneMappingCheckBox",
|
||||
"enableHdrEditingCheckBox"
|
||||
})
|
||||
private void startTransformation(View view) {
|
||||
@ -211,6 +215,8 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
||||
bundle.putFloat(ROTATE_DEGREES, Float.parseFloat(selectedRotate));
|
||||
}
|
||||
bundle.putBoolean(ENABLE_FALLBACK, enableFallbackCheckBox.isChecked());
|
||||
bundle.putBoolean(
|
||||
ENABLE_REQUEST_SDR_TONE_MAPPING, enableRequestSdrToneMappingCheckBox.isChecked());
|
||||
bundle.putBoolean(ENABLE_HDR_EDITING, enableHdrEditingCheckBox.isChecked());
|
||||
transformerIntent.putExtras(bundle);
|
||||
|
||||
@ -243,6 +249,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
||||
"resolutionHeightSpinner",
|
||||
"scaleSpinner",
|
||||
"rotateSpinner",
|
||||
"enableRequestSdrToneMappingCheckBox",
|
||||
"enableHdrEditingCheckBox"
|
||||
})
|
||||
private void onRemoveAudio(View view) {
|
||||
@ -261,6 +268,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
||||
"resolutionHeightSpinner",
|
||||
"scaleSpinner",
|
||||
"rotateSpinner",
|
||||
"enableRequestSdrToneMappingCheckBox",
|
||||
"enableHdrEditingCheckBox"
|
||||
})
|
||||
private void onRemoveVideo(View view) {
|
||||
@ -278,6 +286,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
||||
"resolutionHeightSpinner",
|
||||
"scaleSpinner",
|
||||
"rotateSpinner",
|
||||
"enableRequestSdrToneMappingCheckBox",
|
||||
"enableHdrEditingCheckBox"
|
||||
})
|
||||
private void enableTrackSpecificOptions(boolean isAudioEnabled, boolean isVideoEnabled) {
|
||||
@ -286,6 +295,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
||||
resolutionHeightSpinner.setEnabled(isVideoEnabled);
|
||||
scaleSpinner.setEnabled(isVideoEnabled);
|
||||
rotateSpinner.setEnabled(isVideoEnabled);
|
||||
enableRequestSdrToneMappingCheckBox.setEnabled(isVideoEnabled);
|
||||
enableHdrEditingCheckBox.setEnabled(isVideoEnabled);
|
||||
|
||||
findViewById(R.id.audio_mime_text_view).setEnabled(isAudioEnabled);
|
||||
@ -293,6 +303,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
||||
findViewById(R.id.resolution_height_text_view).setEnabled(isVideoEnabled);
|
||||
findViewById(R.id.scale).setEnabled(isVideoEnabled);
|
||||
findViewById(R.id.rotate).setEnabled(isVideoEnabled);
|
||||
findViewById(R.id.request_sdr_tone_mapping).setEnabled(isVideoEnabled);
|
||||
findViewById(R.id.hdr_editing).setEnabled(isVideoEnabled);
|
||||
}
|
||||
}
|
||||
|
@ -225,6 +225,8 @@ public final class TransformerActivity extends AppCompatActivity {
|
||||
bundle.getFloat(ConfigurationActivity.ROTATE_DEGREES, /* defaultValue= */ 0);
|
||||
requestBuilder.setRotationDegrees(rotateDegrees);
|
||||
|
||||
requestBuilder.setEnableRequestSdrToneMapping(
|
||||
bundle.getBoolean(ConfigurationActivity.ENABLE_REQUEST_SDR_TONE_MAPPING));
|
||||
requestBuilder.experimental_setEnableHdrEditing(
|
||||
bundle.getBoolean(ConfigurationActivity.ENABLE_HDR_EDITING));
|
||||
transformerBuilder
|
||||
|
@ -169,6 +169,16 @@
|
||||
android:layout_gravity="right"
|
||||
android:checked="true"/>
|
||||
</TableRow>
|
||||
<TableRow
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical" >
|
||||
<TextView
|
||||
android:id="@+id/request_sdr_tone_mapping"
|
||||
android:text="@string/request_sdr_tone_mapping" />
|
||||
<CheckBox
|
||||
android:id="@+id/request_sdr_tone_mapping_checkbox"
|
||||
android:layout_gravity="right" />
|
||||
</TableRow>
|
||||
<TableRow
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical" >
|
||||
|
@ -27,8 +27,9 @@
|
||||
<string name="scale" translatable="false">Scale video</string>
|
||||
<string name="rotate" translatable="false">Rotate video (degrees)</string>
|
||||
<string name="enable_fallback" translatable="false">Enable fallback</string>
|
||||
<string name="transform" translatable="false">Transform</string>
|
||||
<string name="request_sdr_tone_mapping" translatable="false">Request SDR tone-mapping</string>
|
||||
<string name="hdr_editing" translatable="false">[Experimental] HDR editing</string>
|
||||
<string name="transform" translatable="false">Transform</string>
|
||||
<string name="debug_preview" translatable="false">Debug preview:</string>
|
||||
<string name="debug_preview_not_available" translatable="false">No debug preview available.</string>
|
||||
<string name="transformation_started" translatable="false">Transformation started</string>
|
||||
|
@ -58,10 +58,12 @@ public interface Codec {
|
||||
* @param format The {@link Format} (of the input data) used to determine the underlying decoder
|
||||
* and its configuration values.
|
||||
* @param outputSurface The {@link Surface} to which the decoder output is rendered.
|
||||
* @param enableRequestSdrToneMapping Whether to request tone-mapping to SDR.
|
||||
* @return A {@link Codec} for video decoding.
|
||||
* @throws TransformationException If no suitable {@link Codec} can be created.
|
||||
*/
|
||||
Codec createForVideoDecoding(Format format, Surface outputSurface)
|
||||
Codec createForVideoDecoding(
|
||||
Format format, Surface outputSurface, boolean enableRequestSdrToneMapping)
|
||||
throws TransformationException;
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,8 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Codec createForVideoDecoding(Format format, Surface outputSurface)
|
||||
public Codec createForVideoDecoding(
|
||||
Format format, Surface outputSurface, boolean enableRequestSdrToneMapping)
|
||||
throws TransformationException {
|
||||
MediaFormat mediaFormat =
|
||||
MediaFormat.createVideoFormat(
|
||||
@ -63,6 +64,10 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
// cycle. This key ensures no frame dropping when the decoder's output surface is full.
|
||||
mediaFormat.setInteger(MediaFormat.KEY_ALLOW_FRAME_DROP, 0);
|
||||
}
|
||||
if (SDK_INT >= 31 && enableRequestSdrToneMapping) {
|
||||
mediaFormat.setInteger(
|
||||
MediaFormat.KEY_COLOR_TRANSFER_REQUEST, MediaFormat.COLOR_TRANSFER_SDR_VIDEO);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
String mediaCodecName = EncoderUtil.findCodecForFormat(mediaFormat, /* isDecoder= */ true);
|
||||
|
@ -40,6 +40,7 @@ public final class TransformationRequest {
|
||||
private int outputHeight;
|
||||
@Nullable private String audioMimeType;
|
||||
@Nullable private String videoMimeType;
|
||||
private boolean enableRequestSdrToneMapping;
|
||||
private boolean enableHdrEditing;
|
||||
|
||||
/**
|
||||
@ -62,6 +63,7 @@ public final class TransformationRequest {
|
||||
this.outputHeight = transformationRequest.outputHeight;
|
||||
this.audioMimeType = transformationRequest.audioMimeType;
|
||||
this.videoMimeType = transformationRequest.videoMimeType;
|
||||
this.enableRequestSdrToneMapping = transformationRequest.enableRequestSdrToneMapping;
|
||||
this.enableHdrEditing = transformationRequest.enableHdrEditing;
|
||||
}
|
||||
|
||||
@ -194,13 +196,30 @@ public final class TransformationRequest {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether to request tone-mapping to standard dynamic range (SDR). If enabled and
|
||||
* supported, high dynamic range (HDR) input will be tone-mapped into an SDR opto-electrical
|
||||
* transfer function before processing.
|
||||
*
|
||||
* <p>The setting has no effect if the input is already in SDR, or if tone-mapping is not
|
||||
* supported. Currently tone-mapping is only guaranteed to be supported from Android T onwards.
|
||||
*
|
||||
* @param enableRequestSdrToneMapping Whether to request tone-mapping down to SDR.
|
||||
* @return This builder.
|
||||
*/
|
||||
public Builder setEnableRequestSdrToneMapping(boolean enableRequestSdrToneMapping) {
|
||||
this.enableRequestSdrToneMapping = enableRequestSdrToneMapping;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether to attempt to process any input video stream as a high dynamic range (HDR)
|
||||
* signal.
|
||||
*
|
||||
* <p>This method is experimental, and will be renamed or removed in a future release. The HDR
|
||||
* editing feature is under development and is intended for developing/testing HDR processing
|
||||
* and encoding support.
|
||||
* and encoding support. HDR editing can't be enabled at the same time as {@link
|
||||
* #setEnableRequestSdrToneMapping(boolean) SDR tone-mapping}.
|
||||
*
|
||||
* @param enableHdrEditing Whether to attempt to process any input video stream as a high
|
||||
* dynamic range (HDR) signal.
|
||||
@ -221,6 +240,7 @@ public final class TransformationRequest {
|
||||
outputHeight,
|
||||
audioMimeType,
|
||||
videoMimeType,
|
||||
enableRequestSdrToneMapping,
|
||||
enableHdrEditing);
|
||||
}
|
||||
}
|
||||
@ -271,6 +291,9 @@ public final class TransformationRequest {
|
||||
* @see Builder#setVideoMimeType(String)
|
||||
*/
|
||||
@Nullable public final String videoMimeType;
|
||||
/** Whether to request tone-mapping to standard dynamic range (SDR). */
|
||||
public final boolean enableRequestSdrToneMapping;
|
||||
|
||||
/**
|
||||
* Whether to attempt to process any input video stream as a high dynamic range (HDR) signal.
|
||||
*
|
||||
@ -286,7 +309,9 @@ public final class TransformationRequest {
|
||||
int outputHeight,
|
||||
@Nullable String audioMimeType,
|
||||
@Nullable String videoMimeType,
|
||||
boolean enableRequestSdrToneMapping,
|
||||
boolean enableHdrEditing) {
|
||||
checkArgument(!enableHdrEditing || !enableRequestSdrToneMapping);
|
||||
this.flattenForSlowMotion = flattenForSlowMotion;
|
||||
this.scaleX = scaleX;
|
||||
this.scaleY = scaleY;
|
||||
@ -294,6 +319,7 @@ public final class TransformationRequest {
|
||||
this.outputHeight = outputHeight;
|
||||
this.audioMimeType = audioMimeType;
|
||||
this.videoMimeType = videoMimeType;
|
||||
this.enableRequestSdrToneMapping = enableRequestSdrToneMapping;
|
||||
this.enableHdrEditing = enableHdrEditing;
|
||||
}
|
||||
|
||||
@ -313,6 +339,7 @@ public final class TransformationRequest {
|
||||
&& outputHeight == that.outputHeight
|
||||
&& Util.areEqual(audioMimeType, that.audioMimeType)
|
||||
&& Util.areEqual(videoMimeType, that.videoMimeType)
|
||||
&& enableRequestSdrToneMapping == that.enableRequestSdrToneMapping
|
||||
&& enableHdrEditing == that.enableHdrEditing;
|
||||
}
|
||||
|
||||
@ -325,6 +352,7 @@ public final class TransformationRequest {
|
||||
result = 31 * result + outputHeight;
|
||||
result = 31 * result + (audioMimeType != null ? audioMimeType.hashCode() : 0);
|
||||
result = 31 * result + (videoMimeType != null ? videoMimeType.hashCode() : 0);
|
||||
result = 31 * result + (enableRequestSdrToneMapping ? 1 : 0);
|
||||
result = 31 * result + (enableHdrEditing ? 1 : 0);
|
||||
return result;
|
||||
}
|
||||
|
@ -107,6 +107,9 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
if (encoderFactory.videoNeedsEncoding()) {
|
||||
return false;
|
||||
}
|
||||
if (transformationRequest.enableRequestSdrToneMapping) {
|
||||
return false;
|
||||
}
|
||||
if (transformationRequest.enableHdrEditing) {
|
||||
return false;
|
||||
}
|
||||
|
@ -124,7 +124,10 @@ import org.checkerframework.dataflow.qual.Pure;
|
||||
encoderSupportedFormat.width, encoderSupportedFormat.height));
|
||||
|
||||
decoder =
|
||||
decoderFactory.createForVideoDecoding(inputFormat, frameProcessorChain.getInputSurface());
|
||||
decoderFactory.createForVideoDecoding(
|
||||
inputFormat,
|
||||
frameProcessorChain.getInputSurface(),
|
||||
transformationRequest.enableRequestSdrToneMapping);
|
||||
maxPendingFrameCount = getMaxPendingFrameCount();
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user