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