Split parameterized android test utils into utility class.
Includes adjusting how effects are defined, to make it clearer in a test that a given ItemConfig has effects associated with it. PiperOrigin-RevId: 644684308
This commit is contained in:
parent
d27549d29a
commit
afa7935553
@ -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.
|
||||
*
|
||||
* <p>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<ItemConfig> 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<EditedMediaItem> 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}.
|
||||
*
|
||||
* <p>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() {}
|
||||
}
|
@ -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<SequenceConfig> params() {
|
||||
@ -117,161 +118,19 @@ public class ParameterizedInputSequenceExportTest {
|
||||
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<ItemConfig> 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.
|
||||
*
|
||||
* <p>{@link Presentation} of {@code width 480, height 360} is used to ensure software encoders
|
||||
* can encode.
|
||||
*/
|
||||
public Composition buildComposition() {
|
||||
ImmutableList.Builder<EditedMediaItem> editedMediaItems = new ImmutableList.Builder<>();
|
||||
for (ItemConfig itemConfig : itemConfigs) {
|
||||
editedMediaItems.add(itemConfig.build());
|
||||
}
|
||||
|
||||
return new Composition.Builder(new EditedMediaItemSequence(editedMediaItems.build()))
|
||||
.setEffects(
|
||||
.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))))
|
||||
.build();
|
||||
}
|
||||
/* width= */ 480,
|
||||
/* height= */ 360,
|
||||
Presentation.LAYOUT_SCALE_TO_FIT)))));
|
||||
|
||||
@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}.
|
||||
*
|
||||
* <p>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);
|
||||
}
|
||||
assertThat(result.exportResult.videoFrameCount).isEqualTo(sequence.totalExpectedFrameCount);
|
||||
assertThat(new File(result.filePath).length()).isGreaterThan(0);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user