From e985603e0ad557e2536108d2eef9659e545a1668 Mon Sep 17 00:00:00 2001 From: samrobinson Date: Thu, 13 Jun 2024 09:15:29 -0700 Subject: [PATCH] Refactor mixedInput E2E test to use parameterized logic. This CL is a pure refactor of the existing tests, iterative changes will be done in follow-ups. The only logic change is having all MP4 assets have the presentaton of height=360. The old code had some with height 480. PiperOrigin-RevId: 643020773 --- .../media3/transformer/AndroidTestUtil.java | 4 + .../ParameterizedInputSequenceExportTest.java | 261 +++++++++++ .../transformer/TransformerEndToEndTest.java | 56 +++ .../TransformerMixedInputEndToEndTest.java | 411 ------------------ 4 files changed, 321 insertions(+), 411 deletions(-) create mode 100644 libraries/transformer/src/androidTest/java/androidx/media3/transformer/ParameterizedInputSequenceExportTest.java delete mode 100644 libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerMixedInputEndToEndTest.java diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/AndroidTestUtil.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/AndroidTestUtil.java index 36f7c4f1c7..8e5f538f86 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/AndroidTestUtil.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/AndroidTestUtil.java @@ -924,6 +924,10 @@ public final class AndroidTestUtil { return MP4_REMOTE_3840W_2160H_30_SECOND_ROOF_REDMINOTE9_FORMAT; case MP4_REMOTE_7680W_4320H_31_SECOND_ROOF_SAMSUNGS20ULTRA5G: return MP4_REMOTE_7680W_4320H_31_SECOND_ROOF_SAMSUNGS20ULTRA5G_FORMAT; + case BT601_MP4_ASSET_URI_STRING: + return BT601_MP4_ASSET_FORMAT; + case BT601_MOV_ASSET_URI_STRING: + return BT601_MOV_ASSET_FORMAT; default: throw new IllegalArgumentException("The format for the given uri is not found."); } diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/ParameterizedInputSequenceExportTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/ParameterizedInputSequenceExportTest.java new file mode 100644 index 0000000000..e61d21b3ce --- /dev/null +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/ParameterizedInputSequenceExportTest.java @@ -0,0 +1,261 @@ +/* + * Copyright 2024 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; + +import static androidx.media3.transformer.AndroidTestUtil.BT601_MP4_ASSET_FRAME_COUNT; +import static androidx.media3.transformer.AndroidTestUtil.BT601_MP4_ASSET_URI_STRING; +import static androidx.media3.transformer.AndroidTestUtil.JPG_ASSET_URI_STRING; +import static androidx.media3.transformer.AndroidTestUtil.MP4_ASSET_FRAME_COUNT; +import static androidx.media3.transformer.AndroidTestUtil.MP4_ASSET_URI_STRING; +import static androidx.media3.transformer.AndroidTestUtil.PNG_ASSET_URI_STRING; +import static androidx.media3.transformer.AndroidTestUtil.getFormatForTestFile; +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import androidx.annotation.Nullable; +import androidx.media3.common.C; +import androidx.media3.common.ColorInfo; +import androidx.media3.common.Format; +import androidx.media3.common.MediaItem; +import androidx.media3.common.MimeTypes; +import androidx.media3.common.util.Assertions; +import androidx.media3.common.util.Util; +import androidx.media3.effect.Presentation; +import androidx.test.core.app.ApplicationProvider; +import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import java.io.File; +import java.math.RoundingMode; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +// TODO: b/343362776 - Add tests to assert enough silence is generated. +// TODO: b/346289922 - Consider checking frame counts with extractors. +// TODO: b/345483531 - Add support for asserting on duration for image only sequences. + +/** Parameterized end to end {@linkplain EditedMediaItemSequence sequence} export tests. */ +@RunWith(Parameterized.class) +public class ParameterizedInputSequenceExportTest { + private static final ImageItemConfig PNG_ITEM = + new ImageItemConfig(PNG_ASSET_URI_STRING, /* frameCount= */ 34); + private static final ImageItemConfig JPG_ITEM = + new ImageItemConfig(JPG_ASSET_URI_STRING, /* frameCount= */ 41); + private static final VideoItemConfig BT709_ITEM = + new VideoItemConfig(MP4_ASSET_URI_STRING, MP4_ASSET_FRAME_COUNT); + private static final VideoItemConfig BT601_ITEM = + new VideoItemConfig(BT601_MP4_ASSET_URI_STRING, BT601_MP4_ASSET_FRAME_COUNT); + + @Parameters(name = "{0}") + public static ImmutableList params() { + return ImmutableList.of( + new SequenceConfig(PNG_ITEM, BT709_ITEM), + new SequenceConfig(BT709_ITEM, PNG_ITEM), + new SequenceConfig( + BT709_ITEM, BT709_ITEM, PNG_ITEM, JPG_ITEM, BT709_ITEM, PNG_ITEM, BT709_ITEM), + new SequenceConfig( + PNG_ITEM, BT709_ITEM, BT709_ITEM, PNG_ITEM, PNG_ITEM, BT709_ITEM, PNG_ITEM), + new SequenceConfig( + PNG_ITEM, BT709_ITEM, BT601_ITEM, PNG_ITEM, PNG_ITEM, BT601_ITEM, PNG_ITEM), + new SequenceConfig( + PNG_ITEM, JPG_ITEM, BT709_ITEM, BT601_ITEM, BT709_ITEM, PNG_ITEM, BT601_ITEM), + new SequenceConfig( + BT601_ITEM, BT709_ITEM, PNG_ITEM, JPG_ITEM, BT709_ITEM, PNG_ITEM, BT601_ITEM)); + } + + @Rule public final TestName testName = new TestName(); + + @Parameter public SequenceConfig sequence; + + @Test + public void export_completesWithCorrectFrameCount() throws Exception { + Context context = ApplicationProvider.getApplicationContext(); + String testId = testName.getMethodName(); + assumeSequenceFormatsSupported(context, testId, sequence); + Transformer transformer = + new Transformer.Builder(context) + .setEncoderFactory( + new DefaultEncoderFactory.Builder(context).setEnableFallback(false).build()) + .build(); + + ExportTestResult result = + new TransformerAndroidTestRunner.Builder(context, transformer) + .build() + .run(testId, sequence.buildComposition()); + + assertThat(result.exportResult.videoFrameCount).isEqualTo(sequence.totalExpectedFrameCount); + assertThat(new File(result.filePath).length()).isGreaterThan(0); + } + + private static void assumeSequenceFormatsSupported( + Context context, String testId, SequenceConfig sequence) throws Exception { + Assertions.checkState(!sequence.itemConfigs.isEmpty()); + Format outputFormat = Assertions.checkNotNull(sequence.itemConfigs.get(0).outputFormat); + for (ItemConfig item : sequence.itemConfigs) { + AndroidTestUtil.assumeFormatsSupported(context, testId, item.format, outputFormat); + } + } + + /** Test parameters for an {@link EditedMediaItemSequence}. */ + private static final class SequenceConfig { + public final int totalExpectedFrameCount; + public final ImmutableList itemConfigs; + + public SequenceConfig(ItemConfig... itemConfigs) { + this.itemConfigs = ImmutableList.copyOf(itemConfigs); + int frameCountSum = 0; + for (ItemConfig item : itemConfigs) { + frameCountSum += item.frameCount; + } + this.totalExpectedFrameCount = frameCountSum; + } + + /** + * Builds a {@link Composition} from the sequence configuration. + * + *

{@link Presentation} of {@code width 480, height 360} is used to ensure software encoders + * can encode. + */ + public Composition buildComposition() { + ImmutableList.Builder editedMediaItems = new ImmutableList.Builder<>(); + for (ItemConfig itemConfig : itemConfigs) { + editedMediaItems.add(itemConfig.build()); + } + + return new Composition.Builder(new EditedMediaItemSequence(editedMediaItems.build())) + .setEffects( + new Effects( + /* audioProcessors= */ ImmutableList.of(), + ImmutableList.of( + Presentation.createForWidthAndHeight( + /* width= */ 480, /* height= */ 360, Presentation.LAYOUT_SCALE_TO_FIT)))) + .build(); + } + + @Override + public String toString() { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("Seq{"); + for (ItemConfig itemConfig : itemConfigs) { + stringBuilder.append(itemConfig).append(","); + } + stringBuilder.replace(stringBuilder.length() - 2, stringBuilder.length(), "}"); + return stringBuilder.toString(); + } + } + + /** Test parameters for an {@link EditedMediaItem}. */ + private abstract static class ItemConfig { + public final int frameCount; + + private final String uri; + @Nullable private final Format format; + @Nullable private final Format outputFormat; + + public ItemConfig( + String uri, int frameCount, @Nullable Format format, @Nullable Format outputFormat) { + this.uri = uri; + this.frameCount = frameCount; + this.format = format; + this.outputFormat = outputFormat; + } + + public final EditedMediaItem build() { + EditedMediaItem.Builder builder = new EditedMediaItem.Builder(MediaItem.fromUri(uri)); + onBuild(builder); + return builder.build(); + } + + /** + * Called when an {@link EditedMediaItem} is being {@linkplain #build() built}. + * + * @param builder The {@link EditedMediaItem.Builder} to optionally modify before the item is + * built. + */ + protected abstract void onBuild(EditedMediaItem.Builder builder); + + @Override + public String toString() { + return Iterables.getLast(Splitter.on("/").splitToList(uri)); + } + } + + /** {@link ItemConfig} for an image {@link EditedMediaItem} with a duration of one second. */ + private static final class ImageItemConfig extends ItemConfig { + + // Image by default are encoded in H265 and BT709 SDR. + private static final Format OUTPUT_FORMAT = + new Format.Builder() + .setSampleMimeType(MimeTypes.VIDEO_H265) + .setFrameRate(30.f) + .setWidth(480) + .setHeight(360) + .setColorInfo(ColorInfo.SDR_BT709_LIMITED) + .build(); + + private final long durationUs; + private final int frameRate; + + public ImageItemConfig(String uri, int frameCount) { + this(uri, frameCount, C.MICROS_PER_SECOND); + } + + public ImageItemConfig(String uri, int frameRate, long durationUs) { + super( + uri, + /* frameCount= */ (int) + Util.scaleLargeValue( + frameRate, durationUs, C.MICROS_PER_SECOND, RoundingMode.CEILING), + /* format= */ null, + OUTPUT_FORMAT); + this.frameRate = frameRate; + this.durationUs = durationUs; + } + + @Override + protected void onBuild(EditedMediaItem.Builder builder) { + builder.setFrameRate(frameRate).setDurationUs(durationUs); + } + } + + /** + * {@link ItemConfig} for a video {@link EditedMediaItem}. + * + *

Audio is removed and a {@link Presentation} of specified {@code height=360}. + */ + private static final class VideoItemConfig extends ItemConfig { + public VideoItemConfig(String uri, int frameCount) { + super(uri, frameCount, getFormatForTestFile(uri), getFormatForTestFile(uri)); + } + + @Override + protected void onBuild(EditedMediaItem.Builder builder) { + builder + .setEffects( + new Effects( + /* audioProcessors= */ ImmutableList.of(), + ImmutableList.of(Presentation.createForHeight(360)))) + .setRemoveAudio(true); + } + } +} diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerEndToEndTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerEndToEndTest.java index bdf118350c..25ae6f8537 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerEndToEndTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerEndToEndTest.java @@ -100,6 +100,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import java.io.File; import java.nio.ByteBuffer; +import java.util.ArrayList; import java.util.concurrent.atomic.AtomicInteger; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.junit.Before; @@ -417,6 +418,61 @@ public class TransformerEndToEndTest { assertThat(new File(result.filePath).length()).isGreaterThan(0); } + // TODO: b/345483531 - Migrate this test to a Parameterized ImageSequence test. + @Test + public void videoEditing_withShortAlternatingImages_completesWithCorrectFrameCountAndDuration() + throws Exception { + Transformer transformer = + new Transformer.Builder(context) + .setEncoderFactory( + new DefaultEncoderFactory.Builder(context).setEnableFallback(false).build()) + .build(); + + EditedMediaItem image1 = + new EditedMediaItem.Builder(MediaItem.fromUri(PNG_ASSET_URI_STRING)) + .setDurationUs(100_000) + .setFrameRate(30) + .build(); + int image1FrameCount = 3; + EditedMediaItem image2 = + new EditedMediaItem.Builder(MediaItem.fromUri(JPG_ASSET_URI_STRING)) + .setDurationUs(200_000) + .setFrameRate(30) + .build(); + int image2FrameCount = 6; + + ArrayList editedMediaItems = new ArrayList<>(100); + for (int i = 0; i < 50; i++) { + editedMediaItems.add(image1); + editedMediaItems.add(image2); + } + + Composition composition = + new Composition.Builder(new EditedMediaItemSequence(editedMediaItems)) + .setEffects( + new Effects( + /* audioProcessors= */ ImmutableList.of(), + ImmutableList.of( + // To ensure that software encoders can encode. + Presentation.createForWidthAndHeight( + /* width= */ 480, + /* height= */ 360, + Presentation.LAYOUT_SCALE_TO_FIT)))) + .build(); + + ExportTestResult result = + new TransformerAndroidTestRunner.Builder(context, transformer) + .build() + .run(testId, composition); + + // TODO: b/346289922 - Check frame count with extractors. + assertThat(result.exportResult.videoFrameCount) + .isEqualTo(50 * image1FrameCount + 50 * image2FrameCount); + // 50 100ms-images and 50 200ms-images + assertThat(result.exportResult.durationMs).isEqualTo(14_966); + assertThat(new File(result.filePath).length()).isGreaterThan(0); + } + @Test public void videoEditing_effectsOverTime_completesWithConsistentFrameCount() throws Exception { assumeFormatsSupported( diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerMixedInputEndToEndTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerMixedInputEndToEndTest.java deleted file mode 100644 index 8679596651..0000000000 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerMixedInputEndToEndTest.java +++ /dev/null @@ -1,411 +0,0 @@ -/* - * Copyright 2023 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; - -import static androidx.media3.transformer.AndroidTestUtil.BT601_MP4_ASSET_FORMAT; -import static androidx.media3.transformer.AndroidTestUtil.BT601_MP4_ASSET_FRAME_COUNT; -import static androidx.media3.transformer.AndroidTestUtil.BT601_MP4_ASSET_URI_STRING; -import static androidx.media3.transformer.AndroidTestUtil.JPG_ASSET_URI_STRING; -import static androidx.media3.transformer.AndroidTestUtil.MP4_ASSET_FORMAT; -import static androidx.media3.transformer.AndroidTestUtil.MP4_ASSET_FRAME_COUNT; -import static androidx.media3.transformer.AndroidTestUtil.MP4_ASSET_URI_STRING; -import static androidx.media3.transformer.AndroidTestUtil.PNG_ASSET_URI_STRING; -import static androidx.media3.transformer.AndroidTestUtil.assumeFormatsSupported; -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.Format; -import androidx.media3.common.MediaItem; -import androidx.media3.common.MimeTypes; -import androidx.media3.common.VideoFrameProcessor; -import androidx.media3.effect.Presentation; -import androidx.test.core.app.ApplicationProvider; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import com.google.common.collect.ImmutableList; -import java.io.File; -import java.util.ArrayList; -import java.util.List; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TestName; -import org.junit.runner.RunWith; - -/** - * End-to-end instrumentation test for {@link Transformer} for cases that cannot be tested using - * robolectric. - * - *

This test aims at testing input of {@linkplain VideoFrameProcessor.InputType mixed types of - * input}. - */ -@RunWith(AndroidJUnit4.class) -public class TransformerMixedInputEndToEndTest { - - // Image by default are encoded in H265 and BT709 SDR. - private static final Format IMAGE_VIDEO_ENCODING_FORMAT = - new Format.Builder() - .setSampleMimeType(MimeTypes.VIDEO_H265) - .setFrameRate(30.f) - .setWidth(480) - .setHeight(360) - .setColorInfo(ColorInfo.SDR_BT709_LIMITED) - .build(); - - private final Context context = ApplicationProvider.getApplicationContext(); - @Rule public final TestName testName = new TestName(); - - private String testId; - - @Before - public void setUpTestId() { - testId = testName.getMethodName(); - } - - // TODO: b/343362776 - Add tests to assert enough silence is generated. - - @Test - public void videoEditing_withImageThenVideoInputs_completesWithCorrectFrameCount() - throws Exception { - assumeFormatsSupported( - context, - testId, - /* inputFormat= */ MP4_ASSET_FORMAT, - /* outputFormat= */ IMAGE_VIDEO_ENCODING_FORMAT); - Transformer transformer = - new Transformer.Builder(context) - .setEncoderFactory( - new DefaultEncoderFactory.Builder(context).setEnableFallback(false).build()) - .build(); - - int imageFrameCount = 31; - EditedMediaItem imageEditedMediaItem = - createImageEditedMediaItem(PNG_ASSET_URI_STRING, /* frameCount= */ imageFrameCount); - EditedMediaItem videoEditedMediaItem = - createVideoEditedMediaItem(MP4_ASSET_URI_STRING, /* height= */ 360); - - ExportTestResult result = - new TransformerAndroidTestRunner.Builder(context, transformer) - .build() - .run(testId, buildComposition(imageEditedMediaItem, videoEditedMediaItem)); - - assertThat(result.exportResult.videoFrameCount) - .isEqualTo(imageFrameCount + MP4_ASSET_FRAME_COUNT); - assertThat(new File(result.filePath).length()).isGreaterThan(0); - } - - @Test - public void videoEditing_withVideoThenImageInputs_completesWithCorrectFrameCount() - throws Exception { - assumeFormatsSupported( - context, testId, /* inputFormat= */ MP4_ASSET_FORMAT, /* outputFormat= */ MP4_ASSET_FORMAT); - Transformer transformer = - new Transformer.Builder(context) - .setEncoderFactory( - new DefaultEncoderFactory.Builder(context).setEnableFallback(false).build()) - .build(); - - int imageFrameCount = 32; - EditedMediaItem imageEditedMediaItem = - createImageEditedMediaItem(PNG_ASSET_URI_STRING, /* frameCount= */ imageFrameCount); - EditedMediaItem videoEditedMediaItem = - createVideoEditedMediaItem(MP4_ASSET_URI_STRING, /* height= */ 480); - ExportTestResult result = - new TransformerAndroidTestRunner.Builder(context, transformer) - .build() - .run(testId, buildComposition(videoEditedMediaItem, imageEditedMediaItem)); - - assertThat(result.exportResult.videoFrameCount) - .isEqualTo(imageFrameCount + MP4_ASSET_FRAME_COUNT); - assertThat(new File(result.filePath).length()).isGreaterThan(0); - } - - @Test - public void videoEditing_withShortAlternatingImages_completesWithCorrectFrameCount() - throws Exception { - Transformer transformer = - new Transformer.Builder(context) - .setEncoderFactory( - new DefaultEncoderFactory.Builder(context).setEnableFallback(false).build()) - .build(); - - EditedMediaItem image1 = - new EditedMediaItem.Builder(MediaItem.fromUri(PNG_ASSET_URI_STRING)) - .setDurationUs(100_000) - .setFrameRate(30) - .build(); - int image1FrameCount = 3; - EditedMediaItem image2 = - new EditedMediaItem.Builder(MediaItem.fromUri(JPG_ASSET_URI_STRING)) - .setDurationUs(200_000) - .setFrameRate(30) - .build(); - int image2FrameCount = 6; - - ArrayList editedMediaItems = new ArrayList<>(100); - for (int i = 0; i < 50; i++) { - editedMediaItems.add(image1); - editedMediaItems.add(image2); - } - - ExportTestResult result = - new TransformerAndroidTestRunner.Builder(context, transformer) - .build() - .run(testId, buildComposition(editedMediaItems)); - - // TODO: b/346289922 - Check frame count with extractors. - assertThat(result.exportResult.videoFrameCount) - .isEqualTo(50 * image1FrameCount + 50 * image2FrameCount); - // 50 100ms-images and 50 200ms-images - assertThat(result.exportResult.durationMs).isEqualTo(14_966); - assertThat(new File(result.filePath).length()).isGreaterThan(0); - } - - @Test - public void - videoEditing_withComplexMixedColorSpaceSdrVideoAndImageInputsEndWithVideo_completesWithCorrectFrameCount() - throws Exception { - assumeFormatsSupported( - context, - testId, - /* inputFormat= */ MP4_ASSET_FORMAT, - /* outputFormat= */ BT601_MP4_ASSET_FORMAT); - assumeFormatsSupported( - context, - testId, - /* inputFormat= */ BT601_MP4_ASSET_FORMAT, - /* outputFormat= */ BT601_MP4_ASSET_FORMAT); - Transformer transformer = - new Transformer.Builder(context) - .setEncoderFactory( - new DefaultEncoderFactory.Builder(context).setEnableFallback(false).build()) - .build(); - - int imageFrameCount = 33; - EditedMediaItem imageEditedMediaItem1 = - createImageEditedMediaItem(PNG_ASSET_URI_STRING, /* frameCount= */ imageFrameCount); - EditedMediaItem imageEditedMediaItem2 = - createImageEditedMediaItem(JPG_ASSET_URI_STRING, /* frameCount= */ imageFrameCount); - EditedMediaItem bt601VideoEditedMediaItem = - createVideoEditedMediaItem(BT601_MP4_ASSET_URI_STRING, /* height= */ 360); - EditedMediaItem bt709VideoEditedMediaItem = - createVideoEditedMediaItem(MP4_ASSET_URI_STRING, /* height= */ 360); - ExportTestResult result = - new TransformerAndroidTestRunner.Builder(context, transformer) - .build() - .run( - testId, - buildComposition( - bt601VideoEditedMediaItem, - bt709VideoEditedMediaItem, - imageEditedMediaItem1, - imageEditedMediaItem2, - bt709VideoEditedMediaItem, - imageEditedMediaItem1, - bt601VideoEditedMediaItem)); - - assertThat(result.exportResult.videoFrameCount) - .isEqualTo( - 3 * imageFrameCount + 2 * MP4_ASSET_FRAME_COUNT + 2 * BT601_MP4_ASSET_FRAME_COUNT); - assertThat(new File(result.filePath).length()).isGreaterThan(0); - } - - @Test - public void - videoEditing_withComplexMixedColorSpaceSdrVideoAndImageInputsEndWithImage_completesWithCorrectFrameCount() - throws Exception { - - assumeFormatsSupported( - context, - testId, - /* inputFormat= */ MP4_ASSET_FORMAT, - /* outputFormat= */ IMAGE_VIDEO_ENCODING_FORMAT); - assumeFormatsSupported( - context, - testId, - /* inputFormat= */ BT601_MP4_ASSET_FORMAT, - /* outputFormat= */ IMAGE_VIDEO_ENCODING_FORMAT); - Transformer transformer = - new Transformer.Builder(context) - .setEncoderFactory( - new DefaultEncoderFactory.Builder(context).setEnableFallback(false).build()) - .build(); - - int imageFrameCount = 34; - EditedMediaItem imageEditedMediaItem = - createImageEditedMediaItem(PNG_ASSET_URI_STRING, /* frameCount= */ imageFrameCount); - EditedMediaItem bt601VideoEditedMediaItem = - createVideoEditedMediaItem(BT601_MP4_ASSET_URI_STRING, /* height= */ 480); - EditedMediaItem bt709VideoEditedMediaItem = - createVideoEditedMediaItem(MP4_ASSET_URI_STRING, /* height= */ 480); - ExportTestResult result = - new TransformerAndroidTestRunner.Builder(context, transformer) - .build() - .run( - testId, - buildComposition( - imageEditedMediaItem, - bt709VideoEditedMediaItem, - bt601VideoEditedMediaItem, - imageEditedMediaItem, - imageEditedMediaItem, - bt601VideoEditedMediaItem, - imageEditedMediaItem)); - - assertThat(result.exportResult.videoFrameCount) - .isEqualTo(4 * imageFrameCount + MP4_ASSET_FRAME_COUNT + 2 * BT601_MP4_ASSET_FRAME_COUNT); - assertThat(new File(result.filePath).length()).isGreaterThan(0); - } - - @Test - public void - videoEditing_withComplexVideoAndImageInputsEndWithVideo_completesWithCorrectFrameCount() - throws Exception { - assumeFormatsSupported( - context, testId, /* inputFormat= */ MP4_ASSET_FORMAT, /* outputFormat= */ MP4_ASSET_FORMAT); - assumeFormatsSupported( - context, - testId, - /* inputFormat= */ BT601_MP4_ASSET_FORMAT, - /* outputFormat= */ MP4_ASSET_FORMAT); - Transformer transformer = - new Transformer.Builder(context) - .setEncoderFactory( - new DefaultEncoderFactory.Builder(context).setEnableFallback(false).build()) - .build(); - - int imageFrameCount = 33; - EditedMediaItem imageEditedMediaItem1 = - createImageEditedMediaItem(PNG_ASSET_URI_STRING, /* frameCount= */ imageFrameCount); - EditedMediaItem imageEditedMediaItem2 = - createImageEditedMediaItem(JPG_ASSET_URI_STRING, /* frameCount= */ imageFrameCount); - EditedMediaItem videoEditedMediaItem1 = - createVideoEditedMediaItem(MP4_ASSET_URI_STRING, /* height= */ 360); - EditedMediaItem videoEditedMediaItem2 = - createVideoEditedMediaItem(BT601_MP4_ASSET_URI_STRING, /* height= */ 360); - ExportTestResult result = - new TransformerAndroidTestRunner.Builder(context, transformer) - .build() - .run( - testId, - buildComposition( - videoEditedMediaItem1, - videoEditedMediaItem2, - imageEditedMediaItem1, - imageEditedMediaItem2, - videoEditedMediaItem1, - imageEditedMediaItem1, - videoEditedMediaItem2)); - - assertThat(result.exportResult.videoFrameCount) - .isEqualTo( - 3 * imageFrameCount + 2 * MP4_ASSET_FRAME_COUNT + 2 * BT601_MP4_ASSET_FRAME_COUNT); - assertThat(new File(result.filePath).length()).isGreaterThan(0); - } - - @Test - public void - videoEditing_withComplexVideoAndImageInputsEndWithImage_completesWithCorrectFrameCount() - throws Exception { - assumeFormatsSupported( - context, - testId, - /* inputFormat= */ MP4_ASSET_FORMAT, - /* outputFormat= */ IMAGE_VIDEO_ENCODING_FORMAT); - assumeFormatsSupported( - context, - testId, - /* inputFormat= */ BT601_MP4_ASSET_FORMAT, - /* outputFormat= */ IMAGE_VIDEO_ENCODING_FORMAT); - Transformer transformer = - new Transformer.Builder(context) - .setEncoderFactory( - new DefaultEncoderFactory.Builder(context).setEnableFallback(false).build()) - .build(); - - int imageFrameCount = 34; - EditedMediaItem imageEditedMediaItem = - createImageEditedMediaItem(PNG_ASSET_URI_STRING, /* frameCount= */ imageFrameCount); - EditedMediaItem videoEditedMediaItem1 = - createVideoEditedMediaItem(MP4_ASSET_URI_STRING, /* height= */ 480); - EditedMediaItem videoEditedMediaItem2 = - createVideoEditedMediaItem(BT601_MP4_ASSET_URI_STRING, /* height= */ 480); - ExportTestResult result = - new TransformerAndroidTestRunner.Builder(context, transformer) - .build() - .run( - testId, - buildComposition( - imageEditedMediaItem, - videoEditedMediaItem1, - videoEditedMediaItem2, - imageEditedMediaItem, - imageEditedMediaItem, - videoEditedMediaItem2, - imageEditedMediaItem)); - - assertThat(result.exportResult.videoFrameCount) - .isEqualTo(4 * imageFrameCount + MP4_ASSET_FRAME_COUNT + 2 * BT601_MP4_ASSET_FRAME_COUNT); - assertThat(new File(result.filePath).length()).isGreaterThan(0); - } - - /** Creates an {@link EditedMediaItem} with image, with duration of one second. */ - private static EditedMediaItem createImageEditedMediaItem(String uri, int frameCount) { - return new EditedMediaItem.Builder(MediaItem.fromUri(uri)) - .setDurationUs(C.MICROS_PER_SECOND) - .setFrameRate(frameCount) - .build(); - } - - /** - * Creates an {@link EditedMediaItem} with video, with audio removed and a {@link Presentation} of - * specified {@code height}. - */ - private static EditedMediaItem createVideoEditedMediaItem(String uri, int height) { - return new EditedMediaItem.Builder(MediaItem.fromUri(Uri.parse(uri))) - .setEffects( - new Effects( - /* audioProcessors= */ ImmutableList.of(), - ImmutableList.of(Presentation.createForHeight(height)))) - .setRemoveAudio(true) - .build(); - } - - private static Composition buildComposition(List editedMediaItems) { - return new Composition.Builder(new EditedMediaItemSequence(editedMediaItems)) - .setEffects( - new Effects( - /* audioProcessors= */ ImmutableList.of(), - ImmutableList.of( - // To ensure that software encoders can encode. - Presentation.createForWidthAndHeight( - /* width= */ 480, /* height= */ 360, Presentation.LAYOUT_SCALE_TO_FIT)))) - .build(); - } - - private static Composition buildComposition( - EditedMediaItem editedMediaItem, EditedMediaItem... editedMediaItems) { - return buildComposition( - new ImmutableList.Builder() - .add(editedMediaItem) - .add(editedMediaItems) - .build()); - } -}