HDR: Add fallback MH tests.

Test that HDR editing succeeds on devices supporting HDR editing, tone maps on
devices supporting tone mapping, and throws exceptions on all other devices.

Also, only restrict HDR editing and tone mapping support to API 31+ only when
transcoding, not for all transformations.

PiperOrigin-RevId: 472958965
This commit is contained in:
huangdarwin 2022-09-08 12:03:40 +00:00 committed by Marc Baechinger
parent 8b7638bed3
commit 0d8fd3d4fc
4 changed files with 156 additions and 13 deletions

View File

@ -84,6 +84,8 @@ public final class AndroidTestUtil {
.setFrameRate(30.472f)
.build();
public static final String MP4_ASSET_1080P_4_SECOND_HDR10 =
"https://storage.googleapis.com/exoplayer-test-media-1/mp4/samsung-s21-hdr-hdr10.mp4";
public static final String MP4_ASSET_1080P_1_SECOND_HDR10_VIDEO_SDR_CONTAINER =
"asset:///media/mp4/hdr10-video-with-sdr-container.mp4";

View File

@ -15,31 +15,168 @@
*/
package androidx.media3.transformer.mh;
import static androidx.media3.common.MimeTypes.VIDEO_H265;
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 com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.net.Uri;
import androidx.media3.common.C;
import androidx.media3.common.ColorInfo;
import androidx.media3.common.MediaItem;
import androidx.media3.common.util.Util;
import androidx.media3.transformer.EncoderUtil;
import androidx.media3.transformer.TransformationException;
import androidx.media3.transformer.TransformationRequest;
import androidx.media3.transformer.Transformer;
import androidx.media3.transformer.TransformerAndroidTestRunner;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
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 {
private static final ColorInfo HDR10_DEFAULT_COLOR_INFO =
new ColorInfo(
C.COLOR_SPACE_BT2020,
C.COLOR_RANGE_LIMITED,
C.COLOR_TRANSFER_ST2084,
/* hdrStaticInfo= */ null);
@Test
public void transform_noRequestedTranscode_hdr10File_transformsOrThrows() throws Exception {
String testId = "transform_noRequestedTranscode_hdr10File_transformsOrThrows";
Context context = ApplicationProvider.getApplicationContext();
Transformer transformer =
new Transformer.Builder(context)
.setTransformationRequest(
new TransformationRequest.Builder().experimental_setEnableHdrEditing(true).build())
.build();
try {
new TransformerAndroidTestRunner.Builder(context, transformer)
.build()
.run(testId, MediaItem.fromUri(Uri.parse(MP4_ASSET_1080P_4_SECOND_HDR10)));
return;
} catch (TransformationException exception) {
assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class);
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.");
return;
}
}
@Test
public void transformAndTranscode_hdr10File_whenHdrEditingIsSupported() throws Exception {
String testId = "transformAndTranscode_hdr10File_whenHdrEditingIsSupported";
Context context = ApplicationProvider.getApplicationContext();
if (!deviceSupportsHdrEditing(VIDEO_H265, HDR10_DEFAULT_COLOR_INFO)) {
recordTestSkipped(
context,
testId,
/* reason= */ "Skipping on this device due to lack of HDR10 editing support.");
return;
}
Transformer transformer =
new Transformer.Builder(context)
.setTransformationRequest(
new TransformationRequest.Builder()
.experimental_setEnableHdrEditing(true)
.setRotationDegrees(180)
.build())
.build();
new TransformerAndroidTestRunner.Builder(context, transformer)
.build()
.run(testId, MediaItem.fromUri(Uri.parse(MP4_ASSET_1080P_4_SECOND_HDR10)));
}
@Test
public void transformAndTranscode_hdr10File_toneMapsOrThrows_whenHdrEditingUnsupported()
throws Exception {
String testId = "transformAndTranscode_hdr10File_toneMapsOrThrows_whenHdrEditingUnsupported";
Context context = ApplicationProvider.getApplicationContext();
if (deviceSupportsHdrEditing(VIDEO_H265, HDR10_DEFAULT_COLOR_INFO)) {
recordTestSkipped(
context,
testId,
/* reason= */ "Skipping on this device due to presence of HDR10 editing support");
return;
}
AtomicBoolean isToneMappingFallbackApplied = new AtomicBoolean();
Transformer transformer =
new Transformer.Builder(context)
.setTransformationRequest(
new TransformationRequest.Builder()
.experimental_setEnableHdrEditing(true)
.setRotationDegrees(180)
.build())
.addListener(
new Transformer.Listener() {
@Override
public void onFallbackApplied(
MediaItem inputMediaItem,
TransformationRequest originalTransformationRequest,
TransformationRequest fallbackTransformationRequest) {
assertThat(originalTransformationRequest.enableRequestSdrToneMapping).isFalse();
if (fallbackTransformationRequest.enableRequestSdrToneMapping) {
isToneMappingFallbackApplied.set(true);
}
}
})
.build();
try {
new TransformerAndroidTestRunner.Builder(context, transformer)
.build()
.run(testId, MediaItem.fromUri(Uri.parse(MP4_ASSET_1080P_4_SECOND_HDR10)));
} catch (TransformationException exception) {
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.
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;
}
assertThat(isToneMappingFallbackApplied.get()).isTrue();
}
@Test
public void transformUnexpectedColorInfo() throws Exception {
String testId = "transformUnexpectedColorInfo";
Context context = ApplicationProvider.getApplicationContext();
if (Util.SDK_INT < 29) {
recordTestSkipped(
context,
"SetHdrEditingTransformationTest",
testId,
/* reason= */ "Skipping on this API version due to lack of support for"
+ " MediaFormat#getInteger(String, int).");
return;
@ -53,7 +190,11 @@ public class SetHdrEditingTransformationTest {
new TransformerAndroidTestRunner.Builder(context, transformer)
.build()
.run(
/* testId= */ "transformUnexpectedColorInfo",
testId,
MediaItem.fromUri(Uri.parse(MP4_ASSET_1080P_1_SECOND_HDR10_VIDEO_SDR_CONTAINER)));
}
private static boolean deviceSupportsHdrEditing(String mimeType, ColorInfo colorInfo) {
return !EncoderUtil.getSupportedEncoderNamesForHdrEditing(mimeType, colorInfo).isEmpty();
}
}

View File

@ -17,12 +17,10 @@
package androidx.media3.transformer;
import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.common.util.Util.SDK_INT;
import static androidx.media3.exoplayer.source.SampleStream.FLAG_REQUIRE_FORMAT;
import android.content.Context;
import androidx.media3.common.C;
import androidx.media3.common.ColorInfo;
import androidx.media3.common.DebugViewProvider;
import androidx.media3.common.Effect;
import androidx.media3.common.Format;
@ -99,15 +97,6 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
return false;
}
Format inputFormat = checkNotNull(formatHolder.format);
if (SDK_INT < 31 && ColorInfo.isTransferHdr(inputFormat.colorInfo)) {
throw TransformationException.createForCodec(
new IllegalArgumentException("HDR editing not supported under API 31."),
/* isVideo= */ true,
/* isDecoder= */ false,
inputFormat,
/* mediaCodecName= */ null,
TransformationException.ERROR_CODE_HDR_EDITING_UNSUPPORTED);
}
if (shouldTranscode(inputFormat)) {
samplePipeline =
new VideoTranscodingSamplePipeline(

View File

@ -18,6 +18,7 @@ package androidx.media3.transformer;
import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.common.util.Assertions.checkState;
import static androidx.media3.common.util.Util.SDK_INT;
import android.content.Context;
import android.media.MediaCodec;
@ -75,6 +76,16 @@ import org.checkerframework.dataflow.qual.Pure;
Transformer.AsyncErrorListener asyncErrorListener,
DebugViewProvider debugViewProvider)
throws TransformationException {
if (SDK_INT < 31 && ColorInfo.isTransferHdr(inputFormat.colorInfo)) {
throw TransformationException.createForCodec(
new IllegalArgumentException("HDR editing and tone mapping not supported under API 31."),
/* isVideo= */ true,
/* isDecoder= */ false,
inputFormat,
/* mediaCodecName= */ null,
TransformationException.ERROR_CODE_HDR_EDITING_UNSUPPORTED);
}
decoderInputBuffer =
new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DISABLED);
encoderOutputBuffer =