Commit 8bdd2784 by huangdarwin Committed by microkatz

HDR: Implement ForceInterpretHdrVideoAsSdr

Also, document that we tone map when no HDR features are explicitly set

PiperOrigin-RevId: 487310971
parent d6c8e3a8
...@@ -68,6 +68,7 @@ public final class ConfigurationActivity extends AppCompatActivity { ...@@ -68,6 +68,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
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 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 ENABLE_HDR_EDITING = "enable_hdr_editing"; public static final String ENABLE_HDR_EDITING = "enable_hdr_editing";
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";
...@@ -153,6 +154,7 @@ public final class ConfigurationActivity extends AppCompatActivity { ...@@ -153,6 +154,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
private @MonotonicNonNull CheckBox enableFallbackCheckBox; private @MonotonicNonNull CheckBox enableFallbackCheckBox;
private @MonotonicNonNull CheckBox enableDebugPreviewCheckBox; private @MonotonicNonNull CheckBox enableDebugPreviewCheckBox;
private @MonotonicNonNull CheckBox enableRequestSdrToneMappingCheckBox; private @MonotonicNonNull CheckBox enableRequestSdrToneMappingCheckBox;
private @MonotonicNonNull CheckBox forceInterpretHdrVideoAsSdrCheckBox;
private @MonotonicNonNull CheckBox enableHdrEditingCheckBox; private @MonotonicNonNull CheckBox enableHdrEditingCheckBox;
private @MonotonicNonNull Button selectDemoEffectsButton; private @MonotonicNonNull Button selectDemoEffectsButton;
private boolean @MonotonicNonNull [] demoEffectsSelections; private boolean @MonotonicNonNull [] demoEffectsSelections;
...@@ -248,6 +250,8 @@ public final class ConfigurationActivity extends AppCompatActivity { ...@@ -248,6 +250,8 @@ public final class ConfigurationActivity extends AppCompatActivity {
enableRequestSdrToneMappingCheckBox = findViewById(R.id.request_sdr_tone_mapping_checkbox); enableRequestSdrToneMappingCheckBox = findViewById(R.id.request_sdr_tone_mapping_checkbox);
enableRequestSdrToneMappingCheckBox.setEnabled(isRequestSdrToneMappingSupported()); enableRequestSdrToneMappingCheckBox.setEnabled(isRequestSdrToneMappingSupported());
findViewById(R.id.request_sdr_tone_mapping).setEnabled(isRequestSdrToneMappingSupported()); findViewById(R.id.request_sdr_tone_mapping).setEnabled(isRequestSdrToneMappingSupported());
forceInterpretHdrVideoAsSdrCheckBox =
findViewById(R.id.force_interpret_hdr_video_as_sdr_checkbox);
enableHdrEditingCheckBox = findViewById(R.id.hdr_editing_checkbox); enableHdrEditingCheckBox = findViewById(R.id.hdr_editing_checkbox);
demoEffectsSelections = new boolean[DEMO_EFFECTS.length]; demoEffectsSelections = new boolean[DEMO_EFFECTS.length];
...@@ -306,6 +310,7 @@ public final class ConfigurationActivity extends AppCompatActivity { ...@@ -306,6 +310,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
"enableFallbackCheckBox", "enableFallbackCheckBox",
"enableDebugPreviewCheckBox", "enableDebugPreviewCheckBox",
"enableRequestSdrToneMappingCheckBox", "enableRequestSdrToneMappingCheckBox",
"forceInterpretHdrVideoAsSdrCheckBox",
"enableHdrEditingCheckBox", "enableHdrEditingCheckBox",
"demoEffectsSelections" "demoEffectsSelections"
}) })
...@@ -346,6 +351,8 @@ public final class ConfigurationActivity extends AppCompatActivity { ...@@ -346,6 +351,8 @@ public final class ConfigurationActivity extends AppCompatActivity {
bundle.putBoolean(ENABLE_DEBUG_PREVIEW, enableDebugPreviewCheckBox.isChecked()); bundle.putBoolean(ENABLE_DEBUG_PREVIEW, enableDebugPreviewCheckBox.isChecked());
bundle.putBoolean( bundle.putBoolean(
ENABLE_REQUEST_SDR_TONE_MAPPING, enableRequestSdrToneMappingCheckBox.isChecked()); ENABLE_REQUEST_SDR_TONE_MAPPING, enableRequestSdrToneMappingCheckBox.isChecked());
bundle.putBoolean(
FORCE_INTERPRET_HDR_VIDEO_AS_SDR, forceInterpretHdrVideoAsSdrCheckBox.isChecked());
bundle.putBoolean(ENABLE_HDR_EDITING, enableHdrEditingCheckBox.isChecked()); bundle.putBoolean(ENABLE_HDR_EDITING, enableHdrEditingCheckBox.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);
...@@ -585,6 +592,7 @@ public final class ConfigurationActivity extends AppCompatActivity { ...@@ -585,6 +592,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
"rotateSpinner", "rotateSpinner",
"enableDebugPreviewCheckBox", "enableDebugPreviewCheckBox",
"enableRequestSdrToneMappingCheckBox", "enableRequestSdrToneMappingCheckBox",
"forceInterpretHdrVideoAsSdrCheckBox",
"enableHdrEditingCheckBox", "enableHdrEditingCheckBox",
"selectDemoEffectsButton" "selectDemoEffectsButton"
}) })
...@@ -606,6 +614,7 @@ public final class ConfigurationActivity extends AppCompatActivity { ...@@ -606,6 +614,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
"rotateSpinner", "rotateSpinner",
"enableDebugPreviewCheckBox", "enableDebugPreviewCheckBox",
"enableRequestSdrToneMappingCheckBox", "enableRequestSdrToneMappingCheckBox",
"forceInterpretHdrVideoAsSdrCheckBox",
"enableHdrEditingCheckBox", "enableHdrEditingCheckBox",
"selectDemoEffectsButton" "selectDemoEffectsButton"
}) })
...@@ -626,6 +635,7 @@ public final class ConfigurationActivity extends AppCompatActivity { ...@@ -626,6 +635,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
"rotateSpinner", "rotateSpinner",
"enableDebugPreviewCheckBox", "enableDebugPreviewCheckBox",
"enableRequestSdrToneMappingCheckBox", "enableRequestSdrToneMappingCheckBox",
"forceInterpretHdrVideoAsSdrCheckBox",
"enableHdrEditingCheckBox", "enableHdrEditingCheckBox",
"selectDemoEffectsButton" "selectDemoEffectsButton"
}) })
...@@ -638,6 +648,7 @@ public final class ConfigurationActivity extends AppCompatActivity { ...@@ -638,6 +648,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
enableDebugPreviewCheckBox.setEnabled(isVideoEnabled); enableDebugPreviewCheckBox.setEnabled(isVideoEnabled);
enableRequestSdrToneMappingCheckBox.setEnabled( enableRequestSdrToneMappingCheckBox.setEnabled(
isRequestSdrToneMappingSupported() && isVideoEnabled); isRequestSdrToneMappingSupported() && isVideoEnabled);
forceInterpretHdrVideoAsSdrCheckBox.setEnabled(isVideoEnabled);
enableHdrEditingCheckBox.setEnabled(isVideoEnabled); enableHdrEditingCheckBox.setEnabled(isVideoEnabled);
selectDemoEffectsButton.setEnabled(isVideoEnabled); selectDemoEffectsButton.setEnabled(isVideoEnabled);
...@@ -648,6 +659,7 @@ public final class ConfigurationActivity extends AppCompatActivity { ...@@ -648,6 +659,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
findViewById(R.id.rotate).setEnabled(isVideoEnabled); findViewById(R.id.rotate).setEnabled(isVideoEnabled);
findViewById(R.id.request_sdr_tone_mapping) findViewById(R.id.request_sdr_tone_mapping)
.setEnabled(isRequestSdrToneMappingSupported() && isVideoEnabled); .setEnabled(isRequestSdrToneMappingSupported() && isVideoEnabled);
findViewById(R.id.force_interpret_hdr_video_as_sdr).setEnabled(isVideoEnabled);
findViewById(R.id.hdr_editing).setEnabled(isVideoEnabled); findViewById(R.id.hdr_editing).setEnabled(isVideoEnabled);
} }
......
...@@ -272,6 +272,8 @@ public final class TransformerActivity extends AppCompatActivity { ...@@ -272,6 +272,8 @@ public final class TransformerActivity extends AppCompatActivity {
requestBuilder.setEnableRequestSdrToneMapping( requestBuilder.setEnableRequestSdrToneMapping(
bundle.getBoolean(ConfigurationActivity.ENABLE_REQUEST_SDR_TONE_MAPPING)); bundle.getBoolean(ConfigurationActivity.ENABLE_REQUEST_SDR_TONE_MAPPING));
requestBuilder.experimental_setForceInterpretHdrVideoAsSdr(
bundle.getBoolean(ConfigurationActivity.FORCE_INTERPRET_HDR_VIDEO_AS_SDR));
requestBuilder.experimental_setEnableHdrEditing( requestBuilder.experimental_setEnableHdrEditing(
bundle.getBoolean(ConfigurationActivity.ENABLE_HDR_EDITING)); bundle.getBoolean(ConfigurationActivity.ENABLE_HDR_EDITING));
transformerBuilder transformerBuilder
......
...@@ -217,6 +217,16 @@ ...@@ -217,6 +217,16 @@
android:id="@+id/hdr_editing_checkbox" android:id="@+id/hdr_editing_checkbox"
android:layout_gravity="end" /> android:layout_gravity="end" />
</TableRow> </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>
</TableLayout> </TableLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>
<Button <Button
......
...@@ -31,7 +31,8 @@ ...@@ -31,7 +31,8 @@
<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="request_sdr_tone_mapping" translatable="false">Request SDR tone-mapping (API 31+)</string>
<string name="hdr_editing" translatable="false">[Experimental] HDR editing</string> <string name="force_interpret_hdr_video_as_sdr" translatable="false">[Experimental] Force interpret HDR video as SDR (API 29+)</string>
<string name="hdr_editing" translatable="false">[Experimental] HDR editing (API 31+)</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>
......
...@@ -23,12 +23,14 @@ import static com.google.android.exoplayer2.util.MimeTypes.VIDEO_H265; ...@@ -23,12 +23,14 @@ import static com.google.android.exoplayer2.util.MimeTypes.VIDEO_H265;
import android.content.Context; import android.content.Context;
import android.os.Build; import android.os.Build;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.mediacodec.MediaCodecInfo; import com.google.android.exoplayer2.mediacodec.MediaCodecInfo;
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil; import com.google.android.exoplayer2.mediacodec.MediaCodecUtil;
import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.video.ColorInfo;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import java.io.File; import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
...@@ -86,6 +88,19 @@ public final class AndroidTestUtil { ...@@ -86,6 +88,19 @@ public final class AndroidTestUtil {
public static final String MP4_ASSET_1080P_4_SECOND_HDR10 = public static final String MP4_ASSET_1080P_4_SECOND_HDR10 =
"https://storage.googleapis.com/exoplayer-test-media-1/mp4/samsung-s21-hdr-hdr10.mp4"; "https://storage.googleapis.com/exoplayer-test-media-1/mp4/samsung-s21-hdr-hdr10.mp4";
public static final Format MP4_ASSET_1080P_4_SECOND_HDR10_FORMAT =
new Format.Builder()
.setSampleMimeType(VIDEO_H265)
.setWidth(1920)
.setHeight(1080)
.setFrameRate(23.517f)
.setColorInfo(
new ColorInfo(
C.COLOR_SPACE_BT2020,
C.COLOR_RANGE_LIMITED,
C.COLOR_TRANSFER_ST2084,
/* hdrStaticInfo= */ null))
.build();
public static final String MP4_ASSET_1080P_1_SECOND_HDR10_VIDEO_SDR_CONTAINER = public static final String MP4_ASSET_1080P_1_SECOND_HDR10_VIDEO_SDR_CONTAINER =
"asset:///media/mp4/hdr10-video-with-sdr-container.mp4"; "asset:///media/mp4/hdr10-video-with-sdr-container.mp4";
......
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.transformer.mh;
import static com.google.android.exoplayer2.transformer.AndroidTestUtil.MP4_ASSET_1080P_4_SECOND_HDR10;
import static com.google.android.exoplayer2.transformer.AndroidTestUtil.MP4_ASSET_1080P_4_SECOND_HDR10_FORMAT;
import static com.google.android.exoplayer2.transformer.mh.analysis.FileUtil.assertFileHasColorTransfer;
import android.content.Context;
import android.net.Uri;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.transformer.AndroidTestUtil;
import com.google.android.exoplayer2.transformer.TransformationException;
import com.google.android.exoplayer2.transformer.TransformationRequest;
import com.google.android.exoplayer2.transformer.TransformationTestResult;
import com.google.android.exoplayer2.transformer.Transformer;
import com.google.android.exoplayer2.transformer.TransformerAndroidTestRunner;
import com.google.android.exoplayer2.util.Log;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* {@link Transformer} instrumentation test for {@linkplain
* TransformationRequest#forceInterpretHdrVideoAsSdr forcing HDR contents to be interpreted as SDR}.
*/
@RunWith(AndroidJUnit4.class)
public class SetForceInterpretHdrVideoAsSdrTest {
public static final String TAG = "SetForceInterpretHdrVideoAsSdrTest";
@Test
public void forceInterpretHdrVideoAsSdrTest_hdr10File_transformsOrThrows() throws Exception {
String testId = "forceInterpretHdrVideoAsSdrTest_hdr10File_transformsOrThrows";
Context context = ApplicationProvider.getApplicationContext();
if (AndroidTestUtil.skipAndLogIfInsufficientCodecSupport(
context,
testId,
/* decodingFormat= */ MP4_ASSET_1080P_4_SECOND_HDR10_FORMAT,
/* encodingFormat= */ null)) {
return;
}
Transformer transformer =
new Transformer.Builder(context)
.setTransformationRequest(
new TransformationRequest.Builder()
.experimental_setForceInterpretHdrVideoAsSdr(true)
.build())
.build();
try {
TransformationTestResult transformationTestResult =
new TransformerAndroidTestRunner.Builder(context, transformer)
.build()
.run(testId, MediaItem.fromUri(Uri.parse(MP4_ASSET_1080P_4_SECOND_HDR10)));
assertFileHasColorTransfer(transformationTestResult.filePath, C.COLOR_TRANSFER_SDR);
Log.i(TAG, "Transformed.");
} catch (TransformationException exception) {
if (exception.errorCode != TransformationException.ERROR_CODE_DECODING_FORMAT_UNSUPPORTED
&& exception.errorCode != TransformationException.ERROR_CODE_HDR_DECODING_UNSUPPORTED) {
throw exception;
}
}
}
}
...@@ -79,7 +79,7 @@ public class SetHdrEditingTest { ...@@ -79,7 +79,7 @@ public class SetHdrEditingTest {
Log.i(TAG, checkNotNull(exception.getCause()).toString()); Log.i(TAG, checkNotNull(exception.getCause()).toString());
assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class); assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class);
assertThat(exception.errorCode) assertThat(exception.errorCode)
.isEqualTo(TransformationException.ERROR_CODE_HDR_EDITING_UNSUPPORTED); .isEqualTo(TransformationException.ERROR_CODE_HDR_ENCODING_UNSUPPORTED);
} }
} }
...@@ -156,7 +156,7 @@ public class SetHdrEditingTest { ...@@ -156,7 +156,7 @@ public class SetHdrEditingTest {
assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class); assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class);
assertThat(exception.errorCode) assertThat(exception.errorCode)
.isAnyOf( .isAnyOf(
TransformationException.ERROR_CODE_HDR_EDITING_UNSUPPORTED, TransformationException.ERROR_CODE_HDR_ENCODING_UNSUPPORTED,
TransformationException.ERROR_CODE_DECODING_FORMAT_UNSUPPORTED); TransformationException.ERROR_CODE_DECODING_FORMAT_UNSUPPORTED);
assertThat(isFallbackListenerInvoked.get()).isFalse(); assertThat(isFallbackListenerInvoked.get()).isFalse();
return; return;
......
...@@ -80,7 +80,7 @@ public class SetHdrToSdrToneMapTest { ...@@ -80,7 +80,7 @@ public class SetHdrToSdrToneMapTest {
assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class); assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class);
assertThat(exception.errorCode) assertThat(exception.errorCode)
.isAnyOf( .isAnyOf(
TransformationException.ERROR_CODE_HDR_EDITING_UNSUPPORTED, TransformationException.ERROR_CODE_HDR_ENCODING_UNSUPPORTED,
TransformationException.ERROR_CODE_DECODING_FORMAT_UNSUPPORTED); TransformationException.ERROR_CODE_DECODING_FORMAT_UNSUPPORTED);
return; return;
} }
...@@ -126,7 +126,7 @@ public class SetHdrToSdrToneMapTest { ...@@ -126,7 +126,7 @@ public class SetHdrToSdrToneMapTest {
assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class); assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class);
assertThat(exception.errorCode) assertThat(exception.errorCode)
.isAnyOf( .isAnyOf(
TransformationException.ERROR_CODE_HDR_EDITING_UNSUPPORTED, TransformationException.ERROR_CODE_HDR_ENCODING_UNSUPPORTED,
TransformationException.ERROR_CODE_DECODING_FORMAT_UNSUPPORTED); TransformationException.ERROR_CODE_DECODING_FORMAT_UNSUPPORTED);
return; return;
} }
......
...@@ -69,7 +69,7 @@ public final class TransformationException extends Exception { ...@@ -69,7 +69,7 @@ public final class TransformationException extends Exception {
ERROR_CODE_ENCODER_INIT_FAILED, ERROR_CODE_ENCODER_INIT_FAILED,
ERROR_CODE_ENCODING_FAILED, ERROR_CODE_ENCODING_FAILED,
ERROR_CODE_OUTPUT_FORMAT_UNSUPPORTED, ERROR_CODE_OUTPUT_FORMAT_UNSUPPORTED,
ERROR_CODE_HDR_EDITING_UNSUPPORTED, ERROR_CODE_HDR_ENCODING_UNSUPPORTED,
ERROR_CODE_FRAME_PROCESSING_FAILED, ERROR_CODE_FRAME_PROCESSING_FAILED,
ERROR_CODE_MUXING_FAILED, ERROR_CODE_MUXING_FAILED,
}) })
...@@ -137,6 +137,8 @@ public final class TransformationException extends Exception { ...@@ -137,6 +137,8 @@ public final class TransformationException extends Exception {
public static final int ERROR_CODE_DECODING_FAILED = 3002; public static final int ERROR_CODE_DECODING_FAILED = 3002;
/** Caused by trying to decode content whose format is not supported. */ /** Caused by trying to decode content whose format is not supported. */
public static final int ERROR_CODE_DECODING_FORMAT_UNSUPPORTED = 3003; public static final int ERROR_CODE_DECODING_FORMAT_UNSUPPORTED = 3003;
/** Caused by the decoder not supporting HDR formats. */
public static final int ERROR_CODE_HDR_DECODING_UNSUPPORTED = 3004;
// Encoding errors (4xxx). // Encoding errors (4xxx).
...@@ -152,7 +154,7 @@ public final class TransformationException extends Exception { ...@@ -152,7 +154,7 @@ public final class TransformationException extends Exception {
*/ */
public static final int ERROR_CODE_OUTPUT_FORMAT_UNSUPPORTED = 4003; public static final int ERROR_CODE_OUTPUT_FORMAT_UNSUPPORTED = 4003;
/** Caused by the encoder not supporting HDR formats. */ /** Caused by the encoder not supporting HDR formats. */
public static final int ERROR_CODE_HDR_EDITING_UNSUPPORTED = 4004; public static final int ERROR_CODE_HDR_ENCODING_UNSUPPORTED = 4004;
// Video editing errors (5xxx). // Video editing errors (5xxx).
...@@ -178,10 +180,11 @@ public final class TransformationException extends Exception { ...@@ -178,10 +180,11 @@ public final class TransformationException extends Exception {
.put("ERROR_CODE_DECODER_INIT_FAILED", ERROR_CODE_DECODER_INIT_FAILED) .put("ERROR_CODE_DECODER_INIT_FAILED", ERROR_CODE_DECODER_INIT_FAILED)
.put("ERROR_CODE_DECODING_FAILED", ERROR_CODE_DECODING_FAILED) .put("ERROR_CODE_DECODING_FAILED", ERROR_CODE_DECODING_FAILED)
.put("ERROR_CODE_DECODING_FORMAT_UNSUPPORTED", ERROR_CODE_DECODING_FORMAT_UNSUPPORTED) .put("ERROR_CODE_DECODING_FORMAT_UNSUPPORTED", ERROR_CODE_DECODING_FORMAT_UNSUPPORTED)
.put("ERROR_CODE_HDR_DECODING_UNSUPPORTED", ERROR_CODE_HDR_DECODING_UNSUPPORTED)
.put("ERROR_CODE_ENCODER_INIT_FAILED", ERROR_CODE_ENCODER_INIT_FAILED) .put("ERROR_CODE_ENCODER_INIT_FAILED", ERROR_CODE_ENCODER_INIT_FAILED)
.put("ERROR_CODE_ENCODING_FAILED", ERROR_CODE_ENCODING_FAILED) .put("ERROR_CODE_ENCODING_FAILED", ERROR_CODE_ENCODING_FAILED)
.put("ERROR_CODE_OUTPUT_FORMAT_UNSUPPORTED", ERROR_CODE_OUTPUT_FORMAT_UNSUPPORTED) .put("ERROR_CODE_OUTPUT_FORMAT_UNSUPPORTED", ERROR_CODE_OUTPUT_FORMAT_UNSUPPORTED)
.put("ERROR_CODE_HDR_EDITING_UNSUPPORTED", ERROR_CODE_HDR_EDITING_UNSUPPORTED) .put("ERROR_CODE_HDR_ENCODING_UNSUPPORTED", ERROR_CODE_HDR_ENCODING_UNSUPPORTED)
.put("ERROR_CODE_FRAME_PROCESSING_FAILED", ERROR_CODE_FRAME_PROCESSING_FAILED) .put("ERROR_CODE_FRAME_PROCESSING_FAILED", ERROR_CODE_FRAME_PROCESSING_FAILED)
.put("ERROR_CODE_MUXING_FAILED", ERROR_CODE_MUXING_FAILED) .put("ERROR_CODE_MUXING_FAILED", ERROR_CODE_MUXING_FAILED)
.buildOrThrow(); .buildOrThrow();
......
...@@ -40,6 +40,7 @@ public final class TransformationRequest { ...@@ -40,6 +40,7 @@ public final class TransformationRequest {
@Nullable private String audioMimeType; @Nullable private String audioMimeType;
@Nullable private String videoMimeType; @Nullable private String videoMimeType;
private boolean enableRequestSdrToneMapping; private boolean enableRequestSdrToneMapping;
private boolean forceInterpretHdrVideoAsSdr;
private boolean enableHdrEditing; private boolean enableHdrEditing;
/** /**
...@@ -63,6 +64,7 @@ public final class TransformationRequest { ...@@ -63,6 +64,7 @@ public final class TransformationRequest {
this.audioMimeType = transformationRequest.audioMimeType; this.audioMimeType = transformationRequest.audioMimeType;
this.videoMimeType = transformationRequest.videoMimeType; this.videoMimeType = transformationRequest.videoMimeType;
this.enableRequestSdrToneMapping = transformationRequest.enableRequestSdrToneMapping; this.enableRequestSdrToneMapping = transformationRequest.enableRequestSdrToneMapping;
this.forceInterpretHdrVideoAsSdr = transformationRequest.forceInterpretHdrVideoAsSdr;
this.enableHdrEditing = transformationRequest.enableHdrEditing; this.enableHdrEditing = transformationRequest.enableHdrEditing;
} }
...@@ -216,31 +218,78 @@ public final class TransformationRequest { ...@@ -216,31 +218,78 @@ public final class TransformationRequest {
* supported, high dynamic range (HDR) input will be tone-mapped into an SDR opto-electrical * supported, high dynamic range (HDR) input will be tone-mapped into an SDR opto-electrical
* transfer function before processing. * transfer function before processing.
* *
* <p>The default value is {@code true}, which corresponds to tone-mapping output if possible.
*
* <p>The setting has no effect if the input is already in SDR, or if tone-mapping is not * <p>The setting has no effect if the input is already in SDR, or if tone-mapping is not
* supported. Currently tone-mapping is only guaranteed to be supported from Android T onwards. * supported. Currently tone-mapping is only guaranteed to be supported from Android T onwards.
* *
* <p>Setting this as {@code true} will set {@linkplain #experimental_setEnableHdrEditing} and
* {@linkplain #forceInterpretHdrVideoAsSdr} to {@code false}.
*
* @param enableRequestSdrToneMapping Whether to request tone-mapping down to SDR. * @param enableRequestSdrToneMapping Whether to request tone-mapping down to SDR.
* @return This builder. * @return This builder.
*/ */
@CanIgnoreReturnValue @CanIgnoreReturnValue
public Builder setEnableRequestSdrToneMapping(boolean enableRequestSdrToneMapping) { public Builder setEnableRequestSdrToneMapping(boolean enableRequestSdrToneMapping) {
this.enableRequestSdrToneMapping = enableRequestSdrToneMapping; this.enableRequestSdrToneMapping = enableRequestSdrToneMapping;
if (enableRequestSdrToneMapping) {
forceInterpretHdrVideoAsSdr = false;
enableHdrEditing = false;
}
return this;
}
/**
* Sets whether to interpret HDR video as SDR, resulting in washed out video.
*
* <p>The default value is {@code false}, with {@link #setEnableRequestSdrToneMapping} being
* applied.
*
* <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 #experimental_setEnableHdrEditing} 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; return this;
} }
/** /**
* Sets whether to allow processing high dynamic range (HDR) input video streams as HDR. * Sets whether to allow processing high dynamic range (HDR) input video streams as HDR.
* *
* <p>The default value is {@code false}, with {@link #setEnableRequestSdrToneMapping} being
* applied.
*
* <p>This method is experimental, and will be renamed or removed in a future release. The HDR * <p>This method is experimental, and will be renamed or removed in a future release. The HDR
* editing feature is under development and is intended for developing/testing HDR support. HDR * editing feature is under development and is intended for developing/testing HDR support.
* editing can't be enabled at the same time as {@linkplain *
* #setEnableRequestSdrToneMapping(boolean) SDR tone-mapping}. * <p>Setting this as {@code true} will set {@linkplain #experimental_setEnableHdrEditing} and
* {@linkplain #forceInterpretHdrVideoAsSdr} to {@code false}.
* *
* <p>With this flag enabled, HDR streams will correctly edit in HDR, convert via tone-mapping * <p>With this flag enabled, HDR streams will correctly edit in HDR, convert via tone-mapping
* to SDR, or throw an error, based on the device's HDR support. Without both this flag and * to SDR, or throw an error, based on the device's HDR support. SDR streams will be interpreted
* {@linkplain #setEnableRequestSdrToneMapping(boolean) SDR tone-mapping} as false, HDR streams * the same way regardless of this flag's state.
* will be incorrectly interpreted as SDR streams, with no conversion. SDR streams will be
* interpreted the same way regardless of this flag's state.
* *
* @param enableHdrEditing Whether to attempt to process any input video stream as a high * @param enableHdrEditing Whether to attempt to process any input video stream as a high
* dynamic range (HDR) signal. * dynamic range (HDR) signal.
...@@ -249,6 +298,10 @@ public final class TransformationRequest { ...@@ -249,6 +298,10 @@ public final class TransformationRequest {
@CanIgnoreReturnValue @CanIgnoreReturnValue
public Builder experimental_setEnableHdrEditing(boolean enableHdrEditing) { public Builder experimental_setEnableHdrEditing(boolean enableHdrEditing) {
this.enableHdrEditing = enableHdrEditing; this.enableHdrEditing = enableHdrEditing;
if (enableHdrEditing) {
enableRequestSdrToneMapping = false;
forceInterpretHdrVideoAsSdr = false;
}
return this; return this;
} }
...@@ -263,6 +316,7 @@ public final class TransformationRequest { ...@@ -263,6 +316,7 @@ public final class TransformationRequest {
audioMimeType, audioMimeType,
videoMimeType, videoMimeType,
enableRequestSdrToneMapping, enableRequestSdrToneMapping,
forceInterpretHdrVideoAsSdr,
enableHdrEditing); enableHdrEditing);
} }
} }
...@@ -316,6 +370,9 @@ public final class TransformationRequest { ...@@ -316,6 +370,9 @@ public final class TransformationRequest {
/** Whether to request tone-mapping to standard dynamic range (SDR). */ /** Whether to request tone-mapping to standard dynamic range (SDR). */
public final boolean enableRequestSdrToneMapping; 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. * Whether to attempt to process any input video stream as a high dynamic range (HDR) signal.
* *
...@@ -332,8 +389,12 @@ public final class TransformationRequest { ...@@ -332,8 +389,12 @@ public final class TransformationRequest {
@Nullable String audioMimeType, @Nullable String audioMimeType,
@Nullable String videoMimeType, @Nullable String videoMimeType,
boolean enableRequestSdrToneMapping, boolean enableRequestSdrToneMapping,
boolean forceInterpretHdrVideoAsSdr,
boolean enableHdrEditing) { boolean enableHdrEditing) {
checkArgument(!forceInterpretHdrVideoAsSdr || !enableRequestSdrToneMapping);
checkArgument(!enableHdrEditing || !forceInterpretHdrVideoAsSdr);
checkArgument(!enableHdrEditing || !enableRequestSdrToneMapping); checkArgument(!enableHdrEditing || !enableRequestSdrToneMapping);
this.flattenForSlowMotion = flattenForSlowMotion; this.flattenForSlowMotion = flattenForSlowMotion;
this.scaleX = scaleX; this.scaleX = scaleX;
this.scaleY = scaleY; this.scaleY = scaleY;
...@@ -342,6 +403,7 @@ public final class TransformationRequest { ...@@ -342,6 +403,7 @@ public final class TransformationRequest {
this.audioMimeType = audioMimeType; this.audioMimeType = audioMimeType;
this.videoMimeType = videoMimeType; this.videoMimeType = videoMimeType;
this.enableRequestSdrToneMapping = enableRequestSdrToneMapping; this.enableRequestSdrToneMapping = enableRequestSdrToneMapping;
this.forceInterpretHdrVideoAsSdr = forceInterpretHdrVideoAsSdr;
this.enableHdrEditing = enableHdrEditing; this.enableHdrEditing = enableHdrEditing;
} }
...@@ -362,6 +424,7 @@ public final class TransformationRequest { ...@@ -362,6 +424,7 @@ public final class TransformationRequest {
&& Util.areEqual(audioMimeType, that.audioMimeType) && Util.areEqual(audioMimeType, that.audioMimeType)
&& Util.areEqual(videoMimeType, that.videoMimeType) && Util.areEqual(videoMimeType, that.videoMimeType)
&& enableRequestSdrToneMapping == that.enableRequestSdrToneMapping && enableRequestSdrToneMapping == that.enableRequestSdrToneMapping
&& forceInterpretHdrVideoAsSdr == that.forceInterpretHdrVideoAsSdr
&& enableHdrEditing == that.enableHdrEditing; && enableHdrEditing == that.enableHdrEditing;
} }
...@@ -375,6 +438,7 @@ public final class TransformationRequest { ...@@ -375,6 +438,7 @@ public final class TransformationRequest {
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 + (enableRequestSdrToneMapping ? 1 : 0);
result = 31 * result + (forceInterpretHdrVideoAsSdr ? 1 : 0);
result = 31 * result + (enableHdrEditing ? 1 : 0); result = 31 * result + (enableHdrEditing ? 1 : 0);
return result; return result;
} }
......
...@@ -131,6 +131,9 @@ import com.google.common.collect.ImmutableList; ...@@ -131,6 +131,9 @@ import com.google.common.collect.ImmutableList;
if (transformationRequest.enableRequestSdrToneMapping) { if (transformationRequest.enableRequestSdrToneMapping) {
return true; return true;
} }
if (transformationRequest.forceInterpretHdrVideoAsSdr) {
return true;
}
if (transformationRequest.videoMimeType != null if (transformationRequest.videoMimeType != null
&& !transformationRequest.videoMimeType.equals(inputFormat.sampleMimeType)) { && !transformationRequest.videoMimeType.equals(inputFormat.sampleMimeType)) {
return true; return true;
......
...@@ -85,15 +85,27 @@ import org.checkerframework.dataflow.qual.Pure; ...@@ -85,15 +85,27 @@ import org.checkerframework.dataflow.qual.Pure;
transformationRequest.flattenForSlowMotion, transformationRequest.flattenForSlowMotion,
muxerWrapper); muxerWrapper);
if (ColorInfo.isTransferHdr(inputFormat.colorInfo) if (ColorInfo.isTransferHdr(inputFormat.colorInfo)) {
&& (SDK_INT < 31 || deviceNeedsNoToneMappingWorkaround())) { if (transformationRequest.forceInterpretHdrVideoAsSdr) {
if (SDK_INT < 29) {
throw TransformationException.createForCodec( throw TransformationException.createForCodec(
new IllegalArgumentException("HDR editing and tone mapping not supported."), new IllegalArgumentException("Interpreting HDR video as SDR is not supported."),
/* isVideo= */ true,
/* isDecoder= */ true,
inputFormat,
/* mediaCodecName= */ null,
TransformationException.ERROR_CODE_HDR_DECODING_UNSUPPORTED);
}
inputFormat = inputFormat.buildUpon().setColorInfo(ColorInfo.SDR_BT709_LIMITED).build();
} else if (SDK_INT < 31 || deviceNeedsNoToneMappingWorkaround()) {
throw TransformationException.createForCodec(
new IllegalArgumentException("HDR editing and tone mapping is not supported."),
/* isVideo= */ true, /* isVideo= */ true,
/* isDecoder= */ false, /* isDecoder= */ false,
inputFormat, inputFormat,
/* mediaCodecName= */ null, /* mediaCodecName= */ null,
TransformationException.ERROR_CODE_HDR_EDITING_UNSUPPORTED); TransformationException.ERROR_CODE_HDR_ENCODING_UNSUPPORTED);
}
} }
decoderInputBuffer = decoderInputBuffer =
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment