HDR: Add tests for tone mapping.

Also, add checks for output file color.

PiperOrigin-RevId: 477439139
This commit is contained in:
huangdarwin 2022-09-28 13:42:36 +00:00 committed by Marc Baechinger
parent bb811f0da5
commit aec4fe7387
3 changed files with 224 additions and 37 deletions

View File

@ -20,18 +20,16 @@ import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.transformer.AndroidTestUtil.MP4_ASSET_1080P_1_SECOND_HDR10_VIDEO_SDR_CONTAINER;
import static androidx.media3.transformer.AndroidTestUtil.MP4_ASSET_1080P_4_SECOND_HDR10;
import static androidx.media3.transformer.AndroidTestUtil.recordTestSkipped;
import static androidx.media3.transformer.mh.analysis.FileTestUtil.assertFileHasColorTransfer;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.media.MediaFormat;
import android.net.Uri;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.ColorInfo;
import androidx.media3.common.MediaItem;
import androidx.media3.common.util.MediaFormatUtil;
import androidx.media3.common.util.Log;
import androidx.media3.common.util.Util;
import androidx.media3.test.utils.DecodeOneFrameTestUtil;
import androidx.media3.transformer.EncoderUtil;
import androidx.media3.transformer.TransformationException;
import androidx.media3.transformer.TransformationRequest;
@ -44,11 +42,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.Test;
import org.junit.runner.RunWith;
// TODO(b/239172735): Add a SetToneMappingTransformationTest for when we request tone mapping.
// TODO(b/239172735): Add HLG tests after finding a shareable HLG file.
/** {@link Transformer} instrumentation test for applying an HDR frame edit. */
@RunWith(AndroidJUnit4.class)
public class SetHdrEditingTransformationTest {
public static final String TAG = "SetHdrEditingTransformationTest";
private static final ColorInfo HDR10_DEFAULT_COLOR_INFO =
new ColorInfo(
C.COLOR_SPACE_BT2020,
@ -72,9 +70,11 @@ public class SetHdrEditingTransformationTest {
new TransformerAndroidTestRunner.Builder(context, transformer)
.build()
.run(testId, MediaItem.fromUri(Uri.parse(MP4_ASSET_1080P_4_SECOND_HDR10)));
checkHasColorTransfer(transformationTestResult, C.COLOR_TRANSFER_ST2084);
Log.i(TAG, "Transformed.");
assertFileHasColorTransfer(transformationTestResult.filePath, C.COLOR_TRANSFER_ST2084);
return;
} catch (TransformationException exception) {
Log.i(TAG, checkNotNull(exception.getCause()).toString());
assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class);
assertThat(exception.errorCode)
.isEqualTo(TransformationException.ERROR_CODE_HDR_EDITING_UNSUPPORTED);
@ -110,7 +110,7 @@ public class SetHdrEditingTransformationTest {
new TransformerAndroidTestRunner.Builder(context, transformer)
.build()
.run(testId, MediaItem.fromUri(Uri.parse(MP4_ASSET_1080P_4_SECOND_HDR10)));
checkHasColorTransfer(transformationTestResult, C.COLOR_TRANSFER_ST2084);
assertFileHasColorTransfer(transformationTestResult.filePath, C.COLOR_TRANSFER_ST2084);
}
@Test
@ -122,7 +122,7 @@ public class SetHdrEditingTransformationTest {
recordTestSkipped(
context,
testId,
/* reason= */ "Skipping on this device due to presence of HDR10 editing support");
/* reason= */ "Skipping on this device due to presence of HDR10 editing support.");
return;
}
@ -154,8 +154,10 @@ public class SetHdrEditingTransformationTest {
new TransformerAndroidTestRunner.Builder(context, transformer)
.build()
.run(testId, MediaItem.fromUri(Uri.parse(MP4_ASSET_1080P_4_SECOND_HDR10)));
checkHasColorTransfer(transformationTestResult, C.COLOR_TRANSFER_SDR);
Log.i(TAG, "Tone mapped.");
assertFileHasColorTransfer(transformationTestResult.filePath, C.COLOR_TRANSFER_SDR);
} catch (TransformationException exception) {
Log.i(TAG, checkNotNull(exception.getCause()).toString());
assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class);
// TODO(b/245364266): After fixing the bug, replace the API version check with a check that
// isToneMappingFallbackApplied.get() is true.
@ -176,8 +178,6 @@ public class SetHdrEditingTransformationTest {
}
return;
}
assertThat(isToneMappingFallbackApplied.get()).isTrue();
}
@Test
@ -208,30 +208,4 @@ public class SetHdrEditingTransformationTest {
private static boolean deviceSupportsHdrEditing(String mimeType, ColorInfo colorInfo) {
return !EncoderUtil.getSupportedEncoderNamesForHdrEditing(mimeType, colorInfo).isEmpty();
}
private static void checkHasColorTransfer(
TransformationTestResult transformationTestResult, @C.ColorTransfer int expectedColorTransfer)
throws Exception {
if (Util.SDK_INT < 29) {
// Skipping on this API version due to lack of support for MediaFormat#getInteger, which is
// required for MediaFormatUtil#getColorInfo.
return;
}
DecodeOneFrameTestUtil.decodeOneCacheFileFrame(
checkNotNull(transformationTestResult.filePath),
new DecodeOneFrameTestUtil.Listener() {
@Override
public void onContainerExtracted(MediaFormat mediaFormat) {
@Nullable ColorInfo extractedColor = MediaFormatUtil.getColorInfo(mediaFormat);
assertThat(checkNotNull(extractedColor).colorTransfer).isEqualTo(expectedColorTransfer);
}
@Override
public void onFrameDecoded(MediaFormat mediaFormat) {
@Nullable ColorInfo decodedColor = MediaFormatUtil.getColorInfo(mediaFormat);
assertThat(checkNotNull(decodedColor).colorTransfer).isEqualTo(expectedColorTransfer);
}
},
/* surface= */ null);
}
}

