Document and test multi asset ultraHDR support

PiperOrigin-RevId: 619200688
This commit is contained in:
tofunmi 2024-03-26 08:36:11 -07:00 committed by Copybara-Service
parent d00ca1e343
commit f4fefd19f7
7 changed files with 106 additions and 10 deletions

View File

@ -39,6 +39,7 @@
* Relax trim optimization H.264 level checks. * Relax trim optimization H.264 level checks.
* Add support for changing between SDR and HDR input media in a sequence. * Add support for changing between SDR and HDR input media in a sequence.
* Add support for composition-level audio effects. * Add support for composition-level audio effects.
* Add support for transcoding Ultra HDR images into HDR videos.
* Track Selection: * Track Selection:
* `DefaultTrackSelector`: Prefer video tracks with a 'reasonable' frame * `DefaultTrackSelector`: Prefer video tracks with a 'reasonable' frame
rate (>=10fps) over those with a lower or unset frame rate. This ensures rate (>=10fps) over those with a lower or unset frame rate. This ensures

View File

@ -141,6 +141,26 @@ public final class TransformerUltraHdrTest {
assertThat(colorInfo.colorTransfer).isEqualTo(C.COLOR_TRANSFER_SDR); assertThat(colorInfo.colorTransfer).isEqualTo(C.COLOR_TRANSFER_SDR);
} }
@Test
public void exportSdrImageThenUltraHdrImage_exportsSdr() throws Exception {
Composition composition =
createUltraHdrComposition(
/* tonemap= */ false,
oneFrameFromImage(JPG_ASSET_URI_STRING, NO_EFFECT),
oneFrameFromImage(ULTRA_HDR_URI_STRING, NO_EFFECT));
ExportTestResult result =
new TransformerAndroidTestRunner.Builder(context, new Transformer.Builder(context).build())
.build()
.run(testId, composition);
assertThat(result.filePath).isNotNull();
ColorInfo colorInfo =
retrieveTrackFormat(context, result.filePath, C.TRACK_TYPE_VIDEO).colorInfo;
assertThat(colorInfo.colorSpace).isEqualTo(C.COLOR_SPACE_BT709);
assertThat(colorInfo.colorTransfer).isEqualTo(C.COLOR_TRANSFER_SDR);
}
private static Composition createUltraHdrComposition( private static Composition createUltraHdrComposition(
boolean tonemap, EditedMediaItem editedMediaItem, EditedMediaItem... editedMediaItems) { boolean tonemap, EditedMediaItem editedMediaItem, EditedMediaItem... editedMediaItems) {
Composition.Builder builder = Composition.Builder builder =

View File

@ -22,11 +22,15 @@ import static androidx.media3.test.utils.BitmapPixelTestUtil.getBitmapAveragePix
import static androidx.media3.test.utils.BitmapPixelTestUtil.maybeSaveTestBitmap; import static androidx.media3.test.utils.BitmapPixelTestUtil.maybeSaveTestBitmap;
import static androidx.media3.test.utils.BitmapPixelTestUtil.readBitmap; import static androidx.media3.test.utils.BitmapPixelTestUtil.readBitmap;
import static androidx.media3.test.utils.TestUtil.retrieveTrackFormat; import static androidx.media3.test.utils.TestUtil.retrieveTrackFormat;
import static androidx.media3.transformer.AndroidTestUtil.MP4_ASSET_1080P_5_SECOND_HLG10;
import static androidx.media3.transformer.AndroidTestUtil.MP4_ASSET_1080P_5_SECOND_HLG10_FORMAT; import static androidx.media3.transformer.AndroidTestUtil.MP4_ASSET_1080P_5_SECOND_HLG10_FORMAT;
import static androidx.media3.transformer.AndroidTestUtil.ULTRA_HDR_URI_STRING; import static androidx.media3.transformer.AndroidTestUtil.ULTRA_HDR_URI_STRING;
import static androidx.media3.transformer.AndroidTestUtil.extractBitmapsFromVideo; import static androidx.media3.transformer.AndroidTestUtil.extractBitmapsFromVideo;
import static androidx.media3.transformer.AndroidTestUtil.recordTestSkipped; import static androidx.media3.transformer.AndroidTestUtil.recordTestSkipped;
import static androidx.media3.transformer.Composition.HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL;
import static androidx.media3.transformer.SequenceEffectTestUtil.NO_EFFECT; import static androidx.media3.transformer.SequenceEffectTestUtil.NO_EFFECT;
import static androidx.media3.transformer.SequenceEffectTestUtil.clippedVideo;
import static androidx.media3.transformer.SequenceEffectTestUtil.createComposition;
import static androidx.media3.transformer.SequenceEffectTestUtil.oneFrameFromImage; import static androidx.media3.transformer.SequenceEffectTestUtil.oneFrameFromImage;
import static androidx.test.core.app.ApplicationProvider.getApplicationContext; import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
@ -63,6 +67,7 @@ import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
public final class TransformerMhUltraHdrPixelTest { public final class TransformerMhUltraHdrPixelTest {
private static final int ONE_FRAME_END_POSITION_MS = 30;
private static final String PNG_ASSET_BASE_PATH = private static final String PNG_ASSET_BASE_PATH =
"test-generated-goldens/TransformerUltraHdrPixelTest"; "test-generated-goldens/TransformerUltraHdrPixelTest";
@ -77,10 +82,12 @@ public final class TransformerMhUltraHdrPixelTest {
} }
@Test @Test
public void exportUltraHdrImage_withUltraHdrEnabledOnSupportedDevice_succeeds() throws Exception { public void exportUltraHdrImage_withUltraHdrEnabledOnSupportedDevice_exportsHdr()
throws Exception {
assumeDeviceSupportsUltraHdrEditing(); assumeDeviceSupportsUltraHdrEditing();
Composition composition = Composition composition =
createUltraHdrComposition(oneFrameFromImage(ULTRA_HDR_URI_STRING, NO_EFFECT)); createUltraHdrComposition(
/* tonemap= */ false, oneFrameFromImage(ULTRA_HDR_URI_STRING, NO_EFFECT));
ExportTestResult result = ExportTestResult result =
new TransformerAndroidTestRunner.Builder(context, new Transformer.Builder(context).build()) new TransformerAndroidTestRunner.Builder(context, new Transformer.Builder(context).build())
@ -96,6 +103,70 @@ public final class TransformerMhUltraHdrPixelTest {
extractBitmapsFromVideo(context, result.filePath, Config.RGBA_F16), testId); extractBitmapsFromVideo(context, result.filePath, Config.RGBA_F16), testId);
} }
@Test
public void exportHdrVideoThenUltraHdrImage_exportsHdr() throws Exception {
assumeDeviceSupportsUltraHdrEditing();
Composition composition =
createComposition(
/* presentation= */ null,
clippedVideo(MP4_ASSET_1080P_5_SECOND_HLG10, NO_EFFECT, ONE_FRAME_END_POSITION_MS),
oneFrameFromImage(ULTRA_HDR_URI_STRING, NO_EFFECT));
ExportTestResult result =
new TransformerAndroidTestRunner.Builder(context, new Transformer.Builder(context).build())
.build()
.run(testId, composition);
assertThat(result.filePath).isNotNull();
ColorInfo colorInfo =
retrieveTrackFormat(context, result.filePath, C.TRACK_TYPE_VIDEO).colorInfo;
assertThat(colorInfo.colorSpace).isEqualTo(C.COLOR_SPACE_BT2020);
assertThat(colorInfo.colorTransfer).isEqualTo(C.COLOR_TRANSFER_HLG);
assertFp16BitmapsMatchExpectedAndSave(
extractBitmapsFromVideo(context, result.filePath, Config.RGBA_F16), testId);
}
@Test
public void exportUltraHdrImageThenHdrVideo_exportsHdr() throws Exception {
assumeDeviceSupportsUltraHdrEditing();
Composition composition =
createComposition(
/* presentation= */ null,
oneFrameFromImage(ULTRA_HDR_URI_STRING, NO_EFFECT),
clippedVideo(MP4_ASSET_1080P_5_SECOND_HLG10, NO_EFFECT, ONE_FRAME_END_POSITION_MS));
ExportTestResult result =
new TransformerAndroidTestRunner.Builder(context, new Transformer.Builder(context).build())
.build()
.run(testId, composition);
assertThat(result.filePath).isNotNull();
ColorInfo colorInfo =
retrieveTrackFormat(context, result.filePath, C.TRACK_TYPE_VIDEO).colorInfo;
assertThat(colorInfo.colorSpace).isEqualTo(C.COLOR_SPACE_BT2020);
assertThat(colorInfo.colorTransfer).isEqualTo(C.COLOR_TRANSFER_HLG);
}
@Test
public void exportTonemappedHdrVideoThenUltraHdrImage_exportsSdr() throws Exception {
Composition composition =
createUltraHdrComposition(
/* tonemap= */ true,
clippedVideo(MP4_ASSET_1080P_5_SECOND_HLG10, NO_EFFECT, ONE_FRAME_END_POSITION_MS),
oneFrameFromImage(ULTRA_HDR_URI_STRING, NO_EFFECT));
ExportTestResult result =
new TransformerAndroidTestRunner.Builder(context, new Transformer.Builder(context).build())
.build()
.run(testId, composition);
assertThat(result.filePath).isNotNull();
ColorInfo colorInfo =
retrieveTrackFormat(context, result.filePath, C.TRACK_TYPE_VIDEO).colorInfo;
assertThat(colorInfo.colorSpace).isEqualTo(C.COLOR_SPACE_BT709);
assertThat(colorInfo.colorTransfer).isEqualTo(C.COLOR_TRANSFER_SDR);
}
@RequiresApi(29) // getBitmapAveragePixelAbsoluteDifferenceFp16() @RequiresApi(29) // getBitmapAveragePixelAbsoluteDifferenceFp16()
public static void assertFp16BitmapsMatchExpectedAndSave( public static void assertFp16BitmapsMatchExpectedAndSave(
List<Bitmap> actualBitmaps, String testId) throws IOException { List<Bitmap> actualBitmaps, String testId) throws IOException {
@ -117,6 +188,17 @@ public final class TransformerMhUltraHdrPixelTest {
} }
} }
private static Composition createUltraHdrComposition(
boolean tonemap, EditedMediaItem editedMediaItem, EditedMediaItem... editedMediaItems) {
Composition.Builder builder =
new Composition.Builder(new EditedMediaItemSequence(editedMediaItem, editedMediaItems))
.experimentalSetRetainHdrFromUltraHdrImage(true);
if (tonemap) {
builder.setHdrMode(HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL);
}
return builder.build();
}
private void assumeDeviceSupportsUltraHdrEditing() private void assumeDeviceSupportsUltraHdrEditing()
throws JSONException, IOException, DecoderQueryException { throws JSONException, IOException, DecoderQueryException {
if (Util.SDK_INT < 34) { if (Util.SDK_INT < 34) {
@ -130,12 +212,4 @@ public final class TransformerMhUltraHdrPixelTest {
/* inputFormat= */ MP4_ASSET_1080P_5_SECOND_HLG10_FORMAT, /* inputFormat= */ MP4_ASSET_1080P_5_SECOND_HLG10_FORMAT,
/* outputFormat= */ null); /* outputFormat= */ null);
} }
private static Composition createUltraHdrComposition(
EditedMediaItem editedMediaItem, EditedMediaItem... editedMediaItems) {
Composition.Builder builder =
new Composition.Builder(new EditedMediaItemSequence(editedMediaItem, editedMediaItems))
.experimentalSetRetainHdrFromUltraHdrImage(true);
return builder.build();
}
} }

View File

@ -237,6 +237,7 @@ public final class Composition {
* *
* <p>If the {@link HdrMode} is {@link #HDR_MODE_KEEP_HDR}, then setting this to {@code true} * <p>If the {@link HdrMode} is {@link #HDR_MODE_KEEP_HDR}, then setting this to {@code true}
* applies the recovery map (i.e. the gainmap) to the base image to produce HDR video frames. * applies the recovery map (i.e. the gainmap) to the base image to produce HDR video frames.
* This is automatically overridden to true, if the first asset is a HDR video.
* *
* <p>The output video will have the same color encoding as the first {@link EditedMediaItem} * <p>The output video will have the same color encoding as the first {@link EditedMediaItem}
* the sequence. If the Ultra HDR image is first in the sequence, output video will default to * the sequence. If the Ultra HDR image is first in the sequence, output video will default to