diff --git a/libraries/test_utils/build.gradle b/libraries/test_utils/build.gradle
index a5817b9f92..d3901cf175 100644
--- a/libraries/test_utils/build.gradle
+++ b/libraries/test_utils/build.gradle
@@ -26,7 +26,6 @@ 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/test_utils/src/main/java/androidx/media3/test/utils/TestTransformerBuilderFactory.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestTransformerBuilderFactory.java
deleted file mode 100644
index 82c6ae8ac9..0000000000
--- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestTransformerBuilderFactory.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * 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 e579a00166..aafdce151a 100644
--- a/libraries/test_utils_robolectric/build.gradle
+++ b/libraries/test_utils_robolectric/build.gradle
@@ -20,7 +20,6 @@ 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 fe5e61e8de..6e85eed2a0 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,15 +16,10 @@
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;
@@ -35,247 +30,105 @@ import org.robolectric.shadows.ShadowMediaCodec;
import org.robolectric.shadows.ShadowMediaCodecList;
/**
- * A JUnit @Rule to configure {@link ShadowMediaCodec} for transcoding or decoding.
+ * A JUnit @Rule to configure Roboelectric's {@link ShadowMediaCodec}.
*
- * Registers {@link org.robolectric.shadows.ShadowMediaCodec.CodecConfig} instances for ExoPlayer
- * and Transformer tests.
+ *
Registers a {@link org.robolectric.shadows.ShadowMediaCodec.CodecConfig} for each audio/video
+ * MIME type known by ExoPlayer.
*/
@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(/* forTranscoding= */ false);
+ return new ShadowMediaCodecConfig();
}
@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);
- addExoplayerCodec(
- EXOTEST_VIDEO_AVC,
+ configureCodec(
+ /* codecName= */ "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);
- addExoplayerCodec(
- EXOTEST_VIDEO_MPEG2,
+ configureCodec(
+ /* codecName= */ "exotest.video.mpeg2",
MimeTypes.VIDEO_MPEG2,
- generateDecodingCodecConfig(MimeTypes.VIDEO_MPEG2),
ImmutableList.of(mpeg2ProfileLevel),
ImmutableList.of(MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible));
- addExoplayerCodec(
- EXOTEST_VIDEO_VP9,
+ configureCodec(
+ /* codecName= */ "exotest.video.vp9",
MimeTypes.VIDEO_VP9,
- generateDecodingCodecConfig(MimeTypes.VIDEO_VP9),
ImmutableList.of(),
ImmutableList.of(MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible));
// Audio codecs
- 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));
+ 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);
// 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.
- addExoplayerCodec(
- EXOTEST_AUDIO_RAW, MimeTypes.AUDIO_RAW, generateDecodingCodecConfig(MimeTypes.AUDIO_RAW));
+ configureCodec("exotest.audio.raw", MimeTypes.AUDIO_RAW);
}
@Override
protected void after() {
- if (!forTranscoding) {
- MediaCodecUtil.clearDecoderInfoCache();
- } else {
- EncoderUtil.clearCachedEncoders();
- }
+ MediaCodecUtil.clearDecoderInfoCache();
ShadowMediaCodecList.reset();
ShadowMediaCodec.clearCodecs();
}
- 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(
+ private void configureCodec(String codecName, String mimeType) {
+ configureCodec(
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 addExoplayerCodec(
+ private void configureCodec(
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)
- .setIsEncoder(!isDecoder);
+ MediaCodecInfoBuilder.CodecCapabilitiesBuilder.newBuilder().setMediaFormat(mediaFormat);
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());
-
- if (isDecoder) {
- ShadowMediaCodec.addDecoder(codecName, codecConfig);
- } else {
- ShadowMediaCodec.addEncoder(codecName, codecConfig);
- }
+ // 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));
}
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 e60141e212..680910eb21 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
- public Builder setClock(Clock clock) {
+ /* package */ Builder setClock(Clock clock) {
this.clock = clock;
this.listeners = listeners.copy(looper, clock, (listener, flags) -> {});
return this;
diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestMuxer.java b/libraries/transformer/src/test/java/androidx/media3/transformer/TestMuxer.java
similarity index 96%
rename from libraries/test_utils/src/main/java/androidx/media3/test/utils/TestMuxer.java
rename to libraries/transformer/src/test/java/androidx/media3/transformer/TestMuxer.java
index a8d180e714..547a619a95 100644
--- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestMuxer.java
+++ b/libraries/transformer/src/test/java/androidx/media3/transformer/TestMuxer.java
@@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package androidx.media3.test.utils;
+package androidx.media3.transformer;
import androidx.media3.common.Format;
-import androidx.media3.common.util.UnstableApi;
-import androidx.media3.transformer.Muxer;
+import androidx.media3.test.utils.DumpableFormat;
+import androidx.media3.test.utils.Dumper;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
@@ -28,7 +28,6 @@ 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/transformer/src/test/java/androidx/media3/transformer/TransformerEndToEndTest.java b/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerEndToEndTest.java
index c00eefd7a1..986ba3b388 100644
--- a/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerEndToEndTest.java
+++ b/libraries/transformer/src/test/java/androidx/media3/transformer/TransformerEndToEndTest.java
@@ -29,11 +29,15 @@ 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;
@@ -49,13 +53,14 @@ 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.TestTransformerBuilderFactory;
-import androidx.media3.test.utils.robolectric.ShadowMediaCodecConfig;
+import androidx.media3.test.utils.FakeClock;
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;
@@ -69,17 +74,16 @@ 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";
@@ -96,39 +100,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();
- testTransformerBuilderFactory = new TestTransformerBuilderFactory(context);
+ createEncodersAndDecoders();
}
@After
public void tearDown() throws Exception {
Files.delete(Paths.get(outputPath));
+ removeEncodersAndDecoders();
}
@Test
public void startTransformation_videoOnlyPassthrough_completesSuccessfully() throws Exception {
- Transformer transformer =
- testTransformerBuilderFactory.create(/* enableFallback= */ false).build();
+ Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_VIDEO_ONLY);
transformer.startTransformation(mediaItem, outputPath);
TransformerTestRunner.runUntilCompleted(transformer);
- DumpFileAsserts.assertOutput(
- context, testTransformerBuilderFactory.getTestMuxer(), getDumpFileName(FILE_VIDEO_ONLY));
+ DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_VIDEO_ONLY));
}
@Test
public void startTransformation_audioOnlyPassthrough_completesSuccessfully() throws Exception {
- Transformer transformer =
- testTransformerBuilderFactory.create(/* enableFallback= */ false).build();
+ Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_UNSUPPORTED_BY_ENCODER);
@@ -136,16 +140,13 @@ public final class TransformerEndToEndTest {
TransformerTestRunner.runUntilCompleted(transformer);
DumpFileAsserts.assertOutput(
- context,
- testTransformerBuilderFactory.getTestMuxer(),
- getDumpFileName(FILE_AUDIO_UNSUPPORTED_BY_ENCODER));
+ context, testMuxer, getDumpFileName(FILE_AUDIO_UNSUPPORTED_BY_ENCODER));
}
@Test
public void startTransformation_audioOnlyTranscoding_completesSuccessfully() throws Exception {
Transformer transformer =
- testTransformerBuilderFactory
- .create(/* enableFallback= */ false)
+ createTransformerBuilder(/* enableFallback= */ false)
.setTransformationRequest(
new TransformationRequest.Builder()
.setAudioMimeType(MimeTypes.AUDIO_AAC) // supported by encoder and muxer
@@ -157,29 +158,24 @@ public final class TransformerEndToEndTest {
TransformerTestRunner.runUntilCompleted(transformer);
DumpFileAsserts.assertOutput(
- context,
- testTransformerBuilderFactory.getTestMuxer(),
- getDumpFileName(FILE_AUDIO_UNSUPPORTED_BY_ENCODER + ".aac"));
+ context, testMuxer, getDumpFileName(FILE_AUDIO_UNSUPPORTED_BY_ENCODER + ".aac"));
}
@Test
public void startTransformation_audioAndVideo_completesSuccessfully() throws Exception {
- Transformer transformer =
- testTransformerBuilderFactory.create(/* enableFallback= */ false).build();
+ Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
transformer.startTransformation(mediaItem, outputPath);
TransformerTestRunner.runUntilCompleted(transformer);
- DumpFileAsserts.assertOutput(
- context, testTransformerBuilderFactory.getTestMuxer(), getDumpFileName(FILE_AUDIO_VIDEO));
+ DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO));
}
@Test
public void startTransformation_audioAndVideo_withClippingStartAtKeyFrame_completesSuccessfully()
throws Exception {
- Transformer transformer =
- testTransformerBuilderFactory.create(/* enableFallback= */ false).build();
+ Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
MediaItem mediaItem =
new MediaItem.Builder()
.setUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO_INCREASING_TIMESTAMPS_15S)
@@ -196,15 +192,14 @@ public final class TransformerEndToEndTest {
DumpFileAsserts.assertOutput(
context,
- testTransformerBuilderFactory.getTestMuxer(),
+ testMuxer,
getDumpFileName(FILE_AUDIO_VIDEO_INCREASING_TIMESTAMPS_15S + ".clipped"));
}
@Test
public void startTransformation_withSubtitles_completesSuccessfully() throws Exception {
Transformer transformer =
- testTransformerBuilderFactory
- .create(/* enableFallback= */ false)
+ createTransformerBuilder(/* enableFallback= */ false)
.setTransformationRequest(
new TransformationRequest.Builder().setAudioMimeType(MimeTypes.AUDIO_AAC).build())
.build();
@@ -213,17 +208,13 @@ public final class TransformerEndToEndTest {
transformer.startTransformation(mediaItem, outputPath);
TransformerTestRunner.runUntilCompleted(transformer);
- DumpFileAsserts.assertOutput(
- context,
- testTransformerBuilderFactory.getTestMuxer(),
- getDumpFileName(FILE_WITH_SUBTITLES));
+ DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_WITH_SUBTITLES));
}
@Test
public void startTransformation_successiveTransformations_completesSuccessfully()
throws Exception {
- Transformer transformer =
- testTransformerBuilderFactory.create(/* enableFallback= */ false).build();
+ Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
// Transform first media item.
@@ -235,14 +226,12 @@ public final class TransformerEndToEndTest {
transformer.startTransformation(mediaItem, outputPath);
TransformerTestRunner.runUntilCompleted(transformer);
- DumpFileAsserts.assertOutput(
- context, testTransformerBuilderFactory.getTestMuxer(), getDumpFileName(FILE_AUDIO_VIDEO));
+ DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO));
}
@Test
public void startTransformation_concurrentTransformations_throwsError() throws Exception {
- Transformer transformer =
- testTransformerBuilderFactory.create(/* enableFallback= */ false).build();
+ Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_VIDEO_ONLY);
transformer.startTransformation(mediaItem, outputPath);
@@ -254,44 +243,33 @@ public final class TransformerEndToEndTest {
@Test
public void startTransformation_removeAudio_completesSuccessfully() throws Exception {
Transformer transformer =
- testTransformerBuilderFactory
- .create(/* enableFallback= */ false)
- .setRemoveAudio(true)
- .build();
+ createTransformerBuilder(/* 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,
- testTransformerBuilderFactory.getTestMuxer(),
- getDumpFileName(FILE_AUDIO_VIDEO + ".noaudio"));
+ context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO + ".noaudio"));
}
@Test
public void startTransformation_removeVideo_completesSuccessfully() throws Exception {
Transformer transformer =
- testTransformerBuilderFactory
- .create(/* enableFallback= */ false)
- .setRemoveVideo(true)
- .build();
+ createTransformerBuilder(/* 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,
- testTransformerBuilderFactory.getTestMuxer(),
- getDumpFileName(FILE_AUDIO_VIDEO + ".novideo"));
+ context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO + ".novideo"));
}
@Test
public void startTransformation_silentAudio_completesSuccessfully() throws Exception {
Transformer transformer =
- testTransformerBuilderFactory
- .create(/* enableFallback= */ false)
+ createTransformerBuilder(/* enableFallback= */ false)
.experimentalSetForceSilentAudio(true)
.build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
@@ -300,9 +278,7 @@ public final class TransformerEndToEndTest {
TransformerTestRunner.runUntilCompleted(transformer);
DumpFileAsserts.assertOutput(
- context,
- testTransformerBuilderFactory.getTestMuxer(),
- getDumpFileName(FILE_AUDIO_VIDEO + ".silentaudio"));
+ context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO + ".silentaudio"));
}
@Test
@@ -310,8 +286,7 @@ public final class TransformerEndToEndTest {
SonicAudioProcessor sonicAudioProcessor = new SonicAudioProcessor();
sonicAudioProcessor.setOutputSampleRateHz(48000);
Transformer transformer =
- testTransformerBuilderFactory
- .create(/* enableFallback= */ false)
+ createTransformerBuilder(/* enableFallback= */ false)
.setAudioProcessors(ImmutableList.of(sonicAudioProcessor))
.build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
@@ -320,9 +295,7 @@ public final class TransformerEndToEndTest {
TransformerTestRunner.runUntilCompleted(transformer);
DumpFileAsserts.assertOutput(
- context,
- testTransformerBuilderFactory.getTestMuxer(),
- getDumpFileName(FILE_AUDIO_VIDEO + ".48000hz"));
+ context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO + ".48000hz"));
}
@Test
@@ -331,8 +304,7 @@ public final class TransformerEndToEndTest {
Transformer.Listener mockListener2 = mock(Transformer.Listener.class);
Transformer.Listener mockListener3 = mock(Transformer.Listener.class);
Transformer transformer =
- testTransformerBuilderFactory
- .create(/* enableFallback= */ false)
+ createTransformerBuilder(/* enableFallback= */ false)
.addListener(mockListener1)
.addListener(mockListener2)
.addListener(mockListener3)
@@ -353,8 +325,7 @@ public final class TransformerEndToEndTest {
Transformer.Listener mockListener2 = mock(Transformer.Listener.class);
Transformer.Listener mockListener3 = mock(Transformer.Listener.class);
Transformer transformer =
- testTransformerBuilderFactory
- .create(/* enableFallback= */ false)
+ createTransformerBuilder(/* enableFallback= */ false)
.addListener(mockListener1)
.addListener(mockListener2)
.addListener(mockListener3)
@@ -381,8 +352,7 @@ public final class TransformerEndToEndTest {
TransformationRequest fallbackTransformationRequest =
new TransformationRequest.Builder().setAudioMimeType(MimeTypes.AUDIO_AAC).build();
Transformer transformer =
- testTransformerBuilderFactory
- .create(/* enableFallback= */ true)
+ createTransformerBuilder(/* enableFallback= */ true)
.addListener(mockListener1)
.addListener(mockListener2)
.addListener(mockListener3)
@@ -407,8 +377,7 @@ public final class TransformerEndToEndTest {
Transformer.Listener mockListener2 = mock(Transformer.Listener.class);
Transformer.Listener mockListener3 = mock(Transformer.Listener.class);
Transformer transformer1 =
- testTransformerBuilderFactory
- .create(/* enableFallback= */ false)
+ createTransformerBuilder(/* enableFallback= */ false)
.addListener(mockListener1)
.addListener(mockListener2)
.addListener(mockListener3)
@@ -427,8 +396,7 @@ public final class TransformerEndToEndTest {
@Test
public void startTransformation_flattenForSlowMotion_completesSuccessfully() throws Exception {
Transformer transformer =
- testTransformerBuilderFactory
- .create(/* enableFallback= */ false)
+ createTransformerBuilder(/* enableFallback= */ false)
.setTransformationRequest(
new TransformationRequest.Builder().setFlattenForSlowMotion(true).build())
.build();
@@ -437,10 +405,7 @@ public final class TransformerEndToEndTest {
transformer.startTransformation(mediaItem, outputPath);
TransformerTestRunner.runUntilCompleted(transformer);
- DumpFileAsserts.assertOutput(
- context,
- testTransformerBuilderFactory.getTestMuxer(),
- getDumpFileName(FILE_WITH_SEF_SLOW_MOTION));
+ DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_WITH_SEF_SLOW_MOTION));
}
@Test
@@ -455,10 +420,7 @@ public final class TransformerEndToEndTest {
}
};
Transformer transformer =
- testTransformerBuilderFactory
- .create(/* enableFallback= */ false)
- .addListener(listener)
- .build();
+ createTransformerBuilder(/* enableFallback= */ false).addListener(listener).build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
transformer.startTransformation(mediaItem, outputPath);
@@ -474,8 +436,7 @@ public final class TransformerEndToEndTest {
public void startTransformation_withAudioEncoderFormatUnsupported_completesWithError()
throws Exception {
Transformer transformer =
- testTransformerBuilderFactory
- .create(/* enableFallback= */ false)
+ createTransformerBuilder(/* enableFallback= */ false)
.setTransformationRequest(
new TransformationRequest.Builder()
.setAudioMimeType(
@@ -496,8 +457,7 @@ public final class TransformerEndToEndTest {
public void startTransformation_withAudioDecoderFormatUnsupported_completesWithError()
throws Exception {
Transformer transformer =
- testTransformerBuilderFactory
- .create(/* enableFallback= */ false)
+ createTransformerBuilder(/* enableFallback= */ false)
.setTransformationRequest(
new TransformationRequest.Builder()
.setAudioMimeType(MimeTypes.AUDIO_AAC) // supported by encoder and muxer
@@ -515,8 +475,7 @@ public final class TransformerEndToEndTest {
@Test
public void startTransformation_withIoError_completesWithError() throws Exception {
- Transformer transformer =
- testTransformerBuilderFactory.create(/* enableFallback= */ false).build();
+ Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
MediaItem mediaItem = MediaItem.fromUri("asset:///non-existing-path.mp4");
transformer.startTransformation(mediaItem, outputPath);
@@ -536,19 +495,14 @@ public final class TransformerEndToEndTest {
TransformationRequest fallbackTransformationRequest =
new TransformationRequest.Builder().setAudioMimeType(MimeTypes.AUDIO_AAC).build();
Transformer transformer =
- testTransformerBuilderFactory
- .create(/* enableFallback= */ false)
- .addListener(mockListener)
- .build();
+ createTransformerBuilder(/* 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,
- testTransformerBuilderFactory.getTestMuxer(),
- getDumpFileName(FILE_AUDIO_UNSUPPORTED_BY_MUXER + ".fallback"));
+ context, testMuxer, getDumpFileName(FILE_AUDIO_UNSUPPORTED_BY_MUXER + ".fallback"));
verify(mockListener)
.onFallbackApplied(mediaItem, originalTransformationRequest, fallbackTransformationRequest);
}
@@ -562,19 +516,14 @@ public final class TransformerEndToEndTest {
TransformationRequest fallbackTransformationRequest =
new TransformationRequest.Builder().setAudioMimeType(MimeTypes.AUDIO_AAC).build();
Transformer transformer =
- testTransformerBuilderFactory
- .create(/* enableFallback= */ true)
- .addListener(mockListener)
- .build();
+ createTransformerBuilder(/* 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,
- testTransformerBuilderFactory.getTestMuxer(),
- getDumpFileName(FILE_AUDIO_UNSUPPORTED_BY_MUXER + ".fallback"));
+ context, testMuxer, getDumpFileName(FILE_AUDIO_UNSUPPORTED_BY_MUXER + ".fallback"));
verify(mockListener)
.onFallbackApplied(mediaItem, originalTransformationRequest, fallbackTransformationRequest);
}
@@ -584,11 +533,11 @@ public final class TransformerEndToEndTest {
MediaSource.Factory mediaSourceFactory =
new DefaultMediaSourceFactory(
context, new SlowExtractorsFactory(/* delayBetweenReadsMs= */ 10));
+ Muxer.Factory muxerFactory = new TestMuxerFactory(/* maxDelayBetweenSamplesMs= */ 1);
Transformer transformer =
- testTransformerBuilderFactory
- .setMaxDelayBetweenSamplesMs(1)
- .create(/* enableFallback= */ false)
+ createTransformerBuilder(/* enableFallback= */ false)
.setMediaSourceFactory(mediaSourceFactory)
+ .setMuxerFactory(muxerFactory)
.build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
@@ -602,24 +551,20 @@ public final class TransformerEndToEndTest {
@Test
public void startTransformation_withUnsetMaxDelayBetweenSamples_completesSuccessfully()
throws Exception {
+ Muxer.Factory muxerFactory = new TestMuxerFactory(/* maxDelayBetweenSamplesMs= */ C.TIME_UNSET);
Transformer transformer =
- testTransformerBuilderFactory
- .setMaxDelayBetweenSamplesMs(C.TIME_UNSET)
- .create(/* enableFallback= */ false)
- .build();
+ createTransformerBuilder(/* enableFallback= */ false).setMuxerFactory(muxerFactory).build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
transformer.startTransformation(mediaItem, outputPath);
TransformerTestRunner.runUntilCompleted(transformer);
- DumpFileAsserts.assertOutput(
- context, testTransformerBuilderFactory.getTestMuxer(), getDumpFileName(FILE_AUDIO_VIDEO));
+ DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO));
}
@Test
public void startTransformation_afterCancellation_completesSuccessfully() throws Exception {
- Transformer transformer =
- testTransformerBuilderFactory.create(/* enableFallback= */ false).build();
+ Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
transformer.startTransformation(mediaItem, outputPath);
@@ -630,8 +575,7 @@ public final class TransformerEndToEndTest {
transformer.startTransformation(mediaItem, outputPath);
TransformerTestRunner.runUntilCompleted(transformer);
- DumpFileAsserts.assertOutput(
- context, testTransformerBuilderFactory.getTestMuxer(), getDumpFileName(FILE_AUDIO_VIDEO));
+ DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO));
}
@Test
@@ -640,7 +584,7 @@ public final class TransformerEndToEndTest {
anotherThread.start();
Looper looper = anotherThread.getLooper();
Transformer transformer =
- testTransformerBuilderFactory.create(/* enableFallback= */ false).setLooper(looper).build();
+ createTransformerBuilder(/* enableFallback= */ false).setLooper(looper).build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
AtomicReference exception = new AtomicReference<>();
CountDownLatch countDownLatch = new CountDownLatch(1);
@@ -660,14 +604,12 @@ public final class TransformerEndToEndTest {
countDownLatch.await();
assertThat(exception.get()).isNull();
- DumpFileAsserts.assertOutput(
- context, testTransformerBuilderFactory.getTestMuxer(), getDumpFileName(FILE_AUDIO_VIDEO));
+ DumpFileAsserts.assertOutput(context, testMuxer, getDumpFileName(FILE_AUDIO_VIDEO));
}
@Test
public void startTransformation_fromWrongThread_throwsError() throws Exception {
- Transformer transformer =
- testTransformerBuilderFactory.create(/* enableFallback= */ false).build();
+ Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_VIDEO);
HandlerThread anotherThread = new HandlerThread("AnotherThread");
AtomicReference illegalStateException = new AtomicReference<>();
@@ -692,8 +634,7 @@ public final class TransformerEndToEndTest {
@Test
public void getProgress_knownDuration_returnsConsistentStates() throws Exception {
- Transformer transformer =
- testTransformerBuilderFactory.create(/* enableFallback= */ false).build();
+ Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_VIDEO_ONLY);
AtomicInteger previousProgressState =
new AtomicInteger(PROGRESS_STATE_WAITING_FOR_AVAILABILITY);
@@ -739,8 +680,7 @@ public final class TransformerEndToEndTest {
@Test
public void getProgress_knownDuration_givesIncreasingPercentages() throws Exception {
- Transformer transformer =
- testTransformerBuilderFactory.create(/* enableFallback= */ false).build();
+ Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_VIDEO_ONLY);
List progresses = new ArrayList<>();
Handler progressHandler =
@@ -775,8 +715,7 @@ public final class TransformerEndToEndTest {
@Test
public void getProgress_noCurrentTransformation_returnsNoTransformation() throws Exception {
- Transformer transformer =
- testTransformerBuilderFactory.create(/* enableFallback= */ false).build();
+ Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_VIDEO_ONLY);
@Transformer.ProgressState int stateBeforeTransform = transformer.getProgress(progressHolder);
@@ -790,8 +729,7 @@ public final class TransformerEndToEndTest {
@Test
public void getProgress_unknownDuration_returnsConsistentStates() throws Exception {
- Transformer transformer =
- testTransformerBuilderFactory.create(/* enableFallback= */ false).build();
+ Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_UNKNOWN_DURATION);
AtomicInteger previousProgressState =
new AtomicInteger(PROGRESS_STATE_WAITING_FOR_AVAILABILITY);
@@ -834,8 +772,7 @@ public final class TransformerEndToEndTest {
@Test
public void getProgress_fromWrongThread_throwsError() throws Exception {
- Transformer transformer =
- testTransformerBuilderFactory.create(/* enableFallback= */ false).build();
+ Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
HandlerThread anotherThread = new HandlerThread("AnotherThread");
AtomicReference illegalStateException = new AtomicReference<>();
CountDownLatch countDownLatch = new CountDownLatch(1);
@@ -859,8 +796,7 @@ public final class TransformerEndToEndTest {
@Test
public void cancel_afterCompletion_doesNotThrow() throws Exception {
- Transformer transformer =
- testTransformerBuilderFactory.create(/* enableFallback= */ false).build();
+ Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
MediaItem mediaItem = MediaItem.fromUri(ASSET_URI_PREFIX + FILE_VIDEO_ONLY);
transformer.startTransformation(mediaItem, outputPath);
@@ -870,8 +806,7 @@ public final class TransformerEndToEndTest {
@Test
public void cancel_fromWrongThread_throwsError() throws Exception {
- Transformer transformer =
- testTransformerBuilderFactory.create(/* enableFallback= */ false).build();
+ Transformer transformer = createTransformerBuilder(/* enableFallback= */ false).build();
HandlerThread anotherThread = new HandlerThread("AnotherThread");
AtomicReference illegalStateException = new AtomicReference<>();
CountDownLatch countDownLatch = new CountDownLatch(1);
@@ -893,10 +828,147 @@ 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;