Commit af05ceac by tonihei Committed by Oliver Woodman

Call onSeekProcessed immediately after seek command.

OnSeekProcessed is documented to be called as soon as all neccessary state changes
as a result of the seek have been made. As we now mask the state changes directly
when calling seekTo, we can also call this callback immediately without changing
the semantics of the method.

Our tests often use this callback as a way to wait for the internal player
to receive all pending commands and then report back. This is a special test
requirement for cases where we want to make sure no further updates happen as
a result of the player handling commands. To facilitate that, a new action is
added with a more descriptive name that achieves the same goal.

PiperOrigin-RevId: 303296210
parent 09be441e
...@@ -75,7 +75,6 @@ import java.util.concurrent.TimeoutException; ...@@ -75,7 +75,6 @@ import java.util.concurrent.TimeoutException;
@RepeatMode private int repeatMode; @RepeatMode private int repeatMode;
private boolean shuffleModeEnabled; private boolean shuffleModeEnabled;
private int pendingOperationAcks; private int pendingOperationAcks;
private boolean hasPendingSeek;
private boolean hasPendingDiscontinuity; private boolean hasPendingDiscontinuity;
@DiscontinuityReason private int pendingDiscontinuityReason; @DiscontinuityReason private int pendingDiscontinuityReason;
@PlayWhenReadyChangeReason private int pendingPlayWhenReadyChangeReason; @PlayWhenReadyChangeReason private int pendingPlayWhenReadyChangeReason;
...@@ -552,7 +551,6 @@ import java.util.concurrent.TimeoutException; ...@@ -552,7 +551,6 @@ import java.util.concurrent.TimeoutException;
if (windowIndex < 0 || (!timeline.isEmpty() && windowIndex >= timeline.getWindowCount())) { if (windowIndex < 0 || (!timeline.isEmpty() && windowIndex >= timeline.getWindowCount())) {
throw new IllegalSeekPositionException(timeline, windowIndex, positionMs); throw new IllegalSeekPositionException(timeline, windowIndex, positionMs);
} }
hasPendingSeek = true;
pendingOperationAcks++; pendingOperationAcks++;
if (isPlayingAd()) { if (isPlayingAd()) {
// TODO: Investigate adding support for seeking during ads. This is complicated to do in // TODO: Investigate adding support for seeking during ads. This is complicated to do in
...@@ -580,7 +578,7 @@ import java.util.concurrent.TimeoutException; ...@@ -580,7 +578,7 @@ import java.util.concurrent.TimeoutException;
/* positionDiscontinuityReason= */ DISCONTINUITY_REASON_SEEK, /* positionDiscontinuityReason= */ DISCONTINUITY_REASON_SEEK,
/* ignored */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, /* ignored */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
/* ignored */ PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST, /* ignored */ PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST,
/* seekProcessed= */ false); /* seekProcessed= */ true);
} }
/** @deprecated Use {@link #setPlaybackSpeed(float)} instead. */ /** @deprecated Use {@link #setPlaybackSpeed(float)} instead. */
...@@ -889,9 +887,7 @@ import java.util.concurrent.TimeoutException; ...@@ -889,9 +887,7 @@ import java.util.concurrent.TimeoutException;
// Update the masking variables, which are used when the timeline becomes empty. // Update the masking variables, which are used when the timeline becomes empty.
resetMaskingPosition(); resetMaskingPosition();
} }
boolean seekProcessed = hasPendingSeek;
boolean positionDiscontinuity = hasPendingDiscontinuity; boolean positionDiscontinuity = hasPendingDiscontinuity;
hasPendingSeek = false;
hasPendingDiscontinuity = false; hasPendingDiscontinuity = false;
updatePlaybackInfo( updatePlaybackInfo(
playbackInfoUpdate.playbackInfo, playbackInfoUpdate.playbackInfo,
...@@ -899,7 +895,7 @@ import java.util.concurrent.TimeoutException; ...@@ -899,7 +895,7 @@ import java.util.concurrent.TimeoutException;
pendingDiscontinuityReason, pendingDiscontinuityReason,
TIMELINE_CHANGE_REASON_SOURCE_UPDATE, TIMELINE_CHANGE_REASON_SOURCE_UPDATE,
pendingPlayWhenReadyChangeReason, pendingPlayWhenReadyChangeReason,
seekProcessed); /* seekProcessed= */ false);
} }
} }
......
...@@ -319,7 +319,6 @@ public final class AnalyticsCollectorTest { ...@@ -319,7 +319,6 @@ public final class AnalyticsCollectorTest {
.waitForIsLoading(true) .waitForIsLoading(true)
.waitForIsLoading(false) .waitForIsLoading(false)
.seek(/* windowIndex= */ 1, /* positionMs= */ 0) .seek(/* windowIndex= */ 1, /* positionMs= */ 0)
.waitForSeekProcessed()
.play() .play()
.build(); .build();
TestAnalyticsListener listener = runAnalyticsTest(mediaSource, actionSchedule); TestAnalyticsListener listener = runAnalyticsTest(mediaSource, actionSchedule);
...@@ -1088,7 +1087,7 @@ public final class AnalyticsCollectorTest { ...@@ -1088,7 +1087,7 @@ public final class AnalyticsCollectorTest {
midrollAd /* seek adjustment */, midrollAd /* seek adjustment */,
contentAfterMidroll /* ad transition */); contentAfterMidroll /* ad transition */);
assertThat(listener.getEvents(EVENT_SEEK_STARTED)).containsExactly(contentBeforeMidroll); assertThat(listener.getEvents(EVENT_SEEK_STARTED)).containsExactly(contentBeforeMidroll);
assertThat(listener.getEvents(EVENT_SEEK_PROCESSED)).containsExactly(midrollAd); assertThat(listener.getEvents(EVENT_SEEK_PROCESSED)).containsExactly(contentAfterMidroll);
assertThat(listener.getEvents(EVENT_LOADING_CHANGED)) assertThat(listener.getEvents(EVENT_LOADING_CHANGED))
.containsExactly( .containsExactly(
contentBeforeMidroll, contentBeforeMidroll,
......
...@@ -1024,12 +1024,12 @@ public abstract class Action { ...@@ -1024,12 +1024,12 @@ public abstract class Action {
} }
} }
/** Waits for {@link Player.EventListener#onSeekProcessed()}. */ /** Waits until the player acknowledged all pending player commands. */
public static final class WaitForSeekProcessed extends Action { public static final class WaitForPendingPlayerCommands extends Action {
/** @param tag A tag to use for logging. */ /** @param tag A tag to use for logging. */
public WaitForSeekProcessed(String tag) { public WaitForPendingPlayerCommands(String tag) {
super(tag, "WaitForSeekProcessed"); super(tag, "WaitForPendingPlayerCommands");
} }
@Override @Override
...@@ -1042,14 +1042,14 @@ public abstract class Action { ...@@ -1042,14 +1042,14 @@ public abstract class Action {
if (nextAction == null) { if (nextAction == null) {
return; return;
} }
player.addListener( // Send message to player that will arrive after all other pending commands. Thus, the message
new Player.EventListener() { // execution on the app thread will also happen after all other pending command
@Override // acknowledgements have arrived back on the app thread.
public void onSeekProcessed() { player
player.removeListener(this); .createMessage(
nextAction.schedule(player, trackSelector, surface, handler); (type, data) -> nextAction.schedule(player, trackSelector, surface, handler))
} .setHandler(Util.createHandler())
}); .send();
} }
@Override @Override
......
...@@ -45,10 +45,10 @@ import com.google.android.exoplayer2.testutil.Action.Stop; ...@@ -45,10 +45,10 @@ import com.google.android.exoplayer2.testutil.Action.Stop;
import com.google.android.exoplayer2.testutil.Action.ThrowPlaybackException; import com.google.android.exoplayer2.testutil.Action.ThrowPlaybackException;
import com.google.android.exoplayer2.testutil.Action.WaitForIsLoading; import com.google.android.exoplayer2.testutil.Action.WaitForIsLoading;
import com.google.android.exoplayer2.testutil.Action.WaitForMessage; import com.google.android.exoplayer2.testutil.Action.WaitForMessage;
import com.google.android.exoplayer2.testutil.Action.WaitForPendingPlayerCommands;
import com.google.android.exoplayer2.testutil.Action.WaitForPlayWhenReady; import com.google.android.exoplayer2.testutil.Action.WaitForPlayWhenReady;
import com.google.android.exoplayer2.testutil.Action.WaitForPlaybackState; import com.google.android.exoplayer2.testutil.Action.WaitForPlaybackState;
import com.google.android.exoplayer2.testutil.Action.WaitForPositionDiscontinuity; import com.google.android.exoplayer2.testutil.Action.WaitForPositionDiscontinuity;
import com.google.android.exoplayer2.testutil.Action.WaitForSeekProcessed;
import com.google.android.exoplayer2.testutil.Action.WaitForTimelineChanged; import com.google.android.exoplayer2.testutil.Action.WaitForTimelineChanged;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
...@@ -198,17 +198,19 @@ public final class ActionSchedule { ...@@ -198,17 +198,19 @@ public final class ActionSchedule {
*/ */
public Builder seekAndWait(long positionMs) { public Builder seekAndWait(long positionMs) {
return apply(new Seek(tag, positionMs)) return apply(new Seek(tag, positionMs))
.apply(new WaitForSeekProcessed(tag))
.apply(new WaitForPlaybackState(tag, Player.STATE_READY)); .apply(new WaitForPlaybackState(tag, Player.STATE_READY));
} }
/** /**
* Schedules a delay until the player indicates that a seek has been processed. * Schedules a delay until all pending player commands have been handled.
*
* <p>A command is considered as having been handled if it arrived on the playback thread and
* the player acknowledged that it received the command back to the app thread.
* *
* @return The builder, for convenience. * @return The builder, for convenience.
*/ */
public Builder waitForSeekProcessed() { public Builder waitForPendingPlayerCommands() {
return apply(new WaitForSeekProcessed(tag)); return apply(new WaitForPendingPlayerCommands(tag));
} }
/** /**
......
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