Report additional position discontinuities

- Properly report internal discontinuities
- Add DISCONTINUITY_REASON_SEEK_ADJUSTMENT to distinguish
  seek adjustments from other internal discontinuity events

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=176367365
This commit is contained in:
olly 2017-11-20 08:56:23 -08:00 committed by Oliver Woodman
parent c06fe73b66
commit 275292cb63
7 changed files with 139 additions and 26 deletions

View File

@ -2,6 +2,10 @@
### dev-v2 (not yet released) ### ### dev-v2 (not yet released) ###
* Fix reporting of internal position discontinuities via
`Player.onPositionDiscontinuity`. `DISCONTINUITY_REASON_SEEK_ADJUSTMENT` is
added to disambiguate position adjustments during seeks from other types of
internal position discontinuity.
* Allow more flexible loading strategy when playing media containing multiple * Allow more flexible loading strategy when playing media containing multiple
sub-streams, by allowing injection of custom `CompositeSequenceableLoader` sub-streams, by allowing injection of custom `CompositeSequenceableLoader`
factories through `DashMediaSource.Builder`, `HlsMediaSource.Builder`, factories through `DashMediaSource.Builder`, `HlsMediaSource.Builder`,

View File

@ -499,6 +499,8 @@ import java.util.Locale;
return "PERIOD_TRANSITION"; return "PERIOD_TRANSITION";
case Player.DISCONTINUITY_REASON_SEEK: case Player.DISCONTINUITY_REASON_SEEK:
return "SEEK"; return "SEEK";
case Player.DISCONTINUITY_REASON_SEEK_ADJUSTMENT:
return "SEEK_ADJUSTMENT";
case Player.DISCONTINUITY_REASON_INTERNAL: case Player.DISCONTINUITY_REASON_INTERNAL:
return "INTERNAL"; return "INTERNAL";
default: default:

View File

