From 6c7a2c3cfd953ff664d61992bc2c17165d90a1a9 Mon Sep 17 00:00:00 2001 From: tonihei Date: Fri, 28 Feb 2020 12:13:58 +0000 Subject: [PATCH] Ensure seeks are not ignored in ended state. We should only ignore seek to the current position if we are currently READY or BUFFERING. Also, pending initial seek positions should only be saved while we have an empty timeline, independent of the player state. Issue:#6886 PiperOrigin-RevId: 297813854 --- .../exoplayer2/ExoPlayerImplInternal.java | 10 +++-- .../android/exoplayer2/ExoPlayerTest.java | 37 +++++++++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java index 1ba8a6d4a8..16320637d5 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java @@ -902,12 +902,14 @@ import java.util.concurrent.atomic.AtomicBoolean; } try { - if (playbackInfo.timeline.isEmpty() || !playlist.isPrepared()) { + if (playbackInfo.timeline.isEmpty()) { // Save seek position for later, as we are still waiting for a prepared source. pendingInitialSeekPosition = seekPosition; } else if (resolvedSeekPosition == null) { // End playback, as we didn't manage to find a valid seek position. - setState(Player.STATE_ENDED); + if (playbackInfo.playbackState != Player.STATE_IDLE) { + setState(Player.STATE_ENDED); + } resetInternal( /* resetRenderers= */ false, /* resetPosition= */ true, @@ -926,7 +928,9 @@ import java.util.concurrent.atomic.AtomicBoolean; playingPeriodHolder.mediaPeriod.getAdjustedSeekPositionUs( newPeriodPositionUs, seekParameters); } - if (C.usToMs(newPeriodPositionUs) == C.usToMs(playbackInfo.positionUs)) { + if (C.usToMs(newPeriodPositionUs) == C.usToMs(playbackInfo.positionUs) + && (playbackInfo.playbackState == Player.STATE_BUFFERING + || playbackInfo.playbackState == Player.STATE_READY)) { // Seek will be performed to the current position. Do nothing. periodPositionUs = playbackInfo.positionUs; return; diff --git a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java index 1be3e2f7fe..decf198142 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java @@ -5728,6 +5728,43 @@ public final class ExoPlayerTest { assertThat(trackSelectionsAfterError.get().get(1)).isNotNull(); // Audio renderer. } + @Test + public void seekToCurrentPosition_inEndedState_switchesToBufferingStateAndContinuesPlayback() + throws Exception { + MediaSource mediaSource = new FakeMediaSource(new FakeTimeline(/* windowCount = */ 1)); + AtomicInteger windowIndexAfterFinalEndedState = new AtomicInteger(); + ActionSchedule actionSchedule = + new ActionSchedule.Builder("seekToCurrentPosition_inEndedState") + .waitForPlaybackState(Player.STATE_ENDED) + .addMediaSources(mediaSource) + .executeRunnable( + new PlayerRunnable() { + @Override + public void run(SimpleExoPlayer player) { + player.seekTo(player.getCurrentPosition()); + } + }) + .waitForPlaybackState(Player.STATE_READY) + .waitForPlaybackState(Player.STATE_ENDED) + .executeRunnable( + new PlayerRunnable() { + @Override + public void run(SimpleExoPlayer player) { + windowIndexAfterFinalEndedState.set(player.getCurrentWindowIndex()); + } + }) + .build(); + new Builder() + .setMediaSources(mediaSource) + .setActionSchedule(actionSchedule) + .build(context) + .start() + .blockUntilActionScheduleFinished(TIMEOUT_MS) + .blockUntilEnded(TIMEOUT_MS); + + assertThat(windowIndexAfterFinalEndedState.get()).isEqualTo(1); + } + // Internal methods. private static ActionSchedule.Builder addSurfaceSwitch(ActionSchedule.Builder builder) {