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
This commit is contained in:
parent
a39a7f06cf
commit
e985603e0a
@ -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.");
|
||||
}
|
||||
|
@ -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<SequenceConfig> 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<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(
|
||||
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}.
|
||||
*
|
||||
* <p>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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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<EditedMediaItem> 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(
|
||||
|
@ -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.
|
||||
*
|
||||
* <p>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<EditedMediaItem> 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<EditedMediaItem> 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<EditedMediaItem>()
|
||||
.add(editedMediaItem)
|
||||
.add(editedMediaItems)
|
||||
.build());
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user