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;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MediaClock;
import com.google.android.exoplayer2.util.MimeTypes;
import java.io.IOException;
import java.util.ArrayList;
......@@ -51,6 +52,8 @@ public final class ExoPlayerTest extends TestCase {
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,
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
......@@ -58,12 +61,17 @@ public final class ExoPlayerTest extends TestCase {
*/
public void testPlayEmptyTimeline() throws Exception {
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);
assertEquals(0, playerWrapper.positionDiscontinuityCount);
assertEquals(0, playerWrapper.videoRenderer.formatReadCount);
assertEquals(0, playerWrapper.videoRenderer.bufferReadCount);
assertFalse(playerWrapper.videoRenderer.isEnded);
assertEquals(0, renderer.formatReadCount);
assertEquals(0, renderer.bufferReadCount);
assertFalse(renderer.isEnded);
assertEquals(timeline, playerWrapper.timeline);
assertNull(playerWrapper.manifest);
}
/**
......@@ -71,14 +79,19 @@ public final class ExoPlayerTest extends TestCase {
*/
public void testPlaySinglePeriodTimeline() throws Exception {
PlayerWrapper playerWrapper = new PlayerWrapper();
Timeline timeline = new FakeTimeline(
new TimelineWindowDefinition(false, false, 0));
playerWrapper.setup(timeline, null, TEST_VIDEO_FORMAT);
Timeline timeline = new FakeTimeline(new TimelineWindowDefinition(false, false, 0));
Object manifest = new Object();
MediaSource mediaSource = new FakeMediaSource(timeline, manifest, TEST_VIDEO_FORMAT);
FakeRenderer renderer = new FakeRenderer(TEST_VIDEO_FORMAT);
playerWrapper.setup(mediaSource, renderer);
playerWrapper.blockUntilEnded(TIMEOUT_MS);
assertEquals(0, playerWrapper.positionDiscontinuityCount);
assertEquals(1, playerWrapper.videoRenderer.formatReadCount);
assertEquals(1, playerWrapper.videoRenderer.bufferReadCount);
assertTrue(playerWrapper.videoRenderer.isEnded);
assertEquals(1, renderer.formatReadCount);
assertEquals(1, renderer.bufferReadCount);
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 {
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, playerWrapper.videoRenderer.formatReadCount);
assertEquals(1, playerWrapper.videoRenderer.bufferReadCount);
assertTrue(playerWrapper.videoRenderer.isEnded);
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);
assertEquals(2, playerWrapper.positionDiscontinuityCount);
assertEquals(1, audioRenderer.positionResetCount);
assertTrue(videoRenderer.isEnded);
assertTrue(audioRenderer.isEnded);
assertEquals(timeline, playerWrapper.timeline);
assertNull(playerWrapper.manifest);
}
/**
......@@ -107,14 +166,14 @@ public final class ExoPlayerTest extends TestCase {
private final HandlerThread playerThread;
private final Handler handler;
private Timeline expectedTimeline;
private Object expectedManifest;
private Format expectedFormat;
private FakeVideoRenderer videoRenderer;
private ExoPlayer player;
private Timeline timeline;
private Object manifest;
private TrackGroupArray trackGroups;
private Exception exception;
private int positionDiscontinuityCount;
// Written only on the main thread.
private volatile int positionDiscontinuityCount;
public PlayerWrapper() {
endedCountDownLatch = new CountDownLatch(1);
......@@ -136,20 +195,15 @@ public final class ExoPlayerTest extends TestCase {
}
}
public void setup(final Timeline timeline, final Object manifest, final Format format) {
expectedTimeline = timeline;
expectedManifest = manifest;
expectedFormat = format;
public void setup(final MediaSource mediaSource, final Renderer... renderers) {
handler.post(new Runnable() {
@Override
public void run() {
try {
videoRenderer = new FakeVideoRenderer(expectedFormat);
player = ExoPlayerFactory.newInstance(new Renderer[] {videoRenderer},
new DefaultTrackSelector());
player = ExoPlayerFactory.newInstance(renderers, new DefaultTrackSelector());
player.addListener(PlayerWrapper.this);
player.setPlayWhenReady(true);
player.prepare(new FakeMediaSource(timeline, manifest, format));
player.prepare(mediaSource);
} catch (Exception e) {
handleError(e);
}
......@@ -198,14 +252,13 @@ public final class ExoPlayerTest extends TestCase {
@Override
public void onTimelineChanged(Timeline timeline, Object manifest) {
assertEquals(expectedTimeline, timeline);
assertEquals(expectedManifest, manifest);
this.timeline = timeline;
this.manifest = manifest;
}
@Override
public void onTracksChanged(TrackGroupArray trackGroups,
TrackSelectionArray trackSelections) {
assertEquals(new TrackGroupArray(new TrackGroup(expectedFormat)), trackGroups);
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
this.trackGroups = trackGroups;
}
@Override
......@@ -213,6 +266,7 @@ public final class ExoPlayerTest extends TestCase {
handleError(exception);
}
@SuppressWarnings("NonAtomicVolatileUpdate")
@Override
public void onPositionDiscontinuity() {
positionDiscontinuityCount++;
......@@ -287,16 +341,20 @@ public final class ExoPlayerTest extends TestCase {
private final Timeline timeline;
private final Object manifest;
private final Format format;
private final TrackGroupArray trackGroupArray;
private final ArrayList<FakeMediaPeriod> activeMediaPeriods;
private boolean preparedSource;
private boolean releasedSource;
public FakeMediaSource(Timeline timeline, Object manifest, Format format) {
public FakeMediaSource(Timeline timeline, Object manifest, Format... formats) {
this.timeline = timeline;
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<>();
}
......@@ -318,7 +376,7 @@ public final class ExoPlayerTest extends TestCase {
assertTrue(preparedSource);
assertFalse(releasedSource);
assertEquals(0, positionUs);
FakeMediaPeriod mediaPeriod = new FakeMediaPeriod(format);
FakeMediaPeriod mediaPeriod = new FakeMediaPeriod(trackGroupArray);
activeMediaPeriods.add(mediaPeriod);
return mediaPeriod;
}
......@@ -327,8 +385,9 @@ public final class ExoPlayerTest extends TestCase {
public void releasePeriod(MediaPeriod mediaPeriod) {
assertTrue(preparedSource);
assertFalse(releasedSource);
assertTrue(activeMediaPeriods.remove(mediaPeriod));
((FakeMediaPeriod) mediaPeriod).release();
FakeMediaPeriod fakeMediaPeriod = (FakeMediaPeriod) mediaPeriod;
assertTrue(activeMediaPeriods.remove(fakeMediaPeriod));
fakeMediaPeriod.release();
}
@Override
......@@ -347,12 +406,12 @@ public final class ExoPlayerTest extends TestCase {
*/
private static final class FakeMediaPeriod implements MediaPeriod {
private final TrackGroup trackGroup;
private final TrackGroupArray trackGroupArray;
private boolean preparedPeriod;
public FakeMediaPeriod(Format format) {
trackGroup = new TrackGroup(format);
public FakeMediaPeriod(TrackGroupArray trackGroupArray) {
this.trackGroupArray = trackGroupArray;
}
public void release() {
......@@ -374,26 +433,29 @@ public final class ExoPlayerTest extends TestCase {
@Override
public TrackGroupArray getTrackGroups() {
assertTrue(preparedPeriod);
return new TrackGroupArray(trackGroup);
return trackGroupArray;
}
@Override
public long selectTracks(TrackSelection[] selections, boolean[] mayRetainStreamFlags,
SampleStream[] streams, boolean[] streamResetFlags, long positionUs) {
assertTrue(preparedPeriod);
assertEquals(1, selections.length);
assertEquals(1, mayRetainStreamFlags.length);
assertEquals(1, streams.length);
assertEquals(1, streamResetFlags.length);
assertEquals(0, positionUs);
if (streams[0] != null && (selections[0] == null || !mayRetainStreamFlags[0])) {
streams[0] = null;
int rendererCount = selections.length;
for (int i = 0; i < rendererCount; i++) {
if (streams[i] != null && (selections[i] == null || !mayRetainStreamFlags[i])) {
streams[i] = null;
}
}
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;
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;
}
}
return 0;
}
......@@ -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
* given {@link Format}.
* Fake {@link Renderer} that supports any format with the matching MIME type. The renderer
* 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 int formatReadCount;
private int bufferReadCount;
private boolean isEnded;
public int positionResetCount;
public int formatReadCount;
public int bufferReadCount;
public boolean isEnded;
public FakeVideoRenderer(Format expectedFormat) {
super(C.TRACK_TYPE_VIDEO);
public FakeRenderer(Format expectedFormat) {
super(expectedFormat == null ? C.TRACK_TYPE_UNKNOWN
: MimeTypes.getTrackType(expectedFormat.sampleMimeType));
this.expectedFormat = expectedFormat;
}
@Override
protected void onPositionReset(long positionUs, boolean joining) throws ExoPlaybackException {
positionResetCount++;
isEnded = false;
}
......@@ -529,7 +594,21 @@ public final class ExoPlayerTest extends TestCase {
@Override
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