diff --git a/libraries/test_utils/build.gradle b/libraries/test_utils/build.gradle
index d3901cf175..a5817b9f92 100644
--- a/libraries/test_utils/build.gradle
+++ b/libraries/test_utils/build.gradle
@@ -26,6 +26,7 @@ dependencies {
implementation 'androidx.annotation:annotation:' + androidxAnnotationVersion
implementation 'com.squareup.okhttp3:mockwebserver:' + okhttpVersion
implementation project(modulePrefix + 'lib-exoplayer')
+ implementation project(modulePrefix + ':lib-transformer')
testImplementation 'org.robolectric:robolectric:' + robolectricVersion
}
diff --git a/libraries/transformer/src/test/java/androidx/media3/transformer/TestMuxer.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestMuxer.java
similarity index 96%
rename from libraries/transformer/src/test/java/androidx/media3/transformer/TestMuxer.java
rename to libraries/test_utils/src/main/java/androidx/media3/test/utils/TestMuxer.java
index 547a619a95..a8d180e714 100644
--- a/libraries/transformer/src/test/java/androidx/media3/transformer/TestMuxer.java
+++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestMuxer.java
@@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package androidx.media3.transformer;
+package androidx.media3.test.utils;
import androidx.media3.common.Format;
-import androidx.media3.test.utils.DumpableFormat;
-import androidx.media3.test.utils.Dumper;
+import androidx.media3.common.util.UnstableApi;
+import androidx.media3.transformer.Muxer;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
@@ -28,6 +28,7 @@ import java.util.List;
* testing purposes) and delegates the actual muxing operations to another {@link Muxer} created
* using the factory provided.
*/
+@UnstableApi
public final class TestMuxer implements Muxer, Dumper.Dumpable {
private final Muxer muxer;
diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestTransformerBuilderFactory.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestTransformerBuilderFactory.java
new file mode 100644
index 0000000000..82c6ae8ac9
--- /dev/null
+++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestTransformerBuilderFactory.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2022 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.test.utils;
+
+import static androidx.media3.common.util.Assertions.checkStateNotNull;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.os.ParcelFileDescriptor;
+import androidx.media3.common.C;
+import androidx.media3.common.util.Clock;
+import androidx.media3.common.util.UnstableApi;
+import androidx.media3.transformer.DefaultEncoderFactory;
+import androidx.media3.transformer.DefaultMuxer;
+import androidx.media3.transformer.Muxer;
+import androidx.media3.transformer.Transformer;
+import com.google.common.collect.ImmutableList;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
+
+/**
+ * Creates a {@link Transformer.Builder} setting up some of the common resources needed for testing
+ * {@link Transformer}.
+ */
+@UnstableApi
+public final class TestTransformerBuilderFactory {
+
+ private final Context context;
+
+ private static @MonotonicNonNull TestMuxer testMuxer;
+ private long maxDelayBetweenSamplesMs;
+
+ /** Creates a new instance */
+ public TestTransformerBuilderFactory(Context context) {
+ this.context = context;
+ maxDelayBetweenSamplesMs = DefaultMuxer.Factory.DEFAULT_MAX_DELAY_BETWEEN_SAMPLES_MS;
+ }
+
+ /**
+ * Sets the muxer's {@linkplain Muxer#getMaxDelayBetweenSamplesMs() max delay} between samples.
+ */
+ @CanIgnoreReturnValue
+ public TestTransformerBuilderFactory setMaxDelayBetweenSamplesMs(long maxDelayBetweenSamplesMs) {
+ this.maxDelayBetweenSamplesMs = maxDelayBetweenSamplesMs;
+ return this;
+ }
+
+ /** Returns a {@link Transformer.Builder} using the provided values or their defaults. */
+ @SuppressLint("VisibleForTests") // Suppresses warning on setting the clock outside of a test file
+ public Transformer.Builder create(boolean enableFallback) {
+ Clock clock = new FakeClock(/* isAutoAdvancing= */ true);
+ Muxer.Factory defaultMuxerFactory = new DefaultMuxer.Factory(maxDelayBetweenSamplesMs);
+ return new Transformer.Builder(context)
+ .setClock(clock)
+ .setMuxerFactory(new TestMuxerFactory(defaultMuxerFactory))
+ .setEncoderFactory(
+ new DefaultEncoderFactory.Builder(context).setEnableFallback(enableFallback).build());
+ }
+
+ /**
+ * Returns the test muxer used in the {@link Transformer.Builder}.
+ *
+ *
This method should only be called after the transformation is completed.
+ */
+ public TestMuxer getTestMuxer() {
+ return checkStateNotNull(testMuxer);
+ }
+
+ private static final class TestMuxerFactory implements Muxer.Factory {
+
+ private final Muxer.Factory defaultMuxerFactory;
+
+ public TestMuxerFactory(Muxer.Factory defaultMuxerFactory) {
+ this.defaultMuxerFactory = defaultMuxerFactory;
+ }
+
+ @Override
+ public Muxer create(String path) throws Muxer.MuxerException {
+ testMuxer = new TestMuxer(path, defaultMuxerFactory);
+ return testMuxer;
+ }
+
+ @Override
+ public Muxer create(ParcelFileDescriptor parcelFileDescriptor) throws Muxer.MuxerException {
+ testMuxer = new TestMuxer("FD:" + parcelFileDescriptor.getFd(), defaultMuxerFactory);
+ return testMuxer;
+ }
+
+ @Override
+ public ImmutableList getSupportedSampleMimeTypes(@C.TrackType int trackType) {
+ return defaultMuxerFactory.getSupportedSampleMimeTypes(trackType);
+ }
+ }
+}
diff --git a/libraries/test_utils_robolectric/build.gradle b/libraries/test_utils_robolectric/build.gradle
index aafdce151a..e579a00166 100644
--- a/libraries/test_utils_robolectric/build.gradle
+++ b/libraries/test_utils_robolectric/build.gradle
@@ -20,6 +20,7 @@ dependencies {
implementation 'androidx.annotation:annotation:' + androidxAnnotationVersion
implementation 'org.robolectric:robolectric:' + robolectricVersion
implementation project(modulePrefix + 'lib-exoplayer')
+ implementation project(modulePrefix + ':lib-transformer')
implementation project(modulePrefix + 'test-utils')
}
diff --git a/libraries/test_utils_robolectric/src/main/java/androidx/media3/test/utils/robolectric/ShadowMediaCodecConfig.java b/libraries/test_utils_robolectric/src/main/java/androidx/media3/test/utils/robolectric/ShadowMediaCodecConfig.java
index 6e85eed2a0..fe5e61e8de 100644
--- a/libraries/test_utils_robolectric/src/main/java/androidx/media3/test/utils/robolectric/ShadowMediaCodecConfig.java
+++ b/libraries/test_utils_robolectric/src/main/java/androidx/media3/test/utils/robolectric/ShadowMediaCodecConfig.java
@@ -16,10 +16,15 @@
package androidx.media3.test.utils.robolectric;
import android.media.MediaCodecInfo;
+import android.media.MediaCrypto;
import android.media.MediaFormat;
+import android.view.Surface;
+import androidx.annotation.Nullable;
import androidx.media3.common.MimeTypes;
import androidx.media3.common.util.UnstableApi;
+import androidx.media3.common.util.Util;
import androidx.media3.exoplayer.mediacodec.MediaCodecUtil;
+import androidx.media3.transformer.EncoderUtil;
import com.google.common.collect.ImmutableList;
import com.google.common.primitives.Ints;
import java.nio.ByteBuffer;
@@ -30,105 +35,247 @@ import org.robolectric.shadows.ShadowMediaCodec;
import org.robolectric.shadows.ShadowMediaCodecList;
/**
- * A JUnit @Rule to configure Roboelectric's {@link ShadowMediaCodec}.
+ * A JUnit @Rule to configure {@link ShadowMediaCodec} for transcoding or decoding.
*
- * Registers a {@link org.robolectric.shadows.ShadowMediaCodec.CodecConfig} for each audio/video
- * MIME type known by ExoPlayer.
+ *
Registers {@link org.robolectric.shadows.ShadowMediaCodec.CodecConfig} instances for ExoPlayer
+ * and Transformer tests.
*/
@UnstableApi
public final class ShadowMediaCodecConfig extends ExternalResource {
+ private static final String EXOTEST_VIDEO_AVC = "exotest.video.avc";
+ private static final String EXOTEST_VIDEO_MPEG2 = "exotest.video.mpeg2";
+ private static final String EXOTEST_VIDEO_VP9 = "exotest.video.vp9";
+ private static final String EXOTEST_AUDIO_AAC = "exotest.audio.aac";
+ private static final String EXOTEST_AUDIO_AC3 = "exotest.audio.ac3";
+ private static final String EXOTEST_AUDIO_AC4 = "exotest.audio.ac4";
+ private static final String EXOTEST_AUDIO_E_AC3 = "exotest.audio.eac3";
+ private static final String EXOTEST_AUDIO_E_AC3_JOC = "exotest.audio.eac3joc";
+ private static final String EXOTEST_AUDIO_FLAC = "exotest.audio.flac";
+ private static final String EXOTEST_AUDIO_MPEG = "exotest.audio.mpeg";
+ private static final String EXOTEST_AUDIO_MPEG_L2 = "exotest.audio.mpegl2";
+ private static final String EXOTEST_AUDIO_OPUS = "exotest.audio.opus";
+ private static final String EXOTEST_AUDIO_VORBIS = "exotest.audio.vorbis";
+ private static final String EXOTEST_AUDIO_RAW = "exotest.audio.raw";
+
+ private final boolean forTranscoding;
+
+ private ShadowMediaCodecConfig(boolean forTranscoding) {
+ this.forTranscoding = forTranscoding;
+ }
+
+ /** Creates an instance that configures {@link ShadowMediaCodec} for Transformer transcoding. */
+ public static ShadowMediaCodecConfig forTranscoding() {
+ return new ShadowMediaCodecConfig(/* forTranscoding= */ true);
+ }
+
+ /** Creates an instance that configures {@link ShadowMediaCodec} for Exoplayer decoding. */
public static ShadowMediaCodecConfig forAllSupportedMimeTypes() {
- return new ShadowMediaCodecConfig();
+ return new ShadowMediaCodecConfig(/* forTranscoding= */ false);
}
@Override
protected void before() throws Throwable {
+ if (forTranscoding) {
+ addTranscodingCodecs();
+ } else {
+ addDecodingCodecs();
+ }
+ }
+
+ private void addTranscodingCodecs() {
+ ShadowMediaCodec.CodecConfig codecConfig =
+ new ShadowMediaCodec.CodecConfig(
+ /* inputBufferSize= */ 10_000,
+ /* outputBufferSize= */ 10_000,
+ /* codec= */ (in, out) -> out.put(in));
+ addTransformerCodec(MimeTypes.AUDIO_AAC, codecConfig, /* isDecoder= */ true);
+ addTransformerCodec(MimeTypes.AUDIO_AC3, codecConfig, /* isDecoder= */ true);
+ addTransformerCodec(MimeTypes.AUDIO_AMR_NB, codecConfig, /* isDecoder= */ true);
+ addTransformerCodec(MimeTypes.AUDIO_AAC, codecConfig, /* isDecoder= */ false);
+
+ ShadowMediaCodec.CodecConfig throwingCodecConfig =
+ new ShadowMediaCodec.CodecConfig(
+ /* inputBufferSize= */ 10_000,
+ /* outputBufferSize= */ 10_000,
+ new ShadowMediaCodec.CodecConfig.Codec() {
+
+ @Override
+ public void process(ByteBuffer in, ByteBuffer out) {
+ out.put(in);
+ }
+
+ @Override
+ public void onConfigured(
+ MediaFormat format,
+ @Nullable Surface surface,
+ @Nullable MediaCrypto crypto,
+ int flags) {
+ throw new IllegalArgumentException("Format unsupported");
+ }
+ });
+ addTransformerCodec(MimeTypes.AUDIO_AMR_WB, throwingCodecConfig, /* isDecoder= */ true);
+ addTransformerCodec(MimeTypes.AUDIO_AMR_NB, throwingCodecConfig, /* isDecoder= */ false);
+ }
+
+ private void addDecodingCodecs() {
// Video codecs
MediaCodecInfo.CodecProfileLevel avcProfileLevel =
createProfileLevel(
MediaCodecInfo.CodecProfileLevel.AVCProfileHigh,
MediaCodecInfo.CodecProfileLevel.AVCLevel62);
- configureCodec(
- /* codecName= */ "exotest.video.avc",
+ addExoplayerCodec(
+ EXOTEST_VIDEO_AVC,
MimeTypes.VIDEO_H264,
+ generateDecodingCodecConfig(MimeTypes.VIDEO_H264),
ImmutableList.of(avcProfileLevel),
ImmutableList.of(MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible));
+
MediaCodecInfo.CodecProfileLevel mpeg2ProfileLevel =
createProfileLevel(
MediaCodecInfo.CodecProfileLevel.MPEG2ProfileMain,
MediaCodecInfo.CodecProfileLevel.MPEG2LevelML);
- configureCodec(
- /* codecName= */ "exotest.video.mpeg2",
+ addExoplayerCodec(
+ EXOTEST_VIDEO_MPEG2,
MimeTypes.VIDEO_MPEG2,
+ generateDecodingCodecConfig(MimeTypes.VIDEO_MPEG2),
ImmutableList.of(mpeg2ProfileLevel),
ImmutableList.of(MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible));
- configureCodec(
- /* codecName= */ "exotest.video.vp9",
+ addExoplayerCodec(
+ EXOTEST_VIDEO_VP9,
MimeTypes.VIDEO_VP9,
+ generateDecodingCodecConfig(MimeTypes.VIDEO_VP9),
ImmutableList.of(),
ImmutableList.of(MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible));
// Audio codecs
- configureCodec("exotest.audio.aac", MimeTypes.AUDIO_AAC);
- configureCodec("exotest.audio.ac3", MimeTypes.AUDIO_AC3);
- configureCodec("exotest.audio.ac4", MimeTypes.AUDIO_AC4);
- configureCodec("exotest.audio.eac3", MimeTypes.AUDIO_E_AC3);
- configureCodec("exotest.audio.eac3joc", MimeTypes.AUDIO_E_AC3_JOC);
- configureCodec("exotest.audio.flac", MimeTypes.AUDIO_FLAC);
- configureCodec("exotest.audio.mpeg", MimeTypes.AUDIO_MPEG);
- configureCodec("exotest.audio.mpegl2", MimeTypes.AUDIO_MPEG_L2);
- configureCodec("exotest.audio.opus", MimeTypes.AUDIO_OPUS);
- configureCodec("exotest.audio.vorbis", MimeTypes.AUDIO_VORBIS);
+ addExoplayerCodec(
+ EXOTEST_AUDIO_AAC, MimeTypes.AUDIO_AAC, generateDecodingCodecConfig(MimeTypes.AUDIO_AAC));
+ addExoplayerCodec(
+ EXOTEST_AUDIO_AC3, MimeTypes.AUDIO_AC3, generateDecodingCodecConfig(MimeTypes.AUDIO_AC3));
+ addExoplayerCodec(
+ EXOTEST_AUDIO_AC4, MimeTypes.AUDIO_AC4, generateDecodingCodecConfig(MimeTypes.AUDIO_AC4));
+ addExoplayerCodec(
+ EXOTEST_AUDIO_E_AC3,
+ MimeTypes.AUDIO_E_AC3,
+ generateDecodingCodecConfig(MimeTypes.AUDIO_E_AC3));
+ addExoplayerCodec(
+ EXOTEST_AUDIO_E_AC3_JOC,
+ MimeTypes.AUDIO_E_AC3_JOC,
+ generateDecodingCodecConfig(MimeTypes.AUDIO_E_AC3_JOC));
+ addExoplayerCodec(
+ EXOTEST_AUDIO_FLAC,
+ MimeTypes.AUDIO_FLAC,
+ generateDecodingCodecConfig(MimeTypes.AUDIO_FLAC));
+ addExoplayerCodec(
+ EXOTEST_AUDIO_MPEG,
+ MimeTypes.AUDIO_MPEG,
+ generateDecodingCodecConfig(MimeTypes.AUDIO_MPEG));
+ addExoplayerCodec(
+ EXOTEST_AUDIO_MPEG_L2,
+ MimeTypes.AUDIO_MPEG_L2,
+ generateDecodingCodecConfig(MimeTypes.AUDIO_MPEG_L2));
+ addExoplayerCodec(
+ EXOTEST_AUDIO_OPUS,
+ MimeTypes.AUDIO_OPUS,
+ generateDecodingCodecConfig(MimeTypes.AUDIO_OPUS));
+ addExoplayerCodec(
+ EXOTEST_AUDIO_VORBIS,
+ MimeTypes.AUDIO_VORBIS,
+ generateDecodingCodecConfig(MimeTypes.AUDIO_VORBIS));
// Raw audio should use a bypass mode and never need this codec. However, to easily assert
// failures of the bypass mode we want to detect when the raw audio is decoded by this class and
// thus we need a codec to output samples.
- configureCodec("exotest.audio.raw", MimeTypes.AUDIO_RAW);
+ addExoplayerCodec(
+ EXOTEST_AUDIO_RAW, MimeTypes.AUDIO_RAW, generateDecodingCodecConfig(MimeTypes.AUDIO_RAW));
}
@Override
protected void after() {
- MediaCodecUtil.clearDecoderInfoCache();
+ if (!forTranscoding) {
+ MediaCodecUtil.clearDecoderInfoCache();
+ } else {
+ EncoderUtil.clearCachedEncoders();
+ }
ShadowMediaCodecList.reset();
ShadowMediaCodec.clearCodecs();
}
- private void configureCodec(String codecName, String mimeType) {
- configureCodec(
+ private ShadowMediaCodec.CodecConfig generateDecodingCodecConfig(String mimeType) {
+ // TODO: Update ShadowMediaCodec to consider the MediaFormat.KEY_MAX_INPUT_SIZE value passed
+ // to configure() so we don't have to specify large buffers here.
+ CodecImpl codec = new CodecImpl(mimeType);
+ return new ShadowMediaCodec.CodecConfig(
+ /* inputBufferSize= */ 100_000, /* outputBufferSize= */ 100_000, codec);
+ }
+
+ private void addTransformerCodec(
+ String mimeType, ShadowMediaCodec.CodecConfig codecConfig, boolean isDecoder) {
+ String codecName =
+ Util.formatInvariant(
+ isDecoder ? "transformertest.%s.decoder" : "transformertest.%s.encoder",
+ mimeType.replace('/', '.'));
+ addCodec(
codecName,
mimeType,
+ codecConfig,
+ /* profileLevels= */ ImmutableList.of(),
+ /* colorFormats= */ ImmutableList.of(),
+ isDecoder);
+ }
+
+ private void addExoplayerCodec(
+ String codecName, String mimeType, ShadowMediaCodec.CodecConfig codecConfig) {
+ addExoplayerCodec(
+ codecName,
+ mimeType,
+ codecConfig,
/* profileLevels= */ ImmutableList.of(),
/* colorFormats= */ ImmutableList.of());
}
- private void configureCodec(
+ private void addExoplayerCodec(
String codecName,
String mimeType,
+ ShadowMediaCodec.CodecConfig codecConfig,
List profileLevels,
List colorFormats) {
+ addCodec(codecName, mimeType, codecConfig, profileLevels, colorFormats, /* isDecoder= */ true);
+ }
+
+ private void addCodec(
+ String codecName,
+ String mimeType,
+ ShadowMediaCodec.CodecConfig codecConfig,
+ List profileLevels,
+ List colorFormats,
+ boolean isDecoder) {
MediaFormat mediaFormat = new MediaFormat();
mediaFormat.setString(MediaFormat.KEY_MIME, mimeType);
MediaCodecInfoBuilder.CodecCapabilitiesBuilder capabilities =
- MediaCodecInfoBuilder.CodecCapabilitiesBuilder.newBuilder().setMediaFormat(mediaFormat);
+ MediaCodecInfoBuilder.CodecCapabilitiesBuilder.newBuilder()
+ .setMediaFormat(mediaFormat)
+ .setIsEncoder(!isDecoder);
if (!profileLevels.isEmpty()) {
capabilities.setProfileLevels(profileLevels.toArray(new MediaCodecInfo.CodecProfileLevel[0]));
}
if (!colorFormats.isEmpty()) {
capabilities.setColorFormats(Ints.toArray(colorFormats));
}
+
ShadowMediaCodecList.addCodec(
MediaCodecInfoBuilder.newBuilder()
.setName(codecName)
+ .setIsEncoder(!isDecoder)
.setCapabilities(capabilities.build())
.build());
- // TODO: Update ShadowMediaCodec to consider the MediaFormat.KEY_MAX_INPUT_SIZE value passed
- // to configure() so we don't have to specify large buffers here.
- CodecImpl codec = new CodecImpl(mimeType);
- ShadowMediaCodec.addDecoder(
- codecName,
- new ShadowMediaCodec.CodecConfig(
- /* inputBufferSize= */ 100_000, /* outputBufferSize= */ 100_000, codec));
+
+ if (isDecoder) {
+ ShadowMediaCodec.addDecoder(codecName, codecConfig);
+ } else {
+ ShadowMediaCodec.addEncoder(codecName, codecConfig);
+ }
}
private static MediaCodecInfo.CodecProfileLevel createProfileLevel(int profile, int level) {
diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java
index 680910eb21..e60141e212 100644
--- a/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java
+++ b/libraries/transformer/src/main/java/androidx/media3/transformer/Transformer.java
@@ -430,7 +430,7 @@ public final class Transformer {
*/
@CanIgnoreReturnValue
@VisibleForTesting
- /* package */ Builder setClock(Clock clock) {
+ public Builder setClock(Clock clock) {
this.clock = clock;
this.listeners = listeners.copy(looper, clock, (listener, flags) -> {});
return this;
diff --git a/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerEndToEndTest.java b/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerEndToEndTest.java
index 986ba3b388..c00eefd7a1 100644
--- a/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerEndToEndTest.java
+++ b/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerEndToEndTest.java
@@ -29,15 +29,11 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.content.Context;
-import android.media.MediaCrypto;
-import android.media.MediaFormat;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
-import android.os.ParcelFileDescriptor;
-import android.view.Surface;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.MediaItem;
@@ -53,14 +49,13 @@ import androidx.media3.extractor.ExtractorOutput;
import androidx.media3.extractor.ExtractorsFactory;
import androidx.media3.extractor.PositionHolder;
import androidx.media3.test.utils.DumpFileAsserts;
-import androidx.media3.test.utils.FakeClock;
+import androidx.media3.test.utils.TestTransformerBuilderFactory;
+import androidx.media3.test.utils.robolectric.ShadowMediaCodecConfig;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
-import com.google.common.primitives.Ints;
import java.io.IOException;
-import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
@@ -74,16 +69,17 @@ import java.util.concurrent.atomic.AtomicReference;
import org.checkerframework.checker.nullness.compatqual.NullableType;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.robolectric.shadows.MediaCodecInfoBuilder;
-import org.robolectric.shadows.ShadowMediaCodec;
-import org.robolectric.shadows.ShadowMediaCodecList;
/** End-to-end test for {@link Transformer}. */
@RunWith(AndroidJUnit4.class)
public final class TransformerEndToEndTest {
+ @Rule
+ public final ShadowMediaCodecConfig mediaCodecConfig = ShadowMediaCodecConfig.forTranscoding();
+
private static final String ASSET_URI_PREFIX = "asset:///media/";
private static final String FILE_VIDEO_ONLY = "mp4/sample_18byte_nclx_colr.mp4";
private static final String FILE_AUDIO_VIDEO = "mp4/sample.mp4";
@@ -100,39 +96,39 @@ public final class TransformerEndToEndTest {
private Context context;
private String outputPath;
- private TestMuxer testMuxer;
- private FakeClock clock;
private ProgressHolder progressHolder;
+ private TestTransformerBuilderFactory testTransformerBuilderFactory;
@Before
public void setUp() throws Exception {
context = ApplicationProvider.getApplicationContext();
outputPath = Util.createTempFile(context, "TransformerTest").getPath();
- clock = new FakeClock(/* isAutoAdvancing= */ true);
progressHolder = new ProgressHolder();
- createEncodersAndDecoders();
+ testTransformerBuilderFactory = new TestTransformerBuilderFactory(context);
}
@After
public void tearDown() throws Exception {
Files.delete(Paths.get(outputPath));
- removeEncodersAndDecoders();
}
@Test
public void startTransformation_videoOnlyPassthrough_completesSuccessfully() throws Exception {
- Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
+ Transformer transformer =
+ testTransformerBuilderFactory.create(/* enableFallback= */ false).build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_VIDEO_ONLY);
transformer.startTransformation(mediaItem, outputPath);
TransformerTestRunner.runUntilCompleted(transformer);
- DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_VIDEO_ONLY));
+ DumpFileAsserts.assertOutput(
+ context, testTransformerBuilderFactory.getTestMuxer(), getDumpFileName(FILE_VIDEO_ONLY));
}
@Test
public void startTransformation_audioOnlyPassthrough_completesSuccessfully() throws Exception {
- Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
+ Transformer transformer =
+ testTransformerBuilderFactory.create(/* enableFallback= */ false).build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_UNSUPPORTED_BY_ENCODER);
@@ -140,13 +136,16 @@ public final class TransformerEndToEndTest {
TransformerTestRunner.runUntilCompleted(transformer);
DumpFileAsserts.assertOutput(
- context, testMuxer, getDumpFileName(FILE_AUDIO_UNSUPPORTED_BY_ENCODER));
+ context,
+ testTransformerBuilderFactory.getTestMuxer(),
+ getDumpFileName(FILE_AUDIO_UNSUPPORTED_BY_ENCODER));
}
@Test
public void startTransformation_audioOnlyTranscoding_completesSuccessfully() throws Exception {
Transformer transformer =
- createTransformerBuilder(/* enableFallback= */ false)
+ testTransformerBuilderFactory
+ .create(/* enableFallback= */ false)
.setTransformationRequest(
new TransformationRequest.Builder()
.setAudioMimeType(MimeTypes.AUDIO_AAC) // supported by encoder and muxer
@@ -158,24 +157,29 @@ public final class TransformerEndToEndTest {
TransformerTestRunner.runUntilCompleted(transformer);
DumpFileAsserts.assertOutput(
- context, testMuxer, getDumpFileName(FILE_AUDIO_UNSUPPORTED_BY_ENCODER + ".aac"));
+ context,
+ testTransformerBuilderFactory.getTestMuxer(),
+ getDumpFileName(FILE_AUDIO_UNSUPPORTED_BY_ENCODER + ".aac"));
}
@Test
public void startTransformation_audioAndVideo_completesSuccessfully() throws Exception {
- Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
+ Transformer transformer =
+ testTransformerBuilderFactory.create(/* enableFallback= */ false).build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
transformer.startTransformation(mediaItem, outputPath);
TransformerTestRunner.runUntilCompleted(transformer);
- DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO));
+ DumpFileAsserts.assertOutput(
+ context, testTransformerBuilderFactory.getTestMuxer(), getDumpFileName(FILE_AUDIO_VIDEO));
}
@Test
public void startTransformation_audioAndVideo_withClippingStartAtKeyFrame_completesSuccessfully()
throws Exception {
- Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
+ Transformer transformer =
+ testTransformerBuilderFactory.create(/* enableFallback= */ false).build();
MediaItem mediaItem =
new MediaItem.Builder()
.setUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO_INCREASING_TIMESTAMPS_15S)
@@ -192,14 +196,15 @@ public final class TransformerEndToEndTest {
DumpFileAsserts.assertOutput(
context,
- testMuxer,
+ testTransformerBuilderFactory.getTestMuxer(),
getDumpFileName(FILE_AUDIO_VIDEO_INCREASING_TIMESTAMPS_15S + ".clipped"));
}
@Test
public void startTransformation_withSubtitles_completesSuccessfully() throws Exception {
Transformer transformer =
- createTransformerBuilder(/* enableFallback= */ false)
+ testTransformerBuilderFactory
+ .create(/* enableFallback= */ false)
.setTransformationRequest(
new TransformationRequest.Builder().setAudioMimeType(MimeTypes.AUDIO_AAC).build())
.build();
@@ -208,13 +213,17 @@ public final class TransformerEndToEndTest {
transformer.startTransformation(mediaItem, outputPath);
TransformerTestRunner.runUntilCompleted(transformer);
- DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_WITH_SUBTITLES));
+ DumpFileAsserts.assertOutput(
+ context,
+ testTransformerBuilderFactory.getTestMuxer(),
+ getDumpFileName(FILE_WITH_SUBTITLES));
}
@Test
public void startTransformation_successiveTransformations_completesSuccessfully()
throws Exception {
- Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
+ Transformer transformer =
+ testTransformerBuilderFactory.create(/* enableFallback= */ false).build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
// Transform first media item.
@@ -226,12 +235,14 @@ public final class TransformerEndToEndTest {
transformer.startTransformation(mediaItem, outputPath);
TransformerTestRunner.runUntilCompleted(transformer);
- DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO));
+ DumpFileAsserts.assertOutput(
+ context, testTransformerBuilderFactory.getTestMuxer(), getDumpFileName(FILE_AUDIO_VIDEO));
}
@Test
public void startTransformation_concurrentTransformations_throwsError() throws Exception {
- Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
+ Transformer transformer =
+ testTransformerBuilderFactory.create(/* enableFallback= */ false).build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_VIDEO_ONLY);
transformer.startTransformation(mediaItem, outputPath);
@@ -243,33 +254,44 @@ public final class TransformerEndToEndTest {
@Test
public void startTransformation_removeAudio_completesSuccessfully() throws Exception {
Transformer transformer =
- createTransformerBuilder(/* enableFallback= */ false).setRemoveAudio(true).build();
+ testTransformerBuilderFactory
+ .create(/* enableFallback= */ false)
+ .setRemoveAudio(true)
+ .build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
transformer.startTransformation(mediaItem, outputPath);
TransformerTestRunner.runUntilCompleted(transformer);
DumpFileAsserts.assertOutput(
- context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO + ".noaudio"));
+ context,
+ testTransformerBuilderFactory.getTestMuxer(),
+ getDumpFileName(FILE_AUDIO_VIDEO + ".noaudio"));
}
@Test
public void startTransformation_removeVideo_completesSuccessfully() throws Exception {
Transformer transformer =
- createTransformerBuilder(/* enableFallback= */ false).setRemoveVideo(true).build();
+ testTransformerBuilderFactory
+ .create(/* enableFallback= */ false)
+ .setRemoveVideo(true)
+ .build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
transformer.startTransformation(mediaItem, outputPath);
TransformerTestRunner.runUntilCompleted(transformer);
DumpFileAsserts.assertOutput(
- context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO + ".novideo"));
+ context,
+ testTransformerBuilderFactory.getTestMuxer(),
+ getDumpFileName(FILE_AUDIO_VIDEO + ".novideo"));
}
@Test
public void startTransformation_silentAudio_completesSuccessfully() throws Exception {
Transformer transformer =
- createTransformerBuilder(/* enableFallback= */ false)
+ testTransformerBuilderFactory
+ .create(/* enableFallback= */ false)
.experimentalSetForceSilentAudio(true)
.build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
@@ -278,7 +300,9 @@ public final class TransformerEndToEndTest {
TransformerTestRunner.runUntilCompleted(transformer);
DumpFileAsserts.assertOutput(
- context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO + ".silentaudio"));
+ context,
+ testTransformerBuilderFactory.getTestMuxer(),
+ getDumpFileName(FILE_AUDIO_VIDEO + ".silentaudio"));
}
@Test
@@ -286,7 +310,8 @@ public final class TransformerEndToEndTest {
SonicAudioProcessor sonicAudioProcessor = new SonicAudioProcessor();
sonicAudioProcessor.setOutputSampleRateHz(48000);
Transformer transformer =
- createTransformerBuilder(/* enableFallback= */ false)
+ testTransformerBuilderFactory
+ .create(/* enableFallback= */ false)
.setAudioProcessors(ImmutableList.of(sonicAudioProcessor))
.build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
@@ -295,7 +320,9 @@ public final class TransformerEndToEndTest {
TransformerTestRunner.runUntilCompleted(transformer);
DumpFileAsserts.assertOutput(
- context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO + ".48000hz"));
+ context,
+ testTransformerBuilderFactory.getTestMuxer(),
+ getDumpFileName(FILE_AUDIO_VIDEO + ".48000hz"));
}
@Test
@@ -304,7 +331,8 @@ public final class TransformerEndToEndTest {
Transformer.Listener mockListener2 = mock(Transformer.Listener.class);
Transformer.Listener mockListener3 = mock(Transformer.Listener.class);
Transformer transformer =
- createTransformerBuilder(/* enableFallback= */ false)
+ testTransformerBuilderFactory
+ .create(/* enableFallback= */ false)
.addListener(mockListener1)
.addListener(mockListener2)
.addListener(mockListener3)
@@ -325,7 +353,8 @@ public final class TransformerEndToEndTest {
Transformer.Listener mockListener2 = mock(Transformer.Listener.class);
Transformer.Listener mockListener3 = mock(Transformer.Listener.class);
Transformer transformer =
- createTransformerBuilder(/* enableFallback= */ false)
+ testTransformerBuilderFactory
+ .create(/* enableFallback= */ false)
.addListener(mockListener1)
.addListener(mockListener2)
.addListener(mockListener3)
@@ -352,7 +381,8 @@ public final class TransformerEndToEndTest {
TransformationRequest fallbackTransformationRequest =
new TransformationRequest.Builder().setAudioMimeType(MimeTypes.AUDIO_AAC).build();
Transformer transformer =
- createTransformerBuilder(/* enableFallback= */ true)
+ testTransformerBuilderFactory
+ .create(/* enableFallback= */ true)
.addListener(mockListener1)
.addListener(mockListener2)
.addListener(mockListener3)
@@ -377,7 +407,8 @@ public final class TransformerEndToEndTest {
Transformer.Listener mockListener2 = mock(Transformer.Listener.class);
Transformer.Listener mockListener3 = mock(Transformer.Listener.class);
Transformer transformer1 =
- createTransformerBuilder(/* enableFallback= */ false)
+ testTransformerBuilderFactory
+ .create(/* enableFallback= */ false)
.addListener(mockListener1)
.addListener(mockListener2)
.addListener(mockListener3)
@@ -396,7 +427,8 @@ public final class TransformerEndToEndTest {
@Test
public void startTransformation_flattenForSlowMotion_completesSuccessfully() throws Exception {
Transformer transformer =
- createTransformerBuilder(/* enableFallback= */ false)
+ testTransformerBuilderFactory
+ .create(/* enableFallback= */ false)
.setTransformationRequest(
new TransformationRequest.Builder().setFlattenForSlowMotion(true).build())
.build();
@@ -405,7 +437,10 @@ public final class TransformerEndToEndTest {
transformer.startTransformation(mediaItem, outputPath);
TransformerTestRunner.runUntilCompleted(transformer);
- DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_WITH_SEF_SLOW_MOTION));
+ DumpFileAsserts.assertOutput(
+ context,
+ testTransformerBuilderFactory.getTestMuxer(),
+ getDumpFileName(FILE_WITH_SEF_SLOW_MOTION));
}
@Test
@@ -420,7 +455,10 @@ public final class TransformerEndToEndTest {
}
};
Transformer transformer =
- createTransformerBuilder(/* enableFallback= */ false).addListener(listener).build();
+ testTransformerBuilderFactory
+ .create(/* enableFallback= */ false)
+ .addListener(listener)
+ .build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
transformer.startTransformation(mediaItem, outputPath);
@@ -436,7 +474,8 @@ public final class TransformerEndToEndTest {
public void startTransformation_withAudioEncoderFormatUnsupported_completesWithError()
throws Exception {
Transformer transformer =
- createTransformerBuilder(/* enableFallback= */ false)
+ testTransformerBuilderFactory
+ .create(/* enableFallback= */ false)
.setTransformationRequest(
new TransformationRequest.Builder()
.setAudioMimeType(
@@ -457,7 +496,8 @@ public final class TransformerEndToEndTest {
public void startTransformation_withAudioDecoderFormatUnsupported_completesWithError()
throws Exception {
Transformer transformer =
- createTransformerBuilder(/* enableFallback= */ false)
+ testTransformerBuilderFactory
+ .create(/* enableFallback= */ false)
.setTransformationRequest(
new TransformationRequest.Builder()
.setAudioMimeType(MimeTypes.AUDIO_AAC) // supported by encoder and muxer
@@ -475,7 +515,8 @@ public final class TransformerEndToEndTest {
@Test
public void startTransformation_withIoError_completesWithError() throws Exception {
- Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
+ Transformer transformer =
+ testTransformerBuilderFactory.create(/* enableFallback= */ false).build();
MediaItem mediaItem = MediaItem.fromUri("asset:///non-existing-path.mp4");
transformer.startTransformation(mediaItem, outputPath);
@@ -495,14 +536,19 @@ public final class TransformerEndToEndTest {
TransformationRequest fallbackTransformationRequest =
new TransformationRequest.Builder().setAudioMimeType(MimeTypes.AUDIO_AAC).build();
Transformer transformer =
- createTransformerBuilder(/* enableFallback= */ false).addListener(mockListener).build();
+ testTransformerBuilderFactory
+ .create(/* enableFallback= */ false)
+ .addListener(mockListener)
+ .build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_UNSUPPORTED_BY_MUXER);
transformer.startTransformation(mediaItem, outputPath);
TransformerTestRunner.runUntilCompleted(transformer);
DumpFileAsserts.assertOutput(
- context, testMuxer, getDumpFileName(FILE_AUDIO_UNSUPPORTED_BY_MUXER + ".fallback"));
+ context,
+ testTransformerBuilderFactory.getTestMuxer(),
+ getDumpFileName(FILE_AUDIO_UNSUPPORTED_BY_MUXER + ".fallback"));
verify(mockListener)
.onFallbackApplied(mediaItem, originalTransformationRequest, fallbackTransformationRequest);
}
@@ -516,14 +562,19 @@ public final class TransformerEndToEndTest {
TransformationRequest fallbackTransformationRequest =
new TransformationRequest.Builder().setAudioMimeType(MimeTypes.AUDIO_AAC).build();
Transformer transformer =
- createTransformerBuilder(/* enableFallback= */ true).addListener(mockListener).build();
+ testTransformerBuilderFactory
+ .create(/* enableFallback= */ true)
+ .addListener(mockListener)
+ .build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_UNSUPPORTED_BY_MUXER);
transformer.startTransformation(mediaItem, outputPath);
TransformerTestRunner.runUntilCompleted(transformer);
DumpFileAsserts.assertOutput(
- context, testMuxer, getDumpFileName(FILE_AUDIO_UNSUPPORTED_BY_MUXER + ".fallback"));
+ context,
+ testTransformerBuilderFactory.getTestMuxer(),
+ getDumpFileName(FILE_AUDIO_UNSUPPORTED_BY_MUXER + ".fallback"));
verify(mockListener)
.onFallbackApplied(mediaItem, originalTransformationRequest, fallbackTransformationRequest);
}
@@ -533,11 +584,11 @@ public final class TransformerEndToEndTest {
MediaSource.Factory mediaSourceFactory =
new DefaultMediaSourceFactory(
context, new SlowExtractorsFactory(/* delayBetweenReadsMs= */ 10));
- Muxer.Factory muxerFactory = new TestMuxerFactory(/* maxDelayBetweenSamplesMs= */ 1);
Transformer transformer =
- createTransformerBuilder(/* enableFallback= */ false)
+ testTransformerBuilderFactory
+ .setMaxDelayBetweenSamplesMs(1)
+ .create(/* enableFallback= */ false)
.setMediaSourceFactory(mediaSourceFactory)
- .setMuxerFactory(muxerFactory)
.build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
@@ -551,20 +602,24 @@ public final class TransformerEndToEndTest {
@Test
public void startTransformation_withUnsetMaxDelayBetweenSamples_completesSuccessfully()
throws Exception {
- Muxer.Factory muxerFactory = new TestMuxerFactory(/* maxDelayBetweenSamplesMs= */ C.TIME_UNSET);
Transformer transformer =
- createTransformerBuilder(/* enableFallback= */ false).setMuxerFactory(muxerFactory).build();
+ testTransformerBuilderFactory
+ .setMaxDelayBetweenSamplesMs(C.TIME_UNSET)
+ .create(/* enableFallback= */ false)
+ .build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
transformer.startTransformation(mediaItem, outputPath);
TransformerTestRunner.runUntilCompleted(transformer);
- DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO));
+ DumpFileAsserts.assertOutput(
+ context, testTransformerBuilderFactory.getTestMuxer(), getDumpFileName(FILE_AUDIO_VIDEO));
}
@Test
public void startTransformation_afterCancellation_completesSuccessfully() throws Exception {
- Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
+ Transformer transformer =
+ testTransformerBuilderFactory.create(/* enableFallback= */ false).build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
transformer.startTransformation(mediaItem, outputPath);
@@ -575,7 +630,8 @@ public final class TransformerEndToEndTest {
transformer.startTransformation(mediaItem, outputPath);
TransformerTestRunner.runUntilCompleted(transformer);
- DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO));
+ DumpFileAsserts.assertOutput(
+ context, testTransformerBuilderFactory.getTestMuxer(), getDumpFileName(FILE_AUDIO_VIDEO));
}
@Test
@@ -584,7 +640,7 @@ public final class TransformerEndToEndTest {
anotherThread.start();
Looper looper = anotherThread.getLooper();
Transformer transformer =
- createTransformerBuilder(/* enableFallback= */ false).setLooper(looper).build();
+ testTransformerBuilderFactory.create(/* enableFallback= */ false).setLooper(looper).build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
AtomicReference exception = new AtomicReference<>();
CountDownLatch countDownLatch = new CountDownLatch(1);
@@ -604,12 +660,14 @@ public final class TransformerEndToEndTest {
countDownLatch.await();
assertThat(exception.get()).isNull();
- DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO));
+ DumpFileAsserts.assertOutput(
+ context, testTransformerBuilderFactory.getTestMuxer(), getDumpFileName(FILE_AUDIO_VIDEO));
}
@Test
public void startTransformation_fromWrongThread_throwsError() throws Exception {
- Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
+ Transformer transformer =
+ testTransformerBuilderFactory.create(/* enableFallback= */ false).build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
HandlerThread anotherThread = new HandlerThread("AnotherThread");
AtomicReference illegalStateException = new AtomicReference<>();
@@ -634,7 +692,8 @@ public final class TransformerEndToEndTest {
@Test
public void getProgress_knownDuration_returnsConsistentStates() throws Exception {
- Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
+ Transformer transformer =
+ testTransformerBuilderFactory.create(/* enableFallback= */ false).build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_VIDEO_ONLY);
AtomicInteger previousProgressState =
new AtomicInteger(PROGRESS_STATE_WAITING_FOR_AVAILABILITY);
@@ -680,7 +739,8 @@ public final class TransformerEndToEndTest {
@Test
public void getProgress_knownDuration_givesIncreasingPercentages() throws Exception {
- Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
+ Transformer transformer =
+ testTransformerBuilderFactory.create(/* enableFallback= */ false).build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_VIDEO_ONLY);
List progresses = new ArrayList<>();
Handler progressHandler =
@@ -715,7 +775,8 @@ public final class TransformerEndToEndTest {
@Test
public void getProgress_noCurrentTransformation_returnsNoTransformation() throws Exception {
- Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
+ Transformer transformer =
+ testTransformerBuilderFactory.create(/* enableFallback= */ false).build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_VIDEO_ONLY);
@Transformer.ProgressState int stateBeforeTransform = transformer.getProgress(progressHolder);
@@ -729,7 +790,8 @@ public final class TransformerEndToEndTest {
@Test
public void getProgress_unknownDuration_returnsConsistentStates() throws Exception {
- Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
+ Transformer transformer =
+ testTransformerBuilderFactory.create(/* enableFallback= */ false).build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_UNKNOWN_DURATION);
AtomicInteger previousProgressState =
new AtomicInteger(PROGRESS_STATE_WAITING_FOR_AVAILABILITY);
@@ -772,7 +834,8 @@ public final class TransformerEndToEndTest {
@Test
public void getProgress_fromWrongThread_throwsError() throws Exception {
- Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
+ Transformer transformer =
+ testTransformerBuilderFactory.create(/* enableFallback= */ false).build();
HandlerThread anotherThread = new HandlerThread("AnotherThread");
AtomicReference illegalStateException = new AtomicReference<>();
CountDownLatch countDownLatch = new CountDownLatch(1);
@@ -796,7 +859,8 @@ public final class TransformerEndToEndTest {
@Test
public void cancel_afterCompletion_doesNotThrow() throws Exception {
- Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
+ Transformer transformer =
+ testTransformerBuilderFactory.create(/* enableFallback= */ false).build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_VIDEO_ONLY);
transformer.startTransformation(mediaItem, outputPath);
@@ -806,7 +870,8 @@ public final class TransformerEndToEndTest {
@Test
public void cancel_fromWrongThread_throwsError() throws Exception {
- Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
+ Transformer transformer =
+ testTransformerBuilderFactory.create(/* enableFallback= */ false).build();
HandlerThread anotherThread = new HandlerThread("AnotherThread");
AtomicReference illegalStateException = new AtomicReference<>();
CountDownLatch countDownLatch = new CountDownLatch(1);
@@ -828,147 +893,10 @@ public final class TransformerEndToEndTest {
assertThat(illegalStateException.get()).isNotNull();
}
- private Transformer.Builder createTransformerBuilder(boolean enableFallback) {
- return new Transformer.Builder(context)
- .setClock(clock)
- .setMuxerFactory(new TestMuxerFactory())
- .setEncoderFactory(
- new DefaultEncoderFactory.Builder(context).setEnableFallback(enableFallback).build());
- }
-
- private static void createEncodersAndDecoders() {
- ShadowMediaCodec.CodecConfig codecConfig =
- new ShadowMediaCodec.CodecConfig(
- /* inputBufferSize= */ 10_000,
- /* outputBufferSize= */ 10_000,
- /* codec= */ (in, out) -> out.put(in));
- addCodec(
- MimeTypes.AUDIO_AAC,
- codecConfig,
- /* colorFormats= */ ImmutableList.of(),
- /* isDecoder= */ true);
- addCodec(
- MimeTypes.AUDIO_AC3,
- codecConfig,
- /* colorFormats= */ ImmutableList.of(),
- /* isDecoder= */ true);
- addCodec(
- MimeTypes.AUDIO_AMR_NB,
- codecConfig,
- /* colorFormats= */ ImmutableList.of(),
- /* isDecoder= */ true);
- addCodec(
- MimeTypes.AUDIO_AAC,
- codecConfig,
- /* colorFormats= */ ImmutableList.of(),
- /* isDecoder= */ false);
-
- ShadowMediaCodec.CodecConfig throwingCodecConfig =
- new ShadowMediaCodec.CodecConfig(
- /* inputBufferSize= */ 10_000,
- /* outputBufferSize= */ 10_000,
- new ShadowMediaCodec.CodecConfig.Codec() {
-
- @Override
- public void process(ByteBuffer in, ByteBuffer out) {
- out.put(in);
- }
-
- @Override
- public void onConfigured(
- MediaFormat format,
- @Nullable Surface surface,
- @Nullable MediaCrypto crypto,
- int flags) {
- throw new IllegalArgumentException("Format unsupported");
- }
- });
-
- addCodec(
- MimeTypes.AUDIO_AMR_WB,
- throwingCodecConfig,
- /* colorFormats= */ ImmutableList.of(),
- /* isDecoder= */ true);
- addCodec(
- MimeTypes.AUDIO_AMR_NB,
- throwingCodecConfig,
- /* colorFormats= */ ImmutableList.of(),
- /* isDecoder= */ false);
- }
-
- private static void addCodec(
- String mimeType,
- ShadowMediaCodec.CodecConfig codecConfig,
- List colorFormats,
- boolean isDecoder) {
- String codecName =
- Util.formatInvariant(
- isDecoder ? "exo.%s.decoder" : "exo.%s.encoder", mimeType.replace('/', '-'));
- if (isDecoder) {
- ShadowMediaCodec.addDecoder(codecName, codecConfig);
- } else {
- ShadowMediaCodec.addEncoder(codecName, codecConfig);
- }
-
- MediaFormat mediaFormat = new MediaFormat();
- mediaFormat.setString(MediaFormat.KEY_MIME, mimeType);
- MediaCodecInfoBuilder.CodecCapabilitiesBuilder codecCapabilities =
- MediaCodecInfoBuilder.CodecCapabilitiesBuilder.newBuilder()
- .setMediaFormat(mediaFormat)
- .setIsEncoder(!isDecoder);
-
- if (!colorFormats.isEmpty()) {
- codecCapabilities.setColorFormats(Ints.toArray(colorFormats));
- }
-
- ShadowMediaCodecList.addCodec(
- MediaCodecInfoBuilder.newBuilder()
- .setName(codecName)
- .setIsEncoder(!isDecoder)
- .setCapabilities(codecCapabilities.build())
- .build());
- }
-
- private static void removeEncodersAndDecoders() {
- ShadowMediaCodec.clearCodecs();
- ShadowMediaCodecList.reset();
- EncoderUtil.clearCachedEncoders();
- }
-
private static String getDumpFileName(String originalFileName) {
return DUMP_FILE_OUTPUT_DIRECTORY + '/' + originalFileName + '.' + DUMP_FILE_EXTENSION;
}
- private final class TestMuxerFactory implements Muxer.Factory {
-
- private final Muxer.Factory defaultMuxerFactory;
-
- public TestMuxerFactory() {
- defaultMuxerFactory = new DefaultMuxer.Factory();
- }
-
- public TestMuxerFactory(long maxDelayBetweenSamplesMs) {
- defaultMuxerFactory = new DefaultMuxer.Factory(maxDelayBetweenSamplesMs);
- }
-
- @Override
- public Muxer create(String path) throws Muxer.MuxerException {
- testMuxer = new TestMuxer(path, defaultMuxerFactory);
- return testMuxer;
- }
-
- @Override
- public Muxer create(ParcelFileDescriptor parcelFileDescriptor) throws Muxer.MuxerException {
- testMuxer = new TestMuxer("FD:" + parcelFileDescriptor.getFd(), defaultMuxerFactory);
- return testMuxer;
- }
-
- @Override
- public ImmutableList getSupportedSampleMimeTypes(@C.TrackType int trackType) {
- return defaultMuxerFactory.getSupportedSampleMimeTypes(trackType);
- }
- }
-
private static final class SlowExtractorsFactory implements ExtractorsFactory {
private final long delayBetweenReadsMs;