Commit e4263c4a by kimvde Committed by bachinger

Add fast forward and rewind methods to Player

PiperOrigin-RevId: 378104210
parent d0dc72fb
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
* Add `needsReconfiguration` API to the `MediaCodecAdapter` interface. * Add `needsReconfiguration` API to the `MediaCodecAdapter` interface.
* Update `MediaItem.Builder` javadoc to discourage calling setters that * Update `MediaItem.Builder` javadoc to discourage calling setters that
will be (currently) ignored if another setter is not also called. will be (currently) ignored if another setter is not also called.
* Add `fastForward` and `rewind` methods to `Player`.
* Extractors: * Extractors:
* Add support for MPEG-H 3D Audio in MP4 extractors * Add support for MPEG-H 3D Audio in MP4 extractors
([#8860](https://github.com/google/ExoPlayer/pull/8860)). ([#8860](https://github.com/google/ExoPlayer/pull/8860)).
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.ext.cast; package com.google.android.exoplayer2.ext.cast;
import static com.google.android.exoplayer2.util.Assertions.checkArgument;
import static com.google.android.exoplayer2.util.Util.castNonNull; import static com.google.android.exoplayer2.util.Util.castNonNull;
import static java.lang.Math.min; import static java.lang.Math.min;
...@@ -141,6 +142,8 @@ public final class CastPlayer extends BasePlayer { ...@@ -141,6 +142,8 @@ public final class CastPlayer extends BasePlayer {
private int pendingSeekWindowIndex; private int pendingSeekWindowIndex;
private long pendingSeekPositionMs; private long pendingSeekPositionMs;
@Nullable private PositionInfo pendingMediaItemRemovalPosition; @Nullable private PositionInfo pendingMediaItemRemovalPosition;
private long fastForwardIncrementMs;
private long rewindIncrementMs;
/** /**
* Creates a new cast player that uses a {@link DefaultMediaItemConverter}. * Creates a new cast player that uses a {@link DefaultMediaItemConverter}.
...@@ -178,6 +181,8 @@ public final class CastPlayer extends BasePlayer { ...@@ -178,6 +181,8 @@ public final class CastPlayer extends BasePlayer {
availableCommands = new Commands.Builder().addAll(PERMANENT_AVAILABLE_COMMANDS).build(); availableCommands = new Commands.Builder().addAll(PERMANENT_AVAILABLE_COMMANDS).build();
pendingSeekWindowIndex = C.INDEX_UNSET; pendingSeekWindowIndex = C.INDEX_UNSET;
pendingSeekPositionMs = C.TIME_UNSET; pendingSeekPositionMs = C.TIME_UNSET;
fastForwardIncrementMs = DEFAULT_FAST_FORWARD_INCREMENT_MS;
rewindIncrementMs = DEFAULT_REWIND_INCREMENT_MS;
SessionManager sessionManager = castContext.getSessionManager(); SessionManager sessionManager = castContext.getSessionManager();
sessionManager.addSessionManagerListener(statusListener, CastSession.class); sessionManager.addSessionManagerListener(statusListener, CastSession.class);
...@@ -412,6 +417,28 @@ public final class CastPlayer extends BasePlayer { ...@@ -412,6 +417,28 @@ public final class CastPlayer extends BasePlayer {
} }
@Override @Override
public void setFastForwardIncrement(long fastForwardIncrementMs) {
checkArgument(fastForwardIncrementMs > 0);
this.fastForwardIncrementMs = fastForwardIncrementMs;
}
@Override
public long getFastForwardIncrement() {
return fastForwardIncrementMs;
}
@Override
public void setRewindIncrement(long rewindIncrementMs) {
checkArgument(rewindIncrementMs > 0);
this.rewindIncrementMs = rewindIncrementMs;
}
@Override
public long getRewindIncrement() {
return rewindIncrementMs;
}
@Override
public void setPlaybackParameters(PlaybackParameters playbackParameters) { public void setPlaybackParameters(PlaybackParameters playbackParameters) {
// Unsupported by the RemoteMediaClient API. Do nothing. // Unsupported by the RemoteMediaClient API. Do nothing.
} }
......
...@@ -38,6 +38,8 @@ import static com.google.android.exoplayer2.Player.COMMAND_SET_SHUFFLE_MODE; ...@@ -38,6 +38,8 @@ 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.DEFAULT_FAST_FORWARD_INCREMENT_MS;
import static com.google.android.exoplayer2.Player.DEFAULT_REWIND_INCREMENT_MS;
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_REMOVE; import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_REMOVE;
import static com.google.android.exoplayer2.Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED; import static com.google.android.exoplayer2.Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
...@@ -1103,6 +1105,98 @@ public class CastPlayerTest { ...@@ -1103,6 +1105,98 @@ public class CastPlayerTest {
} }
@Test @Test
@SuppressWarnings("deprecation") // Mocks deprecated method used by the CastPlayer.
public void fastForward_notifiesPositionDiscontinuity() {
when(mockRemoteMediaClient.seek(anyLong())).thenReturn(mockPendingResult);
int[] mediaQueueItemIds = new int[] {1};
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
int currentItemId = 1;
int[] streamTypes = new int[] {MediaInfo.STREAM_TYPE_BUFFERED};
long[] durationsMs = new long[] {2 * DEFAULT_FAST_FORWARD_INCREMENT_MS};
long positionMs = 0;
castPlayer.addMediaItems(mediaItems);
updateTimeLine(
mediaItems, mediaQueueItemIds, currentItemId, streamTypes, durationsMs, positionMs);
castPlayer.fastForward();
Player.PositionInfo oldPosition =
new Player.PositionInfo(
/* windowUid= */ 1,
/* windowIndex= */ 0,
/* periodUid= */ 1,
/* periodIndex= */ 0,
/* positionMs= */ 0,
/* contentPositionMs= */ 0,
/* adGroupIndex= */ C.INDEX_UNSET,
/* adIndexInAdGroup= */ C.INDEX_UNSET);
Player.PositionInfo newPosition =
new Player.PositionInfo(
/* windowUid= */ 1,
/* windowIndex= */ 0,
/* periodUid= */ 1,
/* periodIndex= */ 0,
/* positionMs= */ DEFAULT_FAST_FORWARD_INCREMENT_MS,
/* contentPositionMs= */ DEFAULT_FAST_FORWARD_INCREMENT_MS,
/* adGroupIndex= */ C.INDEX_UNSET,
/* adIndexInAdGroup= */ C.INDEX_UNSET);
InOrder inOrder = Mockito.inOrder(mockListener);
inOrder.verify(mockListener).onPositionDiscontinuity(eq(Player.DISCONTINUITY_REASON_SEEK));
inOrder
.verify(mockListener)
.onPositionDiscontinuity(
eq(oldPosition), eq(newPosition), eq(Player.DISCONTINUITY_REASON_SEEK));
inOrder.verify(mockListener, never()).onPositionDiscontinuity(anyInt());
inOrder.verify(mockListener, never()).onPositionDiscontinuity(any(), any(), anyInt());
}
@Test
@SuppressWarnings("deprecation") // Mocks deprecated method used by the CastPlayer.
public void rewind_notifiesPositionDiscontinuity() {
when(mockRemoteMediaClient.seek(anyLong())).thenReturn(mockPendingResult);
int[] mediaQueueItemIds = new int[] {1};
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
int currentItemId = 1;
int[] streamTypes = new int[] {MediaInfo.STREAM_TYPE_BUFFERED};
long[] durationsMs = new long[] {3 * DEFAULT_REWIND_INCREMENT_MS};
long positionMs = 2 * DEFAULT_REWIND_INCREMENT_MS;
castPlayer.addMediaItems(mediaItems);
updateTimeLine(
mediaItems, mediaQueueItemIds, currentItemId, streamTypes, durationsMs, positionMs);
castPlayer.rewind();
Player.PositionInfo oldPosition =
new Player.PositionInfo(
/* windowUid= */ 1,
/* windowIndex= */ 0,
/* periodUid= */ 1,
/* periodIndex= */ 0,
/* positionMs= */ 2 * DEFAULT_REWIND_INCREMENT_MS,
/* contentPositionMs= */ 2 * DEFAULT_REWIND_INCREMENT_MS,
/* adGroupIndex= */ C.INDEX_UNSET,
/* adIndexInAdGroup= */ C.INDEX_UNSET);
Player.PositionInfo newPosition =
new Player.PositionInfo(
/* windowUid= */ 1,
/* windowIndex= */ 0,
/* periodUid= */ 1,
/* periodIndex= */ 0,
/* positionMs= */ DEFAULT_REWIND_INCREMENT_MS,
/* contentPositionMs= */ DEFAULT_REWIND_INCREMENT_MS,
/* adGroupIndex= */ C.INDEX_UNSET,
/* adIndexInAdGroup= */ C.INDEX_UNSET);
InOrder inOrder = Mockito.inOrder(mockListener);
inOrder.verify(mockListener).onPositionDiscontinuity(eq(Player.DISCONTINUITY_REASON_SEEK));
inOrder
.verify(mockListener)
.onPositionDiscontinuity(
eq(oldPosition), eq(newPosition), eq(Player.DISCONTINUITY_REASON_SEEK));
inOrder.verify(mockListener, never()).onPositionDiscontinuity(anyInt());
inOrder.verify(mockListener, never()).onPositionDiscontinuity(any(), any(), anyInt());
}
@Test
public void isCommandAvailable_isTrueForAvailableCommands() { public void isCommandAvailable_isTrueForAvailableCommands() {
int[] mediaQueueItemIds = new int[] {1, 2}; int[] mediaQueueItemIds = new int[] {1, 2};
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds); List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
......
...@@ -15,6 +15,9 @@ ...@@ -15,6 +15,9 @@
*/ */
package com.google.android.exoplayer2; package com.google.android.exoplayer2;
import static java.lang.Math.max;
import static java.lang.Math.min;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.util.Collections; import java.util.Collections;
...@@ -119,6 +122,16 @@ public abstract class BasePlayer implements Player { ...@@ -119,6 +122,16 @@ public abstract class BasePlayer implements Player {
} }
@Override @Override
public final void fastForward() {
seekToOffset(getFastForwardIncrement());
}
@Override
public final void rewind() {
seekToOffset(-getRewindIncrement());
}
@Override
public final boolean hasPrevious() { public final boolean hasPrevious() {
return getPreviousWindowIndex() != C.INDEX_UNSET; return getPreviousWindowIndex() != C.INDEX_UNSET;
} }
...@@ -246,12 +259,6 @@ public abstract class BasePlayer implements Player { ...@@ -246,12 +259,6 @@ public abstract class BasePlayer implements Player {
: timeline.getWindow(getCurrentWindowIndex(), window).getDurationMs(); : timeline.getWindow(getCurrentWindowIndex(), window).getDurationMs();
} }
@RepeatMode
private int getRepeatModeForNavigation() {
@RepeatMode int repeatMode = getRepeatMode();
return repeatMode == REPEAT_MODE_ONE ? REPEAT_MODE_OFF : repeatMode;
}
protected Commands getAvailableCommands(Commands permanentAvailableCommands) { protected Commands getAvailableCommands(Commands permanentAvailableCommands) {
return new Commands.Builder() return new Commands.Builder()
.addAll(permanentAvailableCommands) .addAll(permanentAvailableCommands)
...@@ -262,4 +269,20 @@ public abstract class BasePlayer implements Player { ...@@ -262,4 +269,20 @@ public abstract class BasePlayer implements Player {
.addIf(COMMAND_SEEK_TO_MEDIA_ITEM, !isPlayingAd()) .addIf(COMMAND_SEEK_TO_MEDIA_ITEM, !isPlayingAd())
.build(); .build();
} }
@RepeatMode
private int getRepeatModeForNavigation() {
@RepeatMode int repeatMode = getRepeatMode();
return repeatMode == REPEAT_MODE_ONE ? REPEAT_MODE_OFF : repeatMode;
}
private void seekToOffset(long offsetMs) {
long positionMs = getCurrentPosition() + offsetMs;
long durationMs = getDuration();
if (durationMs != C.TIME_UNSET) {
positionMs = min(positionMs, durationMs);
}
positionMs = max(positionMs, 0);
seekTo(positionMs);
}
} }
...@@ -248,6 +248,36 @@ public class ForwardingPlayer implements Player { ...@@ -248,6 +248,36 @@ public class ForwardingPlayer implements Player {
} }
@Override @Override
public void setFastForwardIncrement(long fastForwardIncrementMs) {
player.setFastForwardIncrement(fastForwardIncrementMs);
}
@Override
public long getFastForwardIncrement() {
return player.getFastForwardIncrement();
}
@Override
public void fastForward() {
player.fastForward();
}
@Override
public void setRewindIncrement(long rewindIncrementMs) {
player.setRewindIncrement(rewindIncrementMs);
}
@Override
public long getRewindIncrement() {
return player.getRewindIncrement();
}
@Override
public void rewind() {
player.rewind();
}
@Override
public boolean hasPrevious() { public boolean hasPrevious() {
return player.hasPrevious(); return player.hasPrevious();
} }
......
...@@ -22,6 +22,7 @@ import android.view.SurfaceHolder; ...@@ -22,6 +22,7 @@ import android.view.SurfaceHolder;
import android.view.SurfaceView; import android.view.SurfaceView;
import android.view.TextureView; import android.view.TextureView;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.IntRange;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.audio.AudioAttributes; import com.google.android.exoplayer2.audio.AudioAttributes;
import com.google.android.exoplayer2.audio.AudioListener; import com.google.android.exoplayer2.audio.AudioListener;
...@@ -842,6 +843,11 @@ public interface Player { ...@@ -842,6 +843,11 @@ public interface Player {
default void onMetadata(Metadata metadata) {} default void onMetadata(Metadata metadata) {}
} }
/** The default {@link #fastForward()} increment, in milliseconds. */
long DEFAULT_FAST_FORWARD_INCREMENT_MS = 15_000;
/** The default {@link #rewind()} increment, in milliseconds. */
long DEFAULT_REWIND_INCREMENT_MS = 5000;
/** /**
* 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}.
...@@ -1537,6 +1543,46 @@ public interface Player { ...@@ -1537,6 +1543,46 @@ public interface Player {
void seekTo(int windowIndex, long positionMs); void seekTo(int windowIndex, long positionMs);
/** /**
* Sets the {@link #fastForward()} increment.
*
* @param fastForwardIncrementMs The fast forward increment, in milliseconds.
* @throws IllegalArgumentException If {@code fastForwardIncrementMs} is non-positive.
*/
void setFastForwardIncrement(@IntRange(from = 1) long fastForwardIncrementMs);
/**
* Returns the {@link #fastForward()} increment.
*
* <p>The default value is {@link #DEFAULT_FAST_FORWARD_INCREMENT_MS}.
*
* @return The fast forward increment, in milliseconds.
*/
long getFastForwardIncrement();
/** Seeks forward in the current window by {@link #getFastForwardIncrement()} milliseconds. */
void fastForward();
/**
* Sets the {@link #rewind()} increment.
*
* @param rewindIncrementMs The rewind increment, in milliseconds.
* @throws IllegalArgumentException If {@code rewindIncrementMs} is non-positive.
*/
void setRewindIncrement(@IntRange(from = 1) long rewindIncrementMs);
/**
* Returns the {@link #rewind()} increment.
*
* <p>The default value is {@link #DEFAULT_REWIND_INCREMENT_MS}.
*
* @return The rewind increment, in milliseconds.
*/
long getRewindIncrement();
/** Seeks back in the current window by {@link #getRewindIncrement()} milliseconds. */
void rewind();
/**
* Returns whether a previous window exists, which may depend on the current repeat mode and * Returns whether a previous window exists, which may depend on the current repeat mode and
* whether shuffle mode is enabled. * whether shuffle mode is enabled.
* *
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2; package com.google.android.exoplayer2;
import static com.google.android.exoplayer2.util.Assertions.checkArgument;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull; import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import static com.google.android.exoplayer2.util.Assertions.checkState; import static com.google.android.exoplayer2.util.Assertions.checkState;
import static com.google.android.exoplayer2.util.Util.castNonNull; import static com.google.android.exoplayer2.util.Util.castNonNull;
...@@ -102,6 +103,8 @@ import java.util.concurrent.CopyOnWriteArraySet; ...@@ -102,6 +103,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
private boolean pauseAtEndOfMediaItems; private boolean pauseAtEndOfMediaItems;
private Commands availableCommands; private Commands availableCommands;
private MediaMetadata mediaMetadata; private MediaMetadata mediaMetadata;
private long fastForwardIncrementMs;
private long rewindIncrementMs;
// Playback information when there is no pending seek/set source operation. // Playback information when there is no pending seek/set source operation.
private PlaybackInfo playbackInfo; private PlaybackInfo playbackInfo;
...@@ -210,6 +213,8 @@ import java.util.concurrent.CopyOnWriteArraySet; ...@@ -210,6 +213,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
.add(COMMAND_SEEK_TO_MEDIA_ITEM) .add(COMMAND_SEEK_TO_MEDIA_ITEM)
.build(); .build();
mediaMetadata = MediaMetadata.EMPTY; mediaMetadata = MediaMetadata.EMPTY;
fastForwardIncrementMs = DEFAULT_FAST_FORWARD_INCREMENT_MS;
rewindIncrementMs = DEFAULT_REWIND_INCREMENT_MS;
maskingWindowIndex = C.INDEX_UNSET; maskingWindowIndex = C.INDEX_UNSET;
playbackInfoUpdateHandler = clock.createHandler(applicationLooper, /* callback= */ null); playbackInfoUpdateHandler = clock.createHandler(applicationLooper, /* callback= */ null);
playbackInfoUpdateListener = playbackInfoUpdateListener =
...@@ -711,6 +716,28 @@ import java.util.concurrent.CopyOnWriteArraySet; ...@@ -711,6 +716,28 @@ import java.util.concurrent.CopyOnWriteArraySet;
} }
@Override @Override
public void setFastForwardIncrement(long fastForwardIncrementMs) {
checkArgument(fastForwardIncrementMs > 0);
this.fastForwardIncrementMs = fastForwardIncrementMs;
}
@Override
public long getFastForwardIncrement() {
return fastForwardIncrementMs;
}
@Override
public void setRewindIncrement(long rewindIncrementMs) {
checkArgument(rewindIncrementMs > 0);
this.rewindIncrementMs = rewindIncrementMs;
}
@Override
public long getRewindIncrement() {
return rewindIncrementMs;
}
@Override
public void setPlaybackParameters(PlaybackParameters playbackParameters) { public void setPlaybackParameters(PlaybackParameters playbackParameters) {
if (playbackParameters == null) { if (playbackParameters == null) {
playbackParameters = PlaybackParameters.DEFAULT; playbackParameters = PlaybackParameters.DEFAULT;
......
...@@ -1553,6 +1553,30 @@ public class SimpleExoPlayer extends BasePlayer ...@@ -1553,6 +1553,30 @@ public class SimpleExoPlayer extends BasePlayer
} }
@Override @Override
public void setFastForwardIncrement(long fastForwardIncrementMs) {
verifyApplicationThread();
player.setFastForwardIncrement(fastForwardIncrementMs);
}
@Override
public long getFastForwardIncrement() {
verifyApplicationThread();
return player.getFastForwardIncrement();
}
@Override
public void setRewindIncrement(long rewindIncrementMs) {
verifyApplicationThread();
player.setRewindIncrement(rewindIncrementMs);
}
@Override
public long getRewindIncrement() {
verifyApplicationThread();
return player.getRewindIncrement();
}
@Override
public void setPlaybackParameters(PlaybackParameters playbackParameters) { public void setPlaybackParameters(PlaybackParameters playbackParameters) {
verifyApplicationThread(); verifyApplicationThread();
player.setPlaybackParameters(playbackParameters); player.setPlaybackParameters(playbackParameters);
......
...@@ -38,6 +38,8 @@ import static com.google.android.exoplayer2.Player.COMMAND_SET_SHUFFLE_MODE; ...@@ -38,6 +38,8 @@ 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.DEFAULT_FAST_FORWARD_INCREMENT_MS;
import static com.google.android.exoplayer2.Player.DEFAULT_REWIND_INCREMENT_MS;
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;
import static com.google.android.exoplayer2.robolectric.TestPlayerRunHelper.playUntilStartOfWindow; import static com.google.android.exoplayer2.robolectric.TestPlayerRunHelper.playUntilStartOfWindow;
...@@ -10335,6 +10337,128 @@ public final class ExoPlayerTest { ...@@ -10335,6 +10337,128 @@ public final class ExoPlayerTest {
} }
@Test @Test
public void fastForward_callsOnPositionDiscontinuity() throws Exception {
ExoPlayer player = new TestExoPlayerBuilder(context).build();
Player.Listener listener = mock(Player.Listener.class);
player.addListener(listener);
Timeline fakeTimeline =
new FakeTimeline(
new TimelineWindowDefinition(
/* isSeekable= */ true,
/* isDynamic= */ true,
/* durationUs= */ C.msToUs(2 * DEFAULT_FAST_FORWARD_INCREMENT_MS)));
player.setMediaSource(new FakeMediaSource(fakeTimeline));
player.prepare();
TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_READY);
player.fastForward();
ArgumentCaptor<Player.PositionInfo> oldPosition =
ArgumentCaptor.forClass(Player.PositionInfo.class);
ArgumentCaptor<Player.PositionInfo> newPosition =
ArgumentCaptor.forClass(Player.PositionInfo.class);
verify(listener, never())
.onPositionDiscontinuity(any(), any(), not(eq(Player.DISCONTINUITY_REASON_SEEK)));
verify(listener)
.onPositionDiscontinuity(
oldPosition.capture(), newPosition.capture(), eq(Player.DISCONTINUITY_REASON_SEEK));
List<Player.PositionInfo> oldPositions = oldPosition.getAllValues();
List<Player.PositionInfo> newPositions = newPosition.getAllValues();
assertThat(oldPositions.get(0).windowIndex).isEqualTo(0);
assertThat(oldPositions.get(0).positionMs).isEqualTo(0);
assertThat(oldPositions.get(0).contentPositionMs).isEqualTo(0);
assertThat(newPositions.get(0).windowIndex).isEqualTo(0);
assertThat(newPositions.get(0).positionMs).isEqualTo(DEFAULT_FAST_FORWARD_INCREMENT_MS);
assertThat(newPositions.get(0).contentPositionMs).isEqualTo(DEFAULT_FAST_FORWARD_INCREMENT_MS);
player.release();
}
@Test
public void fastForward_pastDuration_seeksToDuration() throws Exception {
ExoPlayer player = new TestExoPlayerBuilder(context).build();
Timeline fakeTimeline =
new FakeTimeline(
new TimelineWindowDefinition(
/* isSeekable= */ true,
/* isDynamic= */ true,
/* durationUs= */ C.msToUs(DEFAULT_FAST_FORWARD_INCREMENT_MS / 2)));
player.setMediaSource(new FakeMediaSource(fakeTimeline));
player.prepare();
TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_READY);
player.fastForward();
assertThat(player.getCurrentPosition()).isEqualTo(DEFAULT_FAST_FORWARD_INCREMENT_MS / 2);
player.release();
}
@Test
public void rewind_callsOnPositionDiscontinuity() throws Exception {
ExoPlayer player = new TestExoPlayerBuilder(context).build();
Player.Listener listener = mock(Player.Listener.class);
player.addListener(listener);
Timeline fakeTimeline =
new FakeTimeline(
new TimelineWindowDefinition(
/* isSeekable= */ true,
/* isDynamic= */ true,
/* durationUs= */ C.msToUs(3 * DEFAULT_REWIND_INCREMENT_MS)));
player.setMediaSource(new FakeMediaSource(fakeTimeline));
player.prepare();
TestPlayerRunHelper.playUntilPosition(
player, /* windowIndex= */ 0, /* positionMs= */ 2 * DEFAULT_REWIND_INCREMENT_MS);
player.rewind();
ArgumentCaptor<Player.PositionInfo> oldPosition =
ArgumentCaptor.forClass(Player.PositionInfo.class);
ArgumentCaptor<Player.PositionInfo> newPosition =
ArgumentCaptor.forClass(Player.PositionInfo.class);
verify(listener, never())
.onPositionDiscontinuity(any(), any(), not(eq(Player.DISCONTINUITY_REASON_SEEK)));
verify(listener)
.onPositionDiscontinuity(
oldPosition.capture(), newPosition.capture(), eq(Player.DISCONTINUITY_REASON_SEEK));
List<Player.PositionInfo> oldPositions = oldPosition.getAllValues();
List<Player.PositionInfo> newPositions = newPosition.getAllValues();
assertThat(oldPositions.get(0).windowIndex).isEqualTo(0);
assertThat(oldPositions.get(0).positionMs)
.isIn(Range.closed(2 * DEFAULT_REWIND_INCREMENT_MS - 20, 2 * DEFAULT_REWIND_INCREMENT_MS));
assertThat(oldPositions.get(0).contentPositionMs)
.isIn(Range.closed(2 * DEFAULT_REWIND_INCREMENT_MS - 20, 2 * DEFAULT_REWIND_INCREMENT_MS));
assertThat(newPositions.get(0).windowIndex).isEqualTo(0);
assertThat(newPositions.get(0).positionMs)
.isIn(Range.closed(DEFAULT_REWIND_INCREMENT_MS - 20, DEFAULT_REWIND_INCREMENT_MS));
assertThat(newPositions.get(0).contentPositionMs)
.isIn(Range.closed(DEFAULT_REWIND_INCREMENT_MS - 20, DEFAULT_REWIND_INCREMENT_MS));
player.release();
}
@Test
public void rewind_pastZero_seeksToZero() throws Exception {
ExoPlayer player = new TestExoPlayerBuilder(context).build();
Timeline fakeTimeline =
new FakeTimeline(
new TimelineWindowDefinition(
/* isSeekable= */ true,
/* isDynamic= */ true,
/* durationUs= */ C.msToUs(DEFAULT_REWIND_INCREMENT_MS)));
player.setMediaSource(new FakeMediaSource(fakeTimeline));
player.prepare();
TestPlayerRunHelper.playUntilPosition(
player, /* windowIndex= */ 0, /* positionMs= */ DEFAULT_REWIND_INCREMENT_MS / 2);
player.rewind();
assertThat(player.getCurrentPosition()).isEqualTo(0);
player.release();
}
@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);
......
...@@ -300,6 +300,26 @@ public class StubExoPlayer extends BasePlayer implements ExoPlayer { ...@@ -300,6 +300,26 @@ public class StubExoPlayer extends BasePlayer implements ExoPlayer {
} }
@Override @Override
public void setFastForwardIncrement(long fastForwardIncrementMs) {
throw new UnsupportedOperationException();
}
@Override
public long getFastForwardIncrement() {
throw new UnsupportedOperationException();
}
@Override
public void setRewindIncrement(long rewindIncrementMs) {
throw new UnsupportedOperationException();
}
@Override
public long getRewindIncrement() {
throw new UnsupportedOperationException();
}
@Override
public void setPlaybackParameters(PlaybackParameters playbackParameters) { public void setPlaybackParameters(PlaybackParameters playbackParameters) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
......
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