From 53cff556bf3de54cebe2715a9ad97f151163da36 Mon Sep 17 00:00:00 2001 From: huangdarwin Date: Tue, 15 Nov 2022 21:22:36 +0000 Subject: [PATCH] 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 --- .../transformer/ConfigurationActivity.java | 61 +++--- .../demo/transformer/TransformerActivity.java | 6 +- .../res/layout/configuration_activity.xml | 25 +-- .../src/main/res/values/strings.xml | 3 +- .../TransformerAndroidTestRunner.java | 4 +- ...a => ForceInterpretHdrVideoAsSdrTest.java} | 13 +- ...drEditingTest.java => HdrEditingTest.java} | 21 +- ...eMapTest.java => HdrToSdrToneMapTest.java} | 34 ++-- .../androidx/media3/transformer/Codec.java | 4 +- .../transformer/DefaultDecoderFactory.java | 4 +- .../media3/transformer/FallbackListener.java | 6 +- .../transformer/TransformationRequest.java | 180 +++++++++--------- .../media3/transformer/Transformer.java | 2 +- .../transformer/TransformerInternal.java | 5 +- .../VideoTranscodingSamplePipeline.java | 30 +-- 15 files changed, 200 insertions(+), 198 deletions(-) rename libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/{SetForceInterpretHdrVideoAsSdrTest.java => ForceInterpretHdrVideoAsSdrTest.java} (90%) rename libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/{SetHdrEditingTest.java => HdrEditingTest.java} (93%) rename libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/{SetHdrToSdrToneMapTest.java => HdrToSdrToneMapTest.java} (90%) diff --git a/demos/transformer/src/main/java/androidx/media3/demo/transformer/ConfigurationActivity.java b/demos/transformer/src/main/java/androidx/media3/demo/transformer/ConfigurationActivity.java index 3c9d4b69ca..b15f0c0f45 100644 --- a/demos/transformer/src/main/java/androidx/media3/demo/transformer/ConfigurationActivity.java +++ b/demos/transformer/src/main/java/androidx/media3/demo/transformer/ConfigurationActivity.java @@ -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 + HDR_MODE_DESCRIPTIONS = + new ImmutableMap.Builder() + .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 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 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); } } diff --git a/demos/transformer/src/main/java/androidx/media3/demo/transformer/TransformerActivity.java b/demos/transformer/src/main/java/androidx/media3/demo/transformer/TransformerActivity.java index 04a1da38e7..16eb49a7d4 100644 --- a/demos/transformer/src/main/java/androidx/media3/demo/transformer/TransformerActivity.java +++ b/demos/transformer/src/main/java/androidx/media3/demo/transformer/TransformerActivity.java @@ -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)) diff --git a/demos/transformer/src/main/res/layout/configuration_activity.xml b/demos/transformer/src/main/res/layout/configuration_activity.xml index bd2feb4484..f652bb8a5c 100644 --- a/demos/transformer/src/main/res/layout/configuration_activity.xml +++ b/demos/transformer/src/main/res/layout/configuration_activity.xml @@ -198,24 +198,15 @@ android:checked="true"/> + android:layout_weight="1" + android:gravity="center_vertical" > - - - - - + android:id="@+id/hdr_mode" + android:text="@string/hdr_mode"/> + diff --git a/demos/transformer/src/main/res/values/strings.xml b/demos/transformer/src/main/res/values/strings.xml index 96dcdfe542..4ea1fe46a7 100644 --- a/demos/transformer/src/main/res/values/strings.xml +++ b/demos/transformer/src/main/res/values/strings.xml @@ -30,8 +30,7 @@ Enable fallback Enable debug preview Trim - Request SDR tone-mapping (API 31+) - [Experimental] Force interpret HDR video as SDR (API 29+) + HDR mode Add demo effects Periodic vignette options Failed to load MediaPipe processor. Check the README for instructions. diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerAndroidTestRunner.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerAndroidTestRunner.java index 13c1bc4db7..7fa81d2008 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerAndroidTestRunner.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerAndroidTestRunner.java @@ -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; } diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/SetForceInterpretHdrVideoAsSdrTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/ForceInterpretHdrVideoAsSdrTest.java similarity index 90% rename from libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/SetForceInterpretHdrVideoAsSdrTest.java rename to libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/ForceInterpretHdrVideoAsSdrTest.java index 212e601a2c..1ea8bfc16f 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/SetForceInterpretHdrVideoAsSdrTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/ForceInterpretHdrVideoAsSdrTest.java @@ -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 { diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/SetHdrEditingTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/HdrEditingTest.java similarity index 93% rename from libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/SetHdrEditingTest.java rename to libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/HdrEditingTest.java index 1213561747..2a3449a03d 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/SetHdrEditingTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/HdrEditingTest.java @@ -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(); diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/SetHdrToSdrToneMapTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/HdrToSdrToneMapTest.java similarity index 90% rename from libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/SetHdrToSdrToneMapTest.java rename to libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/HdrToSdrToneMapTest.java index 8633a691ea..4b171ed129 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/SetHdrToSdrToneMapTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/HdrToSdrToneMapTest.java @@ -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(); diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/Codec.java b/libraries/transformer/src/main/java/androidx/media3/transformer/Codec.java index d33157a683..3067b71121 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/Codec.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/Codec.java @@ -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; } diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultDecoderFactory.java b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultDecoderFactory.java index ce0fbcfc76..294b6a3a2c 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultDecoderFactory.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultDecoderFactory.java @@ -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); } diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/FallbackListener.java b/libraries/transformer/src/main/java/androidx/media3/transformer/FallbackListener.java index 718cc70ec9..5cc43aed9e 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/FallbackListener.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/FallbackListener.java @@ -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(); diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationRequest.java b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationRequest.java index e77cccd7a9..14e42071cd 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationRequest.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformationRequest.java @@ -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. + * + *

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}. + * + *

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. + * + *

Supported on API 31+, by some device and HDR format combinations. + * + *

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. + * + *

Supported on API 31+, by some device and HDR format combinations. Tone-mapping is only + * guaranteed to be supported from Android T onwards. + * + *

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. + * + *

Supported on API 29+. + * + *

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. + * + *

Use of this flag may result in {@code + * TransformationException.ERROR_CODE_HDR_DECODING_UNSUPPORTED} or {@code + * ERROR_CODE_DECODING_FORMAT_UNSUPPORTED}. + * + *

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. * - *

The default value is {@code false}, which corresponds to editing and outputting HDR video - * if possible, and falling back to tone-mapping if not. + *

The default value is {@link #HDR_MODE_KEEP_HDR}. * - *

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. - * - *

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. - * - *

The default value is {@code false}, which corresponds to editing and outputting HDR video - * if possible, and falling back to tone-mapping if not. - * - *

Use of this flag may result in {@code - * TransformationException.ERROR_CODE_HDR_DECODING_UNSUPPORTED} or {@code - * ERROR_CODE_DECODING_FORMAT_UNSUPPORTED}. - * - *

This method is experimental, and will be renamed or removed in a future release. - * - *

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. - * - *

The setting has no effect if the input is already in SDR. - * - *

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; } diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java index e1a628db62..d9ffe41245 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java @@ -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, diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerInternal.java b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerInternal.java index b2bc558344..0fcd7bb05b 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerInternal.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerInternal.java @@ -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 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 bd6aa0b630..4d3bfcb748 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/VideoTranscodingSamplePipeline.java @@ -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(