Commit 6d2fa12e by tonihei Committed by Oliver Woodman

Add getNextWindowIndex to Timeline

(Preparation for Repeat Toggle Function - GitHub Issue #2577)

In addition, Timeline now also got a getPreviousWindowIndex and a
getNextPeriodIndex method with default implementations.

Changed ExoPlayerImplInternal and PlaybackControlView to use these
methods at all occurances of period and window index operations.

Note: Does not include repeat mode yet and no timelines are actually
using it so far. Please wait for the next CLs for this.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=154520664
parent 5c723f4d
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2; package com.google.android.exoplayer2;
import android.support.annotation.IntDef;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import com.google.android.exoplayer2.audio.MediaCodecAudioRenderer; import com.google.android.exoplayer2.audio.MediaCodecAudioRenderer;
import com.google.android.exoplayer2.metadata.MetadataRenderer; import com.google.android.exoplayer2.metadata.MetadataRenderer;
...@@ -30,6 +31,8 @@ import com.google.android.exoplayer2.trackselection.TrackSelectionArray; ...@@ -30,6 +31,8 @@ import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.trackselection.TrackSelector; import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.video.MediaCodecVideoRenderer; import com.google.android.exoplayer2.video.MediaCodecVideoRenderer;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/** /**
* An extensible media player exposing traditional high-level media player functionality, such as * An extensible media player exposing traditional high-level media player functionality, such as
...@@ -252,6 +255,17 @@ public interface ExoPlayer { ...@@ -252,6 +255,17 @@ public interface ExoPlayer {
int STATE_ENDED = 4; int STATE_ENDED = 4;
/** /**
* Repeat modes for playback.
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({REPEAT_MODE_OFF})
@interface RepeatMode {}
/**
* Normal playback without repetition.
*/
int REPEAT_MODE_OFF = 0;
/**
* Register a listener to receive events from the player. The listener's methods will be called on * Register a listener to receive events from the player. The listener's methods will be called on
* the thread that was used to construct the player. * the thread that was used to construct the player.
* *
......
...@@ -948,10 +948,7 @@ import java.io.IOException; ...@@ -948,10 +948,7 @@ import java.io.IOException;
} }
// The current period is in the new timeline. Update the holder and playbackInfo. // The current period is in the new timeline. Update the holder and playbackInfo.
timeline.getPeriod(periodIndex, period); periodHolder.setIndex(periodIndex, isLastPeriod(periodIndex));
boolean isLastPeriod = periodIndex == timeline.getPeriodCount() - 1
&& !timeline.getWindow(period.windowIndex, window).isDynamic;
periodHolder.setIndex(periodIndex, isLastPeriod);
boolean seenReadingPeriod = periodHolder == readingPeriodHolder; boolean seenReadingPeriod = periodHolder == readingPeriodHolder;
if (periodIndex != playbackInfo.periodIndex) { if (periodIndex != playbackInfo.periodIndex) {
playbackInfo = playbackInfo.copyWithPeriodIndex(periodIndex); playbackInfo = playbackInfo.copyWithPeriodIndex(periodIndex);
...@@ -962,10 +959,10 @@ import java.io.IOException; ...@@ -962,10 +959,10 @@ 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++; periodIndex = timeline.getNextPeriodIndex(periodIndex, period, window,
ExoPlayer.REPEAT_MODE_OFF);
boolean isLastPeriod = isLastPeriod(periodIndex);
timeline.getPeriod(periodIndex, period, true); timeline.getPeriod(periodIndex, period, true);
isLastPeriod = periodIndex == timeline.getPeriodCount() - 1
&& !timeline.getWindow(period.windowIndex, window).isDynamic;
if (periodHolder.uid.equals(period.uid)) { if (periodHolder.uid.equals(period.uid)) {
// The holder is consistent with the new timeline. Update its index and continue. // The holder is consistent with the new timeline. Update its index and continue.
periodHolder.setIndex(periodIndex, isLastPeriod); periodHolder.setIndex(periodIndex, isLastPeriod);
...@@ -1023,13 +1020,22 @@ import java.io.IOException; ...@@ -1023,13 +1020,22 @@ import java.io.IOException;
private int resolveSubsequentPeriod(int oldPeriodIndex, Timeline oldTimeline, private int resolveSubsequentPeriod(int oldPeriodIndex, Timeline oldTimeline,
Timeline newTimeline) { Timeline newTimeline) {
int newPeriodIndex = C.INDEX_UNSET; int newPeriodIndex = C.INDEX_UNSET;
while (newPeriodIndex == C.INDEX_UNSET && oldPeriodIndex < oldTimeline.getPeriodCount() - 1) { int maxIterations = oldTimeline.getPeriodCount();
for (int i = 0; i < maxIterations && newPeriodIndex == C.INDEX_UNSET; i++) {
oldPeriodIndex = oldTimeline.getNextPeriodIndex(oldPeriodIndex, period, window,
ExoPlayer.REPEAT_MODE_OFF);
newPeriodIndex = newTimeline.getIndexOfPeriod( newPeriodIndex = newTimeline.getIndexOfPeriod(
oldTimeline.getPeriod(++oldPeriodIndex, period, true).uid); oldTimeline.getPeriod(oldPeriodIndex, period, true).uid);
} }
return newPeriodIndex; return newPeriodIndex;
} }
private boolean isLastPeriod(int periodIndex) {
int windowIndex = timeline.getPeriod(periodIndex, period).windowIndex;
return !timeline.getWindow(windowIndex, window).isDynamic
&& timeline.isLastPeriod(periodIndex, period, window, ExoPlayer.REPEAT_MODE_OFF);
}
/** /**
* Converts a {@link SeekPosition} into the corresponding (periodIndex, periodPositionUs) for the * Converts a {@link SeekPosition} into the corresponding (periodIndex, periodPositionUs) for the
* internal timeline. * internal timeline.
...@@ -1240,7 +1246,8 @@ import java.io.IOException; ...@@ -1240,7 +1246,8 @@ import java.io.IOException;
// We are already buffering the maximum number of periods ahead. // We are already buffering the maximum number of periods ahead.
return; return;
} }
newLoadingPeriodIndex = loadingPeriodHolder.index + 1; newLoadingPeriodIndex = timeline.getNextPeriodIndex(loadingPeriodHolder.index, period, window,
ExoPlayer.REPEAT_MODE_OFF);
} }
if (newLoadingPeriodIndex >= timeline.getPeriodCount()) { if (newLoadingPeriodIndex >= timeline.getPeriodCount()) {
...@@ -1283,9 +1290,8 @@ import java.io.IOException; ...@@ -1283,9 +1290,8 @@ import java.io.IOException;
? newLoadingPeriodStartPositionUs + RENDERER_TIMESTAMP_OFFSET_US ? newLoadingPeriodStartPositionUs + RENDERER_TIMESTAMP_OFFSET_US
: (loadingPeriodHolder.getRendererOffset() : (loadingPeriodHolder.getRendererOffset()
+ timeline.getPeriod(loadingPeriodHolder.index, period).getDurationUs()); + timeline.getPeriod(loadingPeriodHolder.index, period).getDurationUs());
boolean isLastPeriod = isLastPeriod(newLoadingPeriodIndex);
timeline.getPeriod(newLoadingPeriodIndex, period, true); timeline.getPeriod(newLoadingPeriodIndex, period, true);
boolean isLastPeriod = newLoadingPeriodIndex == timeline.getPeriodCount() - 1
&& !timeline.getWindow(period.windowIndex, window).isDynamic;
MediaPeriodHolder newPeriodHolder = new MediaPeriodHolder(renderers, rendererCapabilities, MediaPeriodHolder newPeriodHolder = new MediaPeriodHolder(renderers, rendererCapabilities,
rendererPositionOffsetUs, trackSelector, loadControl, mediaSource, period.uid, rendererPositionOffsetUs, trackSelector, loadControl, mediaSource, period.uid,
newLoadingPeriodIndex, isLastPeriod, newLoadingPeriodStartPositionUs); newLoadingPeriodIndex, isLastPeriod, newLoadingPeriodStartPositionUs);
......
...@@ -137,6 +137,54 @@ public abstract class Timeline { ...@@ -137,6 +137,54 @@ public abstract class Timeline {
public abstract int getWindowCount(); public abstract int getWindowCount();
/** /**
* Returns the index of the window after the window at index {@code windowIndex} depending on the
* {@code repeatMode}.
*
* @param windowIndex Index of a window in the timeline.
* @param repeatMode A repeat mode.
* @return The index of the next window, or {@link C#INDEX_UNSET} if this is the last window.
*/
public int getNextWindowIndex(int windowIndex, @ExoPlayer.RepeatMode int repeatMode) {
return windowIndex == getWindowCount() - 1 ? C.INDEX_UNSET : windowIndex + 1;
}
/**
* Returns the index of the window before the window at index {@code windowIndex} depending on the
* {@code repeatMode}.
*
* @param windowIndex Index of a window in the timeline.
* @param repeatMode A repeat mode.
* @return The index of the previous window, or {@link C#INDEX_UNSET} if this is the first window.
*/
public int getPreviousWindowIndex(int windowIndex, @ExoPlayer.RepeatMode int repeatMode) {
return windowIndex == 0 ? C.INDEX_UNSET : windowIndex - 1;
}
/**
* Returns whether the given window is the last window of the timeline depending on the
* {@code repeatMode}.
*
* @param windowIndex A window index.
* @param repeatMode A repeat mode.
* @return Whether the window of the given index is the last window of the timeline.
*/
public final boolean isLastWindow(int windowIndex, @ExoPlayer.RepeatMode int repeatMode) {
return getNextWindowIndex(windowIndex, repeatMode) == C.INDEX_UNSET;
}
/**
* Returns whether the given window is the first window of the timeline depending on the
* {@code repeatMode}.
*
* @param windowIndex A window index.
* @param repeatMode A repeat mode.
* @return Whether the window of the given index is the first window of the timeline.
*/
public final boolean isFirstWindow(int windowIndex, @ExoPlayer.RepeatMode int repeatMode) {
return getPreviousWindowIndex(windowIndex, repeatMode) == C.INDEX_UNSET;
}
/**
* Populates a {@link Window} with data for the window at the specified index. Does not populate * Populates a {@link Window} with data for the window at the specified index. Does not populate
* {@link Window#id}. * {@link Window#id}.
* *
...@@ -181,6 +229,44 @@ public abstract class Timeline { ...@@ -181,6 +229,44 @@ public abstract class Timeline {
public abstract int getPeriodCount(); public abstract int getPeriodCount();
/** /**
* Returns the index of the period after the period at index {@code periodIndex} depending on the
* {@code repeatMode}.
*
* @param periodIndex Index of a period in the timeline.
* @param period A {@link Period} to be used internally. Must not be null.
* @param window A {@link Window} to be used internally. Must not be null.
* @param repeatMode A repeat mode.
* @return The index of the next period, or {@link C#INDEX_UNSET} if this is the last period.
*/
public final int getNextPeriodIndex(int periodIndex, Period period, Window window,
@ExoPlayer.RepeatMode int repeatMode) {
int windowIndex = getPeriod(periodIndex, period).windowIndex;
if (getWindow(windowIndex, window).lastPeriodIndex == periodIndex) {
int nextWindowIndex = getNextWindowIndex(windowIndex, repeatMode);
if (nextWindowIndex == C.INDEX_UNSET) {
return C.INDEX_UNSET;
}
return getWindow(nextWindowIndex, window).firstPeriodIndex;
}
return periodIndex + 1;
}
/**
* Returns whether the given period is the last period of the timeline depending on the
* {@code repeatMode}.
*
* @param periodIndex A period index.
* @param period A {@link Period} to be used internally. Must not be null.
* @param window A {@link Window} to be used internally. Must not be null.
* @param repeatMode A repeat mode.
* @return Whether the period of the given index is the last period of the timeline.
*/
public final boolean isLastPeriod(int periodIndex, Period period, Window window,
@ExoPlayer.RepeatMode int repeatMode) {
return getNextPeriodIndex(periodIndex, period, window, repeatMode) == C.INDEX_UNSET;
}
/**
* Populates a {@link Period} with data for the period at the specified index. Does not populate * Populates a {@link Period} with data for the period at the specified index. Does not populate
* {@link Period#id} and {@link Period#uid}. * {@link Period#id} and {@link Period#uid}.
* *
......
...@@ -529,8 +529,10 @@ public class PlaybackControlView extends FrameLayout { ...@@ -529,8 +529,10 @@ 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 = windowIndex > 0 || isSeekable || !window.isDynamic; enablePrevious = !timeline.isFirstWindow(windowIndex, ExoPlayer.REPEAT_MODE_OFF)
enableNext = (windowIndex < timeline.getWindowCount() - 1) || window.isDynamic; || isSeekable || !window.isDynamic;
enableNext = !timeline.isLastWindow(windowIndex, ExoPlayer.REPEAT_MODE_OFF)
|| 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();
...@@ -680,9 +682,12 @@ public class PlaybackControlView extends FrameLayout { ...@@ -680,9 +682,12 @@ public class PlaybackControlView extends FrameLayout {
} }
int windowIndex = player.getCurrentWindowIndex(); int windowIndex = player.getCurrentWindowIndex();
timeline.getWindow(windowIndex, window); timeline.getWindow(windowIndex, window);
if (windowIndex > 0 && (player.getCurrentPosition() <= MAX_POSITION_FOR_SEEK_TO_PREVIOUS int previousWindowIndex = timeline.getPreviousWindowIndex(windowIndex,
ExoPlayer.REPEAT_MODE_OFF);
if (previousWindowIndex != C.INDEX_UNSET
&& (player.getCurrentPosition() <= MAX_POSITION_FOR_SEEK_TO_PREVIOUS
|| (window.isDynamic && !window.isSeekable))) { || (window.isDynamic && !window.isSeekable))) {
seekTo(windowIndex - 1, C.TIME_UNSET); seekTo(previousWindowIndex, C.TIME_UNSET);
} else { } else {
seekTo(0); seekTo(0);
} }
...@@ -694,8 +699,9 @@ public class PlaybackControlView extends FrameLayout { ...@@ -694,8 +699,9 @@ public class PlaybackControlView extends FrameLayout {
return; return;
} }
int windowIndex = player.getCurrentWindowIndex(); int windowIndex = player.getCurrentWindowIndex();
if (windowIndex < timeline.getWindowCount() - 1) { int nextWindowIndex = timeline.getNextWindowIndex(windowIndex, ExoPlayer.REPEAT_MODE_OFF);
seekTo(windowIndex + 1, C.TIME_UNSET); if (nextWindowIndex != C.INDEX_UNSET) {
seekTo(nextWindowIndex, C.TIME_UNSET);
} else if (timeline.getWindow(windowIndex, window, false).isDynamic) { } else if (timeline.getWindow(windowIndex, window, false).isDynamic) {
seekTo(windowIndex, C.TIME_UNSET); seekTo(windowIndex, C.TIME_UNSET);
} }
......
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