View File

@ -0,0 +1,155 @@
/*
* 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 androidx.media3.transformer.mh;
import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.transformer.AndroidTestUtil.MP4_ASSET_1080P_4_SECOND_HDR10;
import static androidx.media3.transformer.mh.analysis.FileTestUtil.assertFileHasColorTransfer;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.net.Uri;
import androidx.media3.common.C;
import androidx.media3.common.MediaItem;
import androidx.media3.common.util.Log;
import androidx.media3.common.util.Util;
import androidx.media3.transformer.TransformationException;
import androidx.media3.transformer.TransformationRequest;
import androidx.media3.transformer.TransformationTestResult;
import androidx.media3.transformer.Transformer;
import androidx.media3.transformer.TransformerAndroidTestRunner;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
// TODO(b/239172735): Add HLG tests after finding a shareable HLG file.
/** {@link Transformer} instrumentation test for applying an HDR to SDR tone mapping edit. */
@RunWith(AndroidJUnit4.class)
public class SetHdrToSdrToneMapTransformationTest {
public static final String TAG = "SetHdrToSdrToneMapTransformationTest";
@Test
public void transform_toneMapNoRequestedTranscode_hdr10File_toneMapsOrThrows() throws Exception {
String testId = "transform_toneMapNoRequestedTranscode_hdr10File_toneMapsOrThrows";
Context context = ApplicationProvider.getApplicationContext();
Transformer transformer =
new Transformer.Builder(context)
.setTransformationRequest(
new TransformationRequest.Builder().setEnableRequestSdrToneMapping(true).build())
.addListener(
new Transformer.Listener() {
@Override
public void onFallbackApplied(
MediaItem inputMediaItem,
TransformationRequest originalTransformationRequest,
TransformationRequest fallbackTransformationRequest) {
// Tone mapping flag shouldn't change in fallback when tone mapping is
// requested.
assertThat(originalTransformationRequest.enableRequestSdrToneMapping)
.isEqualTo(fallbackTransformationRequest.enableRequestSdrToneMapping);
}
})
.build();
try {
TransformationTestResult transformationTestResult =
new TransformerAndroidTestRunner.Builder(context, transformer)
.build()
.run(testId, MediaItem.fromUri(Uri.parse(MP4_ASSET_1080P_4_SECOND_HDR10)));
Log.i(TAG, "Tone mapped.");
assertFileHasColorTransfer(transformationTestResult.filePath, C.COLOR_TRANSFER_SDR);
return;
} catch (TransformationException exception) {
Log.i(TAG, checkNotNull(exception.getCause()).toString());
assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class);
if (Util.SDK_INT < 31) {
assertThat(exception.errorCode)
.isEqualTo(TransformationException.ERROR_CODE_HDR_EDITING_UNSUPPORTED);
assertThat(exception)
.hasCauseThat()
.hasMessageThat()
.isEqualTo("HDR editing and tone mapping not supported under API 31.");
} else {
assertThat(exception.errorCode)
.isEqualTo(TransformationException.ERROR_CODE_DECODING_FORMAT_UNSUPPORTED);
assertThat(exception)
.hasCauseThat()
.hasMessageThat()
.isEqualTo("Tone-mapping requested but not supported by the decoder");
}
return;
}
}
@Test
public void transform_toneMapAndTranscode_hdr10File_toneMapsOrThrows() throws Exception {
String testId = "transform_toneMapAndTranscode_hdr10File_toneMapsOrThrows";
Context context = ApplicationProvider.getApplicationContext();
Transformer transformer =
new Transformer.Builder(context)
.setTransformationRequest(
new TransformationRequest.Builder()
.setEnableRequestSdrToneMapping(true)
.setRotationDegrees(180)
.build())
.addListener(
new Transformer.Listener() {
@Override
public void onFallbackApplied(
MediaItem inputMediaItem,
TransformationRequest originalTransformationRequest,
TransformationRequest fallbackTransformationRequest) {
// Tone mapping flag shouldn't change in fallback when tone mapping is
// requested.
assertThat(originalTransformationRequest.enableRequestSdrToneMapping)
.isEqualTo(fallbackTransformationRequest.enableRequestSdrToneMapping);
}
})
.build();
try {
TransformationTestResult transformationTestResult =
new TransformerAndroidTestRunner.Builder(context, transformer)
.build()
.run(testId, MediaItem.fromUri(Uri.parse(MP4_ASSET_1080P_4_SECOND_HDR10)));
Log.i(TAG, "Tone mapped.");
assertFileHasColorTransfer(transformationTestResult.filePath, C.COLOR_TRANSFER_SDR);
return;
} catch (TransformationException exception) {
Log.i(TAG, checkNotNull(exception.getCause()).toString());
assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class);
if (Util.SDK_INT < 31) {
assertThat(exception.errorCode)
.isEqualTo(TransformationException.ERROR_CODE_HDR_EDITING_UNSUPPORTED);
assertThat(exception)
.hasCauseThat()
.hasMessageThat()
.isEqualTo("HDR editing and tone mapping not supported under API 31.");
} else {
assertThat(exception.errorCode)
.isEqualTo(TransformationException.ERROR_CODE_DECODING_FORMAT_UNSUPPORTED);
assertThat(exception)
.hasCauseThat()
.hasMessageThat()
.isEqualTo("Tone-mapping requested but not supported by the decoder");
}
return;
}
}
}

