HDR: Use HdrMode IntDef to select HDR output mode.

This is a simpler API to use than the prior tone mapping, HDR editing, and
forceInterpretHdrAsSdr options

PiperOrigin-RevId: 488742391
This commit is contained in:
huangdarwin 2022-11-15 21:22:36 +00:00 committed by microkatz
parent 1143edc59a
commit 53cff556bf
15 changed files with 200 additions and 198 deletions

View File

@ -42,8 +42,10 @@ import androidx.core.app.ActivityCompat;
import androidx.media3.common.C;
import androidx.media3.common.MimeTypes;
import androidx.media3.common.util.Util;
import androidx.media3.transformer.TransformationRequest;
import com.google.android.material.slider.RangeSlider;
import com.google.android.material.slider.Slider;
import com.google.common.collect.ImmutableMap;
import java.util.Arrays;
import java.util.List;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@ -67,8 +69,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
public static final String TRIM_END_MS = "trim_end_ms";
public static final String ENABLE_FALLBACK = "enable_fallback";
public static final String ENABLE_DEBUG_PREVIEW = "enable_debug_preview";
public static final String ENABLE_REQUEST_SDR_TONE_MAPPING = "enable_request_sdr_tone_mapping";
public static final String FORCE_INTERPRET_HDR_VIDEO_AS_SDR = "force_interpret_hdr_video_as_sdr";
public static final String HDR_MODE = "hdr_mode";
public static final String DEMO_EFFECTS_SELECTIONS = "demo_effects_selections";
public static final String PERIODIC_VIGNETTE_CENTER_X = "periodic_vignette_center_x";
public static final String PERIODIC_VIGNETTE_CENTER_Y = "periodic_vignette_center_y";
@ -131,12 +132,21 @@ public final class ConfigurationActivity extends AppCompatActivity {
"Overlay logo & timer",
"Zoom in start",
};
private static final ImmutableMap<String, @TransformationRequest.HdrMode Integer>
HDR_MODE_DESCRIPTIONS =
new ImmutableMap.Builder<String, @TransformationRequest.HdrMode Integer>()
.put("Keep HDR", TransformationRequest.HDR_MODE_KEEP_HDR)
.put("Tone-map HDR to SDR", TransformationRequest.HDR_MODE_TONE_MAP_HDR_TO_SDR)
.put(
"Force Interpret HDR as SDR",
TransformationRequest.HDR_MODE_EXPERIMENTAL_FORCE_INTERPRET_HDR_AS_SDR)
.build();
private static final String SAME_AS_INPUT_OPTION = "same as input";
private static final int COLOR_FILTERS_INDEX = 2;
private static final int RGB_ADJUSTMENTS_INDEX = 4;
private static final int HSL_ADJUSTMENT_INDEX = 5;
private static final int CONTRAST_INDEX = 6;
private static final int PERIODIC_VIGNETTE_INDEX = 7;
private static final String SAME_AS_INPUT_OPTION = "same as input";
private static final float HALF_DIAGONAL = 1f / (float) Math.sqrt(2);
private @MonotonicNonNull ActivityResultLauncher<Intent> localFilePickerLauncher;
@ -154,8 +164,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
private @MonotonicNonNull CheckBox trimCheckBox;
private @MonotonicNonNull CheckBox enableFallbackCheckBox;
private @MonotonicNonNull CheckBox enableDebugPreviewCheckBox;
private @MonotonicNonNull CheckBox enableRequestSdrToneMappingCheckBox;
private @MonotonicNonNull CheckBox forceInterpretHdrVideoAsSdrCheckBox;
private @MonotonicNonNull Spinner hdrModeSpinner;
private @MonotonicNonNull Button selectDemoEffectsButton;
private boolean @MonotonicNonNull [] demoEffectsSelections;
private @Nullable Uri localFileUri;
@ -247,11 +256,13 @@ public final class ConfigurationActivity extends AppCompatActivity {
enableFallbackCheckBox = findViewById(R.id.enable_fallback_checkbox);
enableDebugPreviewCheckBox = findViewById(R.id.enable_debug_preview_checkbox);
enableRequestSdrToneMappingCheckBox = findViewById(R.id.request_sdr_tone_mapping_checkbox);
enableRequestSdrToneMappingCheckBox.setEnabled(isRequestSdrToneMappingSupported());
findViewById(R.id.request_sdr_tone_mapping).setEnabled(isRequestSdrToneMappingSupported());
forceInterpretHdrVideoAsSdrCheckBox =
findViewById(R.id.force_interpret_hdr_video_as_sdr_checkbox);
ArrayAdapter<String> hdrModeAdapter =
new ArrayAdapter<>(/* context= */ this, R.layout.spinner_item);
hdrModeAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
hdrModeSpinner = findViewById(R.id.hdr_mode_spinner);
hdrModeSpinner.setAdapter(hdrModeAdapter);
hdrModeAdapter.addAll(HDR_MODE_DESCRIPTIONS.keySet());
demoEffectsSelections = new boolean[DEMO_EFFECTS.length];
selectDemoEffectsButton = findViewById(R.id.select_demo_effects_button);
@ -308,8 +319,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
"trimCheckBox",
"enableFallbackCheckBox",
"enableDebugPreviewCheckBox",
"enableRequestSdrToneMappingCheckBox",
"forceInterpretHdrVideoAsSdrCheckBox",
"hdrModeSpinner",
"demoEffectsSelections"
})
private void startTransformation(View view) {
@ -347,10 +357,8 @@ public final class ConfigurationActivity extends AppCompatActivity {
}
bundle.putBoolean(ENABLE_FALLBACK, enableFallbackCheckBox.isChecked());
bundle.putBoolean(ENABLE_DEBUG_PREVIEW, enableDebugPreviewCheckBox.isChecked());
bundle.putBoolean(
ENABLE_REQUEST_SDR_TONE_MAPPING, enableRequestSdrToneMappingCheckBox.isChecked());
bundle.putBoolean(
FORCE_INTERPRET_HDR_VIDEO_AS_SDR, forceInterpretHdrVideoAsSdrCheckBox.isChecked());
String selectedhdrMode = String.valueOf(hdrModeSpinner.getSelectedItem());
bundle.putInt(HDR_MODE, checkNotNull(HDR_MODE_DESCRIPTIONS.get(selectedhdrMode)));
bundle.putBooleanArray(DEMO_EFFECTS_SELECTIONS, demoEffectsSelections);
bundle.putInt(COLOR_FILTER_SELECTION, colorFilterSelection);
bundle.putFloat(CONTRAST_VALUE, contrastValue);
@ -588,8 +596,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
"scaleSpinner",
"rotateSpinner",
"enableDebugPreviewCheckBox",
"enableRequestSdrToneMappingCheckBox",
"forceInterpretHdrVideoAsSdrCheckBox",
"hdrModeSpinner",
"selectDemoEffectsButton"
})
private void onRemoveAudio(View view) {
@ -609,8 +616,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
"scaleSpinner",
"rotateSpinner",
"enableDebugPreviewCheckBox",
"enableRequestSdrToneMappingCheckBox",
"forceInterpretHdrVideoAsSdrCheckBox",
"hdrModeSpinner",
"selectDemoEffectsButton"
})
private void onRemoveVideo(View view) {
@ -629,8 +635,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
"scaleSpinner",
"rotateSpinner",
"enableDebugPreviewCheckBox",
"enableRequestSdrToneMappingCheckBox",
"forceInterpretHdrVideoAsSdrCheckBox",
"hdrModeSpinner",
"selectDemoEffectsButton"
})
private void enableTrackSpecificOptions(boolean isAudioEnabled, boolean isVideoEnabled) {
@ -640,9 +645,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
scaleSpinner.setEnabled(isVideoEnabled);
rotateSpinner.setEnabled(isVideoEnabled);
enableDebugPreviewCheckBox.setEnabled(isVideoEnabled);
enableRequestSdrToneMappingCheckBox.setEnabled(
isRequestSdrToneMappingSupported() && isVideoEnabled);
forceInterpretHdrVideoAsSdrCheckBox.setEnabled(isVideoEnabled);
hdrModeSpinner.setEnabled(isVideoEnabled);
selectDemoEffectsButton.setEnabled(isVideoEnabled);
findViewById(R.id.audio_mime_text_view).setEnabled(isAudioEnabled);
@ -650,12 +653,6 @@ 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(isRequestSdrToneMappingSupported() && isVideoEnabled);
findViewById(R.id.force_interpret_hdr_video_as_sdr).setEnabled(isVideoEnabled);
}
private static boolean isRequestSdrToneMappingSupported() {
return Util.SDK_INT >= 31;
findViewById(R.id.hdr_mode).setEnabled(isVideoEnabled);
}
}

