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 @@
*/
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.
* <p>
* 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);
}
......@@ -131,38 +131,39 @@ 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<Window> 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,28 +174,41 @@ public final class ConcatenatingMediaSource implements MediaSource {
}
@Override
public long getPeriodDurationMs(int index) {
int sourceIndex = getSourceIndexForPeriod(index);
public int getPeriodCount() {
return sourcePeriodOffsets[sourcePeriodOffsets.length - 1];
}
@Override
public long getPeriodDurationMs(int periodIndex) {
int sourceIndex = getSourceIndexForPeriod(periodIndex);
int firstPeriodIndexInSource = getFirstPeriodIndexInSource(sourceIndex);
return timelines[sourceIndex].getPeriodDurationMs(index - firstPeriodIndexInSource);
return timelines[sourceIndex].getPeriodDurationMs(periodIndex - firstPeriodIndexInSource);
}
@Override
public long getPeriodDurationUs(int index) {
int sourceIndex = getSourceIndexForPeriod(index);
public long getPeriodDurationUs(int periodIndex) {
int sourceIndex = getSourceIndexForPeriod(periodIndex);
int firstPeriodIndexInSource = getFirstPeriodIndexInSource(sourceIndex);
return timelines[sourceIndex].getPeriodDurationUs(index - firstPeriodIndexInSource);
return timelines[sourceIndex].getPeriodDurationUs(periodIndex - firstPeriodIndexInSource);
}
@Override
public Object getPeriodId(int index) {
int sourceIndex = getSourceIndexForPeriod(index);
int firstPeriodIndexInSource = getFirstPeriodIndexInSource(index);
Object periodId = timelines[sourceIndex].getPeriodId(index - firstPeriodIndexInSource);
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<Integer, Object>.
@SuppressWarnings("unchecked")
......@@ -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];
}
}
......
......@@ -59,11 +59,6 @@ public final class SinglePeriodTimeline implements Timeline {
}
@Override
public int getPeriodCount() {
return 1;
}
@Override
public boolean isFinal() {
return isFinal;
}
......@@ -74,30 +69,35 @@ 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;
}
......
......@@ -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;
......@@ -499,11 +500,6 @@ public final class DashMediaSource implements MediaSource {
}
@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 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 Object getPeriodId(int index) {
return firstPeriodId + index;
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;
}
......
......@@ -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 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.
*/
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.
*/
......
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