@ -23,12 +23,14 @@ import com.google.android.exoplayer2.testutil.ActionSchedule;
import com.google.android.exoplayer2.testutil.ExoPlayerTestRunner; import com.google.android.exoplayer2.testutil.ExoPlayerTestRunner;
import com.google.android.exoplayer2.testutil.ExoPlayerTestRunner.Builder; import com.google.android.exoplayer2.testutil.ExoPlayerTestRunner.Builder;
import com.google.android.exoplayer2.testutil.FakeMediaClockRenderer; import com.google.android.exoplayer2.testutil.FakeMediaClockRenderer;
import com.google.android.exoplayer2.testutil.FakeMediaPeriod;
import com.google.android.exoplayer2.testutil.FakeMediaSource; import com.google.android.exoplayer2.testutil.FakeMediaSource;
import com.google.android.exoplayer2.testutil.FakeRenderer; import com.google.android.exoplayer2.testutil.FakeRenderer;
import com.google.android.exoplayer2.testutil.FakeShuffleOrder; import com.google.android.exoplayer2.testutil.FakeShuffleOrder;
import com.google.android.exoplayer2.testutil.FakeTimeline; import com.google.android.exoplayer2.testutil.FakeTimeline;
import com.google.android.exoplayer2.testutil.FakeTrackSelection; import com.google.android.exoplayer2.testutil.FakeTrackSelection;
import com.google.android.exoplayer2.testutil.FakeTrackSelector; import com.google.android.exoplayer2.testutil.FakeTrackSelector;
import com.google.android.exoplayer2.upstream.Allocator;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
@ -56,7 +58,7 @@ public final class ExoPlayerTest extends TestCase {
ExoPlayerTestRunner testRunner = new ExoPlayerTestRunner.Builder() ExoPlayerTestRunner testRunner = new ExoPlayerTestRunner.Builder()
.setTimeline(timeline).setRenderers(renderer) .setTimeline(timeline).setRenderers(renderer)
.build().start().blockUntilEnded(TIMEOUT_MS); .build().start().blockUntilEnded(TIMEOUT_MS);
testRunner.assertPositionDiscontinuityCount(0); testRunner.assertNoPositionDiscontinuities();
testRunner.assertTimelinesEqual(); testRunner.assertTimelinesEqual();
assertEquals(0, renderer.formatReadCount); assertEquals(0, renderer.formatReadCount);
assertEquals(0, renderer.bufferReadCount); assertEquals(0, renderer.bufferReadCount);
@ -73,7 +75,7 @@ public final class ExoPlayerTest extends TestCase {
ExoPlayerTestRunner testRunner = new ExoPlayerTestRunner.Builder() ExoPlayerTestRunner testRunner = new ExoPlayerTestRunner.Builder()
.setTimeline(timeline).setManifest(manifest).setRenderers(renderer) .setTimeline(timeline).setManifest(manifest).setRenderers(renderer)
.build().start().blockUntilEnded(TIMEOUT_MS); .build().start().blockUntilEnded(TIMEOUT_MS);
testRunner.assertPositionDiscontinuityCount(0); testRunner.assertNoPositionDiscontinuities();
testRunner.assertTimelinesEqual(timeline); testRunner.assertTimelinesEqual(timeline);
testRunner.assertManifestsEqual(manifest); testRunner.assertManifestsEqual(manifest);
testRunner.assertTrackGroupsEqual(new TrackGroupArray(new TrackGroup(Builder.VIDEO_FORMAT))); testRunner.assertTrackGroupsEqual(new TrackGroupArray(new TrackGroup(Builder.VIDEO_FORMAT)));
@ -91,7 +93,9 @@ public final class ExoPlayerTest extends TestCase {
ExoPlayerTestRunner testRunner = new ExoPlayerTestRunner.Builder() ExoPlayerTestRunner testRunner = new ExoPlayerTestRunner.Builder()
.setTimeline(timeline).setRenderers(renderer) .setTimeline(timeline).setRenderers(renderer)
.build().start().blockUntilEnded(TIMEOUT_MS); .build().start().blockUntilEnded(TIMEOUT_MS);
testRunner.assertPositionDiscontinuityCount(2); testRunner.assertPositionDiscontinuityReasonsEqual(
Player.DISCONTINUITY_REASON_PERIOD_TRANSITION,
Player.DISCONTINUITY_REASON_PERIOD_TRANSITION);
testRunner.assertTimelinesEqual(timeline); testRunner.assertTimelinesEqual(timeline);
assertEquals(3, renderer.formatReadCount); assertEquals(3, renderer.formatReadCount);
assertEquals(1, renderer.bufferReadCount); assertEquals(1, renderer.bufferReadCount);
@ -136,7 +140,9 @@ public final class ExoPlayerTest extends TestCase {
.setTimeline(timeline).setRenderers(videoRenderer, audioRenderer) .setTimeline(timeline).setRenderers(videoRenderer, audioRenderer)
.setSupportedFormats(Builder.VIDEO_FORMAT, Builder.AUDIO_FORMAT) .setSupportedFormats(Builder.VIDEO_FORMAT, Builder.AUDIO_FORMAT)
.build().start().blockUntilEnded(TIMEOUT_MS); .build().start().blockUntilEnded(TIMEOUT_MS);
testRunner.assertPositionDiscontinuityCount(2); testRunner.assertPositionDiscontinuityReasonsEqual(
Player.DISCONTINUITY_REASON_PERIOD_TRANSITION,
Player.DISCONTINUITY_REASON_PERIOD_TRANSITION);
testRunner.assertTimelinesEqual(timeline); testRunner.assertTimelinesEqual(timeline);
assertEquals(1, audioRenderer.positionResetCount); assertEquals(1, audioRenderer.positionResetCount);
assertTrue(videoRenderer.isEnded); assertTrue(videoRenderer.isEnded);
@ -198,7 +204,7 @@ public final class ExoPlayerTest extends TestCase {
ExoPlayerTestRunner testRunner = new ExoPlayerTestRunner.Builder() ExoPlayerTestRunner testRunner = new ExoPlayerTestRunner.Builder()
.setMediaSource(firstSource).setRenderers(renderer).setActionSchedule(actionSchedule) .setMediaSource(firstSource).setRenderers(renderer).setActionSchedule(actionSchedule)
.build().start().blockUntilEnded(TIMEOUT_MS); .build().start().blockUntilEnded(TIMEOUT_MS);
testRunner.assertPositionDiscontinuityCount(0); testRunner.assertNoPositionDiscontinuities();
// The first source's preparation completed with a non-empty timeline. When the player was // The first source's preparation completed with a non-empty timeline. When the player was
// re-prepared with the second source, it immediately exposed an empty timeline, but the source // re-prepared with the second source, it immediately exposed an empty timeline, but the source
// info refresh from the second source was suppressed as we re-prepared with the third source. // info refresh from the second source was suppressed as we re-prepared with the third source.
@ -226,6 +232,16 @@ public final class ExoPlayerTest extends TestCase {
.setTimeline(timeline).setRenderers(renderer).setActionSchedule(actionSchedule) .setTimeline(timeline).setRenderers(renderer).setActionSchedule(actionSchedule)
.build().start().blockUntilEnded(TIMEOUT_MS); .build().start().blockUntilEnded(TIMEOUT_MS);
testRunner.assertPlayedPeriodIndices(0, 1, 1, 2, 2, 0, 0, 0, 1, 2); testRunner.assertPlayedPeriodIndices(0, 1, 1, 2, 2, 0, 0, 0, 1, 2);
testRunner.assertPositionDiscontinuityReasonsEqual(
Player.DISCONTINUITY_REASON_PERIOD_TRANSITION,
Player.DISCONTINUITY_REASON_PERIOD_TRANSITION,
Player.DISCONTINUITY_REASON_PERIOD_TRANSITION,
Player.DISCONTINUITY_REASON_PERIOD_TRANSITION,
Player.DISCONTINUITY_REASON_PERIOD_TRANSITION,
Player.DISCONTINUITY_REASON_PERIOD_TRANSITION,
Player.DISCONTINUITY_REASON_PERIOD_TRANSITION,
Player.DISCONTINUITY_REASON_PERIOD_TRANSITION,
Player.DISCONTINUITY_REASON_PERIOD_TRANSITION);
testRunner.assertTimelinesEqual(timeline); testRunner.assertTimelinesEqual(timeline);
assertTrue(renderer.isEnded); assertTrue(renderer.isEnded);
} }
@ -250,6 +266,12 @@ public final class ExoPlayerTest extends TestCase {
.setMediaSource(mediaSource).setRenderers(renderer).setActionSchedule(actionSchedule) .setMediaSource(mediaSource).setRenderers(renderer).setActionSchedule(actionSchedule)
.build().start().blockUntilEnded(TIMEOUT_MS); .build().start().blockUntilEnded(TIMEOUT_MS);
testRunner.assertPlayedPeriodIndices(0, 1, 0, 2, 1, 2); testRunner.assertPlayedPeriodIndices(0, 1, 0, 2, 1, 2);
testRunner.assertPositionDiscontinuityReasonsEqual(
Player.DISCONTINUITY_REASON_PERIOD_TRANSITION,
Player.DISCONTINUITY_REASON_PERIOD_TRANSITION,
Player.DISCONTINUITY_REASON_PERIOD_TRANSITION,
Player.DISCONTINUITY_REASON_PERIOD_TRANSITION,
Player.DISCONTINUITY_REASON_PERIOD_TRANSITION);
assertTrue(renderer.isEnded); assertTrue(renderer.isEnded);
} }
@ -300,6 +322,63 @@ public final class ExoPlayerTest extends TestCase {
assertEquals(Player.STATE_BUFFERING, (int) playbackStatesWhenSeekProcessed.get(2)); assertEquals(Player.STATE_BUFFERING, (int) playbackStatesWhenSeekProcessed.get(2));
} }
public void testSeekDiscontinuity() throws Exception {
FakeTimeline timeline = new FakeTimeline(1);
ActionSchedule actionSchedule = new ActionSchedule.Builder("testSeekDiscontinuity")
.seek(10).build();
ExoPlayerTestRunner testRunner = new ExoPlayerTestRunner.Builder().setTimeline(timeline)
.setActionSchedule(actionSchedule).build().start().blockUntilEnded(TIMEOUT_MS);
testRunner.assertPositionDiscontinuityReasonsEqual(Player.DISCONTINUITY_REASON_SEEK);
}
public void testSeekDiscontinuityWithAdjustment() throws Exception {
FakeTimeline timeline = new FakeTimeline(1);
FakeMediaSource mediaSource = new FakeMediaSource(timeline, null, Builder.VIDEO_FORMAT) {
@Override
protected FakeMediaPeriod createFakeMediaPeriod(MediaPeriodId id,
TrackGroupArray trackGroupArray, Allocator allocator) {
return new FakeMediaPeriod(trackGroupArray) {
@Override
public long seekToUs(long positionUs) {
return positionUs + 10; // Adjusts the requested seek position.
}
};
}
};
ActionSchedule actionSchedule = new ActionSchedule.Builder("testSeekDiscontinuityAdjust")
.waitForPlaybackState(Player.STATE_READY).seek(10).build();
ExoPlayerTestRunner testRunner = new ExoPlayerTestRunner.Builder().setMediaSource(mediaSource)
.setActionSchedule(actionSchedule).build().start().blockUntilEnded(TIMEOUT_MS);
testRunner.assertPositionDiscontinuityReasonsEqual(Player.DISCONTINUITY_REASON_SEEK,
Player.DISCONTINUITY_REASON_SEEK_ADJUSTMENT);
}
public void testInternalDiscontinuity() throws Exception {
FakeTimeline timeline = new FakeTimeline(1);
FakeMediaSource mediaSource = new FakeMediaSource(timeline, null, Builder.VIDEO_FORMAT) {
@Override
protected FakeMediaPeriod createFakeMediaPeriod(MediaPeriodId id,
TrackGroupArray trackGroupArray, Allocator allocator) {
return new FakeMediaPeriod(trackGroupArray) {
boolean discontinuityRead;
@Override
public long readDiscontinuity() {
if (!discontinuityRead) {
discontinuityRead = true;
return 10; // Return a discontinuity.
}
return C.TIME_UNSET;
}
};
}
};
ActionSchedule actionSchedule = new ActionSchedule.Builder("testInternalDiscontinuity")
.waitForPlaybackState(Player.STATE_READY).build();
ExoPlayerTestRunner testRunner = new ExoPlayerTestRunner.Builder().setMediaSource(mediaSource)
.setActionSchedule(actionSchedule).build().start().blockUntilEnded(TIMEOUT_MS);
testRunner.assertPositionDiscontinuityReasonsEqual(Player.DISCONTINUITY_REASON_INTERNAL);
}
public void testAllActivatedTrackSelectionAreReleasedForSinglePeriod() throws Exception { public void testAllActivatedTrackSelectionAreReleasedForSinglePeriod() throws Exception {
Timeline timeline = new FakeTimeline(/* windowCount= */ 1); Timeline timeline = new FakeTimeline(/* windowCount= */ 1);
MediaSource mediaSource = MediaSource mediaSource =

View File

@ -467,7 +467,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
case ExoPlayerImplInternal.MSG_SOURCE_INFO_REFRESHED: { case ExoPlayerImplInternal.MSG_SOURCE_INFO_REFRESHED: {
int prepareAcks = msg.arg1; int prepareAcks = msg.arg1;
int seekAcks = msg.arg2; int seekAcks = msg.arg2;
handlePlaybackInfo((PlaybackInfo) msg.obj, prepareAcks, seekAcks, false); handlePlaybackInfo((PlaybackInfo) msg.obj, prepareAcks, seekAcks, false,
/* ignored */ DISCONTINUITY_REASON_INTERNAL);
break; break;
} }
case ExoPlayerImplInternal.MSG_TRACKS_CHANGED: { case ExoPlayerImplInternal.MSG_TRACKS_CHANGED: {
@ -485,11 +486,13 @@ import java.util.concurrent.CopyOnWriteArraySet;
} }
case ExoPlayerImplInternal.MSG_SEEK_ACK: { case ExoPlayerImplInternal.MSG_SEEK_ACK: {
boolean seekPositionAdjusted = msg.arg1 != 0; boolean seekPositionAdjusted = msg.arg1 != 0;
handlePlaybackInfo((PlaybackInfo) msg.obj, 0, 1, seekPositionAdjusted); handlePlaybackInfo((PlaybackInfo) msg.obj, 0, 1, seekPositionAdjusted,
DISCONTINUITY_REASON_SEEK_ADJUSTMENT);
break; break;
} }
case ExoPlayerImplInternal.MSG_POSITION_DISCONTINUITY: { case ExoPlayerImplInternal.MSG_POSITION_DISCONTINUITY: {
handlePlaybackInfo((PlaybackInfo) msg.obj, 0, 0, true); @DiscontinuityReason int discontinuityReason = msg.arg1;
handlePlaybackInfo((PlaybackInfo) msg.obj, 0, 0, true, discontinuityReason);
break; break;
} }
case ExoPlayerImplInternal.MSG_PLAYBACK_PARAMETERS_CHANGED: { case ExoPlayerImplInternal.MSG_PLAYBACK_PARAMETERS_CHANGED: {
@ -515,7 +518,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
} }
private void handlePlaybackInfo(PlaybackInfo playbackInfo, int prepareAcks, int seekAcks, private void handlePlaybackInfo(PlaybackInfo playbackInfo, int prepareAcks, int seekAcks,
boolean positionDiscontinuity) { boolean positionDiscontinuity, @DiscontinuityReason int positionDiscontinuityReason) {
Assertions.checkNotNull(playbackInfo.timeline); Assertions.checkNotNull(playbackInfo.timeline);
pendingPrepareAcks -= prepareAcks; pendingPrepareAcks -= prepareAcks;
pendingSeekAcks -= seekAcks; pendingSeekAcks -= seekAcks;
@ -536,9 +539,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
} }
if (positionDiscontinuity) { if (positionDiscontinuity) {
for (Player.EventListener listener : listeners) { for (Player.EventListener listener : listeners) {
listener.onPositionDiscontinuity( listener.onPositionDiscontinuity(positionDiscontinuityReason);
seekAcks > 0 ? DISCONTINUITY_REASON_INTERNAL : DISCONTINUITY_REASON_PERIOD_TRANSITION
);
} }
} }
} }

View File

@ -514,6 +514,10 @@ import java.io.IOException;
long periodPositionUs = playingPeriodHolder.mediaPeriod.readDiscontinuity(); long periodPositionUs = playingPeriodHolder.mediaPeriod.readDiscontinuity();
if (periodPositionUs != C.TIME_UNSET) { if (periodPositionUs != C.TIME_UNSET) {
resetRendererPosition(periodPositionUs); resetRendererPosition(periodPositionUs);
playbackInfo = playbackInfo.fromNewPosition(playbackInfo.periodId, periodPositionUs,
playbackInfo.contentPositionUs);
eventHandler.obtainMessage(MSG_POSITION_DISCONTINUITY, Player.DISCONTINUITY_REASON_INTERNAL,
0, playbackInfo).sendToTarget();
} else { } else {
rendererPositionUs = mediaClock.syncAndGetPositionUs(); rendererPositionUs = mediaClock.syncAndGetPositionUs();
periodPositionUs = playingPeriodHolder.toPeriodTime(rendererPositionUs); periodPositionUs = playingPeriodHolder.toPeriodTime(rendererPositionUs);
@ -875,7 +879,10 @@ import java.io.IOException;
long periodPositionUs = playingPeriodHolder.updatePeriodTrackSelection( long periodPositionUs = playingPeriodHolder.updatePeriodTrackSelection(
playbackInfo.positionUs, recreateStreams, streamResetFlags); playbackInfo.positionUs, recreateStreams, streamResetFlags);
if (periodPositionUs != playbackInfo.positionUs) { if (periodPositionUs != playbackInfo.positionUs) {
playbackInfo.positionUs = periodPositionUs; playbackInfo = playbackInfo.fromNewPosition(playbackInfo.periodId, periodPositionUs,
playbackInfo.contentPositionUs);
eventHandler.obtainMessage(MSG_POSITION_DISCONTINUITY, Player.DISCONTINUITY_REASON_INTERNAL,
0, playbackInfo).sendToTarget();
resetRendererPosition(periodPositionUs); resetRendererPosition(periodPositionUs);
} }
@ -1262,7 +1269,8 @@ import java.io.IOException;
playbackInfo = playbackInfo.fromNewPosition(playingPeriodHolder.info.id, playbackInfo = playbackInfo.fromNewPosition(playingPeriodHolder.info.id,
playingPeriodHolder.info.startPositionUs, playingPeriodHolder.info.contentPositionUs); playingPeriodHolder.info.startPositionUs, playingPeriodHolder.info.contentPositionUs);
updatePlaybackPositions(); updatePlaybackPositions();
eventHandler.obtainMessage(MSG_POSITION_DISCONTINUITY, playbackInfo).sendToTarget(); eventHandler.obtainMessage(MSG_POSITION_DISCONTINUITY,
Player.DISCONTINUITY_REASON_PERIOD_TRANSITION, 0, playbackInfo).sendToTarget();
} }
if (readingPeriodHolder.info.isFinal) { if (readingPeriodHolder.info.isFinal) {

View File

@ -243,7 +243,7 @@ public interface Player {
*/ */
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef({DISCONTINUITY_REASON_PERIOD_TRANSITION, DISCONTINUITY_REASON_SEEK, @IntDef({DISCONTINUITY_REASON_PERIOD_TRANSITION, DISCONTINUITY_REASON_SEEK,
DISCONTINUITY_REASON_INTERNAL}) DISCONTINUITY_REASON_SEEK_ADJUSTMENT, DISCONTINUITY_REASON_INTERNAL})
public @interface DiscontinuityReason {} public @interface DiscontinuityReason {}
/** /**
* Automatic playback transition from one period in the timeline to the next. The period index may * Automatic playback transition from one period in the timeline to the next. The period index may
@ -254,10 +254,15 @@ public interface Player {
* Seek within the current period or to another period. * Seek within the current period or to another period.
*/ */
int DISCONTINUITY_REASON_SEEK = 1; int DISCONTINUITY_REASON_SEEK = 1;
/**
* Seek adjustment due to being unable to seek to the requested position or because the seek was
* permitted to be inexact.
*/
int DISCONTINUITY_REASON_SEEK_ADJUSTMENT = 2;
/** /**
* Discontinuity introduced internally by the source. * Discontinuity introduced internally by the source.
*/ */
int DISCONTINUITY_REASON_INTERNAL = 2; int DISCONTINUITY_REASON_INTERNAL = 3;
/** /**
* Register a listener to receive events from the player. The listener's methods will be called on * Register a listener to receive events from the player. The listener's methods will be called on

View File

@ -23,6 +23,7 @@ import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.LoadControl; import com.google.android.exoplayer2.LoadControl;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Player.DiscontinuityReason;
import com.google.android.exoplayer2.Renderer; import com.google.android.exoplayer2.Renderer;
import com.google.android.exoplayer2.RenderersFactory; import com.google.android.exoplayer2.RenderersFactory;
import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.SimpleExoPlayer;
@ -38,6 +39,7 @@ import com.google.android.exoplayer2.trackselection.MappingTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.video.VideoRendererEventListener; import com.google.android.exoplayer2.video.VideoRendererEventListener;
import java.util.ArrayList;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -317,11 +319,11 @@ public final class ExoPlayerTestRunner extends Player.DefaultEventListener {
private final LinkedList<Timeline> timelines; private final LinkedList<Timeline> timelines;
private final LinkedList<Object> manifests; private final LinkedList<Object> manifests;
private final LinkedList<Integer> periodIndices; private final LinkedList<Integer> periodIndices;
private final ArrayList<Integer> discontinuityReasons;
private SimpleExoPlayer player; private SimpleExoPlayer player;
private Exception exception; private Exception exception;
private TrackGroupArray trackGroups; private TrackGroupArray trackGroups;
private int positionDiscontinuityCount;
private boolean playerWasPrepared; private boolean playerWasPrepared;
private ExoPlayerTestRunner(PlayerFactory playerFactory, MediaSource mediaSource, private ExoPlayerTestRunner(PlayerFactory playerFactory, MediaSource mediaSource,
@ -337,6 +339,7 @@ public final class ExoPlayerTestRunner extends Player.DefaultEventListener {
this.timelines = new LinkedList<>(); this.timelines = new LinkedList<>();
this.manifests = new LinkedList<>(); this.manifests = new LinkedList<>();
this.periodIndices = new LinkedList<>(); this.periodIndices = new LinkedList<>();
this.discontinuityReasons = new ArrayList<>();
this.endedCountDownLatch = new CountDownLatch(1); this.endedCountDownLatch = new CountDownLatch(1);
this.playerThread = new HandlerThread("ExoPlayerTest thread"); this.playerThread = new HandlerThread("ExoPlayerTest thread");
playerThread.start(); playerThread.start();
@ -439,13 +442,24 @@ public final class ExoPlayerTestRunner extends Player.DefaultEventListener {
} }
/** /**
* Asserts that the number of reported discontinuities by * Asserts that {@link Player.EventListener#onPositionDiscontinuity(int)} was not called.
* {@link Player.EventListener#onPositionDiscontinuity(int)} is equal to the provided number.
*
* @param expectedCount The expected number of position discontinuities.
*/ */
public void assertPositionDiscontinuityCount(int expectedCount) { public void assertNoPositionDiscontinuities() {
Assert.assertEquals(expectedCount, positionDiscontinuityCount); Assert.assertTrue(discontinuityReasons.isEmpty());
}
/**
* Asserts that the discontinuity reasons reported by
* {@link Player.EventListener#onPositionDiscontinuity(int)} are equal to the provided values.
*
* @param discontinuityReasons The expected discontinuity reasons.
*/
public void assertPositionDiscontinuityReasonsEqual(
@DiscontinuityReason int... discontinuityReasons) {
Assert.assertEquals(discontinuityReasons.length, this.discontinuityReasons.size());
for (int i = 0; i < discontinuityReasons.length; i++) {
Assert.assertEquals(discontinuityReasons[i], (int) this.discontinuityReasons.get(i));
}
} }
/** /**
@ -522,7 +536,7 @@ public final class ExoPlayerTestRunner extends Player.DefaultEventListener {
@Override @Override
public void onPositionDiscontinuity(@Player.DiscontinuityReason int reason) { public void onPositionDiscontinuity(@Player.DiscontinuityReason int reason) {
positionDiscontinuityCount++; discontinuityReasons.add(reason);
periodIndices.add(player.getCurrentPeriodIndex()); periodIndices.add(player.getCurrentPeriodIndex());
} }