summaryrefslogtreecommitdiffstats
path: root/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/Timeline.java
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/Timeline.java')
-rw-r--r--mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/Timeline.java837
1 files changed, 837 insertions, 0 deletions
diff --git a/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/Timeline.java b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/Timeline.java
new file mode 100644
index 0000000000..c9e3d16ff7
--- /dev/null
+++ b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/Timeline.java
@@ -0,0 +1,837 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mozilla.thirdparty.com.google.android.exoplayer2;
+
+import android.util.Pair;
+import androidx.annotation.Nullable;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.source.ads.AdPlaybackState;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.util.Assertions;
+
+/**
+ * A flexible representation of the structure of media. A timeline is able to represent the
+ * structure of a wide variety of media, from simple cases like a single media file through to
+ * complex compositions of media such as playlists and streams with inserted ads. Instances are
+ * immutable. For cases where media is changing dynamically (e.g. live streams), a timeline provides
+ * a snapshot of the current state.
+ *
+ * <p>A timeline consists of {@link Window Windows} and {@link Period Periods}.
+ *
+ * <ul>
+ * <li>A {@link Window} usually corresponds to one playlist item. It may span one or more periods
+ * and it defines the region within those periods that's currently available for playback. The
+ * window also provides additional information such as whether seeking is supported within the
+ * window and the default position, which is the position from which playback will start when
+ * the player starts playing the window.
+ * <li>A {@link Period} defines a single logical piece of media, for example a media file. It may
+ * also define groups of ads inserted into the media, along with information about whether
+ * those ads have been loaded and played.
+ * </ul>
+ *
+ * <p>The following examples illustrate timelines for various use cases.
+ *
+ * <h3 id="single-file">Single media file or on-demand stream</h3>
+ *
+ * <p style="align:center"><img src="doc-files/timeline-single-file.svg" alt="Example timeline for a
+ * single file"> A timeline for a single media file or on-demand stream consists of a single period
+ * and window. The window spans the whole period, indicating that all parts of the media are
+ * available for playback. The window's default position is typically at the start of the period
+ * (indicated by the black dot in the figure above).
+ *
+ * <h3>Playlist of media files or on-demand streams</h3>
+ *
+ * <p style="align:center"><img src="doc-files/timeline-playlist.svg" alt="Example timeline for a
+ * playlist of files"> A timeline for a playlist of media files or on-demand streams consists of
+ * multiple periods, each with its own window. Each window spans the whole of the corresponding
+ * period, and typically has a default position at the start of the period. The properties of the
+ * periods and windows (e.g. their durations and whether the window is seekable) will often only
+ * become known when the player starts buffering the corresponding file or stream.
+ *
+ * <h3 id="live-limited">Live stream with limited availability</h3>
+ *
+ * <p style="align:center"><img src="doc-files/timeline-live-limited.svg" alt="Example timeline for
+ * a live stream with limited availability"> A timeline for a live stream consists of a period whose
+ * duration is unknown, since it's continually extending as more content is broadcast. If content
+ * only remains available for a limited period of time then the window may start at a non-zero
+ * position, defining the region of content that can still be played. The window will have {@link
+ * Window#isLive} set to true to indicate it's a live stream and {@link Window#isDynamic} set to
+ * true as long as we expect changes to the live window. Its default position is typically near to
+ * the live edge (indicated by the black dot in the figure above).
+ *
+ * <h3>Live stream with indefinite availability</h3>
+ *
+ * <p style="align:center"><img src="doc-files/timeline-live-indefinite.svg" alt="Example timeline
+ * for a live stream with indefinite availability"> A timeline for a live stream with indefinite
+ * availability is similar to the <a href="#live-limited">Live stream with limited availability</a>
+ * case, except that the window starts at the beginning of the period to indicate that all of the
+ * previously broadcast content can still be played.
+ *
+ * <h3 id="live-multi-period">Live stream with multiple periods</h3>
+ *
+ * <p style="align:center"><img src="doc-files/timeline-live-multi-period.svg" alt="Example timeline
+ * for a live stream with multiple periods"> This case arises when a live stream is explicitly
+ * divided into separate periods, for example at content boundaries. This case is similar to the <a
+ * href="#live-limited">Live stream with limited availability</a> case, except that the window may
+ * span more than one period. Multiple periods are also possible in the indefinite availability
+ * case.
+ *
+ * <h3>On-demand stream followed by live stream</h3>
+ *
+ * <p style="align:center"><img src="doc-files/timeline-advanced.svg" alt="Example timeline for an
+ * on-demand stream followed by a live stream"> This case is the concatenation of the <a
+ * href="#single-file">Single media file or on-demand stream</a> and <a href="#multi-period">Live
+ * stream with multiple periods</a> cases. When playback of the on-demand stream ends, playback of
+ * the live stream will start from its default position near the live edge.
+ *
+ * <h3 id="single-file-midrolls">On-demand stream with mid-roll ads</h3>
+ *
+ * <p style="align:center"><img src="doc-files/timeline-single-file-midrolls.svg" alt="Example
+ * timeline for an on-demand stream with mid-roll ad groups"> This case includes mid-roll ad groups,
+ * which are defined as part of the timeline's single period. The period can be queried for
+ * information about the ad groups and the ads they contain.
+ */
+public abstract class Timeline {
+
+ /**
+ * Holds information about a window in a {@link Timeline}. A window usually corresponds to one
+ * playlist item and defines a region of media currently available for playback along with
+ * additional information such as whether seeking is supported within the window. The figure below
+ * shows some of the information defined by a window, as well as how this information relates to
+ * corresponding {@link Period Periods} in the timeline.
+ *
+ * <p style="align:center"><img src="doc-files/timeline-window.svg" alt="Information defined by a
+ * timeline window">
+ */
+ public static final class Window {
+
+ /**
+ * A {@link #uid} for a window that must be used for single-window {@link Timeline Timelines}.
+ */
+ public static final Object SINGLE_WINDOW_UID = new Object();
+
+ /**
+ * A unique identifier for the window. Single-window {@link Timeline Timelines} must use {@link
+ * #SINGLE_WINDOW_UID}.
+ */
+ public Object uid;
+
+ /** A tag for the window. Not necessarily unique. */
+ @Nullable public Object tag;
+
+ /** The manifest of the window. May be {@code null}. */
+ @Nullable public Object manifest;
+
+ /**
+ * The start time of the presentation to which this window belongs in milliseconds since the
+ * epoch, or {@link C#TIME_UNSET} if unknown or not applicable. For informational purposes only.
+ */
+ public long presentationStartTimeMs;
+
+ /**
+ * The window's start time in milliseconds since the epoch, or {@link C#TIME_UNSET} if unknown
+ * or not applicable. For informational purposes only.
+ */
+ public long windowStartTimeMs;
+
+ /**
+ * Whether it's possible to seek within this window.
+ */
+ public boolean isSeekable;
+
+ // TODO: Split this to better describe which parts of the window might change. For example it
+ // should be possible to individually determine whether the start and end positions of the
+ // window may change relative to the underlying periods. For an example of where it's useful to
+ // know that the end position is fixed whilst the start position may still change, see:
+ // https://github.com/google/ExoPlayer/issues/4780.
+ /** Whether this window may change when the timeline is updated. */
+ public boolean isDynamic;
+
+ /**
+ * Whether the media in this window is live. For informational purposes only.
+ *
+ * <p>Check {@link #isDynamic} to know whether this window may still change.
+ */
+ public boolean isLive;
+
+ /** The index of the first period that belongs to this window. */
+ public int firstPeriodIndex;
+
+ /**
+ * The index of the last period that belongs to this window.
+ */
+ public int lastPeriodIndex;
+
+ /**
+ * The default position relative to the start of the window at which to begin playback, in
+ * microseconds. May be {@link C#TIME_UNSET} if and only if the window was populated with a
+ * non-zero default position projection, and if the specified projection cannot be performed
+ * whilst remaining within the bounds of the window.
+ */
+ public long defaultPositionUs;
+
+ /**
+ * The duration of this window in microseconds, or {@link C#TIME_UNSET} if unknown.
+ */
+ public long durationUs;
+
+ /**
+ * The position of the start of this window relative to the start of the first period belonging
+ * to it, in microseconds.
+ */
+ public long positionInFirstPeriodUs;
+
+ /** Creates window. */
+ public Window() {
+ uid = SINGLE_WINDOW_UID;
+ }
+
+ /** Sets the data held by this window. */
+ public Window set(
+ Object uid,
+ @Nullable Object tag,
+ @Nullable Object manifest,
+ long presentationStartTimeMs,
+ long windowStartTimeMs,
+ boolean isSeekable,
+ boolean isDynamic,
+ boolean isLive,
+ long defaultPositionUs,
+ long durationUs,
+ int firstPeriodIndex,
+ int lastPeriodIndex,
+ long positionInFirstPeriodUs) {
+ this.uid = uid;
+ this.tag = tag;
+ this.manifest = manifest;
+ this.presentationStartTimeMs = presentationStartTimeMs;
+ this.windowStartTimeMs = windowStartTimeMs;
+ this.isSeekable = isSeekable;
+ this.isDynamic = isDynamic;
+ this.isLive = isLive;
+ this.defaultPositionUs = defaultPositionUs;
+ this.durationUs = durationUs;
+ this.firstPeriodIndex = firstPeriodIndex;
+ this.lastPeriodIndex = lastPeriodIndex;
+ this.positionInFirstPeriodUs = positionInFirstPeriodUs;
+ return this;
+ }
+
+ /**
+ * Returns the default position relative to the start of the window at which to begin playback,
+ * in milliseconds. May be {@link C#TIME_UNSET} if and only if the window was populated with a
+ * non-zero default position projection, and if the specified projection cannot be performed
+ * whilst remaining within the bounds of the window.
+ */
+ public long getDefaultPositionMs() {
+ return C.usToMs(defaultPositionUs);
+ }
+
+ /**
+ * Returns the default position relative to the start of the window at which to begin playback,
+ * in microseconds. May be {@link C#TIME_UNSET} if and only if the window was populated with a
+ * non-zero default position projection, and if the specified projection cannot be performed
+ * whilst remaining within the bounds of the window.
+ */
+ public long getDefaultPositionUs() {
+ return defaultPositionUs;
+ }
+
+ /**
+ * Returns the duration of the window in milliseconds, or {@link C#TIME_UNSET} if unknown.
+ */
+ public long getDurationMs() {
+ return C.usToMs(durationUs);
+ }
+
+ /**
+ * Returns the duration of this window in microseconds, or {@link C#TIME_UNSET} if unknown.
+ */
+ public long getDurationUs() {
+ return durationUs;
+ }
+
+ /**
+ * Returns the position of the start of this window relative to the start of the first period
+ * belonging to it, in milliseconds.
+ */
+ public long getPositionInFirstPeriodMs() {
+ return C.usToMs(positionInFirstPeriodUs);
+ }
+
+ /**
+ * Returns the position of the start of this window relative to the start of the first period
+ * belonging to it, in microseconds.
+ */
+ public long getPositionInFirstPeriodUs() {
+ return positionInFirstPeriodUs;
+ }
+
+ }
+
+ /**
+ * Holds information about a period in a {@link Timeline}. A period defines a single logical piece
+ * of media, for example a media file. It may also define groups of ads inserted into the media,
+ * along with information about whether those ads have been loaded and played.
+ *
+ * <p>The figure below shows some of the information defined by a period, as well as how this
+ * information relates to a corresponding {@link Window} in the timeline.
+ *
+ * <p style="align:center"><img src="doc-files/timeline-period.svg" alt="Information defined by a
+ * period">
+ */
+ public static final class Period {
+
+ /**
+ * An identifier for the period. Not necessarily unique. May be null if the ids of the period
+ * are not required.
+ */
+ @Nullable public Object id;
+
+ /**
+ * A unique identifier for the period. May be null if the ids of the period are not required.
+ */
+ @Nullable public Object uid;
+
+ /**
+ * The index of the window to which this period belongs.
+ */
+ public int windowIndex;
+
+ /**
+ * The duration of this period in microseconds, or {@link C#TIME_UNSET} if unknown.
+ */
+ public long durationUs;
+
+ private long positionInWindowUs;
+ private AdPlaybackState adPlaybackState;
+
+ /** Creates a new instance with no ad playback state. */
+ public Period() {
+ adPlaybackState = AdPlaybackState.NONE;
+ }
+
+ /**
+ * Sets the data held by this period.
+ *
+ * @param id An identifier for the period. Not necessarily unique. May be null if the ids of the
+ * period are not required.
+ * @param uid A unique identifier for the period. May be null if the ids of the period are not
+ * required.
+ * @param windowIndex The index of the window to which this period belongs.
+ * @param durationUs The duration of this period in microseconds, or {@link C#TIME_UNSET} if
+ * unknown.
+ * @param positionInWindowUs The position of the start of this period relative to the start of
+ * the window to which it belongs, in milliseconds. May be negative if the start of the
+ * period is not within the window.
+ * @return This period, for convenience.
+ */
+ public Period set(
+ @Nullable Object id,
+ @Nullable Object uid,
+ int windowIndex,
+ long durationUs,
+ long positionInWindowUs) {
+ return set(id, uid, windowIndex, durationUs, positionInWindowUs, AdPlaybackState.NONE);
+ }
+
+ /**
+ * Sets the data held by this period.
+ *
+ * @param id An identifier for the period. Not necessarily unique. May be null if the ids of the
+ * period are not required.
+ * @param uid A unique identifier for the period. May be null if the ids of the period are not
+ * required.
+ * @param windowIndex The index of the window to which this period belongs.
+ * @param durationUs The duration of this period in microseconds, or {@link C#TIME_UNSET} if
+ * unknown.
+ * @param positionInWindowUs The position of the start of this period relative to the start of
+ * the window to which it belongs, in milliseconds. May be negative if the start of the
+ * period is not within the window.
+ * @param adPlaybackState The state of the period's ads, or {@link AdPlaybackState#NONE} if
+ * there are no ads.
+ * @return This period, for convenience.
+ */
+ public Period set(
+ @Nullable Object id,
+ @Nullable Object uid,
+ int windowIndex,
+ long durationUs,
+ long positionInWindowUs,
+ AdPlaybackState adPlaybackState) {
+ this.id = id;
+ this.uid = uid;
+ this.windowIndex = windowIndex;
+ this.durationUs = durationUs;
+ this.positionInWindowUs = positionInWindowUs;
+ this.adPlaybackState = adPlaybackState;
+ return this;
+ }
+
+ /**
+ * Returns the duration of the period in milliseconds, or {@link C#TIME_UNSET} if unknown.
+ */
+ public long getDurationMs() {
+ return C.usToMs(durationUs);
+ }
+
+ /**
+ * Returns the duration of this period in microseconds, or {@link C#TIME_UNSET} if unknown.
+ */
+ public long getDurationUs() {
+ return durationUs;
+ }
+
+ /**
+ * Returns the position of the start of this period relative to the start of the window to which
+ * it belongs, in milliseconds. May be negative if the start of the period is not within the
+ * window.
+ */
+ public long getPositionInWindowMs() {
+ return C.usToMs(positionInWindowUs);
+ }
+
+ /**
+ * Returns the position of the start of this period relative to the start of the window to which
+ * it belongs, in microseconds. May be negative if the start of the period is not within the
+ * window.
+ */
+ public long getPositionInWindowUs() {
+ return positionInWindowUs;
+ }
+
+ /**
+ * Returns the number of ad groups in the period.
+ */
+ public int getAdGroupCount() {
+ return adPlaybackState.adGroupCount;
+ }
+
+ /**
+ * Returns the time of the ad group at index {@code adGroupIndex} in the period, in
+ * microseconds.
+ *
+ * @param adGroupIndex The ad group index.
+ * @return The time of the ad group at the index, in microseconds, or {@link
+ * C#TIME_END_OF_SOURCE} for a post-roll ad group.
+ */
+ public long getAdGroupTimeUs(int adGroupIndex) {
+ return adPlaybackState.adGroupTimesUs[adGroupIndex];
+ }
+
+ /**
+ * Returns the index of the first ad in the specified ad group that should be played, or the
+ * number of ads in the ad group if no ads should be played.
+ *
+ * @param adGroupIndex The ad group index.
+ * @return The index of the first ad that should be played, or the number of ads in the ad group
+ * if no ads should be played.
+ */
+ public int getFirstAdIndexToPlay(int adGroupIndex) {
+ return adPlaybackState.adGroups[adGroupIndex].getFirstAdIndexToPlay();
+ }
+
+ /**
+ * Returns the index of the next ad in the specified ad group that should be played after
+ * playing {@code adIndexInAdGroup}, or the number of ads in the ad group if no later ads should
+ * be played.
+ *
+ * @param adGroupIndex The ad group index.
+ * @param lastPlayedAdIndex The last played ad index in the ad group.
+ * @return The index of the next ad that should be played, or the number of ads in the ad group
+ * if the ad group does not have any ads remaining to play.
+ */
+ public int getNextAdIndexToPlay(int adGroupIndex, int lastPlayedAdIndex) {
+ return adPlaybackState.adGroups[adGroupIndex].getNextAdIndexToPlay(lastPlayedAdIndex);
+ }
+
+ /**
+ * Returns whether the ad group at index {@code adGroupIndex} has been played.
+ *
+ * @param adGroupIndex The ad group index.
+ * @return Whether the ad group at index {@code adGroupIndex} has been played.
+ */
+ public boolean hasPlayedAdGroup(int adGroupIndex) {
+ return !adPlaybackState.adGroups[adGroupIndex].hasUnplayedAds();
+ }
+
+ /**
+ * Returns the index of the ad group at or before {@code positionUs}, if that ad group is
+ * unplayed. Returns {@link C#INDEX_UNSET} if the ad group at or before {@code positionUs} has
+ * no ads remaining to be played, or if there is no such ad group.
+ *
+ * @param positionUs The position at or before which to find an ad group, in microseconds.
+ * @return The index of the ad group, or {@link C#INDEX_UNSET}.
+ */
+ public int getAdGroupIndexForPositionUs(long positionUs) {
+ return adPlaybackState.getAdGroupIndexForPositionUs(positionUs);
+ }
+
+ /**
+ * Returns the index of the next ad group after {@code positionUs} that has ads remaining to be
+ * played. Returns {@link C#INDEX_UNSET} if there is no such ad group.
+ *
+ * @param positionUs The position after which to find an ad group, in microseconds.
+ * @return The index of the ad group, or {@link C#INDEX_UNSET}.
+ */
+ public int getAdGroupIndexAfterPositionUs(long positionUs) {
+ return adPlaybackState.getAdGroupIndexAfterPositionUs(positionUs, durationUs);
+ }
+
+ /**
+ * Returns the number of ads in the ad group at index {@code adGroupIndex}, or
+ * {@link C#LENGTH_UNSET} if not yet known.
+ *
+ * @param adGroupIndex The ad group index.
+ * @return The number of ads in the ad group, or {@link C#LENGTH_UNSET} if not yet known.
+ */
+ public int getAdCountInAdGroup(int adGroupIndex) {
+ return adPlaybackState.adGroups[adGroupIndex].count;
+ }
+
+ /**
+ * Returns whether the URL for the specified ad is known.
+ *
+ * @param adGroupIndex The ad group index.
+ * @param adIndexInAdGroup The ad index in the ad group.
+ * @return Whether the URL for the specified ad is known.
+ */
+ public boolean isAdAvailable(int adGroupIndex, int adIndexInAdGroup) {
+ AdPlaybackState.AdGroup adGroup = adPlaybackState.adGroups[adGroupIndex];
+ return adGroup.count != C.LENGTH_UNSET
+ && adGroup.states[adIndexInAdGroup] != AdPlaybackState.AD_STATE_UNAVAILABLE;
+ }
+
+ /**
+ * Returns the duration of the ad at index {@code adIndexInAdGroup} in the ad group at
+ * {@code adGroupIndex}, in microseconds, or {@link C#TIME_UNSET} if not yet known.
+ *
+ * @param adGroupIndex The ad group index.
+ * @param adIndexInAdGroup The ad index in the ad group.
+ * @return The duration of the ad, or {@link C#TIME_UNSET} if not yet known.
+ */
+ public long getAdDurationUs(int adGroupIndex, int adIndexInAdGroup) {
+ AdPlaybackState.AdGroup adGroup = adPlaybackState.adGroups[adGroupIndex];
+ return adGroup.count != C.LENGTH_UNSET ? adGroup.durationsUs[adIndexInAdGroup] : C.TIME_UNSET;
+ }
+
+ /**
+ * Returns the position offset in the first unplayed ad at which to begin playback, in
+ * microseconds.
+ */
+ public long getAdResumePositionUs() {
+ return adPlaybackState.adResumePositionUs;
+ }
+
+ }
+
+ /** An empty timeline. */
+ public static final Timeline EMPTY =
+ new Timeline() {
+
+ @Override
+ public int getWindowCount() {
+ return 0;
+ }
+
+ @Override
+ public Window getWindow(int windowIndex, Window window, long defaultPositionProjectionUs) {
+ throw new IndexOutOfBoundsException();
+ }
+
+ @Override
+ public int getPeriodCount() {
+ return 0;
+ }
+
+ @Override
+ public Period getPeriod(int periodIndex, Period period, boolean setIds) {
+ throw new IndexOutOfBoundsException();
+ }
+
+ @Override
+ public int getIndexOfPeriod(Object uid) {
+ return C.INDEX_UNSET;
+ }
+
+ @Override
+ public Object getUidOfPeriod(int periodIndex) {
+ throw new IndexOutOfBoundsException();
+ }
+ };
+
+ /**
+ * Returns whether the timeline is empty.
+ */
+ public final boolean isEmpty() {
+ return getWindowCount() == 0;
+ }
+
+ /**
+ * Returns the number of windows in the timeline.
+ */
+ public abstract int getWindowCount();
+
+ /**
+ * Returns the index of the window after the window at index {@code windowIndex} depending on the
+ * {@code repeatMode} and whether shuffling is enabled.
+ *
+ * @param windowIndex Index of a window in the timeline.
+ * @param repeatMode A repeat mode.
+ * @param shuffleModeEnabled Whether shuffling is enabled.
+ * @return The index of the next window, or {@link C#INDEX_UNSET} if this is the last window.
+ */
+ public int getNextWindowIndex(int windowIndex, @Player.RepeatMode int repeatMode,
+ boolean shuffleModeEnabled) {
+ switch (repeatMode) {
+ case Player.REPEAT_MODE_OFF:
+ return windowIndex == getLastWindowIndex(shuffleModeEnabled) ? C.INDEX_UNSET
+ : windowIndex + 1;
+ case Player.REPEAT_MODE_ONE:
+ return windowIndex;
+ case Player.REPEAT_MODE_ALL:
+ return windowIndex == getLastWindowIndex(shuffleModeEnabled)
+ ? getFirstWindowIndex(shuffleModeEnabled) : windowIndex + 1;
+ default:
+ throw new IllegalStateException();
+ }
+ }
+
+ /**
+ * Returns the index of the window before the window at index {@code windowIndex} depending on the
+ * {@code repeatMode} and whether shuffling is enabled.
+ *
+ * @param windowIndex Index of a window in the timeline.
+ * @param repeatMode A repeat mode.
+ * @param shuffleModeEnabled Whether shuffling is enabled.
+ * @return The index of the previous window, or {@link C#INDEX_UNSET} if this is the first window.
+ */
+ public int getPreviousWindowIndex(int windowIndex, @Player.RepeatMode int repeatMode,
+ boolean shuffleModeEnabled) {
+ switch (repeatMode) {
+ case Player.REPEAT_MODE_OFF:
+ return windowIndex == getFirstWindowIndex(shuffleModeEnabled) ? C.INDEX_UNSET
+ : windowIndex - 1;
+ case Player.REPEAT_MODE_ONE:
+ return windowIndex;
+ case Player.REPEAT_MODE_ALL:
+ return windowIndex == getFirstWindowIndex(shuffleModeEnabled)
+ ? getLastWindowIndex(shuffleModeEnabled) : windowIndex - 1;
+ default:
+ throw new IllegalStateException();
+ }
+ }
+
+ /**
+ * Returns the index of the last window in the playback order depending on whether shuffling is
+ * enabled.
+ *
+ * @param shuffleModeEnabled Whether shuffling is enabled.
+ * @return The index of the last window in the playback order, or {@link C#INDEX_UNSET} if the
+ * timeline is empty.
+ */
+ public int getLastWindowIndex(boolean shuffleModeEnabled) {
+ return isEmpty() ? C.INDEX_UNSET : getWindowCount() - 1;
+ }
+
+ /**
+ * Returns the index of the first window in the playback order depending on whether shuffling is
+ * enabled.
+ *
+ * @param shuffleModeEnabled Whether shuffling is enabled.
+ * @return The index of the first window in the playback order, or {@link C#INDEX_UNSET} if the
+ * timeline is empty.
+ */
+ public int getFirstWindowIndex(boolean shuffleModeEnabled) {
+ return isEmpty() ? C.INDEX_UNSET : 0;
+ }
+
+ /**
+ * Populates a {@link Window} with data for the window at the specified index.
+ *
+ * @param windowIndex The index of the window.
+ * @param window The {@link Window} to populate. Must not be null.
+ * @return The populated {@link Window}, for convenience.
+ */
+ public final Window getWindow(int windowIndex, Window window) {
+ return getWindow(windowIndex, window, /* defaultPositionProjectionUs= */ 0);
+ }
+
+ /** @deprecated Use {@link #getWindow(int, Window)} instead. Tags will always be set. */
+ @Deprecated
+ public final Window getWindow(int windowIndex, Window window, boolean setTag) {
+ return getWindow(windowIndex, window, /* defaultPositionProjectionUs= */ 0);
+ }
+
+ /**
+ * Populates a {@link Window} with data for the window at the specified index.
+ *
+ * @param windowIndex The index of the window.
+ * @param window The {@link Window} to populate. Must not be null.
+ * @param defaultPositionProjectionUs A duration into the future that the populated window's
+ * default start position should be projected.
+ * @return The populated {@link Window}, for convenience.
+ */
+ public abstract Window getWindow(
+ int windowIndex, Window window, long defaultPositionProjectionUs);
+
+ /**
+ * Returns the number of periods in the timeline.
+ */
+ public abstract int getPeriodCount();
+
+ /**
+ * Returns the index of the period after the period at index {@code periodIndex} depending on the
+ * {@code repeatMode} and whether shuffling is enabled.
+ *
+ * @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.
+ * @param shuffleModeEnabled Whether shuffling is enabled.
+ * @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,
+ @Player.RepeatMode int repeatMode, boolean shuffleModeEnabled) {
+ int windowIndex = getPeriod(periodIndex, period).windowIndex;
+ if (getWindow(windowIndex, window).lastPeriodIndex == periodIndex) {
+ int nextWindowIndex = getNextWindowIndex(windowIndex, repeatMode, shuffleModeEnabled);
+ 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} and whether shuffling is enabled.
+ *
+ * @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.
+ * @param shuffleModeEnabled Whether shuffling is enabled.
+ * @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,
+ @Player.RepeatMode int repeatMode, boolean shuffleModeEnabled) {
+ return getNextPeriodIndex(periodIndex, period, window, repeatMode, shuffleModeEnabled)
+ == C.INDEX_UNSET;
+ }
+
+ /**
+ * Calls {@link #getPeriodPosition(Window, Period, int, long, long)} with a zero default position
+ * projection.
+ */
+ public final Pair<Object, Long> getPeriodPosition(
+ Window window, Period period, int windowIndex, long windowPositionUs) {
+ return Assertions.checkNotNull(
+ getPeriodPosition(
+ window, period, windowIndex, windowPositionUs, /* defaultPositionProjectionUs= */ 0));
+ }
+
+ /**
+ * Converts (windowIndex, windowPositionUs) to the corresponding (periodUid, periodPositionUs).
+ *
+ * @param window A {@link Window} that may be overwritten.
+ * @param period A {@link Period} that may be overwritten.
+ * @param windowIndex The window index.
+ * @param windowPositionUs The window time, or {@link C#TIME_UNSET} to use the window's default
+ * start position.
+ * @param defaultPositionProjectionUs If {@code windowPositionUs} is {@link C#TIME_UNSET}, the
+ * duration into the future by which the window's position should be projected.
+ * @return The corresponding (periodUid, periodPositionUs), or null if {@code #windowPositionUs}
+ * is {@link C#TIME_UNSET}, {@code defaultPositionProjectionUs} is non-zero, and the window's
+ * position could not be projected by {@code defaultPositionProjectionUs}.
+ */
+ @Nullable
+ public final Pair<Object, Long> getPeriodPosition(
+ Window window,
+ Period period,
+ int windowIndex,
+ long windowPositionUs,
+ long defaultPositionProjectionUs) {
+ Assertions.checkIndex(windowIndex, 0, getWindowCount());
+ getWindow(windowIndex, window, defaultPositionProjectionUs);
+ if (windowPositionUs == C.TIME_UNSET) {
+ windowPositionUs = window.getDefaultPositionUs();
+ if (windowPositionUs == C.TIME_UNSET) {
+ return null;
+ }
+ }
+ int periodIndex = window.firstPeriodIndex;
+ long periodPositionUs = window.getPositionInFirstPeriodUs() + windowPositionUs;
+ long periodDurationUs = getPeriod(periodIndex, period, /* setIds= */ true).getDurationUs();
+ while (periodDurationUs != C.TIME_UNSET && periodPositionUs >= periodDurationUs
+ && periodIndex < window.lastPeriodIndex) {
+ periodPositionUs -= periodDurationUs;
+ periodDurationUs = getPeriod(++periodIndex, period, /* setIds= */ true).getDurationUs();
+ }
+ return Pair.create(Assertions.checkNotNull(period.uid), periodPositionUs);
+ }
+
+ /**
+ * Populates a {@link Period} with data for the period with the specified unique identifier.
+ *
+ * @param periodUid The unique identifier of the period.
+ * @param period The {@link Period} to populate. Must not be null.
+ * @return The populated {@link Period}, for convenience.
+ */
+ public Period getPeriodByUid(Object periodUid, Period period) {
+ return getPeriod(getIndexOfPeriod(periodUid), period, /* setIds= */ true);
+ }
+
+ /**
+ * Populates a {@link Period} with data for the period at the specified index. {@link Period#id}
+ * and {@link Period#uid} will be set to null.
+ *
+ * @param periodIndex The index of the period.
+ * @param period The {@link Period} to populate. Must not be null.
+ * @return The populated {@link Period}, for convenience.
+ */
+ public final Period getPeriod(int periodIndex, Period period) {
+ return getPeriod(periodIndex, period, false);
+ }
+
+ /**
+ * Populates a {@link Period} with data for the period at the specified index.
+ *
+ * @param periodIndex The index of the period.
+ * @param period The {@link Period} to populate. Must not be null.
+ * @param setIds Whether {@link Period#id} and {@link Period#uid} should be populated. If false,
+ * the fields will be set to null. The caller should pass false for efficiency reasons unless
+ * the fields are required.
+ * @return The populated {@link Period}, for convenience.
+ */
+ public abstract Period getPeriod(int periodIndex, Period period, boolean setIds);
+
+ /**
+ * Returns the index of the period identified by its unique {@link Period#uid}, or {@link
+ * C#INDEX_UNSET} if the period is not in the timeline.
+ *
+ * @param uid A unique identifier for a period.
+ * @return The index of the period, or {@link C#INDEX_UNSET} if the period was not found.
+ */
+ public abstract int getIndexOfPeriod(Object uid);
+
+ /**
+ * Returns the unique id of the period identified by its index in the timeline.
+ *
+ * @param periodIndex The index of the period.
+ * @return The unique id of the period.
+ */
+ public abstract Object getUidOfPeriod(int periodIndex);
+}