Commit b2a153d5 by andrewlewis Committed by Oliver Woodman

Add a player test for renderers reading ahead.

Also move timeline, manifest and format verifications into the test* methods.

Issue: #2252

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=143761969
parent 9d48d4e4
...@@ -28,6 +28,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelection; ...@@ -28,6 +28,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MediaClock;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -51,6 +52,8 @@ public final class ExoPlayerTest extends TestCase { ...@@ -51,6 +52,8 @@ public final class ExoPlayerTest extends TestCase {
private static final Format TEST_VIDEO_FORMAT = Format.createVideoSampleFormat(null, private static final Format TEST_VIDEO_FORMAT = Format.createVideoSampleFormat(null,
MimeTypes.VIDEO_H264, null, Format.NO_VALUE, Format.NO_VALUE, 1280, 720, Format.NO_VALUE, MimeTypes.VIDEO_H264, null, Format.NO_VALUE, Format.NO_VALUE, 1280, 720, Format.NO_VALUE,
null, null); null, null);
private static final Format TEST_AUDIO_FORMAT = Format.createAudioSampleFormat(null,
MimeTypes.AUDIO_AAC, null, Format.NO_VALUE, Format.NO_VALUE, 2, 44100, null, null, 0, null);
/** /**
* Tests playback of a source that exposes an empty timeline. Playback is expected to end without * Tests playback of a source that exposes an empty timeline. Playback is expected to end without
...@@ -58,12 +61,17 @@ public final class ExoPlayerTest extends TestCase { ...@@ -58,12 +61,17 @@ public final class ExoPlayerTest extends TestCase {
*/ */
public void testPlayEmptyTimeline() throws Exception { public void testPlayEmptyTimeline() throws Exception {
PlayerWrapper playerWrapper = new PlayerWrapper(); PlayerWrapper playerWrapper = new PlayerWrapper();
playerWrapper.setup(Timeline.EMPTY, null, null); Timeline timeline = Timeline.EMPTY;
MediaSource mediaSource = new FakeMediaSource(timeline, null);
FakeRenderer renderer = new FakeRenderer(null);
playerWrapper.setup(mediaSource, renderer);
playerWrapper.blockUntilEnded(TIMEOUT_MS); playerWrapper.blockUntilEnded(TIMEOUT_MS);
assertEquals(0, playerWrapper.positionDiscontinuityCount); assertEquals(0, playerWrapper.positionDiscontinuityCount);
assertEquals(0, playerWrapper.videoRenderer.formatReadCount); assertEquals(0, renderer.formatReadCount);
assertEquals(0, playerWrapper.videoRenderer.bufferReadCount); assertEquals(0, renderer.bufferReadCount);
assertFalse(playerWrapper.videoRenderer.isEnded); assertFalse(renderer.isEnded);
assertEquals(timeline, playerWrapper.timeline);
assertNull(playerWrapper.manifest);
} }
/** /**
...@@ -71,14 +79,19 @@ public final class ExoPlayerTest extends TestCase { ...@@ -71,14 +79,19 @@ public final class ExoPlayerTest extends TestCase {
*/ */
public void testPlaySinglePeriodTimeline() throws Exception { public void testPlaySinglePeriodTimeline() throws Exception {
PlayerWrapper playerWrapper = new PlayerWrapper(); PlayerWrapper playerWrapper = new PlayerWrapper();
Timeline timeline = new FakeTimeline( Timeline timeline = new FakeTimeline(new TimelineWindowDefinition(false, false, 0));
new TimelineWindowDefinition(false, false, 0)); Object manifest = new Object();
playerWrapper.setup(timeline, null, TEST_VIDEO_FORMAT); MediaSource mediaSource = new FakeMediaSource(timeline, manifest, TEST_VIDEO_FORMAT);
FakeRenderer renderer = new FakeRenderer(TEST_VIDEO_FORMAT);
playerWrapper.setup(mediaSource, renderer);
playerWrapper.blockUntilEnded(TIMEOUT_MS); playerWrapper.blockUntilEnded(TIMEOUT_MS);
assertEquals(0, playerWrapper.positionDiscontinuityCount); assertEquals(0, playerWrapper.positionDiscontinuityCount);
assertEquals(1, playerWrapper.videoRenderer.formatReadCount); assertEquals(1, renderer.formatReadCount);
assertEquals(1, playerWrapper.videoRenderer.bufferReadCount); assertEquals(1, renderer.bufferReadCount);
assertTrue(playerWrapper.videoRenderer.isEnded); assertTrue(renderer.isEnded);
assertEquals(timeline, playerWrapper.timeline);
assertEquals(manifest, playerWrapper.manifest);
assertEquals(new TrackGroupArray(new TrackGroup(TEST_VIDEO_FORMAT)), playerWrapper.trackGroups);
} }
/** /**
...@@ -90,12 +103,58 @@ public final class ExoPlayerTest extends TestCase { ...@@ -90,12 +103,58 @@ public final class ExoPlayerTest extends TestCase {
new TimelineWindowDefinition(false, false, 0), new TimelineWindowDefinition(false, false, 0),
new TimelineWindowDefinition(false, false, 0), new TimelineWindowDefinition(false, false, 0),
new TimelineWindowDefinition(false, false, 0)); new TimelineWindowDefinition(false, false, 0));
playerWrapper.setup(timeline, null, TEST_VIDEO_FORMAT); MediaSource mediaSource = new FakeMediaSource(timeline, null, TEST_VIDEO_FORMAT);
FakeRenderer renderer = new FakeRenderer(TEST_VIDEO_FORMAT);
playerWrapper.setup(mediaSource, renderer);
playerWrapper.blockUntilEnded(TIMEOUT_MS);
assertEquals(2, playerWrapper.positionDiscontinuityCount);
assertEquals(3, renderer.formatReadCount);
assertEquals(1, renderer.bufferReadCount);
assertTrue(renderer.isEnded);
assertEquals(timeline, playerWrapper.timeline);
assertNull(playerWrapper.manifest);
}
/**
* Tests that the player does not unnecessarily reset renderers when playing a multi-period
* source.
*/
public void testReadAheadToEndDoesNotResetRenderer() throws Exception {
final PlayerWrapper playerWrapper = new PlayerWrapper();
Timeline timeline = new FakeTimeline(
new TimelineWindowDefinition(false, false, 10),
new TimelineWindowDefinition(false, false, 10),
new TimelineWindowDefinition(false, false, 10));
MediaSource mediaSource = new FakeMediaSource(timeline, null, TEST_VIDEO_FORMAT,
TEST_AUDIO_FORMAT);
FakeRenderer videoRenderer = new FakeRenderer(TEST_VIDEO_FORMAT);
FakeMediaClockRenderer audioRenderer = new FakeMediaClockRenderer(TEST_AUDIO_FORMAT) {
@Override
public long getPositionUs() {
// Simulate the playback position lagging behind the reading position: the renderer media
// clock position will be the start of the timeline until the stream is set to be final, at
// which point it jumps to the end of the timeline allowing the playing period to advance.
// TODO: Avoid hard-coding ExoPlayerImplInternal.RENDERER_TIMESTAMP_OFFSET_US.
return isCurrentStreamFinal() ? 60000030 : 60000000;
}
@Override
public boolean isEnded() {
// Allow playback to end once the final period is playing.
return playerWrapper.positionDiscontinuityCount == 2;
}
};
playerWrapper.setup(mediaSource, videoRenderer, audioRenderer);
playerWrapper.blockUntilEnded(TIMEOUT_MS); playerWrapper.blockUntilEnded(TIMEOUT_MS);
assertEquals(2, playerWrapper.positionDiscontinuityCount); assertEquals(2, playerWrapper.positionDiscontinuityCount);
assertEquals(3, playerWrapper.videoRenderer.formatReadCount); assertEquals(1, audioRenderer.positionResetCount);
assertEquals(1, playerWrapper.videoRenderer.bufferReadCount); assertTrue(videoRenderer.isEnded);
assertTrue(playerWrapper.videoRenderer.isEnded); assertTrue(audioRenderer.isEnded);
assertEquals(timeline, playerWrapper.timeline);
assertNull(playerWrapper.manifest);
} }
/** /**
...@@ -107,14 +166,14 @@ public final class ExoPlayerTest extends TestCase { ...@@ -107,14 +166,14 @@ public final class ExoPlayerTest extends TestCase {
private final HandlerThread playerThread; private final HandlerThread playerThread;
private final Handler handler; private final Handler handler;
private Timeline expectedTimeline;
private Object expectedManifest;
private Format expectedFormat;
private FakeVideoRenderer videoRenderer;
private ExoPlayer player; private ExoPlayer player;
private Timeline timeline;
private Object manifest;
private TrackGroupArray trackGroups;
private Exception exception; private Exception exception;
private int positionDiscontinuityCount; // Written only on the main thread.
private volatile int positionDiscontinuityCount;
public PlayerWrapper() { public PlayerWrapper() {
endedCountDownLatch = new CountDownLatch(1); endedCountDownLatch = new CountDownLatch(1);
...@@ -136,20 +195,15 @@ public final class ExoPlayerTest extends TestCase { ...@@ -136,20 +195,15 @@ public final class ExoPlayerTest extends TestCase {
} }
} }
public void setup(final Timeline timeline, final Object manifest, final Format format) { public void setup(final MediaSource mediaSource, final Renderer... renderers) {
expectedTimeline = timeline;
expectedManifest = manifest;
expectedFormat = format;
handler.post(new Runnable() { handler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
try { try {
videoRenderer = new FakeVideoRenderer(expectedFormat); player = ExoPlayerFactory.newInstance(renderers, new DefaultTrackSelector());
player = ExoPlayerFactory.newInstance(new Renderer[] {videoRenderer},
new DefaultTrackSelector());
player.addListener(PlayerWrapper.this); player.addListener(PlayerWrapper.this);
player.setPlayWhenReady(true); player.setPlayWhenReady(true);
player.prepare(new FakeMediaSource(timeline, manifest, format)); player.prepare(mediaSource);
} catch (Exception e) { } catch (Exception e) {
handleError(e); handleError(e);
} }
...@@ -198,14 +252,13 @@ public final class ExoPlayerTest extends TestCase { ...@@ -198,14 +252,13 @@ public final class ExoPlayerTest extends TestCase {
@Override @Override
public void onTimelineChanged(Timeline timeline, Object manifest) { public void onTimelineChanged(Timeline timeline, Object manifest) {
assertEquals(expectedTimeline, timeline); this.timeline = timeline;
assertEquals(expectedManifest, manifest); this.manifest = manifest;
} }
@Override @Override
public void onTracksChanged(TrackGroupArray trackGroups, public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
TrackSelectionArray trackSelections) { this.trackGroups = trackGroups;
assertEquals(new TrackGroupArray(new TrackGroup(expectedFormat)), trackGroups);
} }
@Override @Override
...@@ -213,6 +266,7 @@ public final class ExoPlayerTest extends TestCase { ...@@ -213,6 +266,7 @@ public final class ExoPlayerTest extends TestCase {
handleError(exception); handleError(exception);
} }
@SuppressWarnings("NonAtomicVolatileUpdate")
@Override @Override
public void onPositionDiscontinuity() { public void onPositionDiscontinuity() {
positionDiscontinuityCount++; positionDiscontinuityCount++;
...@@ -287,16 +341,20 @@ public final class ExoPlayerTest extends TestCase { ...@@ -287,16 +341,20 @@ public final class ExoPlayerTest extends TestCase {
private final Timeline timeline; private final Timeline timeline;
private final Object manifest; private final Object manifest;
private final Format format; private final TrackGroupArray trackGroupArray;
private final ArrayList<FakeMediaPeriod> activeMediaPeriods; private final ArrayList<FakeMediaPeriod> activeMediaPeriods;
private boolean preparedSource; private boolean preparedSource;
private boolean releasedSource; private boolean releasedSource;
public FakeMediaSource(Timeline timeline, Object manifest, Format format) { public FakeMediaSource(Timeline timeline, Object manifest, Format... formats) {
this.timeline = timeline; this.timeline = timeline;
this.manifest = manifest; this.manifest = manifest;
this.format = format; TrackGroup[] trackGroups = new TrackGroup[formats.length];
for (int i = 0; i < formats.length; i++) {
trackGroups[i] = new TrackGroup(formats[i]);
}
trackGroupArray = new TrackGroupArray(trackGroups);
activeMediaPeriods = new ArrayList<>(); activeMediaPeriods = new ArrayList<>();
} }
...@@ -318,7 +376,7 @@ public final class ExoPlayerTest extends TestCase { ...@@ -318,7 +376,7 @@ public final class ExoPlayerTest extends TestCase {
assertTrue(preparedSource); assertTrue(preparedSource);
assertFalse(releasedSource); assertFalse(releasedSource);
assertEquals(0, positionUs); assertEquals(0, positionUs);
FakeMediaPeriod mediaPeriod = new FakeMediaPeriod(format); FakeMediaPeriod mediaPeriod = new FakeMediaPeriod(trackGroupArray);
activeMediaPeriods.add(mediaPeriod); activeMediaPeriods.add(mediaPeriod);
return mediaPeriod; return mediaPeriod;
} }
...@@ -327,8 +385,9 @@ public final class ExoPlayerTest extends TestCase { ...@@ -327,8 +385,9 @@ public final class ExoPlayerTest extends TestCase {
public void releasePeriod(MediaPeriod mediaPeriod) { public void releasePeriod(MediaPeriod mediaPeriod) {
assertTrue(preparedSource); assertTrue(preparedSource);
assertFalse(releasedSource); assertFalse(releasedSource);
assertTrue(activeMediaPeriods.remove(mediaPeriod)); FakeMediaPeriod fakeMediaPeriod = (FakeMediaPeriod) mediaPeriod;
((FakeMediaPeriod) mediaPeriod).release(); assertTrue(activeMediaPeriods.remove(fakeMediaPeriod));
fakeMediaPeriod.release();
} }
@Override @Override
...@@ -347,12 +406,12 @@ public final class ExoPlayerTest extends TestCase { ...@@ -347,12 +406,12 @@ public final class ExoPlayerTest extends TestCase {
*/ */
private static final class FakeMediaPeriod implements MediaPeriod { private static final class FakeMediaPeriod implements MediaPeriod {
private final TrackGroup trackGroup; private final TrackGroupArray trackGroupArray;
private boolean preparedPeriod; private boolean preparedPeriod;
public FakeMediaPeriod(Format format) { public FakeMediaPeriod(TrackGroupArray trackGroupArray) {
trackGroup = new TrackGroup(format); this.trackGroupArray = trackGroupArray;
} }
public void release() { public void release() {
...@@ -374,26 +433,29 @@ public final class ExoPlayerTest extends TestCase { ...@@ -374,26 +433,29 @@ public final class ExoPlayerTest extends TestCase {
@Override @Override
public TrackGroupArray getTrackGroups() { public TrackGroupArray getTrackGroups() {
assertTrue(preparedPeriod); assertTrue(preparedPeriod);
return new TrackGroupArray(trackGroup); return trackGroupArray;
} }
@Override @Override
public long selectTracks(TrackSelection[] selections, boolean[] mayRetainStreamFlags, public long selectTracks(TrackSelection[] selections, boolean[] mayRetainStreamFlags,
SampleStream[] streams, boolean[] streamResetFlags, long positionUs) { SampleStream[] streams, boolean[] streamResetFlags, long positionUs) {
assertTrue(preparedPeriod); assertTrue(preparedPeriod);
assertEquals(1, selections.length); int rendererCount = selections.length;
assertEquals(1, mayRetainStreamFlags.length); for (int i = 0; i < rendererCount; i++) {
assertEquals(1, streams.length); if (streams[i] != null && (selections[i] == null || !mayRetainStreamFlags[i])) {
assertEquals(1, streamResetFlags.length); streams[i] = null;
assertEquals(0, positionUs); }
if (streams[0] != null && (selections[0] == null || !mayRetainStreamFlags[0])) { }
streams[0] = null; for (int i = 0; i < rendererCount; i++) {
if (streams[i] == null && selections[i] != null) {
TrackSelection selection = selections[i];
assertEquals(1, selection.length());
assertEquals(0, selection.getIndexInTrackGroup(0));
TrackGroup trackGroup = selection.getTrackGroup();
assertTrue(trackGroupArray.indexOf(trackGroup) != C.INDEX_UNSET);
streams[i] = new FakeSampleStream(trackGroup.getFormat(0));
streamResetFlags[i] = true;
} }
if (streams[0] == null && selections[0] != null) {
FakeSampleStream stream = new FakeSampleStream(trackGroup.getFormat(0));
assertEquals(trackGroup, selections[0].getTrackGroup());
streams[0] = stream;
streamResetFlags[0] = true;
} }
return 0; return 0;
} }
...@@ -474,24 +536,27 @@ public final class ExoPlayerTest extends TestCase { ...@@ -474,24 +536,27 @@ public final class ExoPlayerTest extends TestCase {
} }
/** /**
* Fake {@link Renderer} that supports any video format. The renderer verifies that it reads a * Fake {@link Renderer} that supports any format with the matching MIME type. The renderer
* given {@link Format}. * verifies that it reads a given {@link Format}.
*/ */
private static final class FakeVideoRenderer extends BaseRenderer { private static class FakeRenderer extends BaseRenderer {
private final Format expectedFormat; private final Format expectedFormat;
private int formatReadCount; public int positionResetCount;
private int bufferReadCount; public int formatReadCount;
private boolean isEnded; public int bufferReadCount;
public boolean isEnded;
public FakeVideoRenderer(Format expectedFormat) { public FakeRenderer(Format expectedFormat) {
super(C.TRACK_TYPE_VIDEO); super(expectedFormat == null ? C.TRACK_TYPE_UNKNOWN
: MimeTypes.getTrackType(expectedFormat.sampleMimeType));
this.expectedFormat = expectedFormat; this.expectedFormat = expectedFormat;
} }
@Override @Override
protected void onPositionReset(long positionUs, boolean joining) throws ExoPlaybackException { protected void onPositionReset(long positionUs, boolean joining) throws ExoPlaybackException {
positionResetCount++;
isEnded = false; isEnded = false;
} }
...@@ -529,7 +594,21 @@ public final class ExoPlayerTest extends TestCase { ...@@ -529,7 +594,21 @@ public final class ExoPlayerTest extends TestCase {
@Override @Override
public int supportsFormat(Format format) throws ExoPlaybackException { public int supportsFormat(Format format) throws ExoPlaybackException {
return MimeTypes.isVideo(format.sampleMimeType) ? FORMAT_HANDLED : FORMAT_UNSUPPORTED_TYPE; return getTrackType() == MimeTypes.getTrackType(format.sampleMimeType) ? FORMAT_HANDLED
: FORMAT_UNSUPPORTED_TYPE;
}
}
private abstract static class FakeMediaClockRenderer extends FakeRenderer implements MediaClock {
public FakeMediaClockRenderer(Format expectedFormat) {
super(expectedFormat);
}
@Override
public MediaClock getMediaClock() {
return this;
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment