mirror of
https://github.com/samsonjs/media.git
synced 2026-04-11 12:15:47 +00:00
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
This commit is contained in:
parent
e35f9addaf
commit
bb8cbbfbe8
5 changed files with 142 additions and 101 deletions
|
|
@ -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,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<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,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<Integer, Object>.
|
||||
|
|
@ -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];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in a new issue