Commit efc709f3 by tonihei Committed by Oliver Woodman

Remove initial seek counting in ExoPlayerImplInternal.

We can acknoledge seeks before preparation finished immediately now,
because ExoPlayerImpl won't leave the masking state until the first prepare
operation is processed.

As a side effect, it also cleans up the responsibility of the callbacks.
Prepares are always acknowledged with a SOURCE_INFO_REFRESHED, while seeks
are always acknowledged with a SEEK_ACK.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=177144089
parent 1ae50cb9
...@@ -306,11 +306,19 @@ public final class ExoPlayerTest extends TestCase { ...@@ -306,11 +306,19 @@ public final class ExoPlayerTest extends TestCase {
public void testSeekProcessedCallback() throws Exception { public void testSeekProcessedCallback() throws Exception {
Timeline timeline = new FakeTimeline(/* windowCount= */ 2); Timeline timeline = new FakeTimeline(/* windowCount= */ 2);
ActionSchedule actionSchedule = new ActionSchedule.Builder("testSeekProcessedCallback") ActionSchedule actionSchedule = new ActionSchedule.Builder("testSeekProcessedCallback")
// Initial seek before timeline preparation finished. // Initial seek before timeline preparation started. Expect immediate seek processed while
.pause().seek(10).waitForPlaybackState(Player.STATE_READY) // the player is still in STATE_IDLE.
// Re-seek to same position, start playback and wait until playback reaches second window. .pause().seek(5)
.seek(10).play().waitForPositionDiscontinuity() // Wait until the media source starts preparing and issue more initial seeks. Expect only
// Seek twice in concession, expecting the first seek to be replaced. // one seek processed after the source has been prepared.
.waitForPlaybackState(Player.STATE_BUFFERING).seek(2).seek(10)
// Wait until media source prepared and re-seek to same position. Expect a seek processed
// while still being in STATE_READY.
.waitForPlaybackState(Player.STATE_READY).seek(10)
// Start playback and wait until playback reaches second window.
.play().waitForPositionDiscontinuity()
// Seek twice in concession, expecting the first seek to be replaced (and thus except only
// on seek processed callback).
.seek(5).seek(60).build(); .seek(5).seek(60).build();
final List<Integer> playbackStatesWhenSeekProcessed = new ArrayList<>(); final List<Integer> playbackStatesWhenSeekProcessed = new ArrayList<>();
Player.EventListener eventListener = new Player.DefaultEventListener() { Player.EventListener eventListener = new Player.DefaultEventListener() {
...@@ -329,10 +337,11 @@ public final class ExoPlayerTest extends TestCase { ...@@ -329,10 +337,11 @@ public final class ExoPlayerTest extends TestCase {
new ExoPlayerTestRunner.Builder() new ExoPlayerTestRunner.Builder()
.setTimeline(timeline).setEventListener(eventListener).setActionSchedule(actionSchedule) .setTimeline(timeline).setEventListener(eventListener).setActionSchedule(actionSchedule)
.build().start().blockUntilEnded(TIMEOUT_MS); .build().start().blockUntilEnded(TIMEOUT_MS);
assertEquals(3, playbackStatesWhenSeekProcessed.size()); assertEquals(4, playbackStatesWhenSeekProcessed.size());
assertEquals(Player.STATE_BUFFERING, (int) playbackStatesWhenSeekProcessed.get(0)); assertEquals(Player.STATE_IDLE, (int) playbackStatesWhenSeekProcessed.get(0));
assertEquals(Player.STATE_READY, (int) playbackStatesWhenSeekProcessed.get(1)); assertEquals(Player.STATE_BUFFERING, (int) playbackStatesWhenSeekProcessed.get(1));
assertEquals(Player.STATE_BUFFERING, (int) playbackStatesWhenSeekProcessed.get(2)); assertEquals(Player.STATE_READY, (int) playbackStatesWhenSeekProcessed.get(2));
assertEquals(Player.STATE_BUFFERING, (int) playbackStatesWhenSeekProcessed.get(3));
} }
public void testSeekDiscontinuity() throws Exception { public void testSeekDiscontinuity() throws Exception {
...@@ -742,7 +751,7 @@ public final class ExoPlayerTest extends TestCase { ...@@ -742,7 +751,7 @@ public final class ExoPlayerTest extends TestCase {
.waitForPlaybackState(Player.STATE_IDLE) .waitForPlaybackState(Player.STATE_IDLE)
// If we were still using the first timeline, this would throw. // If we were still using the first timeline, this would throw.
.seek(/* windowIndex= */ 1, /* positionMs= */ 0) .seek(/* windowIndex= */ 1, /* positionMs= */ 0)
.prepareSource(secondSource) .prepareSource(secondSource, /* resetPosition= */ false, /* resetState= */ true)
.build(); .build();
ExoPlayerTestRunner testRunner = new ExoPlayerTestRunner.Builder() ExoPlayerTestRunner testRunner = new ExoPlayerTestRunner.Builder()
.setTimeline(timeline) .setTimeline(timeline)
......
...@@ -450,8 +450,7 @@ import java.util.concurrent.CopyOnWriteArraySet; ...@@ -450,8 +450,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
} }
case ExoPlayerImplInternal.MSG_SOURCE_INFO_REFRESHED: { case ExoPlayerImplInternal.MSG_SOURCE_INFO_REFRESHED: {
int prepareOrStopAcks = msg.arg1; int prepareOrStopAcks = msg.arg1;
int seekAcks = msg.arg2; handlePlaybackInfo((PlaybackInfo) msg.obj, prepareOrStopAcks, 0, false,
handlePlaybackInfo((PlaybackInfo) msg.obj, prepareOrStopAcks, seekAcks, false,
/* ignored */ DISCONTINUITY_REASON_INTERNAL); /* ignored */ DISCONTINUITY_REASON_INTERNAL);
break; break;
} }
...@@ -510,13 +509,13 @@ import java.util.concurrent.CopyOnWriteArraySet; ...@@ -510,13 +509,13 @@ import java.util.concurrent.CopyOnWriteArraySet;
boolean timelineOrManifestChanged = this.playbackInfo.timeline != playbackInfo.timeline boolean timelineOrManifestChanged = this.playbackInfo.timeline != playbackInfo.timeline
|| this.playbackInfo.manifest != playbackInfo.manifest; || this.playbackInfo.manifest != playbackInfo.manifest;
this.playbackInfo = playbackInfo; this.playbackInfo = playbackInfo;
if (playbackInfo.timeline.isEmpty()) {
// Update the masking variables, which are used when the timeline is empty.
maskingPeriodIndex = 0;
maskingWindowIndex = 0;
maskingWindowPositionMs = 0;
}
if (timelineOrManifestChanged || waitingForInitialTimeline) { if (timelineOrManifestChanged || waitingForInitialTimeline) {
if (playbackInfo.timeline.isEmpty()) {
// Update the masking variables, which are used when the timeline becomes empty.
maskingPeriodIndex = 0;
maskingWindowIndex = 0;
maskingWindowPositionMs = 0;
}
@Player.TimelineChangeReason int reason = waitingForInitialTimeline @Player.TimelineChangeReason int reason = waitingForInitialTimeline
? Player.TIMELINE_CHANGE_REASON_PREPARED : Player.TIMELINE_CHANGE_REASON_DYNAMIC; ? Player.TIMELINE_CHANGE_REASON_PREPARED : Player.TIMELINE_CHANGE_REASON_DYNAMIC;
waitingForInitialTimeline = false; waitingForInitialTimeline = false;
......
...@@ -125,8 +125,7 @@ import java.io.IOException; ...@@ -125,8 +125,7 @@ import java.io.IOException;
private long elapsedRealtimeUs; private long elapsedRealtimeUs;
private int pendingPrepareCount; private int pendingPrepareCount;
private int pendingInitialSeekCount; private SeekPosition pendingInitialSeekPosition;
private SeekPosition pendingSeekPosition;
private long rendererPositionUs; private long rendererPositionUs;
private MediaPeriodHolder loadingPeriodHolder; private MediaPeriodHolder loadingPeriodHolder;
...@@ -631,8 +630,9 @@ import java.io.IOException; ...@@ -631,8 +630,9 @@ import java.io.IOException;
private void seekToInternal(SeekPosition seekPosition) throws ExoPlaybackException { private void seekToInternal(SeekPosition seekPosition) throws ExoPlaybackException {
Timeline timeline = playbackInfo.timeline; Timeline timeline = playbackInfo.timeline;
if (timeline == null) { if (timeline == null) {
pendingInitialSeekCount++; pendingInitialSeekPosition = seekPosition;
pendingSeekPosition = seekPosition; eventHandler.obtainMessage(MSG_SEEK_ACK, /* seekAdjusted = */ 0, 0,
playbackInfo.copyWithTimeline(Timeline.EMPTY, null)).sendToTarget();
return; return;
} }
...@@ -781,7 +781,7 @@ import java.io.IOException; ...@@ -781,7 +781,7 @@ import java.io.IOException;
} }
int prepareOrStopAcks = pendingPrepareCount + 1; int prepareOrStopAcks = pendingPrepareCount + 1;
pendingPrepareCount = 0; pendingPrepareCount = 0;
notifySourceInfoRefresh(prepareOrStopAcks, 0, publicPlaybackInfo); notifySourceInfoRefresh(prepareOrStopAcks, publicPlaybackInfo);
loadControl.onStopped(); loadControl.onStopped();
setState(Player.STATE_IDLE); setState(Player.STATE_IDLE);
} }
...@@ -825,6 +825,7 @@ import java.io.IOException; ...@@ -825,6 +825,7 @@ import java.io.IOException;
? 0 ? 0
: timeline.getWindow(timeline.getFirstWindowIndex(shuffleModeEnabled), window) : timeline.getWindow(timeline.getFirstWindowIndex(shuffleModeEnabled), window)
.firstPeriodIndex; .firstPeriodIndex;
pendingInitialSeekPosition = null;
playbackInfo = playbackInfo.fromNewPosition(firstPeriodIndex, C.TIME_UNSET, C.TIME_UNSET); playbackInfo = playbackInfo.fromNewPosition(firstPeriodIndex, C.TIME_UNSET, C.TIME_UNSET);
} else { } else {
// The new start position is the current playback position. // The new start position is the current playback position.
...@@ -1009,15 +1010,13 @@ import java.io.IOException; ...@@ -1009,15 +1010,13 @@ import java.io.IOException;
if (oldTimeline == null) { if (oldTimeline == null) {
int processedPrepareAcks = pendingPrepareCount; int processedPrepareAcks = pendingPrepareCount;
pendingPrepareCount = 0; pendingPrepareCount = 0;
if (pendingInitialSeekCount > 0) { if (pendingInitialSeekPosition != null) {
Pair<Integer, Long> periodPosition = resolveSeekPosition(pendingSeekPosition); Pair<Integer, Long> periodPosition = resolveSeekPosition(pendingInitialSeekPosition);
int processedInitialSeekCount = pendingInitialSeekCount; pendingInitialSeekPosition = null;
pendingInitialSeekCount = 0;
pendingSeekPosition = null;
if (periodPosition == null) { if (periodPosition == null) {
// The seek position was valid for the timeline that it was performed into, but the // The seek position was valid for the timeline that it was performed into, but the
// timeline has changed and a suitable seek position could not be resolved in the new one. // timeline has changed and a suitable seek position could not be resolved in the new one.
handleSourceInfoRefreshEndedPlayback(processedPrepareAcks, processedInitialSeekCount); handleSourceInfoRefreshEndedPlayback(processedPrepareAcks);
} else { } else {
int periodIndex = periodPosition.first; int periodIndex = periodPosition.first;
long positionUs = periodPosition.second; long positionUs = periodPosition.second;
...@@ -1025,11 +1024,11 @@ import java.io.IOException; ...@@ -1025,11 +1024,11 @@ import java.io.IOException;
mediaPeriodInfoSequence.resolvePeriodPositionForAds(periodIndex, positionUs); mediaPeriodInfoSequence.resolvePeriodPositionForAds(periodIndex, positionUs);
playbackInfo = playbackInfo.fromNewPosition(periodId, periodId.isAd() ? 0 : positionUs, playbackInfo = playbackInfo.fromNewPosition(periodId, periodId.isAd() ? 0 : positionUs,
positionUs); positionUs);
notifySourceInfoRefresh(processedPrepareAcks, processedInitialSeekCount); notifySourceInfoRefresh(processedPrepareAcks);
} }
} else if (playbackInfo.startPositionUs == C.TIME_UNSET) { } else if (playbackInfo.startPositionUs == C.TIME_UNSET) {
if (timeline.isEmpty()) { if (timeline.isEmpty()) {
handleSourceInfoRefreshEndedPlayback(processedPrepareAcks, 0); handleSourceInfoRefreshEndedPlayback(processedPrepareAcks);
} else { } else {
Pair<Integer, Long> defaultPosition = getPeriodPosition(timeline, Pair<Integer, Long> defaultPosition = getPeriodPosition(timeline,
timeline.getFirstWindowIndex(shuffleModeEnabled), C.TIME_UNSET); timeline.getFirstWindowIndex(shuffleModeEnabled), C.TIME_UNSET);
...@@ -1039,10 +1038,10 @@ import java.io.IOException; ...@@ -1039,10 +1038,10 @@ import java.io.IOException;
startPositionUs); startPositionUs);
playbackInfo = playbackInfo.fromNewPosition(periodId, playbackInfo = playbackInfo.fromNewPosition(periodId,
periodId.isAd() ? 0 : startPositionUs, startPositionUs); periodId.isAd() ? 0 : startPositionUs, startPositionUs);
notifySourceInfoRefresh(processedPrepareAcks, 0); notifySourceInfoRefresh(processedPrepareAcks);
} }
} else { } else {
notifySourceInfoRefresh(processedPrepareAcks, 0); notifySourceInfoRefresh(processedPrepareAcks);
} }
return; return;
} }
...@@ -1169,30 +1168,29 @@ import java.io.IOException; ...@@ -1169,30 +1168,29 @@ import java.io.IOException;
} }
private void handleSourceInfoRefreshEndedPlayback() { private void handleSourceInfoRefreshEndedPlayback() {
handleSourceInfoRefreshEndedPlayback(0, 0); handleSourceInfoRefreshEndedPlayback(0);
} }
private void handleSourceInfoRefreshEndedPlayback(int prepareAcks, int seekAcks) { private void handleSourceInfoRefreshEndedPlayback(int prepareAcks) {
setState(Player.STATE_ENDED); setState(Player.STATE_ENDED);
// Reset, but retain the source so that it can still be used should a seek occur. // Reset, but retain the source so that it can still be used should a seek occur.
resetInternal(false, true); resetInternal(false, true);
// Set the playback position to 0 for notifying the eventHandler (instead of C.TIME_UNSET). // Set the playback position to 0 for notifying the eventHandler (instead of C.TIME_UNSET).
notifySourceInfoRefresh(prepareAcks, seekAcks, notifySourceInfoRefresh(prepareAcks,
playbackInfo.fromNewPosition(playbackInfo.periodId.periodIndex, 0, C.TIME_UNSET)); playbackInfo.fromNewPosition(playbackInfo.periodId.periodIndex, 0, C.TIME_UNSET));
} }
private void notifySourceInfoRefresh() { private void notifySourceInfoRefresh() {
notifySourceInfoRefresh(0, 0); notifySourceInfoRefresh(0);
} }
private void notifySourceInfoRefresh(int prepareOrStopAcks, int seekAcks) { private void notifySourceInfoRefresh(int prepareOrStopAcks) {
notifySourceInfoRefresh(prepareOrStopAcks, seekAcks, playbackInfo); notifySourceInfoRefresh(prepareOrStopAcks, playbackInfo);
} }
private void notifySourceInfoRefresh(int prepareOrStopAcks, int seekAcks, private void notifySourceInfoRefresh(int prepareOrStopAcks, PlaybackInfo playbackInfo) {
PlaybackInfo playbackInfo) { eventHandler.obtainMessage(MSG_SOURCE_INFO_REFRESHED, prepareOrStopAcks, 0, playbackInfo)
eventHandler.obtainMessage(MSG_SOURCE_INFO_REFRESHED, prepareOrStopAcks, seekAcks, .sendToTarget();
playbackInfo).sendToTarget();
} }
/** /**
......
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