mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Improve tests to use output surfaces more realistically
Prior to this change, there were some unrealistic quirks in our Robolectric tests. For example, onRenderedFirstFrame would be called when using FakeVideoRenderer, despite no output to render the frame to ever being set. This change improves the realism of these tests. These changes are required for some improvements being made to how outputs are set on video renderers. PiperOrigin-RevId: 367652169
This commit is contained in:
parent
253c8ce2ad
commit
e499f6d38c
6 changed files with 86 additions and 33 deletions
|
|
@ -26,6 +26,8 @@ import static org.mockito.Mockito.atLeastOnce;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
import android.graphics.SurfaceTexture;
|
||||||
|
import android.view.Surface;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import com.google.android.exoplayer2.analytics.AnalyticsListener;
|
import com.google.android.exoplayer2.analytics.AnalyticsListener;
|
||||||
|
|
@ -87,6 +89,7 @@ public class SimpleExoPlayerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void releaseAfterRendererEvents_triggersPendingVideoEventsInListener() throws Exception {
|
public void releaseAfterRendererEvents_triggersPendingVideoEventsInListener() throws Exception {
|
||||||
|
Surface surface = new Surface(new SurfaceTexture(/* texName= */ 0));
|
||||||
SimpleExoPlayer player =
|
SimpleExoPlayer player =
|
||||||
new SimpleExoPlayer.Builder(
|
new SimpleExoPlayer.Builder(
|
||||||
ApplicationProvider.getApplicationContext(),
|
ApplicationProvider.getApplicationContext(),
|
||||||
|
|
@ -98,11 +101,13 @@ public class SimpleExoPlayerTest {
|
||||||
player.addListener(listener);
|
player.addListener(listener);
|
||||||
player.setMediaSource(
|
player.setMediaSource(
|
||||||
new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.VIDEO_FORMAT));
|
new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.VIDEO_FORMAT));
|
||||||
|
player.setVideoSurface(surface);
|
||||||
player.prepare();
|
player.prepare();
|
||||||
player.play();
|
player.play();
|
||||||
runUntilPlaybackState(player, Player.STATE_READY);
|
runUntilPlaybackState(player, Player.STATE_READY);
|
||||||
|
|
||||||
player.release();
|
player.release();
|
||||||
|
surface.release();
|
||||||
ShadowLooper.runMainLooperToNextTask();
|
ShadowLooper.runMainLooperToNextTask();
|
||||||
|
|
||||||
verify(listener, atLeastOnce()).onEvents(any(), any()); // EventListener
|
verify(listener, atLeastOnce()).onEvents(any(), any()); // EventListener
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,7 @@ import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
import android.graphics.SurfaceTexture;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
|
|
@ -1638,6 +1639,9 @@ public final class AnalyticsCollectorTest {
|
||||||
public void onEvents_isReportedWithCorrectEventTimes() throws Exception {
|
public void onEvents_isReportedWithCorrectEventTimes() throws Exception {
|
||||||
SimpleExoPlayer player =
|
SimpleExoPlayer player =
|
||||||
new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext()).build();
|
new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext()).build();
|
||||||
|
Surface surface = new Surface(new SurfaceTexture(/* texName= */ 0));
|
||||||
|
player.setVideoSurface(surface);
|
||||||
|
|
||||||
AnalyticsListener listener = mock(AnalyticsListener.class);
|
AnalyticsListener listener = mock(AnalyticsListener.class);
|
||||||
Format[] formats =
|
Format[] formats =
|
||||||
new Format[] {
|
new Format[] {
|
||||||
|
|
@ -1663,6 +1667,7 @@ public final class AnalyticsCollectorTest {
|
||||||
TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_IDLE);
|
TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_IDLE);
|
||||||
ShadowLooper.runMainLooperToNextTask();
|
ShadowLooper.runMainLooperToNextTask();
|
||||||
player.release();
|
player.release();
|
||||||
|
surface.release();
|
||||||
|
|
||||||
// Verify that expected individual callbacks have been called and capture EventTimes.
|
// Verify that expected individual callbacks have been called and capture EventTimes.
|
||||||
ArgumentCaptor<AnalyticsListener.EventTime> individualTimelineChangedEventTimes =
|
ArgumentCaptor<AnalyticsListener.EventTime> individualTimelineChangedEventTimes =
|
||||||
|
|
@ -1982,11 +1987,13 @@ public final class AnalyticsCollectorTest {
|
||||||
@Nullable ActionSchedule actionSchedule,
|
@Nullable ActionSchedule actionSchedule,
|
||||||
RenderersFactory renderersFactory)
|
RenderersFactory renderersFactory)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
Surface surface = new Surface(new SurfaceTexture(/* texName= */ 0));
|
||||||
TestAnalyticsListener listener = new TestAnalyticsListener();
|
TestAnalyticsListener listener = new TestAnalyticsListener();
|
||||||
try {
|
try {
|
||||||
new ExoPlayerTestRunner.Builder(ApplicationProvider.getApplicationContext())
|
new ExoPlayerTestRunner.Builder(ApplicationProvider.getApplicationContext())
|
||||||
.setMediaSources(mediaSource)
|
.setMediaSources(mediaSource)
|
||||||
.setRenderersFactory(renderersFactory)
|
.setRenderersFactory(renderersFactory)
|
||||||
|
.setVideoSurface(surface)
|
||||||
.setAnalyticsListener(listener)
|
.setAnalyticsListener(listener)
|
||||||
.setActionSchedule(actionSchedule)
|
.setActionSchedule(actionSchedule)
|
||||||
.build()
|
.build()
|
||||||
|
|
@ -1995,6 +2002,8 @@ public final class AnalyticsCollectorTest {
|
||||||
.blockUntilEnded(TIMEOUT_MS);
|
.blockUntilEnded(TIMEOUT_MS);
|
||||||
} catch (ExoPlaybackException e) {
|
} catch (ExoPlaybackException e) {
|
||||||
// Ignore ExoPlaybackException as these may be expected.
|
// Ignore ExoPlaybackException as these may be expected.
|
||||||
|
} finally {
|
||||||
|
surface.release();
|
||||||
}
|
}
|
||||||
return listener;
|
return listener;
|
||||||
}
|
}
|
||||||
|
|
@ -2358,12 +2367,7 @@ public final class AnalyticsCollectorTest {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "{"
|
return "{" + "type=" + eventType + ", windowAndPeriodId=" + eventWindowAndPeriodId + '}';
|
||||||
+ "type="
|
|
||||||
+ Long.numberOfTrailingZeros(eventType)
|
|
||||||
+ ", windowAndPeriodId="
|
|
||||||
+ eventWindowAndPeriodId
|
|
||||||
+ '}';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2375,14 +2379,12 @@ public final class AnalyticsCollectorTest {
|
||||||
*/
|
*/
|
||||||
private static final class EmptyDrmCallback implements MediaDrmCallback {
|
private static final class EmptyDrmCallback implements MediaDrmCallback {
|
||||||
@Override
|
@Override
|
||||||
public byte[] executeProvisionRequest(UUID uuid, ExoMediaDrm.ProvisionRequest request)
|
public byte[] executeProvisionRequest(UUID uuid, ExoMediaDrm.ProvisionRequest request) {
|
||||||
throws MediaDrmCallbackException {
|
|
||||||
return new byte[0];
|
return new byte[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] executeKeyRequest(UUID uuid, ExoMediaDrm.KeyRequest request)
|
public byte[] executeKeyRequest(UUID uuid, ExoMediaDrm.KeyRequest request) {
|
||||||
throws MediaDrmCallbackException {
|
|
||||||
return new byte[0];
|
return new byte[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,11 +67,13 @@ public final class DecoderVideoRendererTest {
|
||||||
.setHeight(1080)
|
.setHeight(1080)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
private Surface surface;
|
||||||
private DecoderVideoRenderer renderer;
|
private DecoderVideoRenderer renderer;
|
||||||
@Mock private VideoRendererEventListener eventListener;
|
@Mock private VideoRendererEventListener eventListener;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
|
surface = new Surface(new SurfaceTexture(/* texName= */ 0));
|
||||||
renderer =
|
renderer =
|
||||||
new DecoderVideoRenderer(
|
new DecoderVideoRenderer(
|
||||||
/* allowedJoiningTimeMs= */ 0,
|
/* allowedJoiningTimeMs= */ 0,
|
||||||
|
|
@ -168,17 +170,18 @@ public final class DecoderVideoRendererTest {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
renderer.setOutputSurface(new Surface(new SurfaceTexture(/* texName= */ 0)));
|
renderer.setOutputSurface(surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void shutDown() throws Exception {
|
public void shutDown() {
|
||||||
if (renderer.getState() == Renderer.STATE_STARTED) {
|
if (renderer.getState() == Renderer.STATE_STARTED) {
|
||||||
renderer.stop();
|
renderer.stop();
|
||||||
}
|
}
|
||||||
if (renderer.getState() == Renderer.STATE_ENABLED) {
|
if (renderer.getState() == Renderer.STATE_ENABLED) {
|
||||||
renderer.disable();
|
renderer.disable();
|
||||||
}
|
}
|
||||||
|
surface.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSample
|
||||||
import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem.format;
|
import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem.format;
|
||||||
import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem.oneByteSample;
|
import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSampleStreamItem.oneByteSample;
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
|
||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
import static org.mockito.ArgumentMatchers.anyLong;
|
import static org.mockito.ArgumentMatchers.anyLong;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
|
@ -52,6 +51,7 @@ import com.google.android.exoplayer2.upstream.DefaultAllocator;
|
||||||
import com.google.android.exoplayer2.util.MimeTypes;
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
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;
|
||||||
|
|
@ -75,6 +75,7 @@ public class MediaCodecVideoRendererTest {
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
private Looper testMainLooper;
|
private Looper testMainLooper;
|
||||||
|
private Surface surface;
|
||||||
private MediaCodecVideoRenderer mediaCodecVideoRenderer;
|
private MediaCodecVideoRenderer mediaCodecVideoRenderer;
|
||||||
@Nullable private Format currentOutputFormat;
|
@Nullable private Format currentOutputFormat;
|
||||||
|
|
||||||
|
|
@ -118,8 +119,13 @@ public class MediaCodecVideoRendererTest {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
mediaCodecVideoRenderer.handleMessage(
|
surface = new Surface(new SurfaceTexture(/* texName= */ 0));
|
||||||
Renderer.MSG_SET_SURFACE, new Surface(new SurfaceTexture(/* texName= */ 0)));
|
mediaCodecVideoRenderer.handleMessage(Renderer.MSG_SET_SURFACE, surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void cleanUp() {
|
||||||
|
surface.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -323,7 +329,7 @@ public class MediaCodecVideoRendererTest {
|
||||||
}
|
}
|
||||||
shadowOf(testMainLooper).idle();
|
shadowOf(testMainLooper).idle();
|
||||||
|
|
||||||
verify(eventListener).onRenderedFirstFrame(any());
|
verify(eventListener).onRenderedFirstFrame(eq(surface), /* renderTimeMs= */ anyLong());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -353,7 +359,7 @@ public class MediaCodecVideoRendererTest {
|
||||||
}
|
}
|
||||||
shadowOf(testMainLooper).idle();
|
shadowOf(testMainLooper).idle();
|
||||||
|
|
||||||
verify(eventListener, never()).onRenderedFirstFrame(any());
|
verify(eventListener, never()).onRenderedFirstFrame(eq(surface), /* renderTimeMs= */ anyLong());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -383,7 +389,7 @@ public class MediaCodecVideoRendererTest {
|
||||||
}
|
}
|
||||||
shadowOf(testMainLooper).idle();
|
shadowOf(testMainLooper).idle();
|
||||||
|
|
||||||
verify(eventListener).onRenderedFirstFrame(any());
|
verify(eventListener).onRenderedFirstFrame(eq(surface), /* renderTimeMs= */ anyLong());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -437,7 +443,8 @@ public class MediaCodecVideoRendererTest {
|
||||||
|
|
||||||
// Expect only the first frame of the first stream to have been rendered.
|
// Expect only the first frame of the first stream to have been rendered.
|
||||||
shadowLooper.idle();
|
shadowLooper.idle();
|
||||||
verify(eventListener, times(2)).onRenderedFirstFrame(any());
|
verify(eventListener, times(2))
|
||||||
|
.onRenderedFirstFrame(eq(surface), /* renderTimeMs= */ anyLong());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -488,12 +495,13 @@ public class MediaCodecVideoRendererTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
shadowLooper.idle();
|
shadowLooper.idle();
|
||||||
verify(eventListener).onRenderedFirstFrame(any());
|
verify(eventListener).onRenderedFirstFrame(eq(surface), /* renderTimeMs= */ anyLong());
|
||||||
|
|
||||||
// Render to streamOffsetUs and verify the new first frame gets rendered.
|
// Render to streamOffsetUs and verify the new first frame gets rendered.
|
||||||
mediaCodecVideoRenderer.render(/* positionUs= */ 100, SystemClock.elapsedRealtime() * 1000);
|
mediaCodecVideoRenderer.render(/* positionUs= */ 100, SystemClock.elapsedRealtime() * 1000);
|
||||||
|
|
||||||
shadowLooper.idle();
|
shadowLooper.idle();
|
||||||
verify(eventListener, times(2)).onRenderedFirstFrame(any());
|
verify(eventListener, times(2))
|
||||||
|
.onRenderedFirstFrame(eq(surface), /* renderTimeMs= */ anyLong());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import static junit.framework.TestCase.assertFalse;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.HandlerThread;
|
import android.os.HandlerThread;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
import android.view.Surface;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||||
|
|
@ -80,6 +81,7 @@ public final class ExoPlayerTestRunner implements Player.EventListener, ActionSc
|
||||||
private Format[] supportedFormats;
|
private Format[] supportedFormats;
|
||||||
private Object manifest;
|
private Object manifest;
|
||||||
private ActionSchedule actionSchedule;
|
private ActionSchedule actionSchedule;
|
||||||
|
private Surface surface;
|
||||||
private Player.EventListener eventListener;
|
private Player.EventListener eventListener;
|
||||||
private AnalyticsListener analyticsListener;
|
private AnalyticsListener analyticsListener;
|
||||||
private Integer expectedPlayerEndedCount;
|
private Integer expectedPlayerEndedCount;
|
||||||
|
|
@ -276,6 +278,17 @@ public final class ExoPlayerTestRunner implements Player.EventListener, ActionSc
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the video {@link Surface}. The default value is {@code null}.
|
||||||
|
*
|
||||||
|
* @param surface The {@link Surface} to be used by the player.
|
||||||
|
* @return This builder.
|
||||||
|
*/
|
||||||
|
public Builder setVideoSurface(Surface surface) {
|
||||||
|
this.surface = surface;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets an {@link Player.EventListener} to be registered to listen to player events.
|
* Sets an {@link Player.EventListener} to be registered to listen to player events.
|
||||||
*
|
*
|
||||||
|
|
@ -334,6 +347,7 @@ public final class ExoPlayerTestRunner implements Player.EventListener, ActionSc
|
||||||
skipSettingMediaSources,
|
skipSettingMediaSources,
|
||||||
initialWindowIndex,
|
initialWindowIndex,
|
||||||
initialPositionMs,
|
initialPositionMs,
|
||||||
|
surface,
|
||||||
actionSchedule,
|
actionSchedule,
|
||||||
eventListener,
|
eventListener,
|
||||||
analyticsListener,
|
analyticsListener,
|
||||||
|
|
@ -347,6 +361,7 @@ public final class ExoPlayerTestRunner implements Player.EventListener, ActionSc
|
||||||
private final boolean skipSettingMediaSources;
|
private final boolean skipSettingMediaSources;
|
||||||
private final int initialWindowIndex;
|
private final int initialWindowIndex;
|
||||||
private final long initialPositionMs;
|
private final long initialPositionMs;
|
||||||
|
@Nullable private final Surface surface;
|
||||||
@Nullable private final ActionSchedule actionSchedule;
|
@Nullable private final ActionSchedule actionSchedule;
|
||||||
@Nullable private final Player.EventListener eventListener;
|
@Nullable private final Player.EventListener eventListener;
|
||||||
@Nullable private final AnalyticsListener analyticsListener;
|
@Nullable private final AnalyticsListener analyticsListener;
|
||||||
|
|
@ -376,6 +391,7 @@ public final class ExoPlayerTestRunner implements Player.EventListener, ActionSc
|
||||||
boolean skipSettingMediaSources,
|
boolean skipSettingMediaSources,
|
||||||
int initialWindowIndex,
|
int initialWindowIndex,
|
||||||
long initialPositionMs,
|
long initialPositionMs,
|
||||||
|
@Nullable Surface surface,
|
||||||
@Nullable ActionSchedule actionSchedule,
|
@Nullable ActionSchedule actionSchedule,
|
||||||
@Nullable Player.EventListener eventListener,
|
@Nullable Player.EventListener eventListener,
|
||||||
@Nullable AnalyticsListener analyticsListener,
|
@Nullable AnalyticsListener analyticsListener,
|
||||||
|
|
@ -386,6 +402,7 @@ public final class ExoPlayerTestRunner implements Player.EventListener, ActionSc
|
||||||
this.skipSettingMediaSources = skipSettingMediaSources;
|
this.skipSettingMediaSources = skipSettingMediaSources;
|
||||||
this.initialWindowIndex = initialWindowIndex;
|
this.initialWindowIndex = initialWindowIndex;
|
||||||
this.initialPositionMs = initialPositionMs;
|
this.initialPositionMs = initialPositionMs;
|
||||||
|
this.surface = surface;
|
||||||
this.actionSchedule = actionSchedule;
|
this.actionSchedule = actionSchedule;
|
||||||
this.eventListener = eventListener;
|
this.eventListener = eventListener;
|
||||||
this.analyticsListener = analyticsListener;
|
this.analyticsListener = analyticsListener;
|
||||||
|
|
@ -430,6 +447,12 @@ public final class ExoPlayerTestRunner implements Player.EventListener, ActionSc
|
||||||
() -> {
|
() -> {
|
||||||
try {
|
try {
|
||||||
player = playerBuilder.setLooper(Looper.myLooper()).build();
|
player = playerBuilder.setLooper(Looper.myLooper()).build();
|
||||||
|
if (surface != null) {
|
||||||
|
player.setVideoSurface(surface);
|
||||||
|
}
|
||||||
|
if (pauseAtEndOfMediaItems) {
|
||||||
|
player.setPauseAtEndOfMediaItems(true);
|
||||||
|
}
|
||||||
player.addListener(ExoPlayerTestRunner.this);
|
player.addListener(ExoPlayerTestRunner.this);
|
||||||
if (eventListener != null) {
|
if (eventListener != null) {
|
||||||
player.addListener(eventListener);
|
player.addListener(eventListener);
|
||||||
|
|
@ -437,15 +460,12 @@ public final class ExoPlayerTestRunner implements Player.EventListener, ActionSc
|
||||||
if (analyticsListener != null) {
|
if (analyticsListener != null) {
|
||||||
player.addAnalyticsListener(analyticsListener);
|
player.addAnalyticsListener(analyticsListener);
|
||||||
}
|
}
|
||||||
if (pauseAtEndOfMediaItems) {
|
|
||||||
player.setPauseAtEndOfMediaItems(true);
|
|
||||||
}
|
|
||||||
player.play();
|
player.play();
|
||||||
if (actionSchedule != null) {
|
if (actionSchedule != null) {
|
||||||
actionSchedule.start(
|
actionSchedule.start(
|
||||||
player,
|
player,
|
||||||
playerBuilder.getTrackSelector(),
|
playerBuilder.getTrackSelector(),
|
||||||
/* surface= */ null,
|
surface,
|
||||||
handler,
|
handler,
|
||||||
/* callback= */ ExoPlayerTestRunner.this);
|
/* callback= */ ExoPlayerTestRunner.this);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ package com.google.android.exoplayer2.testutil;
|
||||||
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
|
import android.view.Surface;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||||
import com.google.android.exoplayer2.Format;
|
import com.google.android.exoplayer2.Format;
|
||||||
|
|
@ -33,6 +35,7 @@ public class FakeVideoRenderer extends FakeRenderer {
|
||||||
private final VideoRendererEventListener.EventDispatcher eventDispatcher;
|
private final VideoRendererEventListener.EventDispatcher eventDispatcher;
|
||||||
private final DecoderCounters decoderCounters;
|
private final DecoderCounters decoderCounters;
|
||||||
private @MonotonicNonNull Format format;
|
private @MonotonicNonNull Format format;
|
||||||
|
@Nullable private Surface surface;
|
||||||
private long streamOffsetUs;
|
private long streamOffsetUs;
|
||||||
private boolean renderedFirstFrameAfterReset;
|
private boolean renderedFirstFrameAfterReset;
|
||||||
private boolean mayRenderFirstFrameAfterEnableIfNotStarted;
|
private boolean mayRenderFirstFrameAfterEnableIfNotStarted;
|
||||||
|
|
@ -58,9 +61,7 @@ public class FakeVideoRenderer extends FakeRenderer {
|
||||||
throws ExoPlaybackException {
|
throws ExoPlaybackException {
|
||||||
super.onStreamChanged(formats, startPositionUs, offsetUs);
|
super.onStreamChanged(formats, startPositionUs, offsetUs);
|
||||||
streamOffsetUs = offsetUs;
|
streamOffsetUs = offsetUs;
|
||||||
if (renderedFirstFrameAfterReset) {
|
renderedFirstFrameAfterReset = false;
|
||||||
renderedFirstFrameAfterReset = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -93,19 +94,33 @@ public class FakeVideoRenderer extends FakeRenderer {
|
||||||
this.format = format;
|
this.format = format;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(int messageType, @Nullable Object payload) throws ExoPlaybackException {
|
||||||
|
switch (messageType) {
|
||||||
|
case MSG_SET_SURFACE:
|
||||||
|
surface = (Surface) payload;
|
||||||
|
renderedFirstFrameAfterReset = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
super.handleMessage(messageType, payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean shouldProcessBuffer(long bufferTimeUs, long playbackPositionUs) {
|
protected boolean shouldProcessBuffer(long bufferTimeUs, long playbackPositionUs) {
|
||||||
boolean shouldProcess = super.shouldProcessBuffer(bufferTimeUs, playbackPositionUs);
|
boolean shouldProcess = super.shouldProcessBuffer(bufferTimeUs, playbackPositionUs);
|
||||||
boolean shouldRenderFirstFrame =
|
boolean shouldRenderFirstFrame =
|
||||||
!renderedFirstFrameAfterEnable
|
surface != null
|
||||||
? (getState() == Renderer.STATE_STARTED || mayRenderFirstFrameAfterEnableIfNotStarted)
|
&& (!renderedFirstFrameAfterEnable
|
||||||
: !renderedFirstFrameAfterReset;
|
? (getState() == Renderer.STATE_STARTED
|
||||||
|
|| mayRenderFirstFrameAfterEnableIfNotStarted)
|
||||||
|
: !renderedFirstFrameAfterReset);
|
||||||
shouldProcess |= shouldRenderFirstFrame && playbackPositionUs >= streamOffsetUs;
|
shouldProcess |= shouldRenderFirstFrame && playbackPositionUs >= streamOffsetUs;
|
||||||
if (shouldProcess && !renderedFirstFrameAfterReset) {
|
if (shouldProcess && !renderedFirstFrameAfterReset && surface != null) {
|
||||||
@MonotonicNonNull Format format = Assertions.checkNotNull(this.format);
|
@MonotonicNonNull Format format = Assertions.checkNotNull(this.format);
|
||||||
eventDispatcher.videoSizeChanged(
|
eventDispatcher.videoSizeChanged(
|
||||||
format.width, format.height, format.rotationDegrees, format.pixelWidthHeightRatio);
|
format.width, format.height, format.rotationDegrees, format.pixelWidthHeightRatio);
|
||||||
eventDispatcher.renderedFirstFrame(/* surface= */ null);
|
eventDispatcher.renderedFirstFrame(surface);
|
||||||
renderedFirstFrameAfterReset = true;
|
renderedFirstFrameAfterReset = true;
|
||||||
renderedFirstFrameAfterEnable = true;
|
renderedFirstFrameAfterEnable = true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue