Add repeat mode mechanics to Exoplayer.

(Relating to GitHub Issue #2577)

All getter, setter and callbacks have been added and value of repeatMode is
passed to getNextXXXIndex methods.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=155071985
This commit is contained in:
tonihei 2017-05-04 06:11:12 -07:00 committed by Oliver Woodman
parent 3a5548d72a
commit 4d1826dd3d
14 changed files with 134 additions and 16 deletions

View File

@ -95,6 +95,11 @@ import java.util.Locale;
+ getStateString(state) + "]"); + getStateString(state) + "]");
} }
@Override
public void onRepeatModeChanged(@ExoPlayer.RepeatMode int repeatMode) {
Log.d(TAG, "repeatMode [" + getRepeatModeString(repeatMode) + "]");
}
@Override @Override
public void onPositionDiscontinuity() { public void onPositionDiscontinuity() {
Log.d(TAG, "positionDiscontinuity"); Log.d(TAG, "positionDiscontinuity");
@ -461,4 +466,12 @@ import java.util.Locale;
return enabled ? "[X]" : "[ ]"; return enabled ? "[X]" : "[ ]";
} }
private static String getRepeatModeString(@ExoPlayer.RepeatMode int repeatMode) {
switch (repeatMode) {
case ExoPlayer.REPEAT_MODE_OFF:
return "OFF";
default:
return "?";
}
}
} }

View File

