diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/ParameterizedAndroidTestUtil.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/ParameterizedAndroidTestUtil.java
new file mode 100644
index 0000000000..aa317d30fe
--- /dev/null
+++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/ParameterizedAndroidTestUtil.java
@@ -0,0 +1,201 @@
+/*
+ * 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.common.MimeTypes.VIDEO_H265;
+import static androidx.media3.common.util.Assertions.checkNotNull;
+import static androidx.media3.common.util.Assertions.checkState;
+import static androidx.media3.transformer.AndroidTestUtil.assumeFormatsSupported;
+
+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.util.Util;
+import androidx.media3.transformer.AndroidTestUtil.AssetInfo;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import java.math.RoundingMode;
+import java.util.Objects;
+
+/** Test utilities for parameterized instrumentation tests. */
+/* package */ final class ParameterizedAndroidTestUtil {
+
+ /**
+ * Assume that the device supports the inputs and output of the sequence.
+ *
+ *
See {@link AndroidTestUtil#assumeFormatsSupported(Context, String, Format, Format)}.
+ *
+ * @param context The {@link Context context}.
+ * @param testId The test ID.
+ * @param sequence The {@link SequenceConfig}.
+ * @throws Exception If an error occurs checking device support.
+ */
+ public static void assumeSequenceFormatsSupported(
+ Context context, String testId, SequenceConfig sequence) throws Exception {
+ checkState(!sequence.itemConfigs.isEmpty());
+ Format outputFormat = checkNotNull(sequence.itemConfigs.get(0).outputFormat);
+ for (ItemConfig item : sequence.itemConfigs) {
+ assumeFormatsSupported(context, testId, item.format, outputFormat);
+ }
+ }
+
+ /** Test parameters for an {@link EditedMediaItemSequence}. */
+ public 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. */
+ public Composition buildComposition(Effects compositionEffects) {
+ ImmutableList.Builder editedMediaItems = new ImmutableList.Builder<>();
+ for (ItemConfig itemConfig : itemConfigs) {
+ editedMediaItems.add(itemConfig.build());
+ }
+
+ return new Composition.Builder(new EditedMediaItemSequence(editedMediaItems.build()))
+ .setEffects(compositionEffects)
+ .build();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append("Seq{");
+ for (ItemConfig itemConfig : itemConfigs) {
+ stringBuilder.append(itemConfig).append(",");
+ }
+ stringBuilder.replace(stringBuilder.length() - 1, stringBuilder.length(), "}");
+ return stringBuilder.toString();
+ }
+ }
+
+ /** Test parameters for an {@link EditedMediaItem}. */
+ public abstract static class ItemConfig {
+ public final int frameCount;
+ @Nullable public final Format format;
+ @Nullable public final Format outputFormat;
+
+ protected final Effects effects;
+
+ private final String uri;
+
+ public ItemConfig(
+ String uri,
+ int frameCount,
+ @Nullable Format format,
+ @Nullable Format outputFormat,
+ Effects effects) {
+ this.uri = uri;
+ this.frameCount = frameCount;
+ this.format = format;
+ this.outputFormat = outputFormat;
+ this.effects = effects;
+ }
+
+ public final EditedMediaItem build() {
+ EditedMediaItem.Builder builder =
+ new EditedMediaItem.Builder(MediaItem.fromUri(uri)).setEffects(effects);
+ 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))
+ + (Objects.equals(effects, Effects.EMPTY) ? "" : "-effects");
+ }
+ }
+
+ /** {@link ItemConfig} for an SDR image {@link EditedMediaItem}. */
+ public static final class SdrImageItemConfig extends ItemConfig {
+
+ private final int frameRate;
+ private final long durationUs;
+
+ public SdrImageItemConfig(AssetInfo assetInfo, int frameCount) {
+ this(assetInfo, frameCount, C.MICROS_PER_SECOND);
+ }
+
+ public SdrImageItemConfig(AssetInfo assetInfo, int frameRate, long durationUs) {
+ this(assetInfo, frameRate, durationUs, Effects.EMPTY);
+ }
+
+ public SdrImageItemConfig(
+ AssetInfo assetInfo, int frameRate, long durationUs, Effects effects) {
+ super(
+ assetInfo.uri,
+ /* frameCount= */ (int)
+ Util.scaleLargeValue(
+ frameRate, durationUs, C.MICROS_PER_SECOND, RoundingMode.CEILING),
+ /* format= */ assetInfo.videoFormat,
+ /* outputFormat= */ assetInfo
+ .videoFormat
+ .buildUpon()
+ // Image by default are encoded in H265 and BT709 SDR.
+ .setSampleMimeType(VIDEO_H265)
+ .setColorInfo(ColorInfo.SDR_BT709_LIMITED)
+ .setFrameRate(frameRate)
+ .build(),
+ effects);
+ 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.
+ */
+ public static final class VideoItemConfig extends ItemConfig {
+ public VideoItemConfig(AssetInfo asset, Effects effects) {
+ super(asset.uri, asset.videoFrameCount, asset.videoFormat, asset.videoFormat, effects);
+ }
+
+ @Override
+ protected void onBuild(EditedMediaItem.Builder builder) {
+ builder.setRemoveAudio(true);
+ }
+ }
+
+ private ParameterizedAndroidTestUtil() {}
+}
diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/ParameterizedInputSequenceExportTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/ParameterizedInputSequenceExportTest.java
index fb784626a1..95a16ed8b6 100644
--- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/ParameterizedInputSequenceExportTest.java
+++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/ParameterizedInputSequenceExportTest.java
@@ -14,32 +14,23 @@
* limitations under the License.
*
*/
-
package androidx.media3.transformer;
import static androidx.media3.transformer.AndroidTestUtil.BT601_MP4_ASSET;
import static androidx.media3.transformer.AndroidTestUtil.JPG_ASSET;
import static androidx.media3.transformer.AndroidTestUtil.MP4_ASSET;
import static androidx.media3.transformer.AndroidTestUtil.PNG_ASSET;
+import static androidx.media3.transformer.ParameterizedAndroidTestUtil.assumeSequenceFormatsSupported;
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.media3.transformer.AndroidTestUtil.AssetInfo;
+import androidx.media3.transformer.ParameterizedAndroidTestUtil.SdrImageItemConfig;
+import androidx.media3.transformer.ParameterizedAndroidTestUtil.SequenceConfig;
+import androidx.media3.transformer.ParameterizedAndroidTestUtil.VideoItemConfig;
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;
@@ -57,12 +48,22 @@ import org.junit.runners.Parameterized.Parameters;
/** 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, /* frameCount= */ 34);
- private static final ImageItemConfig JPG_ITEM =
- new ImageItemConfig(JPG_ASSET, /* frameCount= */ 41);
- private static final VideoItemConfig BT709_ITEM = new VideoItemConfig(MP4_ASSET);
- private static final VideoItemConfig BT601_ITEM = new VideoItemConfig(BT601_MP4_ASSET);
+ private static final SdrImageItemConfig PNG_ITEM =
+ new SdrImageItemConfig(PNG_ASSET, /* frameCount= */ 34);
+ private static final SdrImageItemConfig JPG_ITEM =
+ new SdrImageItemConfig(JPG_ASSET, /* frameCount= */ 41);
+ private static final VideoItemConfig BT709_ITEM =
+ new VideoItemConfig(
+ MP4_ASSET,
+ new Effects(
+ /* audioProcessors= */ ImmutableList.of(),
+ ImmutableList.of(Presentation.createForHeight(360))));
+ private static final VideoItemConfig BT601_ITEM =
+ new VideoItemConfig(
+ BT601_MP4_ASSET,
+ new Effects(
+ /* audioProcessors= */ ImmutableList.of(),
+ ImmutableList.of(Presentation.createForHeight(360))));
@Parameters(name = "{0}")
public static ImmutableList params() {
@@ -117,161 +118,19 @@ public class ParameterizedInputSequenceExportTest {
ExportTestResult result =
new TransformerAndroidTestRunner.Builder(context, transformer)
.build()
- .run(testId, sequence.buildComposition());
+ .run(
+ testId,
+ sequence.buildComposition(
+ // Presentation of 480Wx360H is used to ensure software encoders can encode.
+ new Effects(
+ /* audioProcessors= */ ImmutableList.of(),
+ ImmutableList.of(
+ Presentation.createForWidthAndHeight(
+ /* width= */ 480,
+ /* height= */ 360,
+ Presentation.LAYOUT_SCALE_TO_FIT)))));
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(AssetInfo assetInfo, int frameCount) {
- this(assetInfo, frameCount, C.MICROS_PER_SECOND);
- }
-
- public ImageItemConfig(AssetInfo assetInfo, int frameRate, long durationUs) {
- super(
- assetInfo.uri,
- /* frameCount= */ (int)
- Util.scaleLargeValue(
- frameRate, durationUs, C.MICROS_PER_SECOND, RoundingMode.CEILING),
- /* format= */ assetInfo.videoFormat,
- 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(AssetInfo asset) {
- super(asset.uri, asset.videoFrameCount, asset.videoFormat, asset.videoFormat);
- }
-
- @Override
- protected void onBuild(EditedMediaItem.Builder builder) {
- builder
- .setEffects(
- new Effects(
- /* audioProcessors= */ ImmutableList.of(),
- ImmutableList.of(Presentation.createForHeight(360))))
- .setRemoveAudio(true);
- }
- }
}