Commit c4e99902 by kimvde Committed by kim-vde

Add seekToPrevious method to Player

PiperOrigin-RevId: 383623440
parent d9618b51
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* Core Library: * Core Library:
* Add `needsReconfiguration` API to the `MediaCodecAdapter` interface. * Add `needsReconfiguration` API to the `MediaCodecAdapter` interface.
* Add `seekForward` and `seekBack` methods to `Player`. * Add `seekForward`, `seekBack` and `seekToPrevious` methods to `Player`.
* Make `Player` depend on the new `PlaybackException` class instead of * Make `Player` depend on the new `PlaybackException` class instead of
`ExoPlaybackException`: `ExoPlaybackException`:
* `Player.getPlayerError` now returns a `PlaybackException`. * `Player.getPlayerError` now returns a `PlaybackException`.
......
...@@ -145,6 +145,24 @@ public abstract class BasePlayer implements Player { ...@@ -145,6 +145,24 @@ public abstract class BasePlayer implements Player {
} }
@Override @Override
public final void seekToPrevious() {
Timeline timeline = getCurrentTimeline();
if (timeline.isEmpty() || isPlayingAd()) {
return;
}
boolean hasPrevious = hasPrevious();
if (isCurrentWindowLive() && !isCurrentWindowSeekable()) {
if (hasPrevious) {
previous();
}
} else if (hasPrevious && getCurrentPosition() <= MAX_POSITION_FOR_SEEK_TO_PREVIOUS_MS) {
previous();
} else {
seekTo(/* positionMs= */ 0);
}
}
@Override
public final boolean hasNext() { public final boolean hasNext() {
return getNextWindowIndex() != C.INDEX_UNSET; return getNextWindowIndex() != C.INDEX_UNSET;
} }
......
...@@ -278,6 +278,11 @@ public class ForwardingPlayer implements Player { ...@@ -278,6 +278,11 @@ public class ForwardingPlayer implements Player {
} }
@Override @Override
public void seekToPrevious() {
player.seekToPrevious();
}
@Override
public boolean hasNext() { public boolean hasNext() {
return player.hasNext(); return player.hasNext();
} }
......
...@@ -884,6 +884,9 @@ public interface Player { ...@@ -884,6 +884,9 @@ public interface Player {
default void onMetadata(Metadata metadata) {} default void onMetadata(Metadata metadata) {}
} }
/** The maximum position for which {@link #seekToPrevious()} seeks to the previous window. */
int MAX_POSITION_FOR_SEEK_TO_PREVIOUS_MS = 3000;
/** /**
* Playback state. One of {@link #STATE_IDLE}, {@link #STATE_BUFFERING}, {@link #STATE_READY} or * Playback state. One of {@link #STATE_IDLE}, {@link #STATE_BUFFERING}, {@link #STATE_READY} or
* {@link #STATE_ENDED}. * {@link #STATE_ENDED}.
...@@ -1654,6 +1657,27 @@ public interface Player { ...@@ -1654,6 +1657,27 @@ public interface Player {
void previous(); void previous();
/** /**
* Seeks to an earlier position in the current or previous window (if available). More precisely:
*
* <ul>
* <li>If the timeline is empty or seeking is not possible, does nothing.
* <li>Otherwise, if the current window is {@link #isCurrentWindowLive() live} and {@link
* #isCurrentWindowSeekable() unseekable}, then:
* <ul>
* <li>If {@link #hasPrevious() a previous window exists}, seeks to the default position
* of the previous window.
* <li>Otherwise, does nothing.
* </ul>
* <li>Otherwise, if {@link #hasPrevious() a previous window exists} and the {@link
* #getCurrentPosition() current position} is less than {@link
* #MAX_POSITION_FOR_SEEK_TO_PREVIOUS_MS}, seeks to the default position of the previous
* window.
* <li>Otherwise, seeks to 0 in the current window.
* </ul>
*/
void seekToPrevious();
/**
* Returns whether a next window exists, which may depend on the current repeat mode and whether * Returns whether a next window exists, which may depend on the current repeat mode and whether
* shuffle mode is enabled. * shuffle mode is enabled.
* *
......
...@@ -40,6 +40,7 @@ import static com.google.android.exoplayer2.Player.COMMAND_SET_SHUFFLE_MODE; ...@@ -40,6 +40,7 @@ import static com.google.android.exoplayer2.Player.COMMAND_SET_SHUFFLE_MODE;
import static com.google.android.exoplayer2.Player.COMMAND_SET_SPEED_AND_PITCH; import static com.google.android.exoplayer2.Player.COMMAND_SET_SPEED_AND_PITCH;
import static com.google.android.exoplayer2.Player.COMMAND_SET_VIDEO_SURFACE; import static com.google.android.exoplayer2.Player.COMMAND_SET_VIDEO_SURFACE;
import static com.google.android.exoplayer2.Player.COMMAND_SET_VOLUME; import static com.google.android.exoplayer2.Player.COMMAND_SET_VOLUME;
import static com.google.android.exoplayer2.Player.MAX_POSITION_FOR_SEEK_TO_PREVIOUS_MS;
import static com.google.android.exoplayer2.Player.STATE_ENDED; import static com.google.android.exoplayer2.Player.STATE_ENDED;
import static com.google.android.exoplayer2.robolectric.RobolectricUtil.runMainLooperUntil; import static com.google.android.exoplayer2.robolectric.RobolectricUtil.runMainLooperUntil;
import static com.google.android.exoplayer2.robolectric.TestPlayerRunHelper.playUntilPosition; import static com.google.android.exoplayer2.robolectric.TestPlayerRunHelper.playUntilPosition;
...@@ -10521,6 +10522,30 @@ public final class ExoPlayerTest { ...@@ -10521,6 +10522,30 @@ public final class ExoPlayerTest {
} }
@Test @Test
public void seekToPrevious_closeToStart_seeksToPreviousWindow() {
ExoPlayer player = new TestExoPlayerBuilder(context).build();
player.addMediaSources(ImmutableList.of(new FakeMediaSource(), new FakeMediaSource()));
player.seekTo(/* windowIndex= */ 1, /* positionMs= */ MAX_POSITION_FOR_SEEK_TO_PREVIOUS_MS);
player.seekToPrevious();
assertThat(player.getCurrentWindowIndex()).isEqualTo(0);
assertThat(player.getCurrentPosition()).isEqualTo(0);
}
@Test
public void seekToPrevious_notCloseToStart_seeksToZero() {
ExoPlayer player = new TestExoPlayerBuilder(context).build();
player.addMediaSources(ImmutableList.of(new FakeMediaSource(), new FakeMediaSource()));
player.seekTo(/* windowIndex= */ 1, /* positionMs= */ MAX_POSITION_FOR_SEEK_TO_PREVIOUS_MS + 1);
player.seekToPrevious();
assertThat(player.getCurrentWindowIndex()).isEqualTo(1);
assertThat(player.getCurrentPosition()).isEqualTo(0);
}
@Test
public void stop_doesNotCallOnPositionDiscontinuity() throws Exception { public void stop_doesNotCallOnPositionDiscontinuity() throws Exception {
ExoPlayer player = new TestExoPlayerBuilder(context).build(); ExoPlayer player = new TestExoPlayerBuilder(context).build();
Player.Listener listener = mock(Player.Listener.class); Player.Listener listener = mock(Player.Listener.class);
......
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