View File

@ -0,0 +1,58 @@
/*
* 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 androidx.media3.transformer.mh.analysis;
import static androidx.media3.common.util.Assertions.checkNotNull;
import static com.google.common.truth.Truth.assertThat;
import android.media.MediaFormat;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.ColorInfo;
import androidx.media3.common.util.MediaFormatUtil;
import androidx.media3.common.util.Util;
import androidx.media3.test.utils.DecodeOneFrameTestUtil;
/** Utilities for reading color info from a file. */
public class FileTestUtil {
public static void assertFileHasColorTransfer(
@Nullable String filePath, @C.ColorTransfer int expectedColorTransfer) throws Exception {
if (Util.SDK_INT < 29) {
// Skipping on this API version due to lack of support for MediaFormat#getInteger, which is
// required for MediaFormatUtil#getColorInfo.
return;
}
DecodeOneFrameTestUtil.decodeOneCacheFileFrame(
checkNotNull(filePath),
new DecodeOneFrameTestUtil.Listener() {
@Override
public void onContainerExtracted(MediaFormat mediaFormat) {
@Nullable ColorInfo extractedColor = MediaFormatUtil.getColorInfo(mediaFormat);
assertThat(checkNotNull(extractedColor).colorTransfer).isEqualTo(expectedColorTransfer);
}
@Override
public void onFrameDecoded(MediaFormat mediaFormat) {
@Nullable ColorInfo decodedColor = MediaFormatUtil.getColorInfo(mediaFormat);
assertThat(checkNotNull(decodedColor).colorTransfer).isEqualTo(expectedColorTransfer);
}
},
/* surface= */ null);
}
private FileTestUtil() {}
}