diff --git a/library/src/main/java/com/google/android/exoplayer2/Timeline.java b/library/src/main/java/com/google/android/exoplayer2/Timeline.java
index b46c3a6149..1cc2773ad5 100644
--- a/library/src/main/java/com/google/android/exoplayer2/Timeline.java
+++ b/library/src/main/java/com/google/android/exoplayer2/Timeline.java
@@ -15,8 +15,6 @@
*/
package com.google.android.exoplayer2;
-import com.google.android.exoplayer2.source.MediaSource.Listener;
-
/**
* The player's timeline consisting of one or more periods. Instances are immutable.
*/
@@ -44,26 +42,27 @@ public interface Timeline {
long getAbsoluteStartTime();
/**
- * Returns the duration of the period at {@code index} in the timeline, in milliseconds, or
+ * Returns the duration of the period at {@code periodIndex} in the timeline, in milliseconds, or
* {@link ExoPlayer#UNKNOWN_TIME} if not known.
*
- * @param index The index of the period.
+ * @param periodIndex The index of the period.
* @return The duration of the period in milliseconds, or {@link ExoPlayer#UNKNOWN_TIME}.
*/
- long getPeriodDurationMs(int index);
+ long getPeriodDurationMs(int periodIndex);
/**
- * Returns the duration of the period at {@code index} in the timeline, in microseconds, or
+ * Returns the duration of the period at {@code periodIndex} in the timeline, in microseconds, or
* {@link C#UNSET_TIME_US} if not known.
*
- * @param index The index of the period.
+ * @param periodIndex The index of the period.
* @return The duration of the period in microseconds, or {@link C#UNSET_TIME_US}.
*/
- long getPeriodDurationUs(int index);
+ long getPeriodDurationUs(int periodIndex);
/**
- * Returns a unique identifier for the period at {@code index}, or {@code null} if the period at
- * {@code index} is not known. The identifier is stable across {@link Timeline} instances.
+ * Returns a unique identifier for the period at {@code periodIndex}, or {@code null} if the
+ * period at {@code periodIndex} is not known. The identifier is stable across {@link Timeline}
+ * instances.
*
* When the timeline changes the player uses period identifiers to determine what periods are
* unchanged. Implementations that associate an object with each period can return the object for
@@ -71,10 +70,18 @@ public interface Timeline {
* identifiers that can't clash with (for example) identifiers used by other timelines that may be
* concatenated with this one.
*
- * @param index A period index.
+ * @param periodIndex A period index.
* @return An identifier for the period, or {@code null} if the period is not known.
*/
- Object getPeriodId(int index);
+ Object getPeriodId(int periodIndex);
+
+ /**
+ * Returns the index of the window to which the period with the specified index belongs.
+ *
+ * @param periodIndex The period index.
+ * @return The index of the corresponding seek window.
+ */
+ int getPeriodWindowIndex(int periodIndex);
/**
* Returns the index of the period identified by {@code id}, or {@link #NO_PERIOD_INDEX} if the
@@ -91,10 +98,10 @@ public interface Timeline {
int getWindowCount();
/**
- * Returns the {@link Window} at {@code index}, which represents positions that can be seeked
- * to in the timeline. The windows may change when
- * {@link Listener#onSourceInfoRefreshed(Timeline, Object)} is called.
+ * Returns the {@link Window} at the specified index.
+ *
+ * @param windowIndex The window index.
*/
- Window getWindow(int index);
+ Window getWindow(int windowIndex);
}
diff --git a/library/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java b/library/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java
index a54475e011..66d3011f06 100644
--- a/library/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java
+++ b/library/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java
@@ -131,37 +131,38 @@ public final class ConcatenatingMediaSource implements MediaSource {
private final Timeline[] timelines;
private final boolean isFinal;
- private final int[] sourceOffsets;
+ private final int[] sourcePeriodOffsets;
+ private final int[] sourceWindowOffsets;
private final Window[] windows;
public ConcatenatedTimeline(Timeline[] timelines) {
boolean isFinal = true;
- int[] sourceOffsets = new int[timelines.length];
- int sourceOffset = 0;
+ int[] sourcePeriodOffsets = new int[timelines.length];
+ int[] sourceWindowOffsets = new int[timelines.length];
+ int periodCount = 0;
+ int windowCount = 0;
ArrayList concatenatedWindows = new ArrayList<>();
for (int i = 0; i < timelines.length; i++) {
Timeline timeline = timelines[i];
isFinal &= timeline.isFinal();
// Offset the windows so they are relative to the source.
- int windowCount = timeline.getWindowCount();
- for (int j = 0; j < windowCount; j++) {
+ int sourceWindowCount = timeline.getWindowCount();
+ for (int j = 0; j < sourceWindowCount; j++) {
Window sourceWindow = timeline.getWindow(j);
- concatenatedWindows.add(sourceWindow.copyOffsetByPeriodCount(sourceOffset));
+ concatenatedWindows.add(sourceWindow.copyOffsetByPeriodCount(periodCount));
}
- sourceOffset += timeline.getPeriodCount();
- sourceOffsets[i] = sourceOffset;
+ periodCount += timeline.getPeriodCount();
+ sourcePeriodOffsets[i] = periodCount;
+ windowCount += timeline.getWindowCount();
+ sourceWindowOffsets[i] = windowCount;
}
this.timelines = timelines;
this.isFinal = isFinal;
- this.sourceOffsets = sourceOffsets;
+ this.sourcePeriodOffsets = sourcePeriodOffsets;
+ this.sourceWindowOffsets = sourceWindowOffsets;
windows = concatenatedWindows.toArray(new Window[concatenatedWindows.size()]);
}
- @Override
- public int getPeriodCount() {
- return sourceOffsets[sourceOffsets.length - 1];
- }
-
@Override
public boolean isFinal() {
return isFinal;
@@ -173,27 +174,40 @@ public final class ConcatenatingMediaSource implements MediaSource {
}
@Override
- public long getPeriodDurationMs(int index) {
- int sourceIndex = getSourceIndexForPeriod(index);
- int firstPeriodIndexInSource = getFirstPeriodIndexInSource(sourceIndex);
- return timelines[sourceIndex].getPeriodDurationMs(index - firstPeriodIndexInSource);
+ public int getPeriodCount() {
+ return sourcePeriodOffsets[sourcePeriodOffsets.length - 1];
}
@Override
- public long getPeriodDurationUs(int index) {
- int sourceIndex = getSourceIndexForPeriod(index);
+ public long getPeriodDurationMs(int periodIndex) {
+ int sourceIndex = getSourceIndexForPeriod(periodIndex);
int firstPeriodIndexInSource = getFirstPeriodIndexInSource(sourceIndex);
- return timelines[sourceIndex].getPeriodDurationUs(index - firstPeriodIndexInSource);
+ return timelines[sourceIndex].getPeriodDurationMs(periodIndex - firstPeriodIndexInSource);
}
@Override
- public Object getPeriodId(int index) {
- int sourceIndex = getSourceIndexForPeriod(index);
- int firstPeriodIndexInSource = getFirstPeriodIndexInSource(index);
- Object periodId = timelines[sourceIndex].getPeriodId(index - firstPeriodIndexInSource);
+ public long getPeriodDurationUs(int periodIndex) {
+ int sourceIndex = getSourceIndexForPeriod(periodIndex);
+ int firstPeriodIndexInSource = getFirstPeriodIndexInSource(sourceIndex);
+ return timelines[sourceIndex].getPeriodDurationUs(periodIndex - firstPeriodIndexInSource);
+ }
+
+ @Override
+ public Object getPeriodId(int periodIndex) {
+ int sourceIndex = getSourceIndexForPeriod(periodIndex);
+ int firstPeriodIndexInSource = getFirstPeriodIndexInSource(periodIndex);
+ Object periodId = timelines[sourceIndex].getPeriodId(periodIndex - firstPeriodIndexInSource);
return Pair.create(sourceIndex, periodId);
}
+ @Override
+ public int getPeriodWindowIndex(int periodIndex) {
+ int sourceIndex = getSourceIndexForPeriod(periodIndex);
+ int firstPeriodIndexInSource = getFirstPeriodIndexInSource(periodIndex);
+ return (sourceIndex > 0 ? sourceWindowOffsets[sourceIndex - 1] : 0)
+ + timelines[sourceIndex].getPeriodWindowIndex(periodIndex - firstPeriodIndexInSource);
+ }
+
@Override
public int getIndexOfPeriod(Object id) {
// The id was returned by getPeriodId, so it is always a Pair.
@@ -215,16 +229,16 @@ public final class ConcatenatingMediaSource implements MediaSource {
}
@Override
- public Window getWindow(int index) {
- return windows[index];
+ public Window getWindow(int windowIndex) {
+ return windows[windowIndex];
}
private int getSourceIndexForPeriod(int periodIndex) {
- return Util.binarySearchFloor(sourceOffsets, periodIndex, true, false) + 1;
+ return Util.binarySearchFloor(sourcePeriodOffsets, periodIndex, true, false) + 1;
}
private int getFirstPeriodIndexInSource(int sourceIndex) {
- return sourceIndex == 0 ? 0 : sourceOffsets[sourceIndex - 1];
+ return sourceIndex == 0 ? 0 : sourcePeriodOffsets[sourceIndex - 1];
}
}
diff --git a/library/src/main/java/com/google/android/exoplayer2/source/SinglePeriodTimeline.java b/library/src/main/java/com/google/android/exoplayer2/source/SinglePeriodTimeline.java
index 46da9ebfbf..3544468810 100644
--- a/library/src/main/java/com/google/android/exoplayer2/source/SinglePeriodTimeline.java
+++ b/library/src/main/java/com/google/android/exoplayer2/source/SinglePeriodTimeline.java
@@ -58,11 +58,6 @@ public final class SinglePeriodTimeline implements Timeline {
this.isFinal = true; // TODO: Remove.
}
- @Override
- public int getPeriodCount() {
- return 1;
- }
-
@Override
public boolean isFinal() {
return isFinal;
@@ -74,29 +69,34 @@ public final class SinglePeriodTimeline implements Timeline {
}
@Override
- public long getPeriodDurationMs(int index) {
- if (index != 0) {
- throw new IndexOutOfBoundsException();
- }
+ public int getPeriodCount() {
+ return 1;
+ }
+
+ @Override
+ public long getPeriodDurationMs(int periodIndex) {
+ Assertions.checkIndex(periodIndex, 0, 1);
return durationUs == C.UNSET_TIME_US ? ExoPlayer.UNKNOWN_TIME : (durationUs / 1000);
}
@Override
- public long getPeriodDurationUs(int index) {
- if (index != 0) {
- throw new IndexOutOfBoundsException();
- }
+ public long getPeriodDurationUs(int periodIndex) {
+ Assertions.checkIndex(periodIndex, 0, 1);
return durationUs;
}
@Override
- public Object getPeriodId(int index) {
- if (index != 0) {
- throw new IndexOutOfBoundsException();
- }
+ public Object getPeriodId(int periodIndex) {
+ Assertions.checkIndex(periodIndex, 0, 1);
return id;
}
+ @Override
+ public int getPeriodWindowIndex(int periodIndex) {
+ Assertions.checkIndex(periodIndex, 0, 1);
+ return 0;
+ }
+
@Override
public int getIndexOfPeriod(Object id) {
return id.equals(this.id) ? 0 : Timeline.NO_PERIOD_INDEX;
@@ -108,7 +108,8 @@ public final class SinglePeriodTimeline implements Timeline {
}
@Override
- public Window getWindow(int index) {
+ public Window getWindow(int windowIndex) {
+ Assertions.checkIndex(windowIndex, 0, 1);
return window;
}
diff --git a/library/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java b/library/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java
index 291267da03..13bf9f305b 100644
--- a/library/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java
+++ b/library/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java
@@ -37,6 +37,7 @@ import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.Loader;
import com.google.android.exoplayer2.upstream.ParsingLoadable;
+import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import java.io.BufferedReader;
import java.io.IOException;
@@ -498,11 +499,6 @@ public final class DashMediaSource implements MediaSource {
this.window = window;
}
- @Override
- public int getPeriodCount() {
- return manifest.getPeriodCount();
- }
-
@Override
public boolean isFinal() {
return !manifest.dynamic;
@@ -514,24 +510,31 @@ public final class DashMediaSource implements MediaSource {
}
@Override
- public long getPeriodDurationMs(int index) {
- if (index < 0 || index >= manifest.getPeriodCount()) {
- throw new IndexOutOfBoundsException();
- }
- return manifest.getPeriodDurationMs(index);
+ public int getPeriodCount() {
+ return manifest.getPeriodCount();
}
@Override
- public long getPeriodDurationUs(int index) {
- if (index < 0 || index >= manifest.getPeriodCount()) {
- throw new IndexOutOfBoundsException();
- }
- return manifest.getPeriodDurationUs(index);
+ public long getPeriodDurationMs(int periodIndex) {
+ Assertions.checkIndex(periodIndex, 0, manifest.getPeriodCount());
+ return manifest.getPeriodDurationMs(periodIndex);
}
@Override
- public Object getPeriodId(int index) {
- return firstPeriodId + index;
+ public long getPeriodDurationUs(int periodIndex) {
+ Assertions.checkIndex(periodIndex, 0, manifest.getPeriodCount());
+ return manifest.getPeriodDurationUs(periodIndex);
+ }
+
+ @Override
+ public Object getPeriodId(int periodIndex) {
+ return firstPeriodId + Assertions.checkIndex(periodIndex, 0, manifest.getPeriodCount());
+ }
+
+ @Override
+ public int getPeriodWindowIndex(int periodIndex) {
+ Assertions.checkIndex(periodIndex, 0, manifest.getPeriodCount());
+ return 0;
}
@Override
@@ -545,7 +548,8 @@ public final class DashMediaSource implements MediaSource {
}
@Override
- public Window getWindow(int index) {
+ public Window getWindow(int windowIndex) {
+ Assertions.checkIndex(windowIndex, 0, 1);
return window;
}
diff --git a/library/src/main/java/com/google/android/exoplayer2/util/Assertions.java b/library/src/main/java/com/google/android/exoplayer2/util/Assertions.java
index 237e16192f..dccfb32203 100644
--- a/library/src/main/java/com/google/android/exoplayer2/util/Assertions.java
+++ b/library/src/main/java/com/google/android/exoplayer2/util/Assertions.java
@@ -27,10 +27,9 @@ public final class Assertions {
private Assertions() {}
/**
- * Ensures the truth of an expression involving one or more arguments passed to the calling
- * method.
+ * Throws {@link IllegalArgumentException} if {@code expression} evaluates to false.
*
- * @param expression A boolean expression.
+ * @param expression The expression to evaluate.
* @throws IllegalArgumentException If {@code expression} is false.
*/
public static void checkArgument(boolean expression) {
@@ -40,11 +39,10 @@ public final class Assertions {
}
/**
- * Ensures the truth of an expression involving one or more arguments passed to the calling
- * method.
+ * Throws {@link IllegalArgumentException} if {@code expression} evaluates to false.
*
- * @param expression A boolean expression.
- * @param errorMessage The exception message to use if the check fails. The message is converted
+ * @param expression The expression to evaluate.
+ * @param errorMessage The exception message if an exception is thrown. The message is converted
* to a {@link String} using {@link String#valueOf(Object)}.
* @throws IllegalArgumentException If {@code expression} is false.
*/
@@ -55,9 +53,25 @@ public final class Assertions {
}
/**
- * Ensures the truth of an expression involving the state of the calling instance.
+ * Throws {@link IndexOutOfBoundsException} if {@code index} falls outside the specified bounds.
*
- * @param expression A boolean expression.
+ * @param index The index to test.
+ * @param start The start of the allowed range (inclusive).
+ * @param limit The end of the allowed range (exclusive).
+ * @return The {@code index} that was validated.
+ * @throws IndexOutOfBoundsException If {@code index} falls outside the specified bounds.
+ */
+ public static int checkIndex(int index, int start, int limit) {
+ if (index < start || index >= limit) {
+ throw new IndexOutOfBoundsException();
+ }
+ return index;
+ }
+
+ /**
+ * Throws {@link IllegalStateException} if {@code expression} evaluates to false.
+ *
+ * @param expression The expression to evaluate.
* @throws IllegalStateException If {@code expression} is false.
*/
public static void checkState(boolean expression) {
@@ -67,11 +81,11 @@ public final class Assertions {
}
/**
- * Ensures the truth of an expression involving the state of the calling instance.
+ * Throws {@link IllegalStateException} if {@code expression} evaluates to false.
*
- * @param expression A boolean expression.
- * @param errorMessage The exception message to use if the check fails. The message is converted
- * to a string using {@link String#valueOf(Object)}.
+ * @param expression The expression to evaluate.
+ * @param errorMessage The exception message if an exception is thrown. The message is converted
+ * to a {@link String} using {@link String#valueOf(Object)}.
* @throws IllegalStateException If {@code expression} is false.
*/
public static void checkState(boolean expression, Object errorMessage) {
@@ -81,7 +95,7 @@ public final class Assertions {
}
/**
- * Ensures that an object reference is not null.
+ * Throws {@link NullPointerException} if {@code reference} is null.
*
* @param reference An object reference.
* @return The non-null reference that was validated.
@@ -95,7 +109,7 @@ public final class Assertions {
}
/**
- * Ensures that an object reference is not null.
+ * Throws {@link NullPointerException} if {@code reference} is null.
*
* @param reference An object reference.
* @param errorMessage The exception message to use if the check fails. The message is converted
@@ -111,9 +125,9 @@ public final class Assertions {
}
/**
- * Ensures that a string passed as an argument to the calling method is not null or 0-length.
+ * Throws {@link IllegalArgumentException} if {@code string} is null or zero length.
*
- * @param string A string.
+ * @param string The string to check.
* @return The non-null, non-empty string that was validated.
* @throws IllegalArgumentException If {@code string} is null or 0-length.
*/
@@ -125,9 +139,9 @@ public final class Assertions {
}
/**
- * Ensures that a string passed as an argument to the calling method is not null or 0-length.
+ * Throws {@link IllegalArgumentException} if {@code string} is null or zero length.
*
- * @param string A string.
+ * @param string The string to check.
* @param errorMessage The exception message to use if the check fails. The message is converted
* to a string using {@link String#valueOf(Object)}.
* @return The non-null, non-empty string that was validated.
@@ -141,7 +155,8 @@ public final class Assertions {
}
/**
- * Ensures that the calling thread is the application's main thread.
+ * Throws {@link IllegalStateException} if the calling thread is not the application's main
+ * thread.
*
* @throws IllegalStateException If the calling thread is not the application's main thread.
*/