Increase the maxImage on ImageReader, hoping less flaky tests
PiperOrigin-RevId: 679528425
This commit is contained in:
parent
b9aed0a937
commit
e0e9f5b057
@ -17,6 +17,7 @@
|
|||||||
package androidx.media3.transformer.mh.performance;
|
package androidx.media3.transformer.mh.performance;
|
||||||
|
|
||||||
import static androidx.media3.common.Player.STATE_ENDED;
|
import static androidx.media3.common.Player.STATE_ENDED;
|
||||||
|
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||||
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
||||||
import static androidx.media3.test.utils.BitmapPixelTestUtil.MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE;
|
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.createArgb8888BitmapFromRgba8888Image;
|
||||||
@ -58,6 +59,7 @@ import androidx.media3.test.utils.BitmapPixelTestUtil;
|
|||||||
import androidx.media3.transformer.SurfaceTestActivity;
|
import androidx.media3.transformer.SurfaceTestActivity;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import androidx.test.platform.app.InstrumentationRegistry;
|
import androidx.test.platform.app.InstrumentationRegistry;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -69,362 +71,321 @@ import org.junit.After;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.experimental.runners.Enclosed;
|
|
||||||
import org.junit.rules.TestName;
|
import org.junit.rules.TestName;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.Parameterized;
|
|
||||||
import org.junit.runners.Parameterized.Parameter;
|
|
||||||
import org.junit.runners.Parameterized.Parameters;
|
|
||||||
|
|
||||||
/** Pixel tests 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
|
// 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
|
// they are not run on all devices. This is because they use ImageReader, which has a tendency to
|
||||||
// drop frames.
|
// drop frames.
|
||||||
@RunWith(Enclosed.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class EffectPlaybackPixelTest {
|
public class EffectPlaybackPixelTest {
|
||||||
|
|
||||||
private static final String TEST_DIRECTORY = "test-generated-goldens/ExoPlayerPlaybackTest";
|
private static final String TEST_DIRECTORY = "test-generated-goldens/ExoPlayerPlaybackTest";
|
||||||
private static final long TEST_TIMEOUT_MS = 10_000;
|
private static final long TEST_TIMEOUT_MS = 10_000;
|
||||||
|
|
||||||
/**
|
/** Playback test for {@link Effect}-enabled playback. */
|
||||||
* The test asserts the first frame is rendered for {@link Player#setPlayWhenReady playWhenReady}
|
@Rule public final TestName testName = new TestName();
|
||||||
* is set to either {@code true} or {@code false}.
|
|
||||||
*/
|
|
||||||
@RunWith(Parameterized.class)
|
|
||||||
public static class RenderedFirstFrameTest {
|
|
||||||
private final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
|
|
||||||
private @MonotonicNonNull ExoPlayer player;
|
|
||||||
private @MonotonicNonNull ImageReader outputImageReader;
|
|
||||||
|
|
||||||
@Parameter public boolean playWhenReady;
|
// Force the test to run in foreground to make it faster and avoid ImageReader frame drops.
|
||||||
|
@Rule
|
||||||
|
public ActivityScenarioRule<SurfaceTestActivity> rule =
|
||||||
|
new ActivityScenarioRule<>(SurfaceTestActivity.class);
|
||||||
|
|
||||||
@Parameters(name = "playWhenReady={0}")
|
private final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
|
||||||
public static ImmutableList<Boolean> parameters() {
|
private @MonotonicNonNull ExoPlayer player;
|
||||||
return ImmutableList.of(true, false);
|
private @MonotonicNonNull ImageReader outputImageReader;
|
||||||
|
private String testId;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
testId = testName.getMethodName();
|
||||||
|
// Setting maxImages=10 ensures image reader gets all rendered frames from
|
||||||
|
// VideoFrameProcessor. Using maxImages=10 runs successfully on a Pixel3.
|
||||||
|
outputImageReader =
|
||||||
|
ImageReader.newInstance(
|
||||||
|
MP4_ASSET.videoFormat.width,
|
||||||
|
MP4_ASSET.videoFormat.height,
|
||||||
|
PixelFormat.RGBA_8888,
|
||||||
|
// Use a larger count to avoid ImageReader dropping frames
|
||||||
|
/* maxImages= */ 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
instrumentation.runOnMainSync(() -> release(player, outputImageReader));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void exoplayerEffectsPreviewTest_playWhenReadySetToFalse_ensuresFirstFrameRendered()
|
||||||
|
throws Exception {
|
||||||
|
AtomicReference<Bitmap> renderedFirstFrameBitmap = new AtomicReference<>();
|
||||||
|
ConditionVariable hasRenderedFirstFrameCondition = new ConditionVariable();
|
||||||
|
|
||||||
|
instrumentation.runOnMainSync(
|
||||||
|
() -> {
|
||||||
|
player = new ExoPlayer.Builder(ApplicationProvider.getApplicationContext()).build();
|
||||||
|
checkStateNotNull(outputImageReader);
|
||||||
|
outputImageReader.setOnImageAvailableListener(
|
||||||
|
imageReader -> {
|
||||||
|
try (Image image = imageReader.acquireLatestImage()) {
|
||||||
|
renderedFirstFrameBitmap.set(createArgb8888BitmapFromRgba8888Image(image));
|
||||||
|
}
|
||||||
|
hasRenderedFirstFrameCondition.open();
|
||||||
|
},
|
||||||
|
Util.createHandlerForCurrentOrMainLooper());
|
||||||
|
|
||||||
|
setOutputSurfaceAndSizeOnPlayer(
|
||||||
|
player,
|
||||||
|
checkNotNull(findVideoRenderer(player)),
|
||||||
|
outputImageReader.getSurface(),
|
||||||
|
new Size(MP4_ASSET.videoFormat.width, MP4_ASSET.videoFormat.height));
|
||||||
|
|
||||||
|
player.setPlayWhenReady(false);
|
||||||
|
player.setVideoEffects(ImmutableList.of(createTimestampOverlay()));
|
||||||
|
|
||||||
|
// Adding an EventLogger to use its log output in case the test fails.
|
||||||
|
player.addAnalyticsListener(new EventLogger());
|
||||||
|
player.setMediaItem(MediaItem.fromUri(MP4_ASSET.uri));
|
||||||
|
player.prepare();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!hasRenderedFirstFrameCondition.block(TEST_TIMEOUT_MS)) {
|
||||||
|
throw new TimeoutException(
|
||||||
|
Util.formatInvariant("First frame not rendered in %d ms.", TEST_TIMEOUT_MS));
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
assertThat(renderedFirstFrameBitmap.get()).isNotNull();
|
||||||
public void tearDown() {
|
float averagePixelAbsoluteDifference =
|
||||||
instrumentation.runOnMainSync(() -> release(player, outputImageReader));
|
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
|
@Test
|
||||||
public void exoplayerEffectsPreviewTest_ensuresFirstFrameRendered() throws Exception {
|
public void exoplayerEffectsPreviewTest_ensuresAllFramesRendered() throws Exception {
|
||||||
String testId =
|
// Internal reference: b/264252759.
|
||||||
Util.formatInvariant(
|
assumeTrue(
|
||||||
"exoplayerEffectsPreviewTest_withPlayWhenReady[%b]_ensuresFirstFrameRendered",
|
"This test should run on real devices because OpenGL to ImageReader rendering is"
|
||||||
playWhenReady);
|
+ "not always reliable on emulators.",
|
||||||
AtomicReference<Bitmap> renderedFirstFrameBitmap = new AtomicReference<>();
|
!Util.isRunningOnEmulator());
|
||||||
ConditionVariable hasRenderedFirstFrameCondition = new ConditionVariable();
|
|
||||||
outputImageReader =
|
|
||||||
ImageReader.newInstance(
|
|
||||||
MP4_ASSET.videoFormat.width,
|
|
||||||
MP4_ASSET.videoFormat.height,
|
|
||||||
PixelFormat.RGBA_8888,
|
|
||||||
/* maxImages= */ 1);
|
|
||||||
|
|
||||||
instrumentation.runOnMainSync(
|
ArrayList<BitmapPixelTestUtil.ImageBuffer> readImageBuffers = new ArrayList<>();
|
||||||
() -> {
|
AtomicInteger renderedFramesCount = new AtomicInteger();
|
||||||
player = new ExoPlayer.Builder(ApplicationProvider.getApplicationContext()).build();
|
ConditionVariable playerEnded = new ConditionVariable();
|
||||||
|
ConditionVariable readAllOutputFrames = new ConditionVariable();
|
||||||
|
|
||||||
checkStateNotNull(outputImageReader);
|
instrumentation.runOnMainSync(
|
||||||
outputImageReader.setOnImageAvailableListener(
|
() -> {
|
||||||
imageReader -> {
|
Context context = ApplicationProvider.getApplicationContext();
|
||||||
try (Image image = imageReader.acquireLatestImage()) {
|
Renderer videoRenderer =
|
||||||
renderedFirstFrameBitmap.set(createArgb8888BitmapFromRgba8888Image(image));
|
new NoFrameDroppedVideoRenderer(context, MediaCodecSelector.DEFAULT);
|
||||||
|
player =
|
||||||
|
new ExoPlayer.Builder(context)
|
||||||
|
.setRenderersFactory(
|
||||||
|
new DefaultRenderersFactory(context) {
|
||||||
|
@Override
|
||||||
|
protected void buildVideoRenderers(
|
||||||
|
Context context,
|
||||||
|
@ExtensionRendererMode int extensionRendererMode,
|
||||||
|
MediaCodecSelector mediaCodecSelector,
|
||||||
|
boolean enableDecoderFallback,
|
||||||
|
Handler eventHandler,
|
||||||
|
VideoRendererEventListener eventListener,
|
||||||
|
long allowedVideoJoiningTimeMs,
|
||||||
|
ArrayList<Renderer> out) {
|
||||||
|
out.add(videoRenderer);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
|
||||||
|
checkStateNotNull(outputImageReader);
|
||||||
|
outputImageReader.setOnImageAvailableListener(
|
||||||
|
imageReader -> {
|
||||||
|
try (Image image = imageReader.acquireNextImage()) {
|
||||||
|
readImageBuffers.add(BitmapPixelTestUtil.copyByteBufferFromRbga8888Image(image));
|
||||||
|
}
|
||||||
|
if (renderedFramesCount.incrementAndGet() == MP4_ASSET.videoFrameCount) {
|
||||||
|
readAllOutputFrames.open();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Util.createHandlerForCurrentOrMainLooper());
|
||||||
|
|
||||||
|
setOutputSurfaceAndSizeOnPlayer(
|
||||||
|
player,
|
||||||
|
videoRenderer,
|
||||||
|
outputImageReader.getSurface(),
|
||||||
|
new Size(MP4_ASSET.videoFormat.width, MP4_ASSET.videoFormat.height));
|
||||||
|
player.setPlayWhenReady(true);
|
||||||
|
player.setVideoEffects(ImmutableList.of(createTimestampOverlay()));
|
||||||
|
|
||||||
|
// Adding an EventLogger to use its log output in case the test fails.
|
||||||
|
player.addAnalyticsListener(new EventLogger());
|
||||||
|
player.addListener(
|
||||||
|
new Player.Listener() {
|
||||||
|
@Override
|
||||||
|
public void onPlaybackStateChanged(@Player.State int playbackState) {
|
||||||
|
if (playbackState == STATE_ENDED) {
|
||||||
|
playerEnded.open();
|
||||||
}
|
}
|
||||||
hasRenderedFirstFrameCondition.open();
|
}
|
||||||
},
|
});
|
||||||
Util.createHandlerForCurrentOrMainLooper());
|
player.setMediaItem(MediaItem.fromUri(MP4_ASSET.uri));
|
||||||
|
player.prepare();
|
||||||
|
});
|
||||||
|
|
||||||
setOutputSurfaceAndSizeOnPlayer(
|
if (!playerEnded.block(TEST_TIMEOUT_MS)) {
|
||||||
player,
|
throw new TimeoutException(
|
||||||
findVideoRenderer(player),
|
Util.formatInvariant("Playback not ended in %d ms.", TEST_TIMEOUT_MS));
|
||||||
outputImageReader.getSurface(),
|
}
|
||||||
new Size(MP4_ASSET.videoFormat.width, MP4_ASSET.videoFormat.height));
|
|
||||||
|
|
||||||
player.setPlayWhenReady(playWhenReady);
|
if (!readAllOutputFrames.block(TEST_TIMEOUT_MS)) {
|
||||||
player.setVideoEffects(ImmutableList.of(createTimestampOverlay()));
|
throw new TimeoutException(
|
||||||
|
Util.formatInvariant(
|
||||||
|
"Haven't received all frames in %d ms after playback ends.", TEST_TIMEOUT_MS));
|
||||||
|
}
|
||||||
|
|
||||||
// Adding an EventLogger to use its log output in case the test fails.
|
ArrayList<Float> averagePixelDifferences =
|
||||||
player.addAnalyticsListener(new EventLogger());
|
new ArrayList<>(/* initialCapacity= */ readImageBuffers.size());
|
||||||
player.setMediaItem(MediaItem.fromUri(MP4_ASSET.uri));
|
for (int i = 0; i < readImageBuffers.size(); i++) {
|
||||||
player.prepare();
|
Bitmap actualBitmap = createArgb8888BitmapFromRgba8888ImageBuffer(readImageBuffers.get(i));
|
||||||
});
|
|
||||||
|
|
||||||
if (!hasRenderedFirstFrameCondition.block(TEST_TIMEOUT_MS)) {
|
|
||||||
throw new TimeoutException(
|
|
||||||
Util.formatInvariant("First frame not rendered in %d ms.", TEST_TIMEOUT_MS));
|
|
||||||
}
|
|
||||||
|
|
||||||
assertThat(renderedFirstFrameBitmap.get()).isNotNull();
|
|
||||||
float averagePixelAbsoluteDifference =
|
float averagePixelAbsoluteDifference =
|
||||||
getBitmapAveragePixelAbsoluteDifferenceArgb8888(
|
getBitmapAveragePixelAbsoluteDifferenceArgb8888(
|
||||||
/* expected= */ readBitmap(TEST_DIRECTORY + "/first_frame.png"),
|
/* expected= */ readBitmap(
|
||||||
/* actual= */ renderedFirstFrameBitmap.get(),
|
Util.formatInvariant("%s/%s/frame_%d.png", TEST_DIRECTORY, testId, i)),
|
||||||
testId);
|
/* actual= */ actualBitmap,
|
||||||
assertThat(averagePixelAbsoluteDifference)
|
/* testId= */ Util.formatInvariant("%s_frame_%d", testId, i));
|
||||||
|
averagePixelDifferences.add(averagePixelAbsoluteDifference);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < averagePixelDifferences.size(); i++) {
|
||||||
|
float averagePixelDifference = averagePixelDifferences.get(i);
|
||||||
|
assertWithMessage(
|
||||||
|
Util.formatInvariant(
|
||||||
|
"Frame %d with average pixel difference %f. ", i, averagePixelDifference))
|
||||||
|
.that(averagePixelDifference)
|
||||||
.isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE);
|
.isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE);
|
||||||
// TODO: b/315800590 - Verify onFirstFrameRendered is invoked only once.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Playback test for {@link Effect}-enabled playback. */
|
@Test
|
||||||
public static class PlaybackTest {
|
public void exoplayerEffectsPreview_withTimestampWrapper_ensuresAllFramesRendered()
|
||||||
@Rule public final TestName testName = new TestName();
|
throws Exception {
|
||||||
|
// Internal reference: b/264252759.
|
||||||
|
assumeTrue(
|
||||||
|
"This test should run on real devices because OpenGL to ImageReader rendering is"
|
||||||
|
+ "not always reliable on emulators.",
|
||||||
|
!Util.isRunningOnEmulator());
|
||||||
|
|
||||||
// Force the test to run in foreground to make it faster and avoid ImageReader frame drops.
|
ArrayList<BitmapPixelTestUtil.ImageBuffer> readImageBuffers = new ArrayList<>();
|
||||||
@Rule
|
AtomicInteger renderedFramesCount = new AtomicInteger();
|
||||||
public ActivityScenarioRule<SurfaceTestActivity> rule =
|
ConditionVariable playerEnded = new ConditionVariable();
|
||||||
new ActivityScenarioRule<>(SurfaceTestActivity.class);
|
ConditionVariable readAllOutputFrames = new ConditionVariable();
|
||||||
|
// Setting maxImages=10 ensures image reader gets all rendered frames from
|
||||||
|
// VideoFrameProcessor. Using maxImages=10 runs successfully on a Pixel3.
|
||||||
|
outputImageReader =
|
||||||
|
ImageReader.newInstance(
|
||||||
|
MP4_ASSET.videoFormat.width,
|
||||||
|
MP4_ASSET.videoFormat.height,
|
||||||
|
PixelFormat.RGBA_8888,
|
||||||
|
/* maxImages= */ 10);
|
||||||
|
|
||||||
private final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
|
instrumentation.runOnMainSync(
|
||||||
private @MonotonicNonNull ExoPlayer player;
|
() -> {
|
||||||
private @MonotonicNonNull ImageReader outputImageReader;
|
Context context = ApplicationProvider.getApplicationContext();
|
||||||
private String testId;
|
Renderer videoRenderer =
|
||||||
|
new NoFrameDroppedVideoRenderer(context, MediaCodecSelector.DEFAULT);
|
||||||
|
player =
|
||||||
|
new ExoPlayer.Builder(context)
|
||||||
|
.setRenderersFactory(
|
||||||
|
new DefaultRenderersFactory(context) {
|
||||||
|
@Override
|
||||||
|
protected void buildVideoRenderers(
|
||||||
|
Context context,
|
||||||
|
@ExtensionRendererMode int extensionRendererMode,
|
||||||
|
MediaCodecSelector mediaCodecSelector,
|
||||||
|
boolean enableDecoderFallback,
|
||||||
|
Handler eventHandler,
|
||||||
|
VideoRendererEventListener eventListener,
|
||||||
|
long allowedVideoJoiningTimeMs,
|
||||||
|
ArrayList<Renderer> out) {
|
||||||
|
out.add(videoRenderer);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
|
||||||
@Before
|
checkStateNotNull(outputImageReader);
|
||||||
public void setUpTestId() {
|
outputImageReader.setOnImageAvailableListener(
|
||||||
testId = testName.getMethodName();
|
imageReader -> {
|
||||||
|
try (Image image = imageReader.acquireNextImage()) {
|
||||||
|
readImageBuffers.add(BitmapPixelTestUtil.copyByteBufferFromRbga8888Image(image));
|
||||||
|
}
|
||||||
|
if (renderedFramesCount.incrementAndGet() == MP4_ASSET.videoFrameCount) {
|
||||||
|
readAllOutputFrames.open();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Util.createHandlerForCurrentOrMainLooper());
|
||||||
|
|
||||||
|
setOutputSurfaceAndSizeOnPlayer(
|
||||||
|
player,
|
||||||
|
videoRenderer,
|
||||||
|
outputImageReader.getSurface(),
|
||||||
|
new Size(MP4_ASSET.videoFormat.width, MP4_ASSET.videoFormat.height));
|
||||||
|
player.setPlayWhenReady(true);
|
||||||
|
player.setVideoEffects(
|
||||||
|
ImmutableList.of(
|
||||||
|
new TimestampWrapper(
|
||||||
|
new Brightness(0.5f), /* startTimeUs= */ 166833, /* endTimeUs= */ 510000)));
|
||||||
|
|
||||||
|
// Adding an EventLogger to use its log output in case the test fails.
|
||||||
|
player.addAnalyticsListener(new EventLogger());
|
||||||
|
player.addListener(
|
||||||
|
new Player.Listener() {
|
||||||
|
@Override
|
||||||
|
public void onPlaybackStateChanged(@Player.State int playbackState) {
|
||||||
|
if (playbackState == STATE_ENDED) {
|
||||||
|
playerEnded.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
player.setMediaItem(MediaItem.fromUri(MP4_ASSET.uri));
|
||||||
|
player.prepare();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!playerEnded.block(TEST_TIMEOUT_MS)) {
|
||||||
|
throw new TimeoutException(
|
||||||
|
Util.formatInvariant("Playback not ended in %d ms.", TEST_TIMEOUT_MS));
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
if (!readAllOutputFrames.block(TEST_TIMEOUT_MS)) {
|
||||||
public void tearDown() {
|
throw new TimeoutException(
|
||||||
instrumentation.runOnMainSync(() -> release(player, outputImageReader));
|
Util.formatInvariant(
|
||||||
|
"Haven't received all frames in %d ms after playback ends.", TEST_TIMEOUT_MS));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
ArrayList<Float> averagePixelDifferences =
|
||||||
public void exoplayerEffectsPreviewTest_ensuresAllFramesRendered() throws Exception {
|
new ArrayList<>(/* initialCapacity= */ readImageBuffers.size());
|
||||||
// Internal reference: b/264252759.
|
for (int i = 0; i < readImageBuffers.size(); i++) {
|
||||||
assumeTrue(
|
Bitmap actualBitmap = createArgb8888BitmapFromRgba8888ImageBuffer(readImageBuffers.get(i));
|
||||||
"This test should run on real devices because OpenGL to ImageReader rendering is"
|
float averagePixelAbsoluteDifference =
|
||||||
+ "not always reliable on emulators.",
|
getBitmapAveragePixelAbsoluteDifferenceArgb8888(
|
||||||
!Util.isRunningOnEmulator());
|
/* expected= */ readBitmap(
|
||||||
|
Util.formatInvariant("%s/%s/frame_%d.png", TEST_DIRECTORY, testId, i)),
|
||||||
ArrayList<BitmapPixelTestUtil.ImageBuffer> readImageBuffers = new ArrayList<>();
|
/* actual= */ actualBitmap,
|
||||||
AtomicInteger renderedFramesCount = new AtomicInteger();
|
/* testId= */ Util.formatInvariant("%s_frame_%d", testId, i));
|
||||||
ConditionVariable playerEnded = new ConditionVariable();
|
averagePixelDifferences.add(averagePixelAbsoluteDifference);
|
||||||
ConditionVariable readAllOutputFrames = new ConditionVariable();
|
|
||||||
// Setting maxImages=10 ensures image reader gets all rendered frames from
|
|
||||||
// VideoFrameProcessor. Using maxImages=10 runs successfully on a Pixel3.
|
|
||||||
outputImageReader =
|
|
||||||
ImageReader.newInstance(
|
|
||||||
MP4_ASSET.videoFormat.width,
|
|
||||||
MP4_ASSET.videoFormat.height,
|
|
||||||
PixelFormat.RGBA_8888,
|
|
||||||
/* maxImages= */ 10);
|
|
||||||
|
|
||||||
instrumentation.runOnMainSync(
|
|
||||||
() -> {
|
|
||||||
Context context = ApplicationProvider.getApplicationContext();
|
|
||||||
Renderer videoRenderer =
|
|
||||||
new NoFrameDroppedVideoRenderer(context, MediaCodecSelector.DEFAULT);
|
|
||||||
player =
|
|
||||||
new ExoPlayer.Builder(context)
|
|
||||||
.setRenderersFactory(
|
|
||||||
new DefaultRenderersFactory(context) {
|
|
||||||
@Override
|
|
||||||
protected void buildVideoRenderers(
|
|
||||||
Context context,
|
|
||||||
@ExtensionRendererMode int extensionRendererMode,
|
|
||||||
MediaCodecSelector mediaCodecSelector,
|
|
||||||
boolean enableDecoderFallback,
|
|
||||||
Handler eventHandler,
|
|
||||||
VideoRendererEventListener eventListener,
|
|
||||||
long allowedVideoJoiningTimeMs,
|
|
||||||
ArrayList<Renderer> out) {
|
|
||||||
out.add(videoRenderer);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.build();
|
|
||||||
|
|
||||||
checkStateNotNull(outputImageReader);
|
|
||||||
outputImageReader.setOnImageAvailableListener(
|
|
||||||
imageReader -> {
|
|
||||||
try (Image image = imageReader.acquireNextImage()) {
|
|
||||||
readImageBuffers.add(
|
|
||||||
BitmapPixelTestUtil.copyByteBufferFromRbga8888Image(image));
|
|
||||||
}
|
|
||||||
if (renderedFramesCount.incrementAndGet() == MP4_ASSET.videoFrameCount) {
|
|
||||||
readAllOutputFrames.open();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Util.createHandlerForCurrentOrMainLooper());
|
|
||||||
|
|
||||||
setOutputSurfaceAndSizeOnPlayer(
|
|
||||||
player,
|
|
||||||
videoRenderer,
|
|
||||||
outputImageReader.getSurface(),
|
|
||||||
new Size(MP4_ASSET.videoFormat.width, MP4_ASSET.videoFormat.height));
|
|
||||||
player.setPlayWhenReady(true);
|
|
||||||
player.setVideoEffects(ImmutableList.of(createTimestampOverlay()));
|
|
||||||
|
|
||||||
// Adding an EventLogger to use its log output in case the test fails.
|
|
||||||
player.addAnalyticsListener(new EventLogger());
|
|
||||||
player.addListener(
|
|
||||||
new Player.Listener() {
|
|
||||||
@Override
|
|
||||||
public void onPlaybackStateChanged(@Player.State int playbackState) {
|
|
||||||
if (playbackState == STATE_ENDED) {
|
|
||||||
playerEnded.open();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
player.setMediaItem(MediaItem.fromUri(MP4_ASSET.uri));
|
|
||||||
player.prepare();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!playerEnded.block(TEST_TIMEOUT_MS)) {
|
|
||||||
throw new TimeoutException(
|
|
||||||
Util.formatInvariant("Playback not ended in %d ms.", TEST_TIMEOUT_MS));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!readAllOutputFrames.block(TEST_TIMEOUT_MS)) {
|
|
||||||
throw new TimeoutException(
|
|
||||||
Util.formatInvariant(
|
|
||||||
"Haven't received all frames in %d ms after playback ends.", TEST_TIMEOUT_MS));
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayList<Float> averagePixelDifferences =
|
|
||||||
new ArrayList<>(/* initialCapacity= */ readImageBuffers.size());
|
|
||||||
for (int i = 0; i < readImageBuffers.size(); i++) {
|
|
||||||
Bitmap actualBitmap = createArgb8888BitmapFromRgba8888ImageBuffer(readImageBuffers.get(i));
|
|
||||||
float averagePixelAbsoluteDifference =
|
|
||||||
getBitmapAveragePixelAbsoluteDifferenceArgb8888(
|
|
||||||
/* expected= */ readBitmap(
|
|
||||||
Util.formatInvariant("%s/%s/frame_%d.png", TEST_DIRECTORY, testId, i)),
|
|
||||||
/* actual= */ actualBitmap,
|
|
||||||
/* testId= */ Util.formatInvariant("%s_frame_%d", testId, i));
|
|
||||||
averagePixelDifferences.add(averagePixelAbsoluteDifference);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < averagePixelDifferences.size(); i++) {
|
|
||||||
float averagePixelDifference = averagePixelDifferences.get(i);
|
|
||||||
assertWithMessage(
|
|
||||||
Util.formatInvariant(
|
|
||||||
"Frame %d with average pixel difference %f. ", i, averagePixelDifference))
|
|
||||||
.that(averagePixelDifference)
|
|
||||||
.isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
for (int i = 0; i < averagePixelDifferences.size(); i++) {
|
||||||
public void exoplayerEffectsPreview_withTimestampWrapper_ensuresAllFramesRendered()
|
float averagePixelDifference = averagePixelDifferences.get(i);
|
||||||
throws Exception {
|
assertWithMessage(
|
||||||
// Internal reference: b/264252759.
|
Util.formatInvariant(
|
||||||
assumeTrue(
|
"Frame %d with average pixel difference %f. ", i, averagePixelDifference))
|
||||||
"This test should run on real devices because OpenGL to ImageReader rendering is"
|
.that(averagePixelDifference)
|
||||||
+ "not always reliable on emulators.",
|
.isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE);
|
||||||
!Util.isRunningOnEmulator());
|
|
||||||
|
|
||||||
ArrayList<BitmapPixelTestUtil.ImageBuffer> readImageBuffers = new ArrayList<>();
|
|
||||||
AtomicInteger renderedFramesCount = new AtomicInteger();
|
|
||||||
ConditionVariable playerEnded = new ConditionVariable();
|
|
||||||
ConditionVariable readAllOutputFrames = new ConditionVariable();
|
|
||||||
// Setting maxImages=10 ensures image reader gets all rendered frames from
|
|
||||||
// VideoFrameProcessor. Using maxImages=10 runs successfully on a Pixel3.
|
|
||||||
outputImageReader =
|
|
||||||
ImageReader.newInstance(
|
|
||||||
MP4_ASSET.videoFormat.width,
|
|
||||||
MP4_ASSET.videoFormat.height,
|
|
||||||
PixelFormat.RGBA_8888,
|
|
||||||
/* maxImages= */ 10);
|
|
||||||
|
|
||||||
instrumentation.runOnMainSync(
|
|
||||||
() -> {
|
|
||||||
Context context = ApplicationProvider.getApplicationContext();
|
|
||||||
Renderer videoRenderer =
|
|
||||||
new NoFrameDroppedVideoRenderer(context, MediaCodecSelector.DEFAULT);
|
|
||||||
player =
|
|
||||||
new ExoPlayer.Builder(context)
|
|
||||||
.setRenderersFactory(
|
|
||||||
new DefaultRenderersFactory(context) {
|
|
||||||
@Override
|
|
||||||
protected void buildVideoRenderers(
|
|
||||||
Context context,
|
|
||||||
@ExtensionRendererMode int extensionRendererMode,
|
|
||||||
MediaCodecSelector mediaCodecSelector,
|
|
||||||
boolean enableDecoderFallback,
|
|
||||||
Handler eventHandler,
|
|
||||||
VideoRendererEventListener eventListener,
|
|
||||||
long allowedVideoJoiningTimeMs,
|
|
||||||
ArrayList<Renderer> out) {
|
|
||||||
out.add(videoRenderer);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.build();
|
|
||||||
|
|
||||||
checkStateNotNull(outputImageReader);
|
|
||||||
outputImageReader.setOnImageAvailableListener(
|
|
||||||
imageReader -> {
|
|
||||||
try (Image image = imageReader.acquireNextImage()) {
|
|
||||||
readImageBuffers.add(
|
|
||||||
BitmapPixelTestUtil.copyByteBufferFromRbga8888Image(image));
|
|
||||||
}
|
|
||||||
if (renderedFramesCount.incrementAndGet() == MP4_ASSET.videoFrameCount) {
|
|
||||||
readAllOutputFrames.open();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Util.createHandlerForCurrentOrMainLooper());
|
|
||||||
|
|
||||||
setOutputSurfaceAndSizeOnPlayer(
|
|
||||||
player,
|
|
||||||
videoRenderer,
|
|
||||||
outputImageReader.getSurface(),
|
|
||||||
new Size(MP4_ASSET.videoFormat.width, MP4_ASSET.videoFormat.height));
|
|
||||||
player.setPlayWhenReady(true);
|
|
||||||
player.setVideoEffects(
|
|
||||||
ImmutableList.of(
|
|
||||||
new TimestampWrapper(
|
|
||||||
new Brightness(0.5f), /* startTimeUs= */ 166833, /* endTimeUs= */ 510000)));
|
|
||||||
|
|
||||||
// Adding an EventLogger to use its log output in case the test fails.
|
|
||||||
player.addAnalyticsListener(new EventLogger());
|
|
||||||
player.addListener(
|
|
||||||
new Player.Listener() {
|
|
||||||
@Override
|
|
||||||
public void onPlaybackStateChanged(@Player.State int playbackState) {
|
|
||||||
if (playbackState == STATE_ENDED) {
|
|
||||||
playerEnded.open();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
player.setMediaItem(MediaItem.fromUri(MP4_ASSET.uri));
|
|
||||||
player.prepare();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!playerEnded.block(TEST_TIMEOUT_MS)) {
|
|
||||||
throw new TimeoutException(
|
|
||||||
Util.formatInvariant("Playback not ended in %d ms.", TEST_TIMEOUT_MS));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!readAllOutputFrames.block(TEST_TIMEOUT_MS)) {
|
|
||||||
throw new TimeoutException(
|
|
||||||
Util.formatInvariant(
|
|
||||||
"Haven't received all frames in %d ms after playback ends.", TEST_TIMEOUT_MS));
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayList<Float> averagePixelDifferences =
|
|
||||||
new ArrayList<>(/* initialCapacity= */ readImageBuffers.size());
|
|
||||||
for (int i = 0; i < readImageBuffers.size(); i++) {
|
|
||||||
Bitmap actualBitmap = createArgb8888BitmapFromRgba8888ImageBuffer(readImageBuffers.get(i));
|
|
||||||
float averagePixelAbsoluteDifference =
|
|
||||||
getBitmapAveragePixelAbsoluteDifferenceArgb8888(
|
|
||||||
/* expected= */ readBitmap(
|
|
||||||
Util.formatInvariant("%s/%s/frame_%d.png", TEST_DIRECTORY, testId, i)),
|
|
||||||
/* actual= */ actualBitmap,
|
|
||||||
/* testId= */ Util.formatInvariant("%s_frame_%d", testId, i));
|
|
||||||
averagePixelDifferences.add(averagePixelAbsoluteDifference);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < averagePixelDifferences.size(); i++) {
|
|
||||||
float averagePixelDifference = averagePixelDifferences.get(i);
|
|
||||||
assertWithMessage(
|
|
||||||
Util.formatInvariant(
|
|
||||||
"Frame %d with average pixel difference %f. ", i, averagePixelDifference))
|
|
||||||
.that(averagePixelDifference)
|
|
||||||
.isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user