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
This commit is contained in:
tonihei 2017-04-28 02:11:03 -07:00 committed by Oliver Woodman
parent 5c723f4d3d
commit 6d2fa12e16
4 changed files with 130 additions and 18 deletions

View file

@ -15,6 +15,7 @@
*/
package com.google.android.exoplayer2;
import android.support.annotation.IntDef;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.audio.MediaCodecAudioRenderer;
import com.google.android.exoplayer2.metadata.MetadataRenderer;
@ -30,6 +31,8 @@ import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.upstream.DataSource;
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
@ -251,6 +254,17 @@ public interface ExoPlayer {
*/
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
* the thread that was used to construct the player.

View file

@ -948,10 +948,7 @@ import java.io.IOException;
}
// The current period is in the new timeline. Update the holder and playbackInfo.
timeline.getPeriod(periodIndex, period);
boolean isLastPeriod = periodIndex == timeline.getPeriodCount() - 1
&& !timeline.getWindow(period.windowIndex, window).isDynamic;
periodHolder.setIndex(periodIndex, isLastPeriod);
periodHolder.setIndex(periodIndex, isLastPeriod(periodIndex));
boolean seenReadingPeriod = periodHolder == readingPeriodHolder;
if (periodIndex != playbackInfo.periodIndex) {
playbackInfo = playbackInfo.copyWithPeriodIndex(periodIndex);
@ -962,10 +959,10 @@ import java.io.IOException;
while (periodHolder.next != null) {
MediaPeriodHolder previousPeriodHolder = periodHolder;
periodHolder = periodHolder.next;
periodIndex++;
periodIndex = timeline.getNextPeriodIndex(periodIndex, period, window,
ExoPlayer.REPEAT_MODE_OFF);
boolean isLastPeriod = isLastPeriod(periodIndex);
timeline.getPeriod(periodIndex, period, true);
isLastPeriod = periodIndex == timeline.getPeriodCount() - 1
&& !timeline.getWindow(period.windowIndex, window).isDynamic;
if (periodHolder.uid.equals(period.uid)) {
// The holder is consistent with the new timeline. Update its index and continue.
periodHolder.setIndex(periodIndex, isLastPeriod);
@ -1023,13 +1020,22 @@ import java.io.IOException;
private int resolveSubsequentPeriod(int oldPeriodIndex, Timeline oldTimeline,
Timeline newTimeline) {
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(
oldTimeline.getPeriod(++oldPeriodIndex, period, true).uid);
oldTimeline.getPeriod(oldPeriodIndex, period, true).uid);
}
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
* internal timeline.
@ -1240,7 +1246,8 @@ import java.io.IOException;
// We are already buffering the maximum number of periods ahead.
return;
}
newLoadingPeriodIndex = loadingPeriodHolder.index + 1;
newLoadingPeriodIndex = timeline.getNextPeriodIndex(loadingPeriodHolder.index, period, window,
ExoPlayer.REPEAT_MODE_OFF);
}
if (newLoadingPeriodIndex >= timeline.getPeriodCount()) {
@ -1283,9 +1290,8 @@ import java.io.IOException;
? newLoadingPeriodStartPositionUs + RENDERER_TIMESTAMP_OFFSET_US
: (loadingPeriodHolder.getRendererOffset()
+ timeline.getPeriod(loadingPeriodHolder.index, period).getDurationUs());
boolean isLastPeriod = isLastPeriod(newLoadingPeriodIndex);
timeline.getPeriod(newLoadingPeriodIndex, period, true);
boolean isLastPeriod = newLoadingPeriodIndex == timeline.getPeriodCount() - 1
&& !timeline.getWindow(period.windowIndex, window).isDynamic;
MediaPeriodHolder newPeriodHolder = new MediaPeriodHolder(renderers, rendererCapabilities,
rendererPositionOffsetUs, trackSelector, loadControl, mediaSource, period.uid,
newLoadingPeriodIndex, isLastPeriod, newLoadingPeriodStartPositionUs);

View file

@ -136,6 +136,54 @@ public abstract class Timeline {
*/
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
* {@link Window#id}.
@ -180,6 +228,44 @@ public abstract class Timeline {
*/
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
* {@link Period#id} and {@link Period#uid}.

View file

@ -529,8 +529,10 @@ public class PlaybackControlView extends FrameLayout {
int windowIndex = player.getCurrentWindowIndex();
timeline.getWindow(windowIndex, window);
isSeekable = window.isSeekable;
enablePrevious = windowIndex > 0 || isSeekable || !window.isDynamic;
enableNext = (windowIndex < timeline.getWindowCount() - 1) || window.isDynamic;
enablePrevious = !timeline.isFirstWindow(windowIndex, ExoPlayer.REPEAT_MODE_OFF)
|| isSeekable || !window.isDynamic;
enableNext = !timeline.isLastWindow(windowIndex, ExoPlayer.REPEAT_MODE_OFF)
|| window.isDynamic;
if (timeline.getPeriod(player.getCurrentPeriodIndex(), period).isAd) {
// Always hide player controls during ads.
hide();
@ -680,9 +682,12 @@ public class PlaybackControlView extends FrameLayout {
}
int windowIndex = player.getCurrentWindowIndex();
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))) {
seekTo(windowIndex - 1, C.TIME_UNSET);
seekTo(previousWindowIndex, C.TIME_UNSET);
} else {
seekTo(0);
}
@ -694,8 +699,9 @@ public class PlaybackControlView extends FrameLayout {
return;
}
int windowIndex = player.getCurrentWindowIndex();
if (windowIndex < timeline.getWindowCount() - 1) {
seekTo(windowIndex + 1, C.TIME_UNSET);
int nextWindowIndex = timeline.getNextWindowIndex(windowIndex, ExoPlayer.REPEAT_MODE_OFF);
if (nextWindowIndex != C.INDEX_UNSET) {
seekTo(nextWindowIndex, C.TIME_UNSET);
} else if (timeline.getWindow(windowIndex, window, false).isDynamic) {
seekTo(windowIndex, C.TIME_UNSET);
}