Commit bb8cbbfb by olly Committed by Oliver Woodman

Allow mapping of period->window

This is a 1:1 mapping. This change formalises the fact,
and makes it possible to easily query the mapping.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=130375111
parent e35f9add
...@@ -15,8 +15,6 @@ ...@@ -15,8 +15,6 @@
*/ */
package com.google.android.exoplayer2; 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. * The player's timeline consisting of one or more periods. Instances are immutable.
*/ */
...@@ -44,26 +42,27 @@ public interface Timeline { ...@@ -44,26 +42,27 @@ public interface Timeline {
long getAbsoluteStartTime(); 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. * {@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}. * @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. * {@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}. * @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 * Returns a unique identifier for the period at {@code periodIndex}, or {@code null} if the
* {@code index} is not known. The identifier is stable across {@link Timeline} instances. * period at {@code periodIndex} is not known. The identifier is stable across {@link Timeline}
* instances.
* <p> * <p>
* When the timeline changes the player uses period identifiers to determine what periods are * 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 * unchanged. Implementations that associate an object with each period can return the object for
...@@ -71,10 +70,18 @@ public interface Timeline { ...@@ -71,10 +70,18 @@ public interface Timeline {
* identifiers that can't clash with (for example) identifiers used by other timelines that may be * identifiers that can't clash with (for example) identifiers used by other timelines that may be
* concatenated with this one. * 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. * @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 * Returns the index of the period identified by {@code id}, or {@link #NO_PERIOD_INDEX} if the
...@@ -91,10 +98,10 @@ public interface Timeline { ...@@ -91,10 +98,10 @@ public interface Timeline {
int getWindowCount(); int getWindowCount();
/** /**
* Returns the {@link Window} at {@code index}, which represents positions that can be seeked * Returns the {@link Window} at the specified index.
* to in the timeline. The windows may change when *
* {@link Listener#onSourceInfoRefreshed(Timeline, Object)} is called. * @param windowIndex The window index.
*/ */
Window getWindow(int index); Window getWindow(int windowIndex);
} }
...@@ -131,38 +131,39 @@ public final class ConcatenatingMediaSource implements MediaSource { ...@@ -131,38 +131,39 @@ public final class ConcatenatingMediaSource implements MediaSource {
private final Timeline[] timelines; private final Timeline[] timelines;
private final boolean isFinal; private final boolean isFinal;
private final int[] sourceOffsets; private final int[] sourcePeriodOffsets;
private final int[] sourceWindowOffsets;
private final Window[] windows; private final Window[] windows;
public ConcatenatedTimeline(Timeline[] timelines) { public ConcatenatedTimeline(Timeline[] timelines) {
boolean isFinal = true; boolean isFinal = true;
int[] sourceOffsets = new int[timelines.length]; int[] sourcePeriodOffsets = new int[timelines.length];
int sourceOffset = 0; int[] sourceWindowOffsets = new int[timelines.length];
int periodCount = 0;
int windowCount = 0;
ArrayList<Window> concatenatedWindows = new ArrayList<>(); ArrayList<Window> concatenatedWindows = new ArrayList<>();
for (int i = 0; i < timelines.length; i++) { for (int i = 0; i < timelines.length; i++) {
Timeline timeline = timelines[i]; Timeline timeline = timelines[i];
isFinal &= timeline.isFinal(); isFinal &= timeline.isFinal();
// Offset the windows so they are relative to the source. // Offset the windows so they are relative to the source.
int windowCount = timeline.getWindowCount(); int sourceWindowCount = timeline.getWindowCount();
for (int j = 0; j < windowCount; j++) { for (int j = 0; j < sourceWindowCount; j++) {
Window sourceWindow = timeline.getWindow(j); Window sourceWindow = timeline.getWindow(j);
concatenatedWindows.add(sourceWindow.copyOffsetByPeriodCount(sourceOffset)); concatenatedWindows.add(sourceWindow.copyOffsetByPeriodCount(periodCount));
} }
sourceOffset += timeline.getPeriodCount(); periodCount += timeline.getPeriodCount();
sourceOffsets[i] = sourceOffset; sourcePeriodOffsets[i] = periodCount;
windowCount += timeline.getWindowCount();
sourceWindowOffsets[i] = windowCount;
} }
this.timelines = timelines; this.timelines = timelines;
this.isFinal = isFinal; this.isFinal = isFinal;
this.sourceOffsets = sourceOffsets; this.sourcePeriodOffsets = sourcePeriodOffsets;
this.sourceWindowOffsets = sourceWindowOffsets;
windows = concatenatedWindows.toArray(new Window[concatenatedWindows.size()]); windows = concatenatedWindows.toArray(new Window[concatenatedWindows.size()]);
} }
@Override @Override
public int getPeriodCount() {
return sourceOffsets[sourceOffsets.length - 1];
}
@Override
public boolean isFinal() { public boolean isFinal() {
return isFinal; return isFinal;
} }
...@@ -173,28 +174,41 @@ public final class ConcatenatingMediaSource implements MediaSource { ...@@ -173,28 +174,41 @@ public final class ConcatenatingMediaSource implements MediaSource {
} }
@Override @Override
public long getPeriodDurationMs(int index) { public int getPeriodCount() {
int sourceIndex = getSourceIndexForPeriod(index); return sourcePeriodOffsets[sourcePeriodOffsets.length - 1];
}
@Override
public long getPeriodDurationMs(int periodIndex) {
int sourceIndex = getSourceIndexForPeriod(periodIndex);
int firstPeriodIndexInSource = getFirstPeriodIndexInSource(sourceIndex); int firstPeriodIndexInSource = getFirstPeriodIndexInSource(sourceIndex);
return timelines[sourceIndex].getPeriodDurationMs(index - firstPeriodIndexInSource); return timelines[sourceIndex].getPeriodDurationMs(periodIndex - firstPeriodIndexInSource);
} }
@Override @Override
public long getPeriodDurationUs(int index) { public long getPeriodDurationUs(int periodIndex) {
int sourceIndex = getSourceIndexForPeriod(index); int sourceIndex = getSourceIndexForPeriod(periodIndex);
int firstPeriodIndexInSource = getFirstPeriodIndexInSource(sourceIndex); int firstPeriodIndexInSource = getFirstPeriodIndexInSource(sourceIndex);
return timelines[sourceIndex].getPeriodDurationUs(index - firstPeriodIndexInSource); return timelines[sourceIndex].getPeriodDurationUs(periodIndex - firstPeriodIndexInSource);
} }
@Override @Override
public Object getPeriodId(int index) { public Object getPeriodId(int periodIndex) {
int sourceIndex = getSourceIndexForPeriod(index); int sourceIndex = getSourceIndexForPeriod(periodIndex);
int firstPeriodIndexInSource = getFirstPeriodIndexInSource(index); int firstPeriodIndexInSource = getFirstPeriodIndexInSource(periodIndex);
Object periodId = timelines[sourceIndex].getPeriodId(index - firstPeriodIndexInSource); Object periodId = timelines[sourceIndex].getPeriodId(periodIndex - firstPeriodIndexInSource);
return Pair.create(sourceIndex, periodId); return Pair.create(sourceIndex, periodId);
} }
@Override @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) { public int getIndexOfPeriod(Object id) {
// The id was returned by getPeriodId, so it is always a Pair<Integer, Object>. // The id was returned by getPeriodId, so it is always a Pair<Integer, Object>.
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
...@@ -215,16 +229,16 @@ public final class ConcatenatingMediaSource implements MediaSource { ...@@ -215,16 +229,16 @@ public final class ConcatenatingMediaSource implements MediaSource {
} }
@Override @Override
public Window getWindow(int index) { public Window getWindow(int windowIndex) {
return windows[index]; return windows[windowIndex];
} }
private int getSourceIndexForPeriod(int periodIndex) { 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) { private int getFirstPeriodIndexInSource(int sourceIndex) {
return sourceIndex == 0 ? 0 : sourceOffsets[sourceIndex - 1]; return sourceIndex == 0 ? 0 : sourcePeriodOffsets[sourceIndex - 1];
} }
} }
......
...@@ -59,11 +59,6 @@ public final class SinglePeriodTimeline implements Timeline { ...@@ -59,11 +59,6 @@ public final class SinglePeriodTimeline implements Timeline {
} }
@Override @Override
public int getPeriodCount() {
return 1;
}
@Override
public boolean isFinal() { public boolean isFinal() {
return isFinal; return isFinal;
} }
...@@ -74,30 +69,35 @@ public final class SinglePeriodTimeline implements Timeline { ...@@ -74,30 +69,35 @@ public final class SinglePeriodTimeline implements Timeline {
} }
@Override @Override
public long getPeriodDurationMs(int index) { public int getPeriodCount() {
if (index != 0) { return 1;
throw new IndexOutOfBoundsException(); }
}
@Override
public long getPeriodDurationMs(int periodIndex) {
Assertions.checkIndex(periodIndex, 0, 1);
return durationUs == C.UNSET_TIME_US ? ExoPlayer.UNKNOWN_TIME : (durationUs / 1000); return durationUs == C.UNSET_TIME_US ? ExoPlayer.UNKNOWN_TIME : (durationUs / 1000);
} }
@Override @Override
public long getPeriodDurationUs(int index) { public long getPeriodDurationUs(int periodIndex) {
if (index != 0) { Assertions.checkIndex(periodIndex, 0, 1);
throw new IndexOutOfBoundsException();
}
return durationUs; return durationUs;
} }
@Override @Override
public Object getPeriodId(int index) { public Object getPeriodId(int periodIndex) {
if (index != 0) { Assertions.checkIndex(periodIndex, 0, 1);
throw new IndexOutOfBoundsException();
}
return id; return id;
} }
@Override @Override
public int getPeriodWindowIndex(int periodIndex) {
Assertions.checkIndex(periodIndex, 0, 1);
return 0;
}
@Override
public int getIndexOfPeriod(Object id) { public int getIndexOfPeriod(Object id) {
return id.equals(this.id) ? 0 : Timeline.NO_PERIOD_INDEX; return id.equals(this.id) ? 0 : Timeline.NO_PERIOD_INDEX;
} }
...@@ -108,7 +108,8 @@ public final class SinglePeriodTimeline implements Timeline { ...@@ -108,7 +108,8 @@ public final class SinglePeriodTimeline implements Timeline {
} }
@Override @Override
public Window getWindow(int index) { public Window getWindow(int windowIndex) {
Assertions.checkIndex(windowIndex, 0, 1);
return window; return window;
} }
......
...@@ -37,6 +37,7 @@ import com.google.android.exoplayer2.upstream.Allocator; ...@@ -37,6 +37,7 @@ import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.Loader; import com.google.android.exoplayer2.upstream.Loader;
import com.google.android.exoplayer2.upstream.ParsingLoadable; import com.google.android.exoplayer2.upstream.ParsingLoadable;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
...@@ -499,11 +500,6 @@ public final class DashMediaSource implements MediaSource { ...@@ -499,11 +500,6 @@ public final class DashMediaSource implements MediaSource {
} }
@Override @Override
public int getPeriodCount() {
return manifest.getPeriodCount();
}
@Override
public boolean isFinal() { public boolean isFinal() {
return !manifest.dynamic; return !manifest.dynamic;
} }
...@@ -514,24 +510,31 @@ public final class DashMediaSource implements MediaSource { ...@@ -514,24 +510,31 @@ public final class DashMediaSource implements MediaSource {
} }
@Override @Override
public long getPeriodDurationMs(int index) { public int getPeriodCount() {
if (index < 0 || index >= manifest.getPeriodCount()) { return manifest.getPeriodCount();
throw new IndexOutOfBoundsException();
}
return manifest.getPeriodDurationMs(index);
} }
@Override @Override
public long getPeriodDurationUs(int index) { public long getPeriodDurationMs(int periodIndex) {
if (index < 0 || index >= manifest.getPeriodCount()) { Assertions.checkIndex(periodIndex, 0, manifest.getPeriodCount());
throw new IndexOutOfBoundsException(); return manifest.getPeriodDurationMs(periodIndex);
} }
return manifest.getPeriodDurationUs(index);
@Override
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 @Override
public Object getPeriodId(int index) { public int getPeriodWindowIndex(int periodIndex) {
return firstPeriodId + index; Assertions.checkIndex(periodIndex, 0, manifest.getPeriodCount());
return 0;
} }
@Override @Override
...@@ -545,7 +548,8 @@ public final class DashMediaSource implements MediaSource { ...@@ -545,7 +548,8 @@ public final class DashMediaSource implements MediaSource {
} }
@Override @Override
public Window getWindow(int index) { public Window getWindow(int windowIndex) {
Assertions.checkIndex(windowIndex, 0, 1);
return window; return window;
} }
......
...@@ -27,10 +27,9 @@ public final class Assertions { ...@@ -27,10 +27,9 @@ public final class Assertions {
private Assertions() {} private Assertions() {}
/** /**
* Ensures the truth of an expression involving one or more arguments passed to the calling * Throws {@link IllegalArgumentException} if {@code expression} evaluates to false.
* method.
* *
* @param expression A boolean expression. * @param expression The expression to evaluate.
* @throws IllegalArgumentException If {@code expression} is false. * @throws IllegalArgumentException If {@code expression} is false.
*/ */
public static void checkArgument(boolean expression) { public static void checkArgument(boolean expression) {
...@@ -40,11 +39,10 @@ public final class Assertions { ...@@ -40,11 +39,10 @@ public final class Assertions {
} }
/** /**
* Ensures the truth of an expression involving one or more arguments passed to the calling * Throws {@link IllegalArgumentException} if {@code expression} evaluates to false.
* method.
* *
* @param expression A boolean expression. * @param expression The expression to evaluate.
* @param errorMessage The exception message to use if the check fails. The message is converted * @param errorMessage The exception message if an exception is thrown. The message is converted
* to a {@link String} using {@link String#valueOf(Object)}. * to a {@link String} using {@link String#valueOf(Object)}.
* @throws IllegalArgumentException If {@code expression} is false. * @throws IllegalArgumentException If {@code expression} is false.
*/ */
...@@ -55,9 +53,25 @@ public final class Assertions { ...@@ -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 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 A boolean expression. * @param expression The expression to evaluate.
* @throws IllegalStateException If {@code expression} is false. * @throws IllegalStateException If {@code expression} is false.
*/ */
public static void checkState(boolean expression) { public static void checkState(boolean expression) {
...@@ -67,11 +81,11 @@ public final class Assertions { ...@@ -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 expression The expression to evaluate.
* @param errorMessage The exception message to use if the check fails. The message is converted * @param errorMessage The exception message if an exception is thrown. The message is converted
* to a string using {@link String#valueOf(Object)}. * to a {@link String} using {@link String#valueOf(Object)}.
* @throws IllegalStateException If {@code expression} is false. * @throws IllegalStateException If {@code expression} is false.
*/ */
public static void checkState(boolean expression, Object errorMessage) { public static void checkState(boolean expression, Object errorMessage) {
...@@ -81,7 +95,7 @@ public final class Assertions { ...@@ -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. * @param reference An object reference.
* @return The non-null reference that was validated. * @return The non-null reference that was validated.
...@@ -95,7 +109,7 @@ public final class Assertions { ...@@ -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 reference An object reference.
* @param errorMessage The exception message to use if the check fails. The message is converted * @param errorMessage The exception message to use if the check fails. The message is converted
...@@ -111,9 +125,9 @@ public final class Assertions { ...@@ -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. * @return The non-null, non-empty string that was validated.
* @throws IllegalArgumentException If {@code string} is null or 0-length. * @throws IllegalArgumentException If {@code string} is null or 0-length.
*/ */
...@@ -125,9 +139,9 @@ public final class Assertions { ...@@ -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 * @param errorMessage The exception message to use if the check fails. The message is converted
* to a string using {@link String#valueOf(Object)}. * to a string using {@link String#valueOf(Object)}.
* @return The non-null, non-empty string that was validated. * @return The non-null, non-empty string that was validated.
...@@ -141,7 +155,8 @@ public final class Assertions { ...@@ -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. * @throws IllegalStateException If the calling thread is not the application's main thread.
*/ */
......
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