diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/AndroidTestUtil.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/AndroidTestUtil.java similarity index 97% rename from libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/AndroidTestUtil.java rename to libraries/transformer/src/androidTest/java/androidx/media3/transformer/AndroidTestUtil.java index 1abe43e5a7..0a8c48c145 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/AndroidTestUtil.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/AndroidTestUtil.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package androidx.media3.transformer.mh; +package androidx.media3.transformer; import static androidx.media3.common.util.Assertions.checkState; import static com.google.common.truth.Truth.assertWithMessage; @@ -24,8 +24,6 @@ import android.net.Uri; import android.os.Build; import androidx.annotation.Nullable; import androidx.media3.common.MediaItem; -import androidx.media3.transformer.TransformationException; -import androidx.media3.transformer.Transformer; import androidx.test.platform.app.InstrumentationRegistry; import java.io.File; import java.io.FileWriter; @@ -35,7 +33,7 @@ import java.util.concurrent.atomic.AtomicReference; import org.checkerframework.checker.nullness.compatqual.NullableType; /** Utilities for instrumentation tests. */ -/* package */ final class AndroidTestUtil { +public final class AndroidTestUtil { public static final String MP4_ASSET_URI_STRING = "asset:///media/mp4/sample.mp4"; public static final String SEF_ASSET_URI_STRING = "asset:///media/mp4/sample_sef_slow_motion.mp4"; public static final String REMOTE_MP4_10_SECONDS_URI_STRING = diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/FrameCountingMuxer.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/FrameCountingMuxer.java new file mode 100644 index 0000000000..27e05ace84 --- /dev/null +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/FrameCountingMuxer.java @@ -0,0 +1,114 @@ +/* + * 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.transformer; + +import android.os.ParcelFileDescriptor; +import androidx.annotation.Nullable; +import androidx.media3.common.C; +import androidx.media3.common.Format; +import androidx.media3.common.MimeTypes; +import com.google.common.collect.ImmutableList; +import java.io.IOException; +import java.nio.ByteBuffer; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; + +/** + * An implementation of {@link Muxer} that forwards operations to another {@link Muxer}, counting + * the number of frames as they go past. + */ +/* package */ final class FrameCountingMuxer implements Muxer { + public static final class Factory implements Muxer.Factory { + + private final Muxer.Factory muxerFactory; + private @MonotonicNonNull FrameCountingMuxer frameCountingMuxer; + + public Factory(Muxer.Factory muxerFactory) { + this.muxerFactory = muxerFactory; + } + + @Override + public Muxer create(String path, String outputMimeType) throws IOException { + frameCountingMuxer = new FrameCountingMuxer(muxerFactory.create(path, outputMimeType)); + return frameCountingMuxer; + } + + @Override + public Muxer create(ParcelFileDescriptor parcelFileDescriptor, String outputMimeType) + throws IOException { + frameCountingMuxer = + new FrameCountingMuxer(muxerFactory.create(parcelFileDescriptor, outputMimeType)); + return frameCountingMuxer; + } + + @Override + public boolean supportsOutputMimeType(String mimeType) { + return muxerFactory.supportsOutputMimeType(mimeType); + } + + @Override + public boolean supportsSampleMimeType(@Nullable String sampleMimeType, String outputMimeType) { + return muxerFactory.supportsSampleMimeType(sampleMimeType, outputMimeType); + } + + @Override + public ImmutableList getSupportedSampleMimeTypes( + @C.TrackType int trackType, String containerMimeType) { + return muxerFactory.getSupportedSampleMimeTypes(trackType, containerMimeType); + } + + @Nullable + public FrameCountingMuxer getLastFrameCountingMuxerCreated() { + return frameCountingMuxer; + } + } + + private final Muxer muxer; + private int videoTrackIndex; + private int frameCount; + + private FrameCountingMuxer(Muxer muxer) throws IOException { + this.muxer = muxer; + } + + @Override + public int addTrack(Format format) throws MuxerException { + int trackIndex = muxer.addTrack(format); + if (MimeTypes.isVideo(format.sampleMimeType)) { + videoTrackIndex = trackIndex; + } + return trackIndex; + } + + @Override + public void writeSampleData( + int trackIndex, ByteBuffer data, boolean isKeyFrame, long presentationTimeUs) + throws MuxerException { + muxer.writeSampleData(trackIndex, data, isKeyFrame, presentationTimeUs); + if (trackIndex == videoTrackIndex) { + frameCount++; + } + } + + @Override + public void release(boolean forCancellation) throws MuxerException { + muxer.release(forCancellation); + } + + /* Returns the number of frames written for the video track. */ + public int getFrameCount() { + return frameCount; + } +} diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerTest.java new file mode 100644 index 0000000000..d8f59dc17b --- /dev/null +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerTest.java @@ -0,0 +1,67 @@ +/* + * 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.transformer; + +import static androidx.media3.common.util.Assertions.checkNotNull; +import static androidx.media3.transformer.AndroidTestUtil.runTransformer; +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import androidx.media3.common.MimeTypes; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * End-to-end instrumentation test for {@link Transformer} for cases that cannot be tested using + * robolectric. + */ +@RunWith(AndroidJUnit4.class) +public class TransformerTest { + // TODO(b/208986865): Also test this for API < 29. Currently the target emulator uses API 30. + // VideoTranscodingSamplePipeline#processData works differently for API < 29, so both versions + // should be tested. + + private static final String VP9_VIDEO_URI_STRING = "asset:///media/vp9/bear-vp9.webm"; + + @Test + public void videoTranscoding_completesWithConsistentFrameCount() throws Exception { + Context context = ApplicationProvider.getApplicationContext(); + FrameCountingMuxer.Factory muxerFactory = + new FrameCountingMuxer.Factory(new FrameworkMuxer.Factory()); + Transformer transformer = + new Transformer.Builder(context) + .setTransformationRequest( + new TransformationRequest.Builder().setVideoMimeType(MimeTypes.VIDEO_H264).build()) + .setMuxerFactory(muxerFactory) + .build(); + // Result of the following command: + // ffprobe -count_frames -select_streams v:0 -show_entries stream=nb_read_frames bear-vp9.webm + int expectedFrameCount = 82; + + runTransformer( + context, + /* testId= */ "videoTranscoding_completesWithConsistentFrameCount", + transformer, + VP9_VIDEO_URI_STRING, + /* timeoutSeconds= */ 120); + + FrameCountingMuxer frameCountingMuxer = + checkNotNull(muxerFactory.getLastFrameCountingMuxerCreated()); + assertThat(frameCountingMuxer.getFrameCount()).isEqualTo(expectedFrameCount); + } +} diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/RemoveAudioTransformationTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/RemoveAudioTransformationTest.java index 17200613a1..fa3e4a8a26 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/RemoveAudioTransformationTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/RemoveAudioTransformationTest.java @@ -15,8 +15,8 @@ */ package androidx.media3.transformer.mh; -import static androidx.media3.transformer.mh.AndroidTestUtil.MP4_ASSET_URI_STRING; -import static androidx.media3.transformer.mh.AndroidTestUtil.runTransformer; +import static androidx.media3.transformer.AndroidTestUtil.MP4_ASSET_URI_STRING; +import static androidx.media3.transformer.AndroidTestUtil.runTransformer; import android.content.Context; import androidx.media3.transformer.Transformer; diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/RemoveVideoTransformationTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/RemoveVideoTransformationTest.java index 73e1138de4..b77cfa08e9 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/RemoveVideoTransformationTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/RemoveVideoTransformationTest.java @@ -15,8 +15,8 @@ */ package androidx.media3.transformer.mh; -import static androidx.media3.transformer.mh.AndroidTestUtil.MP4_ASSET_URI_STRING; -import static androidx.media3.transformer.mh.AndroidTestUtil.runTransformer; +import static androidx.media3.transformer.AndroidTestUtil.MP4_ASSET_URI_STRING; +import static androidx.media3.transformer.AndroidTestUtil.runTransformer; import android.content.Context; import androidx.media3.transformer.Transformer; diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/RepeatedTranscodeTransformationTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/RepeatedTranscodeTransformationTest.java index aec9918141..eb3feca46c 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/RepeatedTranscodeTransformationTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/RepeatedTranscodeTransformationTest.java @@ -15,13 +15,14 @@ */ package androidx.media3.transformer.mh; -import static androidx.media3.transformer.mh.AndroidTestUtil.runTransformer; +import static androidx.media3.transformer.AndroidTestUtil.runTransformer; import static com.google.common.truth.Truth.assertWithMessage; import android.content.Context; import android.graphics.Matrix; import androidx.media3.common.MimeTypes; import androidx.media3.common.util.Assertions; +import androidx.media3.transformer.AndroidTestUtil; import androidx.media3.transformer.TransformationRequest; import androidx.media3.transformer.Transformer; import androidx.test.core.app.ApplicationProvider; diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/SefTransformationTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/SefTransformationTest.java index 53118ed4d0..4212ddc391 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/SefTransformationTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/SefTransformationTest.java @@ -15,8 +15,8 @@ */ package androidx.media3.transformer.mh; -import static androidx.media3.transformer.mh.AndroidTestUtil.SEF_ASSET_URI_STRING; -import static androidx.media3.transformer.mh.AndroidTestUtil.runTransformer; +import static androidx.media3.transformer.AndroidTestUtil.SEF_ASSET_URI_STRING; +import static androidx.media3.transformer.AndroidTestUtil.runTransformer; import android.content.Context; import androidx.media3.transformer.TransformationRequest; diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/SetTransformationMatrixTransformationTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/SetTransformationMatrixTransformationTest.java index 7b87803f17..89533baee3 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/SetTransformationMatrixTransformationTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/SetTransformationMatrixTransformationTest.java @@ -15,8 +15,8 @@ */ package androidx.media3.transformer.mh; -import static androidx.media3.transformer.mh.AndroidTestUtil.REMOTE_MP4_10_SECONDS_URI_STRING; -import static androidx.media3.transformer.mh.AndroidTestUtil.runTransformer; +import static androidx.media3.transformer.AndroidTestUtil.REMOTE_MP4_10_SECONDS_URI_STRING; +import static androidx.media3.transformer.AndroidTestUtil.runTransformer; import android.content.Context; import android.graphics.Matrix; diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/TransformationTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/TransformationTest.java index f69674bfb0..532cb1dc96 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/TransformationTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/TransformationTest.java @@ -15,8 +15,8 @@ */ package androidx.media3.transformer.mh; -import static androidx.media3.transformer.mh.AndroidTestUtil.MP4_ASSET_URI_STRING; -import static androidx.media3.transformer.mh.AndroidTestUtil.runTransformer; +import static androidx.media3.transformer.AndroidTestUtil.MP4_ASSET_URI_STRING; +import static androidx.media3.transformer.AndroidTestUtil.runTransformer; import android.content.Context; import androidx.media3.transformer.Transformer;