From 9b69690856c5c7458daf7bddf287f02efc0d38fb Mon Sep 17 00:00:00 2001 From: kimvde Date: Tue, 20 Aug 2024 00:11:15 -0700 Subject: [PATCH] Move some tests from CompositionPlaybackTest out of performance directory PiperOrigin-RevId: 665202382 --- .../CompositionPlaybackTest.java | 114 +------------ .../CompositionPlayerPixelTest.java | 161 ++++++++++++++++++ ...Test.java => EffectPlaybackPixelTest.java} | 4 +- 3 files changed, 164 insertions(+), 115 deletions(-) rename libraries/transformer/src/androidTest/java/androidx/media3/transformer/{mh/performance => }/CompositionPlaybackTest.java (74%) create mode 100644 libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/performance/CompositionPlayerPixelTest.java rename libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/performance/{EffectPlaybackTest.java => EffectPlaybackPixelTest.java} (99%) diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/performance/CompositionPlaybackTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/CompositionPlaybackTest.java similarity index 74% rename from libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/performance/CompositionPlaybackTest.java rename to libraries/transformer/src/androidTest/java/androidx/media3/transformer/CompositionPlaybackTest.java index f0d51e5f6b..3e8fd9947c 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/performance/CompositionPlaybackTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/CompositionPlaybackTest.java @@ -14,63 +14,30 @@ * limitations under the License. */ -package androidx.media3.transformer.mh.performance; +package androidx.media3.transformer; -import static androidx.media3.test.utils.BitmapPixelTestUtil.MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE; -import static androidx.media3.test.utils.BitmapPixelTestUtil.createArgb8888BitmapFromRgba8888Image; -import static androidx.media3.test.utils.BitmapPixelTestUtil.getBitmapAveragePixelAbsoluteDifferenceArgb8888; -import static androidx.media3.test.utils.BitmapPixelTestUtil.readBitmap; import static androidx.media3.transformer.AndroidTestUtil.MP4_ASSET; import static androidx.media3.transformer.AndroidTestUtil.PNG_ASSET; -import static androidx.media3.transformer.mh.performance.PlaybackTestUtil.createTimestampOverlay; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; import static com.google.common.truth.Truth.assertThat; -import static com.google.common.truth.Truth.assertWithMessage; import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.PixelFormat; -import android.media.Image; -import android.media.ImageReader; -import android.view.SurfaceView; import androidx.media3.common.Effect; import androidx.media3.common.MediaItem; -import androidx.media3.common.util.ConditionVariable; -import androidx.media3.common.util.Size; -import androidx.media3.common.util.Util; import androidx.media3.effect.GlEffect; -import androidx.media3.transformer.Composition; -import androidx.media3.transformer.CompositionPlayer; -import androidx.media3.transformer.EditedMediaItem; -import androidx.media3.transformer.EditedMediaItemSequence; -import androidx.media3.transformer.Effects; -import androidx.media3.transformer.InputTimestampRecordingShaderProgram; -import androidx.media3.transformer.PlayerTestListener; -import androidx.media3.transformer.SurfaceTestActivity; -import androidx.test.ext.junit.rules.ActivityScenarioRule; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicReference; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.junit.After; -import org.junit.Before; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.TestName; import org.junit.runner.RunWith; /** Playback tests for {@link CompositionPlayer} */ -// These tests are in the performance package even though they are not performance tests so that -// they are not run on all devices. This is because they use ImageReader, which has a tendency to -// drop frames. @RunWith(AndroidJUnit4.class) public class CompositionPlaybackTest { - private static final String TEST_DIRECTORY = "test-generated-goldens/ExoPlayerPlaybackTest"; private static final long TEST_TIMEOUT_MS = 10_000; - private static final MediaItem VIDEO_MEDIA_ITEM = MediaItem.fromUri(MP4_ASSET.uri); private static final long VIDEO_DURATION_US = MP4_ASSET.videoDurationUs; private static final ImmutableList VIDEO_TIMESTAMPS_US = MP4_ASSET.videoTimestampsUs; @@ -81,101 +48,22 @@ public class CompositionPlaybackTest { private static final ImmutableList IMAGE_TIMESTAMPS_US = ImmutableList.of(0L, 33_333L, 66_667L, 100_000L, 133_333L, 166_667L); - @Rule public final TestName testName = new TestName(); - - @Rule - public ActivityScenarioRule rule = - new ActivityScenarioRule<>(SurfaceTestActivity.class); - private final Context context = getInstrumentation().getContext().getApplicationContext(); private final PlayerTestListener playerTestListener = new PlayerTestListener(TEST_TIMEOUT_MS); private @MonotonicNonNull CompositionPlayer player; - private @MonotonicNonNull ImageReader outputImageReader; - - private String testId; - private SurfaceView surfaceView; - - @Before - public void setUp() { - rule.getScenario().onActivity(activity -> surfaceView = activity.getSurfaceView()); - testId = testName.getMethodName(); - } @After public void tearDown() { - rule.getScenario().close(); getInstrumentation() .runOnMainSync( () -> { if (player != null) { player.release(); } - if (outputImageReader != null) { - outputImageReader.close(); - } }); } - @Test - public void compositionPlayerPreviewTest_ensuresFirstFrameRenderedCorrectly() throws Exception { - AtomicReference renderedFirstFrameBitmap = new AtomicReference<>(); - ConditionVariable hasRenderedFirstFrameCondition = new ConditionVariable(); - outputImageReader = - ImageReader.newInstance( - MP4_ASSET.videoFormat.width, - MP4_ASSET.videoFormat.height, - PixelFormat.RGBA_8888, - /* maxImages= */ 1); - - getInstrumentation() - .runOnMainSync( - () -> { - player = new CompositionPlayer.Builder(context).build(); - outputImageReader.setOnImageAvailableListener( - imageReader -> { - try (Image image = imageReader.acquireLatestImage()) { - renderedFirstFrameBitmap.set(createArgb8888BitmapFromRgba8888Image(image)); - } - hasRenderedFirstFrameCondition.open(); - }, - Util.createHandlerForCurrentOrMainLooper()); - - player.setVideoSurface( - outputImageReader.getSurface(), - new Size(MP4_ASSET.videoFormat.width, MP4_ASSET.videoFormat.height)); - player.setComposition( - new Composition.Builder( - new EditedMediaItemSequence( - new EditedMediaItem.Builder(MediaItem.fromUri(MP4_ASSET.uri)) - .setEffects( - new Effects( - /* audioProcessors= */ ImmutableList.of(), - /* videoEffects= */ ImmutableList.of( - createTimestampOverlay()))) - .setDurationUs(1_024_000L) - .build())) - .build()); - player.prepare(); - }); - - if (!hasRenderedFirstFrameCondition.block(TEST_TIMEOUT_MS)) { - throw new TimeoutException( - Util.formatInvariant("First frame not rendered in %d ms.", TEST_TIMEOUT_MS)); - } - - assertWithMessage("First frame is not rendered.") - .that(renderedFirstFrameBitmap.get()) - .isNotNull(); - float averagePixelAbsoluteDifference = - getBitmapAveragePixelAbsoluteDifferenceArgb8888( - /* expected= */ readBitmap(TEST_DIRECTORY + "/first_frame.png"), - /* actual= */ renderedFirstFrameBitmap.get(), - testId); - assertThat(averagePixelAbsoluteDifference).isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE); - // TODO: b/315800590 - Verify onFirstFrameRendered is invoked only once. - } - @Test public void playback_sequenceOfVideos_effectsReceiveCorrectTimestamps() throws Exception { InputTimestampRecordingShaderProgram inputTimestampRecordingShaderProgram = diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/performance/CompositionPlayerPixelTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/performance/CompositionPlayerPixelTest.java new file mode 100644 index 0000000000..7443829ef7 --- /dev/null +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/performance/CompositionPlayerPixelTest.java @@ -0,0 +1,161 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://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.mh.performance; + +import static androidx.media3.test.utils.BitmapPixelTestUtil.MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE; +import static androidx.media3.test.utils.BitmapPixelTestUtil.createArgb8888BitmapFromRgba8888Image; +import static androidx.media3.test.utils.BitmapPixelTestUtil.getBitmapAveragePixelAbsoluteDifferenceArgb8888; +import static androidx.media3.test.utils.BitmapPixelTestUtil.readBitmap; +import static androidx.media3.transformer.AndroidTestUtil.MP4_ASSET; +import static androidx.media3.transformer.mh.performance.PlaybackTestUtil.createTimestampOverlay; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.PixelFormat; +import android.media.Image; +import android.media.ImageReader; +import android.view.SurfaceView; +import androidx.media3.common.MediaItem; +import androidx.media3.common.util.ConditionVariable; +import androidx.media3.common.util.Size; +import androidx.media3.common.util.Util; +import androidx.media3.transformer.Composition; +import androidx.media3.transformer.CompositionPlayer; +import androidx.media3.transformer.EditedMediaItem; +import androidx.media3.transformer.EditedMediaItemSequence; +import androidx.media3.transformer.Effects; +import androidx.media3.transformer.SurfaceTestActivity; +import androidx.test.ext.junit.rules.ActivityScenarioRule; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.google.common.collect.ImmutableList; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicReference; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; +import org.junit.runner.RunWith; + +/** Pixel tests for {@link CompositionPlayer} */ +// These tests are in the performance package even though they are not performance tests so that +// they are not run on all devices. This is because they use ImageReader, which has a tendency to +// drop frames. +@RunWith(AndroidJUnit4.class) +public class CompositionPlayerPixelTest { + + private static final String TEST_DIRECTORY = "test-generated-goldens/ExoPlayerPlaybackTest"; + private static final long TEST_TIMEOUT_MS = 10_000; + + @Rule public final TestName testName = new TestName(); + + @Rule + public ActivityScenarioRule rule = + new ActivityScenarioRule<>(SurfaceTestActivity.class); + + private final Context context = getInstrumentation().getContext().getApplicationContext(); + + private @MonotonicNonNull CompositionPlayer player; + private @MonotonicNonNull ImageReader outputImageReader; + + private String testId; + private SurfaceView surfaceView; + + @Before + public void setUp() { + rule.getScenario().onActivity(activity -> surfaceView = activity.getSurfaceView()); + testId = testName.getMethodName(); + } + + @After + public void tearDown() { + rule.getScenario().close(); + getInstrumentation() + .runOnMainSync( + () -> { + if (player != null) { + player.release(); + } + if (outputImageReader != null) { + outputImageReader.close(); + } + }); + } + + @Test + public void compositionPlayerPreviewTest_ensuresFirstFrameRenderedCorrectly() throws Exception { + AtomicReference renderedFirstFrameBitmap = new AtomicReference<>(); + ConditionVariable hasRenderedFirstFrameCondition = new ConditionVariable(); + outputImageReader = + ImageReader.newInstance( + MP4_ASSET.videoFormat.width, + MP4_ASSET.videoFormat.height, + PixelFormat.RGBA_8888, + /* maxImages= */ 1); + + getInstrumentation() + .runOnMainSync( + () -> { + player = new CompositionPlayer.Builder(context).build(); + outputImageReader.setOnImageAvailableListener( + imageReader -> { + try (Image image = imageReader.acquireLatestImage()) { + renderedFirstFrameBitmap.set(createArgb8888BitmapFromRgba8888Image(image)); + } + hasRenderedFirstFrameCondition.open(); + }, + Util.createHandlerForCurrentOrMainLooper()); + + player.setVideoSurface( + outputImageReader.getSurface(), + new Size(MP4_ASSET.videoFormat.width, MP4_ASSET.videoFormat.height)); + player.setComposition( + new Composition.Builder( + new EditedMediaItemSequence( + new EditedMediaItem.Builder(MediaItem.fromUri(MP4_ASSET.uri)) + .setEffects( + new Effects( + /* audioProcessors= */ ImmutableList.of(), + /* videoEffects= */ ImmutableList.of( + createTimestampOverlay()))) + .setDurationUs(1_024_000L) + .build())) + .build()); + player.prepare(); + }); + + if (!hasRenderedFirstFrameCondition.block(TEST_TIMEOUT_MS)) { + throw new TimeoutException( + Util.formatInvariant("First frame not rendered in %d ms.", TEST_TIMEOUT_MS)); + } + + assertWithMessage("First frame is not rendered.") + .that(renderedFirstFrameBitmap.get()) + .isNotNull(); + float averagePixelAbsoluteDifference = + getBitmapAveragePixelAbsoluteDifferenceArgb8888( + /* expected= */ readBitmap(TEST_DIRECTORY + "/first_frame.png"), + /* actual= */ renderedFirstFrameBitmap.get(), + testId); + assertThat(averagePixelAbsoluteDifference).isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE); + // TODO: b/315800590 - Verify onFirstFrameRendered is invoked only once. + } +} diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/performance/EffectPlaybackTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/performance/EffectPlaybackPixelTest.java similarity index 99% rename from libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/performance/EffectPlaybackTest.java rename to libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/performance/EffectPlaybackPixelTest.java index 6a4c50421c..ddef815c81 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/performance/EffectPlaybackTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/performance/EffectPlaybackPixelTest.java @@ -70,12 +70,12 @@ import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameter; import org.junit.runners.Parameterized.Parameters; -/** Test for {@link ExoPlayer#setVideoEffects}. */ +/** Pixel tests for {@link ExoPlayer#setVideoEffects}. */ // These tests are in the performance package even though they are not performance tests so that // they are not run on all devices. This is because they use ImageReader, which has a tendency to // drop frames. @RunWith(Enclosed.class) -public class EffectPlaybackTest { +public class EffectPlaybackPixelTest { private static final String TEST_DIRECTORY = "test-generated-goldens/ExoPlayerPlaybackTest"; private static final long TEST_TIMEOUT_MS = 10_000;