@ -424,6 +424,11 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay
updateButtonVisibilities(); updateButtonVisibilities();
} }
@Override
public void onRepeatModeChanged(int repeatMode) {
// Do nothing.
}
@Override @Override
public void onPositionDiscontinuity() { public void onPositionDiscontinuity() {
if (needRetrySource) { if (needRetrySource) {

View File

@ -126,6 +126,11 @@ public class FlacPlaybackTest extends InstrumentationTestCase {
} }
} }
@Override
public void onRepeatModeChanged(int repeatMode) {
// Do nothing.
}
private void releasePlayerAndQuitLooper() { private void releasePlayerAndQuitLooper() {
player.release(); player.release();
Looper.myLooper().quit(); Looper.myLooper().quit();

View File

@ -126,6 +126,11 @@ public class OpusPlaybackTest extends InstrumentationTestCase {
} }
} }
@Override
public void onRepeatModeChanged(int repeatMode) {
// Do nothing.
}
private void releasePlayerAndQuitLooper() { private void releasePlayerAndQuitLooper() {
player.release(); player.release();
Looper.myLooper().quit(); Looper.myLooper().quit();

View File

@ -158,6 +158,11 @@ public class VpxPlaybackTest extends InstrumentationTestCase {
} }
} }
@Override
public void onRepeatModeChanged(int repeatMode) {
// Do nothing.
}
private void releasePlayerAndQuitLooper() { private void releasePlayerAndQuitLooper() {
player.release(); player.release();
Looper.myLooper().quit(); Looper.myLooper().quit();

View File

@ -340,6 +340,11 @@ public final class ExoPlayerTest extends TestCase {
} }
} }
@Override
public void onRepeatModeChanged(int repeatMode) {
// Do nothing.
}
@Override @Override
public void onTimelineChanged(Timeline timeline, Object manifest) { public void onTimelineChanged(Timeline timeline, Object manifest) {
sourceInfos.add(Pair.create(timeline, manifest)); sourceInfos.add(Pair.create(timeline, manifest));

View File

@ -154,6 +154,13 @@ public interface ExoPlayer {
*/ */
void onPlayerStateChanged(boolean playWhenReady, int playbackState); void onPlayerStateChanged(boolean playWhenReady, int playbackState);
/**
* Called when the value of {@link #getRepeatMode()} changes.
*
* @param repeatMode The {@link RepeatMode} used for playback.
*/
void onRepeatModeChanged(@RepeatMode int repeatMode);
/** /**
* Called when an error occurs. The playback state will transition to {@link #STATE_IDLE} * Called when an error occurs. The playback state will transition to {@link #STATE_IDLE}
* immediately after this method is called. The player instance can still be used, and * immediately after this method is called. The player instance can still be used, and
@ -262,7 +269,7 @@ public interface ExoPlayer {
*/ */
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef({REPEAT_MODE_OFF}) @IntDef({REPEAT_MODE_OFF})
@interface RepeatMode {} public @interface RepeatMode {}
/** /**
* Normal playback without repetition. * Normal playback without repetition.
*/ */
@ -328,6 +335,20 @@ public interface ExoPlayer {
*/ */
boolean getPlayWhenReady(); boolean getPlayWhenReady();
/**
* Sets the {@link RepeatMode} to be used for playback.
*
* @param repeatMode A repeat mode.
*/
void setRepeatMode(@RepeatMode int repeatMode);
/**
* Returns the current {@link RepeatMode} used for playback.
*
* @return The current repeat mode.
*/
@RepeatMode int getRepeatMode();
/** /**
* Whether the player is currently loading the source. * Whether the player is currently loading the source.
* *

View File

@ -51,6 +51,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
private boolean tracksSelected; private boolean tracksSelected;
private boolean playWhenReady; private boolean playWhenReady;
@RepeatMode int repeatMode;
private int playbackState; private int playbackState;
private int pendingSeekAcks; private int pendingSeekAcks;
private int pendingPrepareAcks; private int pendingPrepareAcks;
@ -83,6 +84,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
this.renderers = Assertions.checkNotNull(renderers); this.renderers = Assertions.checkNotNull(renderers);
this.trackSelector = Assertions.checkNotNull(trackSelector); this.trackSelector = Assertions.checkNotNull(trackSelector);
this.playWhenReady = false; this.playWhenReady = false;
this.repeatMode = REPEAT_MODE_OFF;
this.playbackState = STATE_IDLE; this.playbackState = STATE_IDLE;
this.listeners = new CopyOnWriteArraySet<>(); this.listeners = new CopyOnWriteArraySet<>();
emptyTrackSelections = new TrackSelectionArray(new TrackSelection[renderers.length]); emptyTrackSelections = new TrackSelectionArray(new TrackSelection[renderers.length]);
@ -101,7 +103,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
}; };
playbackInfo = new ExoPlayerImplInternal.PlaybackInfo(0, 0); playbackInfo = new ExoPlayerImplInternal.PlaybackInfo(0, 0);
internalPlayer = new ExoPlayerImplInternal(renderers, trackSelector, loadControl, playWhenReady, internalPlayer = new ExoPlayerImplInternal(renderers, trackSelector, loadControl, playWhenReady,
eventHandler, playbackInfo, this); repeatMode, eventHandler, playbackInfo, this);
} }
@Override @Override
@ -164,6 +166,22 @@ import java.util.concurrent.CopyOnWriteArraySet;
return playWhenReady; return playWhenReady;
} }
@Override
public void setRepeatMode(@RepeatMode int repeatMode) {
if (this.repeatMode != repeatMode) {
this.repeatMode = repeatMode;
internalPlayer.setRepeatMode(repeatMode);
for (EventListener listener : listeners) {
listener.onRepeatModeChanged(repeatMode);
}
}
}
@Override
public @RepeatMode int getRepeatMode() {
return repeatMode;
}
@Override @Override
public boolean isLoading() { public boolean isLoading() {
return isLoading; return isLoading;

View File

@ -112,6 +112,7 @@ import java.io.IOException;
private static final int MSG_SOURCE_CONTINUE_LOADING_REQUESTED = 9; private static final int MSG_SOURCE_CONTINUE_LOADING_REQUESTED = 9;
private static final int MSG_TRACK_SELECTION_INVALIDATED = 10; private static final int MSG_TRACK_SELECTION_INVALIDATED = 10;
private static final int MSG_CUSTOM = 11; private static final int MSG_CUSTOM = 11;
private static final int MSG_SET_REPEAT_MODE = 12;
private static final int PREPARING_SOURCE_INTERVAL_MS = 10; private static final int PREPARING_SOURCE_INTERVAL_MS = 10;
private static final int RENDERING_INTERVAL_MS = 10; private static final int RENDERING_INTERVAL_MS = 10;
@ -155,6 +156,7 @@ import java.io.IOException;
private boolean rebuffering; private boolean rebuffering;
private boolean isLoading; private boolean isLoading;
private int state; private int state;
private @ExoPlayer.RepeatMode int repeatMode;
private int customMessagesSent; private int customMessagesSent;
private int customMessagesProcessed; private int customMessagesProcessed;
private long elapsedRealtimeUs; private long elapsedRealtimeUs;
@ -170,12 +172,13 @@ import java.io.IOException;
private Timeline timeline; private Timeline timeline;
public ExoPlayerImplInternal(Renderer[] renderers, TrackSelector trackSelector, public ExoPlayerImplInternal(Renderer[] renderers, TrackSelector trackSelector,
LoadControl loadControl, boolean playWhenReady, Handler eventHandler, LoadControl loadControl, boolean playWhenReady, @ExoPlayer.RepeatMode int repeatMode,
PlaybackInfo playbackInfo, ExoPlayer player) { Handler eventHandler, PlaybackInfo playbackInfo, ExoPlayer player) {
this.renderers = renderers; this.renderers = renderers;
this.trackSelector = trackSelector; this.trackSelector = trackSelector;
this.loadControl = loadControl; this.loadControl = loadControl;
this.playWhenReady = playWhenReady; this.playWhenReady = playWhenReady;
this.repeatMode = repeatMode;
this.eventHandler = eventHandler; this.eventHandler = eventHandler;
this.state = ExoPlayer.STATE_IDLE; this.state = ExoPlayer.STATE_IDLE;
this.playbackInfo = playbackInfo; this.playbackInfo = playbackInfo;
@ -210,6 +213,10 @@ import java.io.IOException;
handler.obtainMessage(MSG_SET_PLAY_WHEN_READY, playWhenReady ? 1 : 0, 0).sendToTarget(); handler.obtainMessage(MSG_SET_PLAY_WHEN_READY, playWhenReady ? 1 : 0, 0).sendToTarget();
} }
public void setRepeatMode(@ExoPlayer.RepeatMode int repeatMode) {
handler.obtainMessage(MSG_SET_REPEAT_MODE, repeatMode, 0).sendToTarget();
}
public void seekTo(Timeline timeline, int windowIndex, long positionUs) { public void seekTo(Timeline timeline, int windowIndex, long positionUs) {
handler.obtainMessage(MSG_SEEK_TO, new SeekPosition(timeline, windowIndex, positionUs)) handler.obtainMessage(MSG_SEEK_TO, new SeekPosition(timeline, windowIndex, positionUs))
.sendToTarget(); .sendToTarget();
@ -304,6 +311,10 @@ import java.io.IOException;
setPlayWhenReadyInternal(msg.arg1 != 0); setPlayWhenReadyInternal(msg.arg1 != 0);
return true; return true;
} }
case MSG_SET_REPEAT_MODE: {
setRepeatModeInternal(msg.arg1);
return true;
}
case MSG_DO_SOME_WORK: { case MSG_DO_SOME_WORK: {
doSomeWork(); doSomeWork();
return true; return true;
@ -411,6 +422,10 @@ import java.io.IOException;
} }
} }
private void setRepeatModeInternal(@ExoPlayer.RepeatMode int repeatMode) {
this.repeatMode = repeatMode;
}
private void startRenderers() throws ExoPlaybackException { private void startRenderers() throws ExoPlaybackException {
rebuffering = false; rebuffering = false;
standaloneMediaClock.start(); standaloneMediaClock.start();
@ -959,8 +974,7 @@ import java.io.IOException;
while (periodHolder.next != null) { while (periodHolder.next != null) {
MediaPeriodHolder previousPeriodHolder = periodHolder; MediaPeriodHolder previousPeriodHolder = periodHolder;
periodHolder = periodHolder.next; periodHolder = periodHolder.next;
periodIndex = timeline.getNextPeriodIndex(periodIndex, period, window, periodIndex = timeline.getNextPeriodIndex(periodIndex, period, window, repeatMode);
ExoPlayer.REPEAT_MODE_OFF);
boolean isLastPeriod = isLastPeriod(periodIndex); boolean isLastPeriod = isLastPeriod(periodIndex);
timeline.getPeriod(periodIndex, period, true); timeline.getPeriod(periodIndex, period, true);
if (periodHolder.uid.equals(period.uid)) { if (periodHolder.uid.equals(period.uid)) {
@ -1022,8 +1036,7 @@ import java.io.IOException;
int newPeriodIndex = C.INDEX_UNSET; int newPeriodIndex = C.INDEX_UNSET;
int maxIterations = oldTimeline.getPeriodCount(); int maxIterations = oldTimeline.getPeriodCount();
for (int i = 0; i < maxIterations && newPeriodIndex == C.INDEX_UNSET; i++) { for (int i = 0; i < maxIterations && newPeriodIndex == C.INDEX_UNSET; i++) {
oldPeriodIndex = oldTimeline.getNextPeriodIndex(oldPeriodIndex, period, window, oldPeriodIndex = oldTimeline.getNextPeriodIndex(oldPeriodIndex, period, window, repeatMode);
ExoPlayer.REPEAT_MODE_OFF);
newPeriodIndex = newTimeline.getIndexOfPeriod( newPeriodIndex = newTimeline.getIndexOfPeriod(
oldTimeline.getPeriod(oldPeriodIndex, period, true).uid); oldTimeline.getPeriod(oldPeriodIndex, period, true).uid);
} }
@ -1033,7 +1046,7 @@ import java.io.IOException;
private boolean isLastPeriod(int periodIndex) { private boolean isLastPeriod(int periodIndex) {
int windowIndex = timeline.getPeriod(periodIndex, period).windowIndex; int windowIndex = timeline.getPeriod(periodIndex, period).windowIndex;
return !timeline.getWindow(windowIndex, window).isDynamic return !timeline.getWindow(windowIndex, window).isDynamic
&& timeline.isLastPeriod(periodIndex, period, window, ExoPlayer.REPEAT_MODE_OFF); && timeline.isLastPeriod(periodIndex, period, window, repeatMode);
} }
/** /**
@ -1247,7 +1260,7 @@ import java.io.IOException;
return; return;
} }
newLoadingPeriodIndex = timeline.getNextPeriodIndex(loadingPeriodHolder.index, period, window, newLoadingPeriodIndex = timeline.getNextPeriodIndex(loadingPeriodHolder.index, period, window,
ExoPlayer.REPEAT_MODE_OFF); repeatMode);
} }
if (newLoadingPeriodIndex >= timeline.getPeriodCount()) { if (newLoadingPeriodIndex >= timeline.getPeriodCount()) {

View File

@ -514,6 +514,16 @@ public class SimpleExoPlayer implements ExoPlayer {
return player.getPlayWhenReady(); return player.getPlayWhenReady();
} }
@Override
public @RepeatMode int getRepeatMode() {
return player.getRepeatMode();
}
@Override
public void setRepeatMode(@RepeatMode int repeatMode) {
player.setRepeatMode(repeatMode);
}
@Override @Override
public boolean isLoading() { public boolean isLoading() {
return player.isLoading(); return player.isLoading();

View File

@ -86,6 +86,11 @@ public final class DebugTextViewHelper implements Runnable, ExoPlayer.EventListe
updateAndPost(); updateAndPost();
} }
@Override
public void onRepeatModeChanged(int repeatMode) {
// Do nothing.
}
@Override @Override
public void onPositionDiscontinuity() { public void onPositionDiscontinuity() {
updateAndPost(); updateAndPost();

View File

@ -529,10 +529,9 @@ public class PlaybackControlView extends FrameLayout {
int windowIndex = player.getCurrentWindowIndex(); int windowIndex = player.getCurrentWindowIndex();
timeline.getWindow(windowIndex, window); timeline.getWindow(windowIndex, window);
isSeekable = window.isSeekable; isSeekable = window.isSeekable;
enablePrevious = !timeline.isFirstWindow(windowIndex, ExoPlayer.REPEAT_MODE_OFF) enablePrevious = !timeline.isFirstWindow(windowIndex, player.getRepeatMode())
|| isSeekable || !window.isDynamic; || isSeekable || !window.isDynamic;
enableNext = !timeline.isLastWindow(windowIndex, ExoPlayer.REPEAT_MODE_OFF) enableNext = !timeline.isLastWindow(windowIndex, player.getRepeatMode()) || window.isDynamic;
|| window.isDynamic;
if (timeline.getPeriod(player.getCurrentPeriodIndex(), period).isAd) { if (timeline.getPeriod(player.getCurrentPeriodIndex(), period).isAd) {
// Always hide player controls during ads. // Always hide player controls during ads.
hide(); hide();
@ -682,8 +681,7 @@ public class PlaybackControlView extends FrameLayout {
} }
int windowIndex = player.getCurrentWindowIndex(); int windowIndex = player.getCurrentWindowIndex();
timeline.getWindow(windowIndex, window); timeline.getWindow(windowIndex, window);
int previousWindowIndex = timeline.getPreviousWindowIndex(windowIndex, int previousWindowIndex = timeline.getPreviousWindowIndex(windowIndex, player.getRepeatMode());
ExoPlayer.REPEAT_MODE_OFF);
if (previousWindowIndex != C.INDEX_UNSET if (previousWindowIndex != C.INDEX_UNSET
&& (player.getCurrentPosition() <= MAX_POSITION_FOR_SEEK_TO_PREVIOUS && (player.getCurrentPosition() <= MAX_POSITION_FOR_SEEK_TO_PREVIOUS
|| (window.isDynamic && !window.isSeekable))) { || (window.isDynamic && !window.isSeekable))) {
@ -699,7 +697,7 @@ public class PlaybackControlView extends FrameLayout {
return; return;
} }
int windowIndex = player.getCurrentWindowIndex(); int windowIndex = player.getCurrentWindowIndex();
int nextWindowIndex = timeline.getNextWindowIndex(windowIndex, ExoPlayer.REPEAT_MODE_OFF); int nextWindowIndex = timeline.getNextWindowIndex(windowIndex, player.getRepeatMode());
if (nextWindowIndex != C.INDEX_UNSET) { if (nextWindowIndex != C.INDEX_UNSET) {
seekTo(nextWindowIndex, C.TIME_UNSET); seekTo(nextWindowIndex, C.TIME_UNSET);
} else if (timeline.getWindow(windowIndex, window, false).isDynamic) { } else if (timeline.getWindow(windowIndex, window, false).isDynamic) {
@ -908,6 +906,11 @@ public class PlaybackControlView extends FrameLayout {
updateProgress(); updateProgress();
} }
@Override
public void onRepeatModeChanged(int repeatMode) {
// Do nothing.
}
@Override @Override
public void onPositionDiscontinuity() { public void onPositionDiscontinuity() {
updateNavigation(); updateNavigation();

View File

@ -806,6 +806,11 @@ public final class SimpleExoPlayerView extends FrameLayout {
maybeShowController(false); maybeShowController(false);
} }
@Override
public void onRepeatModeChanged(int repeatMode) {
// Do nothing.
}
@Override @Override
public void onPlayerError(ExoPlaybackException e) { public void onPlayerError(ExoPlaybackException e) {
// Do nothing. // Do nothing.

View File

@ -213,6 +213,11 @@ public abstract class ExoHostedTest implements HostedTest, ExoPlayer.EventListen
this.playing = playing; this.playing = playing;
} }
@Override
public void onRepeatModeChanged(int repeatMode) {
// Do nothing.
}
@Override @Override
public final void onPlayerError(ExoPlaybackException error) { public final void onPlayerError(ExoPlaybackException error) {
playerWasPrepared = true; playerWasPrepared = true;