View File

@ -269,11 +269,7 @@ public final class TransformerActivity extends AppCompatActivity {
float rotateDegrees =
bundle.getFloat(ConfigurationActivity.ROTATE_DEGREES, /* defaultValue= */ 0);
requestBuilder.setRotationDegrees(rotateDegrees);
requestBuilder.setEnableRequestSdrToneMapping(
bundle.getBoolean(ConfigurationActivity.ENABLE_REQUEST_SDR_TONE_MAPPING));
requestBuilder.experimental_setForceInterpretHdrVideoAsSdr(
bundle.getBoolean(ConfigurationActivity.FORCE_INTERPRET_HDR_VIDEO_AS_SDR));
requestBuilder.setHdrMode(bundle.getInt(ConfigurationActivity.HDR_MODE));
transformerBuilder
.setTransformationRequest(requestBuilder.build())
.setRemoveAudio(bundle.getBoolean(ConfigurationActivity.SHOULD_REMOVE_AUDIO))

View File

@ -198,24 +198,15 @@
android:checked="true"/>
</TableRow>
<TableRow
android:layout_weight="1">
android:layout_weight="1"
android:gravity="center_vertical" >
<TextView
android:layout_gravity="center_vertical"
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="end" />
</TableRow>
<TableRow
android:layout_weight="1">
<TextView
android:layout_gravity="center_vertical"
android:id="@+id/force_interpret_hdr_video_as_sdr"
android:text="@string/force_interpret_hdr_video_as_sdr" />
<CheckBox
android:id="@+id/force_interpret_hdr_video_as_sdr_checkbox"
android:layout_gravity="end" />
android:id="@+id/hdr_mode"
android:text="@string/hdr_mode"/>
<Spinner
android:id="@+id/hdr_mode_spinner"
android:layout_gravity="right|center_vertical"
android:gravity="right" />
</TableRow>
</TableLayout>
</androidx.core.widget.NestedScrollView>

View File

@ -30,8 +30,7 @@
<string name="enable_fallback" translatable="false">Enable fallback</string>
<string name="enable_debug_preview" translatable="false">Enable debug preview</string>
<string name="trim" translatable="false">Trim</string>
<string name="request_sdr_tone_mapping" translatable="false">Request SDR tone-mapping (API 31+)</string>
<string name="force_interpret_hdr_video_as_sdr" translatable="false">[Experimental] Force interpret HDR video as SDR (API 29+)</string>
<string name="hdr_mode" translatable="false">HDR mode</string>
<string name="select_demo_effects" translatable="false">Add demo effects</string>
<string name="periodic_vignette_options" translatable="false">Periodic vignette options</string>
<string name="no_media_pipe_error" translatable="false">Failed to load MediaPipe processor. Check the README for instructions.</string>

View File

@ -397,10 +397,10 @@ public class TransformerAndroidTestRunner {
@Override
public Codec createForVideoDecoding(
Format format, Surface outputSurface, boolean enableRequestSdrToneMapping)
Format format, Surface outputSurface, boolean requestSdrToneMapping)
throws TransformationException {
Codec videoDecoder =
decoderFactory.createForVideoDecoding(format, outputSurface, enableRequestSdrToneMapping);
decoderFactory.createForVideoDecoding(format, outputSurface, requestSdrToneMapping);
videoDecoderName = videoDecoder.getName();
return videoDecoder;
}

View File

@ -39,11 +39,12 @@ import org.junit.runner.RunWith;
/**
* {@link Transformer} instrumentation test for {@linkplain
* TransformationRequest#forceInterpretHdrVideoAsSdr forcing HDR contents to be interpreted as SDR}.
* TransformationRequest#HDR_MODE_EXPERIMENTAL_FORCE_INTERPRET_HDR_AS_SDR forcing HDR contents to be
* interpreted as SDR}.
*/
@RunWith(AndroidJUnit4.class)
public class SetForceInterpretHdrVideoAsSdrTest {
public static final String TAG = "SetForceInterpretHdrVideoAsSdrTest";
public class ForceInterpretHdrVideoAsSdrTest {
public static final String TAG = "ForceInterpretHdrVideoAsSdrTest";
@Test
public void forceInterpretHdrVideoAsSdrTest_hdr10File_transformsOrThrows() throws Exception {
@ -62,7 +63,8 @@ public class SetForceInterpretHdrVideoAsSdrTest {
new Transformer.Builder(context)
.setTransformationRequest(
new TransformationRequest.Builder()
.experimental_setForceInterpretHdrVideoAsSdr(true)
.setHdrMode(
TransformationRequest.HDR_MODE_EXPERIMENTAL_FORCE_INTERPRET_HDR_AS_SDR)
.build())
.build();
try {
@ -97,7 +99,8 @@ public class SetForceInterpretHdrVideoAsSdrTest {
new Transformer.Builder(context)
.setTransformationRequest(
new TransformationRequest.Builder()
.experimental_setForceInterpretHdrVideoAsSdr(true)
.setHdrMode(
TransformationRequest.HDR_MODE_EXPERIMENTAL_FORCE_INTERPRET_HDR_AS_SDR)
.build())
.build();
try {

View File

@ -43,10 +43,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.Test;
import org.junit.runner.RunWith;
/** {@link Transformer} instrumentation test for applying an HDR frame edit. */
/**
* {@link Transformer} instrumentation test for applying an {@linkplain
* TransformationRequest#HDR_MODE_KEEP_HDR HDR frame edit}.
*/
@RunWith(AndroidJUnit4.class)
public class SetHdrEditingTest {
public static final String TAG = "SetHdrEditingTest";
public class HdrEditingTest {
public static final String TAG = "HdrEditingTest";
private static final ColorInfo HDR10_DEFAULT_COLOR_INFO =
new ColorInfo(
C.COLOR_SPACE_BT2020,
@ -176,9 +179,11 @@ public class SetHdrEditingTest {
TransformationRequest originalTransformationRequest,
TransformationRequest fallbackTransformationRequest) {
isFallbackListenerInvoked.set(true);
assertThat(originalTransformationRequest.enableRequestSdrToneMapping).isFalse();
assertThat(originalTransformationRequest.hdrMode)
.isEqualTo(TransformationRequest.HDR_MODE_KEEP_HDR);
isToneMappingFallbackApplied.set(
fallbackTransformationRequest.enableRequestSdrToneMapping);
fallbackTransformationRequest.hdrMode
== TransformationRequest.HDR_MODE_TONE_MAP_HDR_TO_SDR);
}
})
.build();
@ -227,9 +232,11 @@ public class SetHdrEditingTest {
TransformationRequest originalTransformationRequest,
TransformationRequest fallbackTransformationRequest) {
isFallbackListenerInvoked.set(true);
assertThat(originalTransformationRequest.enableRequestSdrToneMapping).isFalse();
assertThat(originalTransformationRequest.hdrMode)
.isEqualTo(TransformationRequest.HDR_MODE_KEEP_HDR);
isToneMappingFallbackApplied.set(
fallbackTransformationRequest.enableRequestSdrToneMapping);
fallbackTransformationRequest.hdrMode
== TransformationRequest.HDR_MODE_TONE_MAP_HDR_TO_SDR);
}
})
.build();

View File

@ -38,11 +38,11 @@ import org.junit.runner.RunWith;
/**
* {@link Transformer} instrumentation test for applying an {@linkplain
* TransformationRequest.Builder#setEnableRequestSdrToneMapping HDR to SDR tone mapping edit}.
* TransformationRequest#HDR_MODE_TONE_MAP_HDR_TO_SDR HDR to SDR tone mapping edit}.
*/
@RunWith(AndroidJUnit4.class)
public class SetHdrToSdrToneMapTest {
public static final String TAG = "SetHdrToSdrToneMapTest";
public class HdrToSdrToneMapTest {
public static final String TAG = "HdrToSdrToneMapTest";
@Test
public void transform_toneMapNoRequestedTranscode_hdr10File_toneMapsOrThrows() throws Exception {
@ -52,7 +52,9 @@ public class SetHdrToSdrToneMapTest {
Transformer transformer =
new Transformer.Builder(context)
.setTransformationRequest(
new TransformationRequest.Builder().setEnableRequestSdrToneMapping(true).build())
new TransformationRequest.Builder()
.setHdrMode(TransformationRequest.HDR_MODE_TONE_MAP_HDR_TO_SDR)
.build())
.addListener(
new Transformer.Listener() {
@Override
@ -62,8 +64,8 @@ public class SetHdrToSdrToneMapTest {
TransformationRequest fallbackTransformationRequest) {
// Tone mapping flag shouldn't change in fallback when tone mapping is
// requested.
assertThat(originalTransformationRequest.enableRequestSdrToneMapping)
.isEqualTo(fallbackTransformationRequest.enableRequestSdrToneMapping);
assertThat(originalTransformationRequest.hdrMode)
.isEqualTo(fallbackTransformationRequest.hdrMode);
}
})
.build();
@ -95,7 +97,9 @@ public class SetHdrToSdrToneMapTest {
Transformer transformer =
new Transformer.Builder(context)
.setTransformationRequest(
new TransformationRequest.Builder().setEnableRequestSdrToneMapping(true).build())
new TransformationRequest.Builder()
.setHdrMode(TransformationRequest.HDR_MODE_TONE_MAP_HDR_TO_SDR)
.build())
.addListener(
new Transformer.Listener() {
@Override
@ -105,8 +109,8 @@ public class SetHdrToSdrToneMapTest {
TransformationRequest fallbackTransformationRequest) {
// Tone mapping flag shouldn't change in fallback when tone mapping is
// requested.
assertThat(originalTransformationRequest.enableRequestSdrToneMapping)
.isEqualTo(fallbackTransformationRequest.enableRequestSdrToneMapping);
assertThat(originalTransformationRequest.hdrMode)
.isEqualTo(fallbackTransformationRequest.hdrMode);
}
})
.build();
@ -139,7 +143,7 @@ public class SetHdrToSdrToneMapTest {
new Transformer.Builder(context)
.setTransformationRequest(
new TransformationRequest.Builder()
.setEnableRequestSdrToneMapping(true)
.setHdrMode(TransformationRequest.HDR_MODE_TONE_MAP_HDR_TO_SDR)
.setRotationDegrees(180)
.build())
.addListener(
@ -151,8 +155,8 @@ public class SetHdrToSdrToneMapTest {
TransformationRequest fallbackTransformationRequest) {
// Tone mapping flag shouldn't change in fallback when tone mapping is
// requested.
assertThat(originalTransformationRequest.enableRequestSdrToneMapping)
.isEqualTo(fallbackTransformationRequest.enableRequestSdrToneMapping);
assertThat(originalTransformationRequest.hdrMode)
.isEqualTo(fallbackTransformationRequest.hdrMode);
}
})
.build();
@ -185,7 +189,7 @@ public class SetHdrToSdrToneMapTest {
new Transformer.Builder(context)
.setTransformationRequest(
new TransformationRequest.Builder()
.setEnableRequestSdrToneMapping(true)
.setHdrMode(TransformationRequest.HDR_MODE_TONE_MAP_HDR_TO_SDR)
.setRotationDegrees(180)
.build())
.addListener(
@ -197,8 +201,8 @@ public class SetHdrToSdrToneMapTest {
TransformationRequest fallbackTransformationRequest) {
// Tone mapping flag shouldn't change in fallback when tone mapping is
// requested.
assertThat(originalTransformationRequest.enableRequestSdrToneMapping)
.isEqualTo(fallbackTransformationRequest.enableRequestSdrToneMapping);
assertThat(originalTransformationRequest.hdrMode)
.isEqualTo(fallbackTransformationRequest.hdrMode);
}
})
.build();

View File

@ -57,12 +57,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.
* @param requestSdrToneMapping 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, boolean enableRequestSdrToneMapping)
Format format, Surface outputSurface, boolean requestSdrToneMapping)
throws TransformationException;
}

View File

@ -72,7 +72,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
@SuppressLint("InlinedApi")
@Override
public Codec createForVideoDecoding(
Format format, Surface outputSurface, boolean enableRequestSdrToneMapping)
Format format, Surface outputSurface, boolean requestSdrToneMapping)
throws TransformationException {
MediaFormat mediaFormat =
MediaFormat.createVideoFormat(
@ -87,7 +87,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
// transformer to decode as many frames as possible in one render cycle.
mediaFormat.setInteger(MediaFormat.KEY_ALLOW_FRAME_DROP, 0);
}
if (SDK_INT >= 31 && enableRequestSdrToneMapping) {
if (SDK_INT >= 31 && requestSdrToneMapping) {
mediaFormat.setInteger(
MediaFormat.KEY_COLOR_TRANSFER_REQUEST, MediaFormat.COLOR_TRANSFER_SDR_VIDEO);
}

View File

@ -95,10 +95,8 @@ import androidx.media3.common.util.Util;
if (transformationRequest.outputHeight != originalTransformationRequest.outputHeight) {
fallbackRequestBuilder.setResolution(transformationRequest.outputHeight);
}
if (transformationRequest.enableRequestSdrToneMapping
!= originalTransformationRequest.enableRequestSdrToneMapping) {
fallbackRequestBuilder.setEnableRequestSdrToneMapping(
transformationRequest.enableRequestSdrToneMapping);
if (transformationRequest.hdrMode != originalTransformationRequest.hdrMode) {
fallbackRequestBuilder.setHdrMode(transformationRequest.hdrMode);
}
fallbackTransformationRequest = fallbackRequestBuilder.build();

View File

@ -17,7 +17,10 @@
package androidx.media3.transformer;
import static androidx.media3.common.util.Assertions.checkArgument;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.MimeTypes;
@ -26,11 +29,66 @@ import androidx.media3.common.util.Util;
import androidx.media3.exoplayer.source.MediaSource;
import androidx.media3.extractor.mp4.Mp4Extractor;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/** A media transformation request. */
@UnstableApi
public final class TransformationRequest {
/**
* The strategy to use to transcode or edit High Dynamic Range (HDR) input video.
*
* <p>One of {@link #HDR_MODE_KEEP_HDR}, {@link #HDR_MODE_TONE_MAP_HDR_TO_SDR}, or {@link
* #HDR_MODE_EXPERIMENTAL_FORCE_INTERPRET_HDR_AS_SDR}.
*
* <p>Standard Dynamic Range (SDR) input video is unaffected by these settings.
*/
@Documented
@Retention(SOURCE)
@Target(TYPE_USE)
@IntDef({
HDR_MODE_KEEP_HDR,
HDR_MODE_TONE_MAP_HDR_TO_SDR,
HDR_MODE_EXPERIMENTAL_FORCE_INTERPRET_HDR_AS_SDR
})
public @interface HdrMode {}
/**
* Processes HDR input as HDR, to generate HDR output.
*
* <p>Supported on API 31+, by some device and HDR format combinations.
*
* <p>If not supported, {@link Transformer} may fall back to {@link
* #HDR_MODE_TONE_MAP_HDR_TO_SDR}.
*/
public static final int HDR_MODE_KEEP_HDR = 0;
/**
* Tone map HDR input to SDR before processing, to generate SDR output.
*
* <p>Supported on API 31+, by some device and HDR format combinations. Tone-mapping is only
* guaranteed to be supported from Android T onwards.
*
* <p>If not supported, {@link Transformer} may throw a {@link TransformationException}.
*/
public static final int HDR_MODE_TONE_MAP_HDR_TO_SDR = 1;
/**
* Interpret HDR input as SDR, resulting in washed out video.
*
* <p>Supported on API 29+.
*
* <p>This is much more widely supported than {@link #HDR_MODE_KEEP_HDR} and {@link
* #HDR_MODE_TONE_MAP_HDR_TO_SDR}. However, as HDR transfer functions and metadata will be
* ignored, contents will be displayed incorrectly, likely with a washed out look.
*
* <p>Use of this flag may result in {@code
* TransformationException.ERROR_CODE_HDR_DECODING_UNSUPPORTED} or {@code
* ERROR_CODE_DECODING_FORMAT_UNSUPPORTED}.
*
* <p>This field is experimental, and will be renamed or removed in a future release.
*/
public static final int HDR_MODE_EXPERIMENTAL_FORCE_INTERPRET_HDR_AS_SDR = 2;
/** A builder for {@link TransformationRequest} instances. */
public static final class Builder {
@ -41,9 +99,7 @@ public final class TransformationRequest {
private int outputHeight;
@Nullable private String audioMimeType;
@Nullable private String videoMimeType;
private boolean enableRequestSdrToneMapping;
private boolean forceInterpretHdrVideoAsSdr;
private boolean enableHdrEditing;
private @HdrMode int hdrMode;
/**
* Creates a new instance with default values.
@ -55,7 +111,6 @@ public final class TransformationRequest {
scaleX = 1;
scaleY = 1;
outputHeight = C.LENGTH_UNSET;
enableHdrEditing = true;
}
private Builder(TransformationRequest transformationRequest) {
@ -66,9 +121,7 @@ public final class TransformationRequest {
this.outputHeight = transformationRequest.outputHeight;
this.audioMimeType = transformationRequest.audioMimeType;
this.videoMimeType = transformationRequest.videoMimeType;
this.enableRequestSdrToneMapping = transformationRequest.enableRequestSdrToneMapping;
this.forceInterpretHdrVideoAsSdr = transformationRequest.forceInterpretHdrVideoAsSdr;
this.enableHdrEditing = transformationRequest.enableHdrEditing;
this.hdrMode = transformationRequest.hdrMode;
}
/**
@ -217,75 +270,45 @@ public final class TransformationRequest {
}
/**
* 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.
* Sets the {@link HdrMode} for HDR video input.
*
* <p>The default value is {@code false}, which corresponds to editing and outputting HDR video
* if possible, and falling back to tone-mapping if not.
* <p>The default value is {@link #HDR_MODE_KEEP_HDR}.
*
* <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.
*
* <p>Setting this as {@code true} will set {@linkplain #enableHdrEditing} and {@linkplain
* #forceInterpretHdrVideoAsSdr} to {@code false}.
*
* @param enableRequestSdrToneMapping Whether to request tone-mapping down to SDR.
* @param hdrMode The {@link HdrMode} used.
* @return This builder.
*/
@CanIgnoreReturnValue
public Builder setHdrMode(@HdrMode int hdrMode) {
this.hdrMode = hdrMode;
return this;
}
/**
* @deprecated This method is now a no-op if {@code false}, and sets {@code
* setHdrMode(HDR_MODE_TONE_MAP_HDR_TO_SDR)} if {@code true}. Use {@link #setHdrMode} with
* {@link #HDR_MODE_TONE_MAP_HDR_TO_SDR} instead.
*/
@Deprecated
@CanIgnoreReturnValue
public Builder setEnableRequestSdrToneMapping(boolean enableRequestSdrToneMapping) {
this.enableRequestSdrToneMapping = enableRequestSdrToneMapping;
if (enableRequestSdrToneMapping) {
enableHdrEditing = false;
forceInterpretHdrVideoAsSdr = false;
return setHdrMode(HDR_MODE_TONE_MAP_HDR_TO_SDR);
}
return this;
}
/**
* Sets whether to interpret HDR video as SDR, resulting in washed out video.
*
* <p>The default value is {@code false}, which corresponds to editing and outputting HDR video
* if possible, and falling back to tone-mapping if not.
*
* <p>Use of this flag may result in {@code
* TransformationException.ERROR_CODE_HDR_DECODING_UNSUPPORTED} or {@code
* ERROR_CODE_DECODING_FORMAT_UNSUPPORTED}.
*
* <p>This method is experimental, and will be renamed or removed in a future release.
*
* <p>If enabled, HDR information will be interpreted as SDR, which is much more widely
* supported than tone mapping or HDR editing. However, as HDR transfer functions and metadata
* will be ignored, contents will be displayed incorrectly, likely with a washed out look.
*
* <p>The setting has no effect if the input is already in SDR.
*
* <p>Setting this as {@code true} will set {@linkplain #enableHdrEditing} and {@linkplain
* #forceInterpretHdrVideoAsSdr} to {@code false}.
*
* @param forceInterpretHdrVideoAsSdr Whether to interpret HDR contents as SDR.
* @return This builder.
*/
// TODO(http://b/258246130): Use IntDef to select between tone mapping, HDR editing, and this.
@CanIgnoreReturnValue
public Builder experimental_setForceInterpretHdrVideoAsSdr(
boolean forceInterpretHdrVideoAsSdr) {
this.forceInterpretHdrVideoAsSdr = forceInterpretHdrVideoAsSdr;
if (forceInterpretHdrVideoAsSdr) {
enableRequestSdrToneMapping = false;
enableHdrEditing = false;
}
return this;
}
/**
* @deprecated This method is now a no-op. (@code experimental_setEnableHdrEditing(true)} is now
* the default behavior.
* @deprecated This method is now a no-op if {@code false}, and sets {@code
* setHdrMode(HDR_MODE_KEEP_HDR)} if {@code true}. {@code
* experimental_setEnableHdrEditing(true)} is now the default behavior. Use {@link
* #setHdrMode} with link {@link #HDR_MODE_KEEP_HDR} instead.
*/
@Deprecated
@CanIgnoreReturnValue
public Builder experimental_setEnableHdrEditing(boolean enableHdrEditing) {
if (enableHdrEditing) {
return setHdrMode(HDR_MODE_KEEP_HDR);
}
return this;
}
@ -299,9 +322,7 @@ public final class TransformationRequest {
outputHeight,
audioMimeType,
videoMimeType,
enableRequestSdrToneMapping,
forceInterpretHdrVideoAsSdr,
enableHdrEditing);
hdrMode);
}
}
@ -351,14 +372,12 @@ 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 force interpreting HDR video as SDR. */
public final boolean forceInterpretHdrVideoAsSdr;
/** Whether to attempt to process any input video stream as a high dynamic range (HDR) signal. */
public final boolean enableHdrEditing;
/**
* The {@link HdrMode} specifying how to handle HDR input video.
*
* @see Builder#setHdrMode(int)
*/
public final @HdrMode int hdrMode;
private TransformationRequest(
boolean flattenForSlowMotion,
@ -368,12 +387,7 @@ public final class TransformationRequest {
int outputHeight,
@Nullable String audioMimeType,
@Nullable String videoMimeType,
boolean enableRequestSdrToneMapping,
boolean forceInterpretHdrVideoAsSdr,
boolean enableHdrEditing) {
checkArgument(!forceInterpretHdrVideoAsSdr || !enableRequestSdrToneMapping);
checkArgument(!enableHdrEditing || !forceInterpretHdrVideoAsSdr);
checkArgument(!enableHdrEditing || !enableRequestSdrToneMapping);
@HdrMode int hdrMode) {
this.flattenForSlowMotion = flattenForSlowMotion;
this.scaleX = scaleX;
@ -382,9 +396,7 @@ public final class TransformationRequest {
this.outputHeight = outputHeight;
this.audioMimeType = audioMimeType;
this.videoMimeType = videoMimeType;
this.enableRequestSdrToneMapping = enableRequestSdrToneMapping;
this.forceInterpretHdrVideoAsSdr = forceInterpretHdrVideoAsSdr;
this.enableHdrEditing = enableHdrEditing;
this.hdrMode = hdrMode;
}
@Override
@ -403,9 +415,7 @@ public final class TransformationRequest {
&& outputHeight == that.outputHeight
&& Util.areEqual(audioMimeType, that.audioMimeType)
&& Util.areEqual(videoMimeType, that.videoMimeType)
&& enableRequestSdrToneMapping == that.enableRequestSdrToneMapping
&& forceInterpretHdrVideoAsSdr == that.forceInterpretHdrVideoAsSdr
&& enableHdrEditing == that.enableHdrEditing;
&& hdrMode == that.hdrMode;
}
@Override
@ -417,9 +427,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 + (forceInterpretHdrVideoAsSdr ? 1 : 0);
result = 31 * result + (enableHdrEditing ? 1 : 0);
result = 31 * result + hdrMode;
return result;
}

View File

@ -514,7 +514,7 @@ public final class Transformer {
* @param fallbackTransformationRequest The alternative {@link TransformationRequest}, with
* supported {@link TransformationRequest#audioMimeType}, {@link
* TransformationRequest#videoMimeType}, {@link TransformationRequest#outputHeight}, and
* {@link TransformationRequest#enableRequestSdrToneMapping} values set.
* {@link TransformationRequest#hdrMode} values set.
*/
default void onFallbackApplied(
MediaItem inputMediaItem,

View File

@ -289,10 +289,7 @@ import java.util.List;
if (encoderFactory.videoNeedsEncoding()) {
return true;
}
if (transformationRequest.enableRequestSdrToneMapping) {
return true;
}
if (transformationRequest.forceInterpretHdrVideoAsSdr) {
if (transformationRequest.hdrMode != TransformationRequest.HDR_MODE_KEEP_HDR) {
return true;
}
if (transformationRequest.videoMimeType != null

View File

@ -17,7 +17,6 @@
package androidx.media3.transformer;
import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.common.util.Assertions.checkState;
import static androidx.media3.common.util.Util.SDK_INT;
import android.content.Context;
@ -87,7 +86,8 @@ import org.checkerframework.dataflow.qual.Pure;
muxerWrapper);
if (ColorInfo.isTransferHdr(inputFormat.colorInfo)) {
if (transformationRequest.forceInterpretHdrVideoAsSdr) {
if (transformationRequest.hdrMode
== TransformationRequest.HDR_MODE_EXPERIMENTAL_FORCE_INTERPRET_HDR_AS_SDR) {
if (SDK_INT < 29) {
throw TransformationException.createForCodec(
new IllegalArgumentException("Interpreting HDR video as SDR is not supported."),
@ -281,7 +281,7 @@ import org.checkerframework.dataflow.qual.Pure;
* processing, with {@link Format#rotationDegrees} of 90 added to the output format.
* @param requestedFormat The requested format.
* @param supportedFormat A format supported by the device.
* @param isToneMappedToSdr Whether tone mapping to SDR will be applied.
* @param supportedHdrMode A {@link TransformationRequest.HdrMode} supported by the device.
* @return The created instance.
*/
@Pure
@ -290,24 +290,21 @@ import org.checkerframework.dataflow.qual.Pure;
boolean hasOutputFormatRotation,
Format requestedFormat,
Format supportedFormat,
boolean isToneMappedToSdr) {
@TransformationRequest.HdrMode int supportedHdrMode) {
// TODO(b/210591626): Also update bitrate etc. once encoder configuration and fallback are
// implemented.
if (transformationRequest.enableRequestSdrToneMapping == isToneMappedToSdr
if (transformationRequest.hdrMode == supportedHdrMode
&& Util.areEqual(requestedFormat.sampleMimeType, supportedFormat.sampleMimeType)
&& (hasOutputFormatRotation
? requestedFormat.width == supportedFormat.width
: requestedFormat.height == supportedFormat.height)) {
return transformationRequest;
}
TransformationRequest.Builder transformationRequestBuilder = transformationRequest.buildUpon();
if (transformationRequest.enableRequestSdrToneMapping != isToneMappedToSdr) {
checkState(isToneMappedToSdr);
transformationRequestBuilder.setEnableRequestSdrToneMapping(true);
}
return transformationRequestBuilder
return transformationRequest
.buildUpon()
.setVideoMimeType(supportedFormat.sampleMimeType)
.setResolution(hasOutputFormatRotation ? requestedFormat.width : requestedFormat.height)
.setHdrMode(supportedHdrMode)
.build();
}
@ -408,8 +405,7 @@ import org.checkerframework.dataflow.qual.Pure;
/** Returns the {@link ColorInfo} expected from the input surface. */
public ColorInfo getSupportedInputColor() {
boolean isHdrEditingEnabled =
transformationRequest.enableHdrEditing
&& !transformationRequest.enableRequestSdrToneMapping
transformationRequest.hdrMode == TransformationRequest.HDR_MODE_KEEP_HDR
&& !supportedEncoderNamesForHdrEditing.isEmpty();
boolean isInputToneMapped =
!isHdrEditingEnabled && ColorInfo.isTransferHdr(inputFormat.colorInfo);
@ -476,13 +472,19 @@ import org.checkerframework.dataflow.qual.Pure;
boolean isInputToneMapped =
ColorInfo.isTransferHdr(inputFormat.colorInfo)
&& !ColorInfo.isTransferHdr(requestedEncoderFormat.colorInfo);
@TransformationRequest.HdrMode
int hdrMode =
isInputToneMapped
? TransformationRequest.HDR_MODE_TONE_MAP_HDR_TO_SDR
: transformationRequest.hdrMode;
fallbackListener.onTransformationRequestFinalized(
createSupportedTransformationRequest(
transformationRequest,
/* hasOutputFormatRotation= */ flipOrientation,
requestedEncoderFormat,
encoderSupportedFormat,
isInputToneMapped));
hdrMode));
encoderSurfaceInfo =
new SurfaceInfo(