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:
parent
1143edc59a
commit
53cff556bf
@ -42,8 +42,10 @@ import androidx.core.app.ActivityCompat;
|
|||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
|
import androidx.media3.transformer.TransformationRequest;
|
||||||
import com.google.android.material.slider.RangeSlider;
|
import com.google.android.material.slider.RangeSlider;
|
||||||
import com.google.android.material.slider.Slider;
|
import com.google.android.material.slider.Slider;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
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 TRIM_END_MS = "trim_end_ms";
|
||||||
public static final String ENABLE_FALLBACK = "enable_fallback";
|
public static final String ENABLE_FALLBACK = "enable_fallback";
|
||||||
public static final String ENABLE_DEBUG_PREVIEW = "enable_debug_preview";
|
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 HDR_MODE = "hdr_mode";
|
||||||
public static final String FORCE_INTERPRET_HDR_VIDEO_AS_SDR = "force_interpret_hdr_video_as_sdr";
|
|
||||||
public static final String DEMO_EFFECTS_SELECTIONS = "demo_effects_selections";
|
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_X = "periodic_vignette_center_x";
|
||||||
public static final String PERIODIC_VIGNETTE_CENTER_Y = "periodic_vignette_center_y";
|
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",
|
"Overlay logo & timer",
|
||||||
"Zoom in start",
|
"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 COLOR_FILTERS_INDEX = 2;
|
||||||
private static final int RGB_ADJUSTMENTS_INDEX = 4;
|
private static final int RGB_ADJUSTMENTS_INDEX = 4;
|
||||||
private static final int HSL_ADJUSTMENT_INDEX = 5;
|
private static final int HSL_ADJUSTMENT_INDEX = 5;
|
||||||
private static final int CONTRAST_INDEX = 6;
|
private static final int CONTRAST_INDEX = 6;
|
||||||
private static final int PERIODIC_VIGNETTE_INDEX = 7;
|
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 static final float HALF_DIAGONAL = 1f / (float) Math.sqrt(2);
|
||||||
|
|
||||||
private @MonotonicNonNull ActivityResultLauncher<Intent> localFilePickerLauncher;
|
private @MonotonicNonNull ActivityResultLauncher<Intent> localFilePickerLauncher;
|
||||||
@ -154,8 +164,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
|||||||
private @MonotonicNonNull CheckBox trimCheckBox;
|
private @MonotonicNonNull CheckBox trimCheckBox;
|
||||||
private @MonotonicNonNull CheckBox enableFallbackCheckBox;
|
private @MonotonicNonNull CheckBox enableFallbackCheckBox;
|
||||||
private @MonotonicNonNull CheckBox enableDebugPreviewCheckBox;
|
private @MonotonicNonNull CheckBox enableDebugPreviewCheckBox;
|
||||||
private @MonotonicNonNull CheckBox enableRequestSdrToneMappingCheckBox;
|
private @MonotonicNonNull Spinner hdrModeSpinner;
|
||||||
private @MonotonicNonNull CheckBox forceInterpretHdrVideoAsSdrCheckBox;
|
|
||||||
private @MonotonicNonNull Button selectDemoEffectsButton;
|
private @MonotonicNonNull Button selectDemoEffectsButton;
|
||||||
private boolean @MonotonicNonNull [] demoEffectsSelections;
|
private boolean @MonotonicNonNull [] demoEffectsSelections;
|
||||||
private @Nullable Uri localFileUri;
|
private @Nullable Uri localFileUri;
|
||||||
@ -247,11 +256,13 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
enableFallbackCheckBox = findViewById(R.id.enable_fallback_checkbox);
|
enableFallbackCheckBox = findViewById(R.id.enable_fallback_checkbox);
|
||||||
enableDebugPreviewCheckBox = findViewById(R.id.enable_debug_preview_checkbox);
|
enableDebugPreviewCheckBox = findViewById(R.id.enable_debug_preview_checkbox);
|
||||||
enableRequestSdrToneMappingCheckBox = findViewById(R.id.request_sdr_tone_mapping_checkbox);
|
|
||||||
enableRequestSdrToneMappingCheckBox.setEnabled(isRequestSdrToneMappingSupported());
|
ArrayAdapter<String> hdrModeAdapter =
|
||||||
findViewById(R.id.request_sdr_tone_mapping).setEnabled(isRequestSdrToneMappingSupported());
|
new ArrayAdapter<>(/* context= */ this, R.layout.spinner_item);
|
||||||
forceInterpretHdrVideoAsSdrCheckBox =
|
hdrModeAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
findViewById(R.id.force_interpret_hdr_video_as_sdr_checkbox);
|
hdrModeSpinner = findViewById(R.id.hdr_mode_spinner);
|
||||||
|
hdrModeSpinner.setAdapter(hdrModeAdapter);
|
||||||
|
hdrModeAdapter.addAll(HDR_MODE_DESCRIPTIONS.keySet());
|
||||||
|
|
||||||
demoEffectsSelections = new boolean[DEMO_EFFECTS.length];
|
demoEffectsSelections = new boolean[DEMO_EFFECTS.length];
|
||||||
selectDemoEffectsButton = findViewById(R.id.select_demo_effects_button);
|
selectDemoEffectsButton = findViewById(R.id.select_demo_effects_button);
|
||||||
@ -308,8 +319,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
|||||||
"trimCheckBox",
|
"trimCheckBox",
|
||||||
"enableFallbackCheckBox",
|
"enableFallbackCheckBox",
|
||||||
"enableDebugPreviewCheckBox",
|
"enableDebugPreviewCheckBox",
|
||||||
"enableRequestSdrToneMappingCheckBox",
|
"hdrModeSpinner",
|
||||||
"forceInterpretHdrVideoAsSdrCheckBox",
|
|
||||||
"demoEffectsSelections"
|
"demoEffectsSelections"
|
||||||
})
|
})
|
||||||
private void startTransformation(View view) {
|
private void startTransformation(View view) {
|
||||||
@ -347,10 +357,8 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
bundle.putBoolean(ENABLE_FALLBACK, enableFallbackCheckBox.isChecked());
|
bundle.putBoolean(ENABLE_FALLBACK, enableFallbackCheckBox.isChecked());
|
||||||
bundle.putBoolean(ENABLE_DEBUG_PREVIEW, enableDebugPreviewCheckBox.isChecked());
|
bundle.putBoolean(ENABLE_DEBUG_PREVIEW, enableDebugPreviewCheckBox.isChecked());
|
||||||
bundle.putBoolean(
|
String selectedhdrMode = String.valueOf(hdrModeSpinner.getSelectedItem());
|
||||||
ENABLE_REQUEST_SDR_TONE_MAPPING, enableRequestSdrToneMappingCheckBox.isChecked());
|
bundle.putInt(HDR_MODE, checkNotNull(HDR_MODE_DESCRIPTIONS.get(selectedhdrMode)));
|
||||||
bundle.putBoolean(
|
|
||||||
FORCE_INTERPRET_HDR_VIDEO_AS_SDR, forceInterpretHdrVideoAsSdrCheckBox.isChecked());
|
|
||||||
bundle.putBooleanArray(DEMO_EFFECTS_SELECTIONS, demoEffectsSelections);
|
bundle.putBooleanArray(DEMO_EFFECTS_SELECTIONS, demoEffectsSelections);
|
||||||
bundle.putInt(COLOR_FILTER_SELECTION, colorFilterSelection);
|
bundle.putInt(COLOR_FILTER_SELECTION, colorFilterSelection);
|
||||||
bundle.putFloat(CONTRAST_VALUE, contrastValue);
|
bundle.putFloat(CONTRAST_VALUE, contrastValue);
|
||||||
@ -588,8 +596,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
|||||||
"scaleSpinner",
|
"scaleSpinner",
|
||||||
"rotateSpinner",
|
"rotateSpinner",
|
||||||
"enableDebugPreviewCheckBox",
|
"enableDebugPreviewCheckBox",
|
||||||
"enableRequestSdrToneMappingCheckBox",
|
"hdrModeSpinner",
|
||||||
"forceInterpretHdrVideoAsSdrCheckBox",
|
|
||||||
"selectDemoEffectsButton"
|
"selectDemoEffectsButton"
|
||||||
})
|
})
|
||||||
private void onRemoveAudio(View view) {
|
private void onRemoveAudio(View view) {
|
||||||
@ -609,8 +616,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
|||||||
"scaleSpinner",
|
"scaleSpinner",
|
||||||
"rotateSpinner",
|
"rotateSpinner",
|
||||||
"enableDebugPreviewCheckBox",
|
"enableDebugPreviewCheckBox",
|
||||||
"enableRequestSdrToneMappingCheckBox",
|
"hdrModeSpinner",
|
||||||
"forceInterpretHdrVideoAsSdrCheckBox",
|
|
||||||
"selectDemoEffectsButton"
|
"selectDemoEffectsButton"
|
||||||
})
|
})
|
||||||
private void onRemoveVideo(View view) {
|
private void onRemoveVideo(View view) {
|
||||||
@ -629,8 +635,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
|||||||
"scaleSpinner",
|
"scaleSpinner",
|
||||||
"rotateSpinner",
|
"rotateSpinner",
|
||||||
"enableDebugPreviewCheckBox",
|
"enableDebugPreviewCheckBox",
|
||||||
"enableRequestSdrToneMappingCheckBox",
|
"hdrModeSpinner",
|
||||||
"forceInterpretHdrVideoAsSdrCheckBox",
|
|
||||||
"selectDemoEffectsButton"
|
"selectDemoEffectsButton"
|
||||||
})
|
})
|
||||||
private void enableTrackSpecificOptions(boolean isAudioEnabled, boolean isVideoEnabled) {
|
private void enableTrackSpecificOptions(boolean isAudioEnabled, boolean isVideoEnabled) {
|
||||||
@ -640,9 +645,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
|||||||
scaleSpinner.setEnabled(isVideoEnabled);
|
scaleSpinner.setEnabled(isVideoEnabled);
|
||||||
rotateSpinner.setEnabled(isVideoEnabled);
|
rotateSpinner.setEnabled(isVideoEnabled);
|
||||||
enableDebugPreviewCheckBox.setEnabled(isVideoEnabled);
|
enableDebugPreviewCheckBox.setEnabled(isVideoEnabled);
|
||||||
enableRequestSdrToneMappingCheckBox.setEnabled(
|
hdrModeSpinner.setEnabled(isVideoEnabled);
|
||||||
isRequestSdrToneMappingSupported() && isVideoEnabled);
|
|
||||||
forceInterpretHdrVideoAsSdrCheckBox.setEnabled(isVideoEnabled);
|
|
||||||
selectDemoEffectsButton.setEnabled(isVideoEnabled);
|
selectDemoEffectsButton.setEnabled(isVideoEnabled);
|
||||||
|
|
||||||
findViewById(R.id.audio_mime_text_view).setEnabled(isAudioEnabled);
|
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.resolution_height_text_view).setEnabled(isVideoEnabled);
|
||||||
findViewById(R.id.scale).setEnabled(isVideoEnabled);
|
findViewById(R.id.scale).setEnabled(isVideoEnabled);
|
||||||
findViewById(R.id.rotate).setEnabled(isVideoEnabled);
|
findViewById(R.id.rotate).setEnabled(isVideoEnabled);
|
||||||
findViewById(R.id.request_sdr_tone_mapping)
|
findViewById(R.id.hdr_mode).setEnabled(isVideoEnabled);
|
||||||
.setEnabled(isRequestSdrToneMappingSupported() && isVideoEnabled);
|
|
||||||
findViewById(R.id.force_interpret_hdr_video_as_sdr).setEnabled(isVideoEnabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isRequestSdrToneMappingSupported() {
|
|
||||||
return Util.SDK_INT >= 31;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -269,11 +269,7 @@ public final class TransformerActivity extends AppCompatActivity {
|
|||||||
float rotateDegrees =
|
float rotateDegrees =
|
||||||
bundle.getFloat(ConfigurationActivity.ROTATE_DEGREES, /* defaultValue= */ 0);
|
bundle.getFloat(ConfigurationActivity.ROTATE_DEGREES, /* defaultValue= */ 0);
|
||||||
requestBuilder.setRotationDegrees(rotateDegrees);
|
requestBuilder.setRotationDegrees(rotateDegrees);
|
||||||
|
requestBuilder.setHdrMode(bundle.getInt(ConfigurationActivity.HDR_MODE));
|
||||||
requestBuilder.setEnableRequestSdrToneMapping(
|
|
||||||
bundle.getBoolean(ConfigurationActivity.ENABLE_REQUEST_SDR_TONE_MAPPING));
|
|
||||||
requestBuilder.experimental_setForceInterpretHdrVideoAsSdr(
|
|
||||||
bundle.getBoolean(ConfigurationActivity.FORCE_INTERPRET_HDR_VIDEO_AS_SDR));
|
|
||||||
transformerBuilder
|
transformerBuilder
|
||||||
.setTransformationRequest(requestBuilder.build())
|
.setTransformationRequest(requestBuilder.build())
|
||||||
.setRemoveAudio(bundle.getBoolean(ConfigurationActivity.SHOULD_REMOVE_AUDIO))
|
.setRemoveAudio(bundle.getBoolean(ConfigurationActivity.SHOULD_REMOVE_AUDIO))
|
||||||
|
@ -198,24 +198,15 @@
|
|||||||
android:checked="true"/>
|
android:checked="true"/>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
<TableRow
|
<TableRow
|
||||||
android:layout_weight="1">
|
android:layout_weight="1"
|
||||||
|
android:gravity="center_vertical" >
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_gravity="center_vertical"
|
android:id="@+id/hdr_mode"
|
||||||
android:id="@+id/request_sdr_tone_mapping"
|
android:text="@string/hdr_mode"/>
|
||||||
android:text="@string/request_sdr_tone_mapping" />
|
<Spinner
|
||||||
<CheckBox
|
android:id="@+id/hdr_mode_spinner"
|
||||||
android:id="@+id/request_sdr_tone_mapping_checkbox"
|
android:layout_gravity="right|center_vertical"
|
||||||
android:layout_gravity="end" />
|
android:gravity="right" />
|
||||||
</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" />
|
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableLayout>
|
</TableLayout>
|
||||||
</androidx.core.widget.NestedScrollView>
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
@ -30,8 +30,7 @@
|
|||||||
<string name="enable_fallback" translatable="false">Enable fallback</string>
|
<string name="enable_fallback" translatable="false">Enable fallback</string>
|
||||||
<string name="enable_debug_preview" translatable="false">Enable debug preview</string>
|
<string name="enable_debug_preview" translatable="false">Enable debug preview</string>
|
||||||
<string name="trim" translatable="false">Trim</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="hdr_mode" translatable="false">HDR mode</string>
|
||||||
<string name="force_interpret_hdr_video_as_sdr" translatable="false">[Experimental] Force interpret HDR video as SDR (API 29+)</string>
|
|
||||||
<string name="select_demo_effects" translatable="false">Add demo effects</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="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>
|
<string name="no_media_pipe_error" translatable="false">Failed to load MediaPipe processor. Check the README for instructions.</string>
|
||||||
|
@ -397,10 +397,10 @@ public class TransformerAndroidTestRunner {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Codec createForVideoDecoding(
|
public Codec createForVideoDecoding(
|
||||||
Format format, Surface outputSurface, boolean enableRequestSdrToneMapping)
|
Format format, Surface outputSurface, boolean requestSdrToneMapping)
|
||||||
throws TransformationException {
|
throws TransformationException {
|
||||||
Codec videoDecoder =
|
Codec videoDecoder =
|
||||||
decoderFactory.createForVideoDecoding(format, outputSurface, enableRequestSdrToneMapping);
|
decoderFactory.createForVideoDecoding(format, outputSurface, requestSdrToneMapping);
|
||||||
videoDecoderName = videoDecoder.getName();
|
videoDecoderName = videoDecoder.getName();
|
||||||
return videoDecoder;
|
return videoDecoder;
|
||||||
}
|
}
|
||||||
|
@ -39,11 +39,12 @@ import org.junit.runner.RunWith;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link Transformer} instrumentation test for {@linkplain
|
* {@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)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class SetForceInterpretHdrVideoAsSdrTest {
|
public class ForceInterpretHdrVideoAsSdrTest {
|
||||||
public static final String TAG = "SetForceInterpretHdrVideoAsSdrTest";
|
public static final String TAG = "ForceInterpretHdrVideoAsSdrTest";
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void forceInterpretHdrVideoAsSdrTest_hdr10File_transformsOrThrows() throws Exception {
|
public void forceInterpretHdrVideoAsSdrTest_hdr10File_transformsOrThrows() throws Exception {
|
||||||
@ -62,7 +63,8 @@ public class SetForceInterpretHdrVideoAsSdrTest {
|
|||||||
new Transformer.Builder(context)
|
new Transformer.Builder(context)
|
||||||
.setTransformationRequest(
|
.setTransformationRequest(
|
||||||
new TransformationRequest.Builder()
|
new TransformationRequest.Builder()
|
||||||
.experimental_setForceInterpretHdrVideoAsSdr(true)
|
.setHdrMode(
|
||||||
|
TransformationRequest.HDR_MODE_EXPERIMENTAL_FORCE_INTERPRET_HDR_AS_SDR)
|
||||||
.build())
|
.build())
|
||||||
.build();
|
.build();
|
||||||
try {
|
try {
|
||||||
@ -97,7 +99,8 @@ public class SetForceInterpretHdrVideoAsSdrTest {
|
|||||||
new Transformer.Builder(context)
|
new Transformer.Builder(context)
|
||||||
.setTransformationRequest(
|
.setTransformationRequest(
|
||||||
new TransformationRequest.Builder()
|
new TransformationRequest.Builder()
|
||||||
.experimental_setForceInterpretHdrVideoAsSdr(true)
|
.setHdrMode(
|
||||||
|
TransformationRequest.HDR_MODE_EXPERIMENTAL_FORCE_INTERPRET_HDR_AS_SDR)
|
||||||
.build())
|
.build())
|
||||||
.build();
|
.build();
|
||||||
try {
|
try {
|
@ -43,10 +43,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
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)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class SetHdrEditingTest {
|
public class HdrEditingTest {
|
||||||
public static final String TAG = "SetHdrEditingTest";
|
public static final String TAG = "HdrEditingTest";
|
||||||
private static final ColorInfo HDR10_DEFAULT_COLOR_INFO =
|
private static final ColorInfo HDR10_DEFAULT_COLOR_INFO =
|
||||||
new ColorInfo(
|
new ColorInfo(
|
||||||
C.COLOR_SPACE_BT2020,
|
C.COLOR_SPACE_BT2020,
|
||||||
@ -176,9 +179,11 @@ public class SetHdrEditingTest {
|
|||||||
TransformationRequest originalTransformationRequest,
|
TransformationRequest originalTransformationRequest,
|
||||||
TransformationRequest fallbackTransformationRequest) {
|
TransformationRequest fallbackTransformationRequest) {
|
||||||
isFallbackListenerInvoked.set(true);
|
isFallbackListenerInvoked.set(true);
|
||||||
assertThat(originalTransformationRequest.enableRequestSdrToneMapping).isFalse();
|
assertThat(originalTransformationRequest.hdrMode)
|
||||||
|
.isEqualTo(TransformationRequest.HDR_MODE_KEEP_HDR);
|
||||||
isToneMappingFallbackApplied.set(
|
isToneMappingFallbackApplied.set(
|
||||||
fallbackTransformationRequest.enableRequestSdrToneMapping);
|
fallbackTransformationRequest.hdrMode
|
||||||
|
== TransformationRequest.HDR_MODE_TONE_MAP_HDR_TO_SDR);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.build();
|
.build();
|
||||||
@ -227,9 +232,11 @@ public class SetHdrEditingTest {
|
|||||||
TransformationRequest originalTransformationRequest,
|
TransformationRequest originalTransformationRequest,
|
||||||
TransformationRequest fallbackTransformationRequest) {
|
TransformationRequest fallbackTransformationRequest) {
|
||||||
isFallbackListenerInvoked.set(true);
|
isFallbackListenerInvoked.set(true);
|
||||||
assertThat(originalTransformationRequest.enableRequestSdrToneMapping).isFalse();
|
assertThat(originalTransformationRequest.hdrMode)
|
||||||
|
.isEqualTo(TransformationRequest.HDR_MODE_KEEP_HDR);
|
||||||
isToneMappingFallbackApplied.set(
|
isToneMappingFallbackApplied.set(
|
||||||
fallbackTransformationRequest.enableRequestSdrToneMapping);
|
fallbackTransformationRequest.hdrMode
|
||||||
|
== TransformationRequest.HDR_MODE_TONE_MAP_HDR_TO_SDR);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.build();
|
.build();
|
@ -38,11 +38,11 @@ import org.junit.runner.RunWith;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link Transformer} instrumentation test for applying an {@linkplain
|
* {@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)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class SetHdrToSdrToneMapTest {
|
public class HdrToSdrToneMapTest {
|
||||||
public static final String TAG = "SetHdrToSdrToneMapTest";
|
public static final String TAG = "HdrToSdrToneMapTest";
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void transform_toneMapNoRequestedTranscode_hdr10File_toneMapsOrThrows() throws Exception {
|
public void transform_toneMapNoRequestedTranscode_hdr10File_toneMapsOrThrows() throws Exception {
|
||||||
@ -52,7 +52,9 @@ public class SetHdrToSdrToneMapTest {
|
|||||||
Transformer transformer =
|
Transformer transformer =
|
||||||
new Transformer.Builder(context)
|
new Transformer.Builder(context)
|
||||||
.setTransformationRequest(
|
.setTransformationRequest(
|
||||||
new TransformationRequest.Builder().setEnableRequestSdrToneMapping(true).build())
|
new TransformationRequest.Builder()
|
||||||
|
.setHdrMode(TransformationRequest.HDR_MODE_TONE_MAP_HDR_TO_SDR)
|
||||||
|
.build())
|
||||||
.addListener(
|
.addListener(
|
||||||
new Transformer.Listener() {
|
new Transformer.Listener() {
|
||||||
@Override
|
@Override
|
||||||
@ -62,8 +64,8 @@ public class SetHdrToSdrToneMapTest {
|
|||||||
TransformationRequest fallbackTransformationRequest) {
|
TransformationRequest fallbackTransformationRequest) {
|
||||||
// Tone mapping flag shouldn't change in fallback when tone mapping is
|
// Tone mapping flag shouldn't change in fallback when tone mapping is
|
||||||
// requested.
|
// requested.
|
||||||
assertThat(originalTransformationRequest.enableRequestSdrToneMapping)
|
assertThat(originalTransformationRequest.hdrMode)
|
||||||
.isEqualTo(fallbackTransformationRequest.enableRequestSdrToneMapping);
|
.isEqualTo(fallbackTransformationRequest.hdrMode);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.build();
|
.build();
|
||||||
@ -95,7 +97,9 @@ public class SetHdrToSdrToneMapTest {
|
|||||||
Transformer transformer =
|
Transformer transformer =
|
||||||
new Transformer.Builder(context)
|
new Transformer.Builder(context)
|
||||||
.setTransformationRequest(
|
.setTransformationRequest(
|
||||||
new TransformationRequest.Builder().setEnableRequestSdrToneMapping(true).build())
|
new TransformationRequest.Builder()
|
||||||
|
.setHdrMode(TransformationRequest.HDR_MODE_TONE_MAP_HDR_TO_SDR)
|
||||||
|
.build())
|
||||||
.addListener(
|
.addListener(
|
||||||
new Transformer.Listener() {
|
new Transformer.Listener() {
|
||||||
@Override
|
@Override
|
||||||
@ -105,8 +109,8 @@ public class SetHdrToSdrToneMapTest {
|
|||||||
TransformationRequest fallbackTransformationRequest) {
|
TransformationRequest fallbackTransformationRequest) {
|
||||||
// Tone mapping flag shouldn't change in fallback when tone mapping is
|
// Tone mapping flag shouldn't change in fallback when tone mapping is
|
||||||
// requested.
|
// requested.
|
||||||
assertThat(originalTransformationRequest.enableRequestSdrToneMapping)
|
assertThat(originalTransformationRequest.hdrMode)
|
||||||
.isEqualTo(fallbackTransformationRequest.enableRequestSdrToneMapping);
|
.isEqualTo(fallbackTransformationRequest.hdrMode);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.build();
|
.build();
|
||||||
@ -139,7 +143,7 @@ public class SetHdrToSdrToneMapTest {
|
|||||||
new Transformer.Builder(context)
|
new Transformer.Builder(context)
|
||||||
.setTransformationRequest(
|
.setTransformationRequest(
|
||||||
new TransformationRequest.Builder()
|
new TransformationRequest.Builder()
|
||||||
.setEnableRequestSdrToneMapping(true)
|
.setHdrMode(TransformationRequest.HDR_MODE_TONE_MAP_HDR_TO_SDR)
|
||||||
.setRotationDegrees(180)
|
.setRotationDegrees(180)
|
||||||
.build())
|
.build())
|
||||||
.addListener(
|
.addListener(
|
||||||
@ -151,8 +155,8 @@ public class SetHdrToSdrToneMapTest {
|
|||||||
TransformationRequest fallbackTransformationRequest) {
|
TransformationRequest fallbackTransformationRequest) {
|
||||||
// Tone mapping flag shouldn't change in fallback when tone mapping is
|
// Tone mapping flag shouldn't change in fallback when tone mapping is
|
||||||
// requested.
|
// requested.
|
||||||
assertThat(originalTransformationRequest.enableRequestSdrToneMapping)
|
assertThat(originalTransformationRequest.hdrMode)
|
||||||
.isEqualTo(fallbackTransformationRequest.enableRequestSdrToneMapping);
|
.isEqualTo(fallbackTransformationRequest.hdrMode);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.build();
|
.build();
|
||||||
@ -185,7 +189,7 @@ public class SetHdrToSdrToneMapTest {
|
|||||||
new Transformer.Builder(context)
|
new Transformer.Builder(context)
|
||||||
.setTransformationRequest(
|
.setTransformationRequest(
|
||||||
new TransformationRequest.Builder()
|
new TransformationRequest.Builder()
|
||||||
.setEnableRequestSdrToneMapping(true)
|
.setHdrMode(TransformationRequest.HDR_MODE_TONE_MAP_HDR_TO_SDR)
|
||||||
.setRotationDegrees(180)
|
.setRotationDegrees(180)
|
||||||
.build())
|
.build())
|
||||||
.addListener(
|
.addListener(
|
||||||
@ -197,8 +201,8 @@ public class SetHdrToSdrToneMapTest {
|
|||||||
TransformationRequest fallbackTransformationRequest) {
|
TransformationRequest fallbackTransformationRequest) {
|
||||||
// Tone mapping flag shouldn't change in fallback when tone mapping is
|
// Tone mapping flag shouldn't change in fallback when tone mapping is
|
||||||
// requested.
|
// requested.
|
||||||
assertThat(originalTransformationRequest.enableRequestSdrToneMapping)
|
assertThat(originalTransformationRequest.hdrMode)
|
||||||
.isEqualTo(fallbackTransformationRequest.enableRequestSdrToneMapping);
|
.isEqualTo(fallbackTransformationRequest.hdrMode);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.build();
|
.build();
|
@ -57,12 +57,12 @@ public interface Codec {
|
|||||||
* @param format The {@link Format} (of the input data) used to determine the underlying decoder
|
* @param format The {@link Format} (of the input data) used to determine the underlying decoder
|
||||||
* and its configuration values.
|
* and its configuration values.
|
||||||
* @param outputSurface The {@link Surface} to which the decoder output is rendered.
|
* @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.
|
* @return A {@link Codec} for video decoding.
|
||||||
* @throws TransformationException If no suitable {@link Codec} can be created.
|
* @throws TransformationException If no suitable {@link Codec} can be created.
|
||||||
*/
|
*/
|
||||||
Codec createForVideoDecoding(
|
Codec createForVideoDecoding(
|
||||||
Format format, Surface outputSurface, boolean enableRequestSdrToneMapping)
|
Format format, Surface outputSurface, boolean requestSdrToneMapping)
|
||||||
throws TransformationException;
|
throws TransformationException;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
@SuppressLint("InlinedApi")
|
@SuppressLint("InlinedApi")
|
||||||
@Override
|
@Override
|
||||||
public Codec createForVideoDecoding(
|
public Codec createForVideoDecoding(
|
||||||
Format format, Surface outputSurface, boolean enableRequestSdrToneMapping)
|
Format format, Surface outputSurface, boolean requestSdrToneMapping)
|
||||||
throws TransformationException {
|
throws TransformationException {
|
||||||
MediaFormat mediaFormat =
|
MediaFormat mediaFormat =
|
||||||
MediaFormat.createVideoFormat(
|
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.
|
// transformer to decode as many frames as possible in one render cycle.
|
||||||
mediaFormat.setInteger(MediaFormat.KEY_ALLOW_FRAME_DROP, 0);
|
mediaFormat.setInteger(MediaFormat.KEY_ALLOW_FRAME_DROP, 0);
|
||||||
}
|
}
|
||||||
if (SDK_INT >= 31 && enableRequestSdrToneMapping) {
|
if (SDK_INT >= 31 && requestSdrToneMapping) {
|
||||||
mediaFormat.setInteger(
|
mediaFormat.setInteger(
|
||||||
MediaFormat.KEY_COLOR_TRANSFER_REQUEST, MediaFormat.COLOR_TRANSFER_SDR_VIDEO);
|
MediaFormat.KEY_COLOR_TRANSFER_REQUEST, MediaFormat.COLOR_TRANSFER_SDR_VIDEO);
|
||||||
}
|
}
|
||||||
|
@ -95,10 +95,8 @@ import androidx.media3.common.util.Util;
|
|||||||
if (transformationRequest.outputHeight != originalTransformationRequest.outputHeight) {
|
if (transformationRequest.outputHeight != originalTransformationRequest.outputHeight) {
|
||||||
fallbackRequestBuilder.setResolution(transformationRequest.outputHeight);
|
fallbackRequestBuilder.setResolution(transformationRequest.outputHeight);
|
||||||
}
|
}
|
||||||
if (transformationRequest.enableRequestSdrToneMapping
|
if (transformationRequest.hdrMode != originalTransformationRequest.hdrMode) {
|
||||||
!= originalTransformationRequest.enableRequestSdrToneMapping) {
|
fallbackRequestBuilder.setHdrMode(transformationRequest.hdrMode);
|
||||||
fallbackRequestBuilder.setEnableRequestSdrToneMapping(
|
|
||||||
transformationRequest.enableRequestSdrToneMapping);
|
|
||||||
}
|
}
|
||||||
fallbackTransformationRequest = fallbackRequestBuilder.build();
|
fallbackTransformationRequest = fallbackRequestBuilder.build();
|
||||||
|
|
||||||
|
@ -17,7 +17,10 @@
|
|||||||
package androidx.media3.transformer;
|
package androidx.media3.transformer;
|
||||||
|
|
||||||
import static androidx.media3.common.util.Assertions.checkArgument;
|
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.annotation.Nullable;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
@ -26,11 +29,66 @@ import androidx.media3.common.util.Util;
|
|||||||
import androidx.media3.exoplayer.source.MediaSource;
|
import androidx.media3.exoplayer.source.MediaSource;
|
||||||
import androidx.media3.extractor.mp4.Mp4Extractor;
|
import androidx.media3.extractor.mp4.Mp4Extractor;
|
||||||
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
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. */
|
/** A media transformation request. */
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public final class TransformationRequest {
|
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. */
|
/** A builder for {@link TransformationRequest} instances. */
|
||||||
public static final class Builder {
|
public static final class Builder {
|
||||||
|
|
||||||
@ -41,9 +99,7 @@ public final class TransformationRequest {
|
|||||||
private int outputHeight;
|
private int outputHeight;
|
||||||
@Nullable private String audioMimeType;
|
@Nullable private String audioMimeType;
|
||||||
@Nullable private String videoMimeType;
|
@Nullable private String videoMimeType;
|
||||||
private boolean enableRequestSdrToneMapping;
|
private @HdrMode int hdrMode;
|
||||||
private boolean forceInterpretHdrVideoAsSdr;
|
|
||||||
private boolean enableHdrEditing;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance with default values.
|
* Creates a new instance with default values.
|
||||||
@ -55,7 +111,6 @@ public final class TransformationRequest {
|
|||||||
scaleX = 1;
|
scaleX = 1;
|
||||||
scaleY = 1;
|
scaleY = 1;
|
||||||
outputHeight = C.LENGTH_UNSET;
|
outputHeight = C.LENGTH_UNSET;
|
||||||
enableHdrEditing = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Builder(TransformationRequest transformationRequest) {
|
private Builder(TransformationRequest transformationRequest) {
|
||||||
@ -66,9 +121,7 @@ public final class TransformationRequest {
|
|||||||
this.outputHeight = transformationRequest.outputHeight;
|
this.outputHeight = transformationRequest.outputHeight;
|
||||||
this.audioMimeType = transformationRequest.audioMimeType;
|
this.audioMimeType = transformationRequest.audioMimeType;
|
||||||
this.videoMimeType = transformationRequest.videoMimeType;
|
this.videoMimeType = transformationRequest.videoMimeType;
|
||||||
this.enableRequestSdrToneMapping = transformationRequest.enableRequestSdrToneMapping;
|
this.hdrMode = transformationRequest.hdrMode;
|
||||||
this.forceInterpretHdrVideoAsSdr = transformationRequest.forceInterpretHdrVideoAsSdr;
|
|
||||||
this.enableHdrEditing = transformationRequest.enableHdrEditing;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -217,75 +270,45 @@ public final class TransformationRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets whether to request tone-mapping to standard dynamic range (SDR). If enabled and
|
* Sets the {@link HdrMode} for HDR video input.
|
||||||
* supported, high dynamic range (HDR) input will be tone-mapped into an SDR opto-electrical
|
|
||||||
* transfer function before processing.
|
|
||||||
*
|
*
|
||||||
* <p>The default value is {@code false}, which corresponds to editing and outputting HDR video
|
* <p>The default value is {@link #HDR_MODE_KEEP_HDR}.
|
||||||
* if possible, and falling back to tone-mapping if not.
|
|
||||||
*
|
*
|
||||||
* <p>The setting has no effect if the input is already in SDR, or if tone-mapping is not
|
* @param hdrMode The {@link HdrMode} used.
|
||||||
* 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.
|
|
||||||
* @return This builder.
|
* @return This builder.
|
||||||
*/
|
*/
|
||||||
@CanIgnoreReturnValue
|
@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) {
|
public Builder setEnableRequestSdrToneMapping(boolean enableRequestSdrToneMapping) {
|
||||||
this.enableRequestSdrToneMapping = enableRequestSdrToneMapping;
|
|
||||||
if (enableRequestSdrToneMapping) {
|
if (enableRequestSdrToneMapping) {
|
||||||
enableHdrEditing = false;
|
return setHdrMode(HDR_MODE_TONE_MAP_HDR_TO_SDR);
|
||||||
forceInterpretHdrVideoAsSdr = false;
|
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets whether to interpret HDR video as SDR, resulting in washed out video.
|
* @deprecated This method is now a no-op if {@code false}, and sets {@code
|
||||||
*
|
* setHdrMode(HDR_MODE_KEEP_HDR)} if {@code true}. {@code
|
||||||
* <p>The default value is {@code false}, which corresponds to editing and outputting HDR video
|
* experimental_setEnableHdrEditing(true)} is now the default behavior. Use {@link
|
||||||
* if possible, and falling back to tone-mapping if not.
|
* #setHdrMode} with link {@link #HDR_MODE_KEEP_HDR} instead.
|
||||||
*
|
|
||||||
* <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
|
@Deprecated
|
||||||
@CanIgnoreReturnValue
|
@CanIgnoreReturnValue
|
||||||
public Builder experimental_setEnableHdrEditing(boolean enableHdrEditing) {
|
public Builder experimental_setEnableHdrEditing(boolean enableHdrEditing) {
|
||||||
|
if (enableHdrEditing) {
|
||||||
|
return setHdrMode(HDR_MODE_KEEP_HDR);
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -299,9 +322,7 @@ public final class TransformationRequest {
|
|||||||
outputHeight,
|
outputHeight,
|
||||||
audioMimeType,
|
audioMimeType,
|
||||||
videoMimeType,
|
videoMimeType,
|
||||||
enableRequestSdrToneMapping,
|
hdrMode);
|
||||||
forceInterpretHdrVideoAsSdr,
|
|
||||||
enableHdrEditing);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -351,14 +372,12 @@ public final class TransformationRequest {
|
|||||||
* @see Builder#setVideoMimeType(String)
|
* @see Builder#setVideoMimeType(String)
|
||||||
*/
|
*/
|
||||||
@Nullable public final String videoMimeType;
|
@Nullable public final String videoMimeType;
|
||||||
/** Whether to request tone-mapping to standard dynamic range (SDR). */
|
/**
|
||||||
public final boolean enableRequestSdrToneMapping;
|
* The {@link HdrMode} specifying how to handle HDR input video.
|
||||||
|
*
|
||||||
/** Whether to force interpreting HDR video as SDR. */
|
* @see Builder#setHdrMode(int)
|
||||||
public final boolean forceInterpretHdrVideoAsSdr;
|
*/
|
||||||
|
public final @HdrMode int hdrMode;
|
||||||
/** Whether to attempt to process any input video stream as a high dynamic range (HDR) signal. */
|
|
||||||
public final boolean enableHdrEditing;
|
|
||||||
|
|
||||||
private TransformationRequest(
|
private TransformationRequest(
|
||||||
boolean flattenForSlowMotion,
|
boolean flattenForSlowMotion,
|
||||||
@ -368,12 +387,7 @@ public final class TransformationRequest {
|
|||||||
int outputHeight,
|
int outputHeight,
|
||||||
@Nullable String audioMimeType,
|
@Nullable String audioMimeType,
|
||||||
@Nullable String videoMimeType,
|
@Nullable String videoMimeType,
|
||||||
boolean enableRequestSdrToneMapping,
|
@HdrMode int hdrMode) {
|
||||||
boolean forceInterpretHdrVideoAsSdr,
|
|
||||||
boolean enableHdrEditing) {
|
|
||||||
checkArgument(!forceInterpretHdrVideoAsSdr || !enableRequestSdrToneMapping);
|
|
||||||
checkArgument(!enableHdrEditing || !forceInterpretHdrVideoAsSdr);
|
|
||||||
checkArgument(!enableHdrEditing || !enableRequestSdrToneMapping);
|
|
||||||
|
|
||||||
this.flattenForSlowMotion = flattenForSlowMotion;
|
this.flattenForSlowMotion = flattenForSlowMotion;
|
||||||
this.scaleX = scaleX;
|
this.scaleX = scaleX;
|
||||||
@ -382,9 +396,7 @@ public final class TransformationRequest {
|
|||||||
this.outputHeight = outputHeight;
|
this.outputHeight = outputHeight;
|
||||||
this.audioMimeType = audioMimeType;
|
this.audioMimeType = audioMimeType;
|
||||||
this.videoMimeType = videoMimeType;
|
this.videoMimeType = videoMimeType;
|
||||||
this.enableRequestSdrToneMapping = enableRequestSdrToneMapping;
|
this.hdrMode = hdrMode;
|
||||||
this.forceInterpretHdrVideoAsSdr = forceInterpretHdrVideoAsSdr;
|
|
||||||
this.enableHdrEditing = enableHdrEditing;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -403,9 +415,7 @@ public final class TransformationRequest {
|
|||||||
&& outputHeight == that.outputHeight
|
&& outputHeight == that.outputHeight
|
||||||
&& Util.areEqual(audioMimeType, that.audioMimeType)
|
&& Util.areEqual(audioMimeType, that.audioMimeType)
|
||||||
&& Util.areEqual(videoMimeType, that.videoMimeType)
|
&& Util.areEqual(videoMimeType, that.videoMimeType)
|
||||||
&& enableRequestSdrToneMapping == that.enableRequestSdrToneMapping
|
&& hdrMode == that.hdrMode;
|
||||||
&& forceInterpretHdrVideoAsSdr == that.forceInterpretHdrVideoAsSdr
|
|
||||||
&& enableHdrEditing == that.enableHdrEditing;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -417,9 +427,7 @@ public final class TransformationRequest {
|
|||||||
result = 31 * result + outputHeight;
|
result = 31 * result + outputHeight;
|
||||||
result = 31 * result + (audioMimeType != null ? audioMimeType.hashCode() : 0);
|
result = 31 * result + (audioMimeType != null ? audioMimeType.hashCode() : 0);
|
||||||
result = 31 * result + (videoMimeType != null ? videoMimeType.hashCode() : 0);
|
result = 31 * result + (videoMimeType != null ? videoMimeType.hashCode() : 0);
|
||||||
result = 31 * result + (enableRequestSdrToneMapping ? 1 : 0);
|
result = 31 * result + hdrMode;
|
||||||
result = 31 * result + (forceInterpretHdrVideoAsSdr ? 1 : 0);
|
|
||||||
result = 31 * result + (enableHdrEditing ? 1 : 0);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -514,7 +514,7 @@ public final class Transformer {
|
|||||||
* @param fallbackTransformationRequest The alternative {@link TransformationRequest}, with
|
* @param fallbackTransformationRequest The alternative {@link TransformationRequest}, with
|
||||||
* supported {@link TransformationRequest#audioMimeType}, {@link
|
* supported {@link TransformationRequest#audioMimeType}, {@link
|
||||||
* TransformationRequest#videoMimeType}, {@link TransformationRequest#outputHeight}, and
|
* TransformationRequest#videoMimeType}, {@link TransformationRequest#outputHeight}, and
|
||||||
* {@link TransformationRequest#enableRequestSdrToneMapping} values set.
|
* {@link TransformationRequest#hdrMode} values set.
|
||||||
*/
|
*/
|
||||||
default void onFallbackApplied(
|
default void onFallbackApplied(
|
||||||
MediaItem inputMediaItem,
|
MediaItem inputMediaItem,
|
||||||
|
@ -289,10 +289,7 @@ import java.util.List;
|
|||||||
if (encoderFactory.videoNeedsEncoding()) {
|
if (encoderFactory.videoNeedsEncoding()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (transformationRequest.enableRequestSdrToneMapping) {
|
if (transformationRequest.hdrMode != TransformationRequest.HDR_MODE_KEEP_HDR) {
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (transformationRequest.forceInterpretHdrVideoAsSdr) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (transformationRequest.videoMimeType != null
|
if (transformationRequest.videoMimeType != null
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
package androidx.media3.transformer;
|
package androidx.media3.transformer;
|
||||||
|
|
||||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
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 static androidx.media3.common.util.Util.SDK_INT;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@ -87,7 +86,8 @@ import org.checkerframework.dataflow.qual.Pure;
|
|||||||
muxerWrapper);
|
muxerWrapper);
|
||||||
|
|
||||||
if (ColorInfo.isTransferHdr(inputFormat.colorInfo)) {
|
if (ColorInfo.isTransferHdr(inputFormat.colorInfo)) {
|
||||||
if (transformationRequest.forceInterpretHdrVideoAsSdr) {
|
if (transformationRequest.hdrMode
|
||||||
|
== TransformationRequest.HDR_MODE_EXPERIMENTAL_FORCE_INTERPRET_HDR_AS_SDR) {
|
||||||
if (SDK_INT < 29) {
|
if (SDK_INT < 29) {
|
||||||
throw TransformationException.createForCodec(
|
throw TransformationException.createForCodec(
|
||||||
new IllegalArgumentException("Interpreting HDR video as SDR is not supported."),
|
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.
|
* processing, with {@link Format#rotationDegrees} of 90 added to the output format.
|
||||||
* @param requestedFormat The requested format.
|
* @param requestedFormat The requested format.
|
||||||
* @param supportedFormat A format supported by the device.
|
* @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.
|
* @return The created instance.
|
||||||
*/
|
*/
|
||||||
@Pure
|
@Pure
|
||||||
@ -290,24 +290,21 @@ import org.checkerframework.dataflow.qual.Pure;
|
|||||||
boolean hasOutputFormatRotation,
|
boolean hasOutputFormatRotation,
|
||||||
Format requestedFormat,
|
Format requestedFormat,
|
||||||
Format supportedFormat,
|
Format supportedFormat,
|
||||||
boolean isToneMappedToSdr) {
|
@TransformationRequest.HdrMode int supportedHdrMode) {
|
||||||
// TODO(b/210591626): Also update bitrate etc. once encoder configuration and fallback are
|
// TODO(b/210591626): Also update bitrate etc. once encoder configuration and fallback are
|
||||||
// implemented.
|
// implemented.
|
||||||
if (transformationRequest.enableRequestSdrToneMapping == isToneMappedToSdr
|
if (transformationRequest.hdrMode == supportedHdrMode
|
||||||
&& Util.areEqual(requestedFormat.sampleMimeType, supportedFormat.sampleMimeType)
|
&& Util.areEqual(requestedFormat.sampleMimeType, supportedFormat.sampleMimeType)
|
||||||
&& (hasOutputFormatRotation
|
&& (hasOutputFormatRotation
|
||||||
? requestedFormat.width == supportedFormat.width
|
? requestedFormat.width == supportedFormat.width
|
||||||
: requestedFormat.height == supportedFormat.height)) {
|
: requestedFormat.height == supportedFormat.height)) {
|
||||||
return transformationRequest;
|
return transformationRequest;
|
||||||
}
|
}
|
||||||
TransformationRequest.Builder transformationRequestBuilder = transformationRequest.buildUpon();
|
return transformationRequest
|
||||||
if (transformationRequest.enableRequestSdrToneMapping != isToneMappedToSdr) {
|
.buildUpon()
|
||||||
checkState(isToneMappedToSdr);
|
|
||||||
transformationRequestBuilder.setEnableRequestSdrToneMapping(true);
|
|
||||||
}
|
|
||||||
return transformationRequestBuilder
|
|
||||||
.setVideoMimeType(supportedFormat.sampleMimeType)
|
.setVideoMimeType(supportedFormat.sampleMimeType)
|
||||||
.setResolution(hasOutputFormatRotation ? requestedFormat.width : requestedFormat.height)
|
.setResolution(hasOutputFormatRotation ? requestedFormat.width : requestedFormat.height)
|
||||||
|
.setHdrMode(supportedHdrMode)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -408,8 +405,7 @@ import org.checkerframework.dataflow.qual.Pure;
|
|||||||
/** Returns the {@link ColorInfo} expected from the input surface. */
|
/** Returns the {@link ColorInfo} expected from the input surface. */
|
||||||
public ColorInfo getSupportedInputColor() {
|
public ColorInfo getSupportedInputColor() {
|
||||||
boolean isHdrEditingEnabled =
|
boolean isHdrEditingEnabled =
|
||||||
transformationRequest.enableHdrEditing
|
transformationRequest.hdrMode == TransformationRequest.HDR_MODE_KEEP_HDR
|
||||||
&& !transformationRequest.enableRequestSdrToneMapping
|
|
||||||
&& !supportedEncoderNamesForHdrEditing.isEmpty();
|
&& !supportedEncoderNamesForHdrEditing.isEmpty();
|
||||||
boolean isInputToneMapped =
|
boolean isInputToneMapped =
|
||||||
!isHdrEditingEnabled && ColorInfo.isTransferHdr(inputFormat.colorInfo);
|
!isHdrEditingEnabled && ColorInfo.isTransferHdr(inputFormat.colorInfo);
|
||||||
@ -476,13 +472,19 @@ import org.checkerframework.dataflow.qual.Pure;
|
|||||||
boolean isInputToneMapped =
|
boolean isInputToneMapped =
|
||||||
ColorInfo.isTransferHdr(inputFormat.colorInfo)
|
ColorInfo.isTransferHdr(inputFormat.colorInfo)
|
||||||
&& !ColorInfo.isTransferHdr(requestedEncoderFormat.colorInfo);
|
&& !ColorInfo.isTransferHdr(requestedEncoderFormat.colorInfo);
|
||||||
|
@TransformationRequest.HdrMode
|
||||||
|
int hdrMode =
|
||||||
|
isInputToneMapped
|
||||||
|
? TransformationRequest.HDR_MODE_TONE_MAP_HDR_TO_SDR
|
||||||
|
: transformationRequest.hdrMode;
|
||||||
|
|
||||||
fallbackListener.onTransformationRequestFinalized(
|
fallbackListener.onTransformationRequestFinalized(
|
||||||
createSupportedTransformationRequest(
|
createSupportedTransformationRequest(
|
||||||
transformationRequest,
|
transformationRequest,
|
||||||
/* hasOutputFormatRotation= */ flipOrientation,
|
/* hasOutputFormatRotation= */ flipOrientation,
|
||||||
requestedEncoderFormat,
|
requestedEncoderFormat,
|
||||||
encoderSupportedFormat,
|
encoderSupportedFormat,
|
||||||
isInputToneMapped));
|
hdrMode));
|
||||||
|
|
||||||
encoderSurfaceInfo =
|
encoderSurfaceInfo =
|
||||||
new SurfaceInfo(
|
new SurfaceInfo(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user