mirror of
https://github.com/samsonjs/media.git
synced 2026-03-26 09:35:47 +00:00
Toward API finalization
- Remove getNewPlayingPeriodIndex from MediaSource - Remove all absolute references to period indices from Window. Their existence prevents ConcatenatingMediaSource from being able to efficiently handle children with large numbers of entries (e.g. a cursor backed source), since it would copy all windows into its merged timeline. - Fix ExoPlayerImplInternal to attempt a restart in the case that the loading (but not yet prepared) period is removed, in absence of a playing period. - Implement logic for finding the "next" period in the old timeline when attempting a retry. Removing some of the nasty US<->MS conversions left as a virtual TODO. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=130659973
This commit is contained in:
parent
eff82e920e
commit
83e466d129
13 changed files with 219 additions and 263 deletions
|
|
@ -161,8 +161,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||
throw new IllegalArgumentException("Windows are not yet known");
|
||||
}
|
||||
Assertions.checkIndex(windowIndex, 0, timeline.getWindowCount());
|
||||
Window window = timeline.getWindow(windowIndex);
|
||||
seekToDefaultPositionForPeriod(window.startPeriodIndex);
|
||||
seekToDefaultPositionForPeriod(timeline.getWindowFirstPeriodIndex(windowIndex));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -181,12 +180,14 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||
throw new IllegalArgumentException("Windows are not yet known");
|
||||
}
|
||||
Assertions.checkIndex(windowIndex, 0, timeline.getWindowCount());
|
||||
Window window = timeline.getWindow(windowIndex);
|
||||
int periodIndex = window.startPeriodIndex;
|
||||
long periodPositionMs = window.startTimeMs + positionMs;
|
||||
int firstPeriodIndex = timeline.getWindowFirstPeriodIndex(windowIndex);
|
||||
int lastPeriodIndex = timeline.getWindowLastPeriodIndex(windowIndex);
|
||||
int periodIndex = firstPeriodIndex;
|
||||
long periodPositionMs = timeline.getWindowOffsetInFirstPeriodUs(windowIndex) / 1000
|
||||
+ positionMs;
|
||||
long periodDurationMs = timeline.getPeriodDurationMs(periodIndex);
|
||||
while (periodDurationMs != UNKNOWN_TIME && periodPositionMs >= periodDurationMs
|
||||
&& periodIndex < window.endPeriodIndex) {
|
||||
&& periodIndex < lastPeriodIndex) {
|
||||
periodPositionMs -= periodDurationMs;
|
||||
periodDurationMs = timeline.getPeriodDurationMs(++periodIndex);
|
||||
}
|
||||
|
|
@ -272,7 +273,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||
if (timeline == null) {
|
||||
return UNKNOWN_TIME;
|
||||
}
|
||||
return timeline.getWindow(getCurrentWindowIndex()).durationMs;
|
||||
long durationUs = timeline.getWindow(getCurrentWindowIndex()).durationUs;
|
||||
return durationUs == C.UNSET_TIME_US ? ExoPlayer.UNKNOWN_TIME : durationUs / 1000;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -282,13 +284,12 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||
}
|
||||
int periodIndex = getCurrentPeriodIndex();
|
||||
int windowIndex = timeline.getPeriodWindowIndex(periodIndex);
|
||||
Window window = timeline.getWindow(windowIndex);
|
||||
long position = getCurrentPositionInPeriod();
|
||||
for (int i = window.startPeriodIndex; i < periodIndex; i++) {
|
||||
position += timeline.getPeriodDurationMs(i);
|
||||
long positionMs = getCurrentPositionInPeriod();
|
||||
for (int i = timeline.getWindowFirstPeriodIndex(windowIndex); i < periodIndex; i++) {
|
||||
positionMs += timeline.getPeriodDurationMs(i);
|
||||
}
|
||||
position -= window.startTimeMs;
|
||||
return position;
|
||||
positionMs -= timeline.getWindowOffsetInFirstPeriodUs(windowIndex) / 1000;
|
||||
return positionMs;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -300,8 +301,11 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||
int periodIndex = getCurrentPeriodIndex();
|
||||
int windowIndex = timeline.getPeriodWindowIndex(periodIndex);
|
||||
Window window = timeline.getWindow(windowIndex);
|
||||
if (window.startPeriodIndex == periodIndex && window.endPeriodIndex == periodIndex
|
||||
&& window.startTimeMs == 0 && window.durationMs == getCurrentPeriodDuration()) {
|
||||
int firstPeriodIndex = timeline.getWindowFirstPeriodIndex(windowIndex);
|
||||
int lastPeriodIndex = timeline.getWindowLastPeriodIndex(windowIndex);
|
||||
if (firstPeriodIndex == periodIndex && lastPeriodIndex == periodIndex
|
||||
&& timeline.getWindowOffsetInFirstPeriodUs(windowIndex) == 0
|
||||
&& window.durationUs == timeline.getPeriodDurationUs(periodIndex)) {
|
||||
return getBufferedPositionInPeriod();
|
||||
}
|
||||
return getCurrentPosition();
|
||||
|
|
|
|||
|
|
@ -498,9 +498,9 @@ import java.io.IOException;
|
|||
if (positionUs == C.UNSET_TIME_US && timeline != null
|
||||
&& periodIndex < timeline.getPeriodCount()) {
|
||||
// We know about the window, so seek to its default initial position now.
|
||||
Window window = timeline.getPeriodWindow(periodIndex);
|
||||
periodIndex = window.defaultInitialPeriodIndex;
|
||||
positionUs = window.defaultInitialTimeMs * 1000;
|
||||
Pair<Integer, Long> defaultPosition = getDefaultPosition(periodIndex);
|
||||
periodIndex = defaultPosition.first;
|
||||
positionUs = defaultPosition.second;
|
||||
}
|
||||
|
||||
if (periodIndex == playbackInfo.periodIndex
|
||||
|
|
@ -795,28 +795,7 @@ import java.io.IOException;
|
|||
if (playingPeriod != null) {
|
||||
int index = timeline.getIndexOfPeriod(playingPeriod.id);
|
||||
if (index == Timeline.NO_PERIOD_INDEX) {
|
||||
int newPlayingPeriodIndex =
|
||||
mediaSource.getNewPlayingPeriodIndex(playingPeriod.index, oldTimeline);
|
||||
if (newPlayingPeriodIndex == Timeline.NO_PERIOD_INDEX) {
|
||||
// There is no period to play, so stop the player.
|
||||
stopInternal();
|
||||
return;
|
||||
}
|
||||
|
||||
// Release all loaded periods.
|
||||
releasePeriodsFrom(playingPeriod);
|
||||
playingPeriod = null;
|
||||
readingPeriod = null;
|
||||
loadingPeriod = null;
|
||||
|
||||
// Find the default initial position in the window and seek to it.
|
||||
Window window = timeline.getPeriodWindow(newPlayingPeriodIndex);
|
||||
newPlayingPeriodIndex = window.defaultInitialPeriodIndex;
|
||||
long newPlayingPositionUs = seekToPeriodPosition(newPlayingPeriodIndex,
|
||||
window.defaultInitialTimeMs);
|
||||
|
||||
playbackInfo = new PlaybackInfo(newPlayingPeriodIndex, newPlayingPositionUs);
|
||||
eventHandler.obtainMessage(MSG_POSITION_DISCONTINUITY, playbackInfo).sendToTarget();
|
||||
attemptRestart(timeline, oldTimeline, playingPeriod.index);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -867,9 +846,8 @@ import java.io.IOException;
|
|||
Object id = loadingPeriod.id;
|
||||
int index = timeline.getIndexOfPeriod(id);
|
||||
if (index == Timeline.NO_PERIOD_INDEX) {
|
||||
loadingPeriod.release();
|
||||
loadingPeriod = null;
|
||||
bufferAheadPeriodCount = 0;
|
||||
attemptRestart(timeline, oldTimeline, loadingPeriod.index);
|
||||
return;
|
||||
} else {
|
||||
loadingPeriod.setIndex(timeline, index);
|
||||
}
|
||||
|
|
@ -888,6 +866,48 @@ import java.io.IOException;
|
|||
}
|
||||
}
|
||||
|
||||
private void attemptRestart(Timeline newTimeline, Timeline oldTimeline, int oldPeriodIndex)
|
||||
throws ExoPlaybackException {
|
||||
int newPeriodIndex = Timeline.NO_PERIOD_INDEX;
|
||||
while (newPeriodIndex == Timeline.NO_PERIOD_INDEX
|
||||
&& oldPeriodIndex < oldTimeline.getPeriodCount() - 1) {
|
||||
newPeriodIndex = newTimeline.getIndexOfPeriod(oldTimeline.getPeriodId(++oldPeriodIndex));
|
||||
}
|
||||
if (newPeriodIndex == Timeline.NO_PERIOD_INDEX) {
|
||||
// We failed to find a replacement period. Stop the player.
|
||||
stopInternal();
|
||||
return;
|
||||
}
|
||||
|
||||
// Release all loaded periods.
|
||||
releasePeriodsFrom(playingPeriod);
|
||||
bufferAheadPeriodCount = 0;
|
||||
playingPeriod = null;
|
||||
readingPeriod = null;
|
||||
loadingPeriod = null;
|
||||
|
||||
// Find the default initial position in the window and seek to it.
|
||||
Pair<Integer, Long> defaultPosition = getDefaultPosition(newPeriodIndex);
|
||||
newPeriodIndex = defaultPosition.first;
|
||||
long newPlayingPositionUs = defaultPosition.second;
|
||||
|
||||
playbackInfo = new PlaybackInfo(newPeriodIndex, newPlayingPositionUs);
|
||||
eventHandler.obtainMessage(MSG_POSITION_DISCONTINUITY, playbackInfo).sendToTarget();
|
||||
}
|
||||
|
||||
private Pair<Integer, Long> getDefaultPosition(int periodIndex) {
|
||||
int windowIndex = timeline.getPeriodWindowIndex(periodIndex);
|
||||
periodIndex = timeline.getWindowFirstPeriodIndex(windowIndex);
|
||||
int maxPeriodIndex = timeline.getWindowLastPeriodIndex(windowIndex);
|
||||
long windowPositionUs = timeline.getWindowOffsetInFirstPeriodUs(windowIndex)
|
||||
+ timeline.getWindow(windowIndex).defaultStartPositionUs;
|
||||
while (periodIndex < maxPeriodIndex
|
||||
&& windowPositionUs > timeline.getPeriodDurationUs(periodIndex)) {
|
||||
windowPositionUs -= timeline.getPeriodDurationUs(periodIndex++);
|
||||
}
|
||||
return Pair.create(periodIndex, windowPositionUs);
|
||||
}
|
||||
|
||||
private void updatePeriods() throws ExoPlaybackException, IOException {
|
||||
if (timeline == null) {
|
||||
// We're waiting to get information about periods.
|
||||
|
|
@ -895,7 +915,6 @@ import java.io.IOException;
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
if (loadingPeriod == null || (loadingPeriod.isFullyBuffered() && !loadingPeriod.isLast
|
||||
&& bufferAheadPeriodCount < MAXIMUM_BUFFER_AHEAD_PERIODS)) {
|
||||
// We don't have a loading period or it's fully loaded, so try and create the next one.
|
||||
|
|
@ -905,15 +924,15 @@ import java.io.IOException;
|
|||
// The period is not available yet.
|
||||
mediaSource.maybeThrowSourceInfoRefreshError();
|
||||
} else {
|
||||
Window window = timeline.getPeriodWindow(newLoadingPeriodIndex);
|
||||
long startPositionUs = loadingPeriod == null ? playbackInfo.positionUs
|
||||
: newLoadingPeriodIndex == window.startPeriodIndex ? C.UNSET_TIME_US
|
||||
: 0;
|
||||
: (newLoadingPeriodIndex == timeline.getWindowFirstPeriodIndex(newLoadingPeriodIndex)
|
||||
? C.UNSET_TIME_US : 0);
|
||||
if (startPositionUs == C.UNSET_TIME_US) {
|
||||
// This is the first period of a new window or we don't have a start position, so seek to
|
||||
// the default position for the window.
|
||||
newLoadingPeriodIndex = window.defaultInitialPeriodIndex;
|
||||
startPositionUs = window.defaultInitialTimeMs * 1000;
|
||||
Pair<Integer, Long> defaultPosition = getDefaultPosition(newLoadingPeriodIndex);
|
||||
newLoadingPeriodIndex = defaultPosition.first;
|
||||
startPositionUs = defaultPosition.second;
|
||||
}
|
||||
MediaPeriod mediaPeriod = mediaSource.createPeriod(newLoadingPeriodIndex, this,
|
||||
loadControl.getAllocator(), startPositionUs);
|
||||
|
|
|
|||
|
|
@ -101,4 +101,29 @@ public interface Timeline {
|
|||
*/
|
||||
Window getWindow(int windowIndex);
|
||||
|
||||
/**
|
||||
* Returns the index of the first period belonging to the {@link Window} at the specified index.
|
||||
*
|
||||
* @param windowIndex The window index.
|
||||
* @return The index of the first period in the window.
|
||||
*/
|
||||
int getWindowFirstPeriodIndex(int windowIndex);
|
||||
|
||||
/**
|
||||
* Returns the index of the last period belonging to the {@link Window} at the specified index.
|
||||
*
|
||||
* @param windowIndex The window index.
|
||||
* @return The index of the last period in the window.
|
||||
*/
|
||||
int getWindowLastPeriodIndex(int windowIndex);
|
||||
|
||||
/**
|
||||
* Returns the start position of the specified window in the first period belonging to it, in
|
||||
* microseconds.
|
||||
*
|
||||
* @param windowIndex The window index.
|
||||
* @return The start position of the window in the first period belonging to it.
|
||||
*/
|
||||
long getWindowOffsetInFirstPeriodUs(int windowIndex);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ package com.google.android.exoplayer2;
|
|||
public final class Window {
|
||||
|
||||
/**
|
||||
* Creates a new {@link Window} consisting of a single period starting at time zero and with the
|
||||
* specified duration. The default initial position is the start of the window.
|
||||
* Creates a new {@link Window} consisting of a single period with the specified duration. The
|
||||
* default start position is zero.
|
||||
*
|
||||
* @param durationUs The duration of the window, in microseconds.
|
||||
* @param isSeekable Whether seeking is supported within the window.
|
||||
|
|
@ -30,77 +30,18 @@ public final class Window {
|
|||
*/
|
||||
public static Window createWindowFromZero(long durationUs, boolean isSeekable,
|
||||
boolean isDynamic) {
|
||||
return createWindow(0, 0, 0, durationUs, durationUs, isSeekable, isDynamic, 0, 0);
|
||||
return new Window(durationUs, isSeekable, isDynamic, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Window} representing the specified time range. The default initial
|
||||
* position is the start of the window.
|
||||
*
|
||||
* @param startPeriodIndex The index of the period containing the start of the window.
|
||||
* @param startTimeUs The start time of the window in microseconds, relative to the start of the
|
||||
* specified start period.
|
||||
* @param endPeriodIndex The index of the period containing the end of the window.
|
||||
* @param endTimeUs The end time of the window in microseconds, relative to the start of the
|
||||
* specified end period.
|
||||
* @param durationUs The duration of the window in microseconds.
|
||||
* @param isSeekable Whether seeking is supported within the window.
|
||||
* @param isDynamic Whether this seek window may change when the timeline is updated.
|
||||
* The default position relative to the start of the window at which to start playback, in
|
||||
* microseconds.
|
||||
*/
|
||||
public static Window createWindow(int startPeriodIndex, long startTimeUs,
|
||||
int endPeriodIndex, long endTimeUs, long durationUs, boolean isSeekable, boolean isDynamic) {
|
||||
return createWindow(startPeriodIndex, startTimeUs, endPeriodIndex, endTimeUs, durationUs,
|
||||
isSeekable, isDynamic, startPeriodIndex, startTimeUs);
|
||||
}
|
||||
|
||||
public final long defaultStartPositionUs;
|
||||
/**
|
||||
* Creates a new {@link Window} representing the specified time range.
|
||||
*
|
||||
* @param startPeriodIndex The index of the period containing the start of the window.
|
||||
* @param startTimeUs The start time of the window in microseconds, relative to the start of the
|
||||
* specified start period.
|
||||
* @param endPeriodIndex The index of the period containing the end of the window.
|
||||
* @param endTimeUs The end time of the window in microseconds, relative to the start of the
|
||||
* specified end period.
|
||||
* @param durationUs The duration of the window in microseconds.
|
||||
* @param isSeekable Whether seeking is supported within the window.
|
||||
* @param isDynamic Whether this seek window may change when the timeline is updated.
|
||||
* @param defaultInitialPeriodIndex The index of the period containing the default position from
|
||||
* which playback should start.
|
||||
* @param defaultInitialTimeUs The time of the default position from which playback should start
|
||||
* in microseconds, relative to the start of the period that contains it.
|
||||
* The duration of the window in microseconds, or {@link C#UNSET_TIME_US} if unknown.
|
||||
*/
|
||||
public static Window createWindow(int startPeriodIndex, long startTimeUs,
|
||||
int endPeriodIndex, long endTimeUs, long durationUs, boolean isSeekable, boolean isDynamic,
|
||||
int defaultInitialPeriodIndex, long defaultInitialTimeUs) {
|
||||
return new Window(startPeriodIndex, startTimeUs / 1000, endPeriodIndex,
|
||||
endTimeUs == C.UNSET_TIME_US ? ExoPlayer.UNKNOWN_TIME : (endTimeUs / 1000),
|
||||
durationUs == C.UNSET_TIME_US ? ExoPlayer.UNKNOWN_TIME : (durationUs / 1000),
|
||||
isSeekable, isDynamic, defaultInitialPeriodIndex, defaultInitialTimeUs / 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* The period index at the start of the window.
|
||||
*/
|
||||
public final int startPeriodIndex;
|
||||
/**
|
||||
* The time at the start of the window relative to the start of the period at
|
||||
* {@link #startPeriodIndex}, in milliseconds.
|
||||
*/
|
||||
public final long startTimeMs;
|
||||
/**
|
||||
* The period index at the end of the window.
|
||||
*/
|
||||
public final int endPeriodIndex;
|
||||
/**
|
||||
* The time at the end of the window relative to the start of the period at
|
||||
* {@link #endPeriodIndex}, in milliseconds.
|
||||
*/
|
||||
public final long endTimeMs;
|
||||
/**
|
||||
* The duration of the window in milliseconds, or {@link C#UNSET_TIME_US} if unknown.
|
||||
*/
|
||||
public final long durationMs;
|
||||
public final long durationUs;
|
||||
/**
|
||||
* Whether it's possible to seek within the window.
|
||||
*/
|
||||
|
|
@ -109,54 +50,30 @@ public final class Window {
|
|||
* Whether this seek window may change when the timeline is updated.
|
||||
*/
|
||||
public final boolean isDynamic;
|
||||
/**
|
||||
* The period index of the default position from which playback should start.
|
||||
*/
|
||||
public final int defaultInitialPeriodIndex;
|
||||
/**
|
||||
* The time of the default position relative to the start of the period at
|
||||
* {@link #defaultInitialPeriodIndex}, in milliseconds.
|
||||
*/
|
||||
public final long defaultInitialTimeMs;
|
||||
|
||||
private Window(int startPeriodIndex, long startTimeMs, int endPeriodIndex, long endTimeMs,
|
||||
long durationMs, boolean isSeekable, boolean isDynamic, int defaultInitialPeriodIndex,
|
||||
long defaultInitialTimeMs) {
|
||||
this.startPeriodIndex = startPeriodIndex;
|
||||
this.startTimeMs = startTimeMs;
|
||||
this.endPeriodIndex = endPeriodIndex;
|
||||
this.endTimeMs = endTimeMs;
|
||||
this.durationMs = durationMs;
|
||||
/**
|
||||
* @param durationUs The duration of the window in microseconds, or {@link C#UNSET_TIME_US} if
|
||||
* unknown.
|
||||
* @param isSeekable Whether seeking is supported within the window.
|
||||
* @param isDynamic Whether this seek window may change when the timeline is updated.
|
||||
* @param defaultStartPositionUs The default position relative to the start of the window at which
|
||||
* to start playback, in microseconds.
|
||||
*/
|
||||
public Window(long durationUs, boolean isSeekable, boolean isDynamic,
|
||||
long defaultStartPositionUs) {
|
||||
this.durationUs = durationUs;
|
||||
this.isSeekable = isSeekable;
|
||||
this.isDynamic = isDynamic;
|
||||
this.defaultInitialPeriodIndex = defaultInitialPeriodIndex;
|
||||
this.defaultInitialTimeMs = defaultInitialTimeMs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new window that is offset by the specified number of periods.
|
||||
*
|
||||
* @param periodCount The number of periods to add to {@link #startPeriodIndex} and
|
||||
* {@link #endPeriodIndex} when constructing the copy.
|
||||
* @return A new window that is offset by the specified number of periods.
|
||||
*/
|
||||
public Window copyOffsetByPeriodCount(int periodCount) {
|
||||
return new Window(startPeriodIndex + periodCount, startTimeMs, endPeriodIndex + periodCount,
|
||||
endTimeMs, durationMs, isSeekable, isDynamic, defaultInitialPeriodIndex + periodCount,
|
||||
defaultInitialTimeMs);
|
||||
this.defaultStartPositionUs = defaultStartPositionUs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = 17;
|
||||
result = 31 * result + startPeriodIndex;
|
||||
result = 31 * result + (int) startTimeMs;
|
||||
result = 31 * result + endPeriodIndex;
|
||||
result = 31 * result + (int) endTimeMs;
|
||||
result = 31 * result + (isSeekable ? 1 : 2);
|
||||
result = 31 * result + (isDynamic ? 1 : 2);
|
||||
result = 31 * result + defaultInitialPeriodIndex;
|
||||
result = 31 * result + (int) defaultInitialTimeMs;
|
||||
result = 31 * result + (int) defaultStartPositionUs;
|
||||
result = 31 * result + (int) durationUs;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -169,21 +86,16 @@ public final class Window {
|
|||
return false;
|
||||
}
|
||||
Window other = (Window) obj;
|
||||
return other.startPeriodIndex == startPeriodIndex
|
||||
&& other.startTimeMs == startTimeMs
|
||||
&& other.endPeriodIndex == endPeriodIndex
|
||||
&& other.endTimeMs == endTimeMs
|
||||
&& other.durationMs == durationMs
|
||||
return other.durationUs == durationUs
|
||||
&& other.isSeekable == isSeekable
|
||||
&& other.isDynamic == isDynamic
|
||||
&& other.defaultInitialPeriodIndex == defaultInitialPeriodIndex
|
||||
&& other.defaultInitialTimeMs == defaultInitialTimeMs;
|
||||
&& other.defaultStartPositionUs == defaultStartPositionUs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Window[" + startPeriodIndex + ", " + startTimeMs + ", " + endPeriodIndex + ", "
|
||||
+ endTimeMs + ", " + isDynamic + "]";
|
||||
return "Window[" + durationUs + ", " + defaultStartPositionUs + ", " + isSeekable + ", "
|
||||
+ isDynamic + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import com.google.android.exoplayer2.source.MediaPeriod.Callback;
|
|||
import com.google.android.exoplayer2.upstream.Allocator;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
|
@ -58,8 +57,8 @@ public final class ConcatenatingMediaSource implements MediaSource {
|
|||
public void onSourceInfoRefreshed(Timeline sourceTimeline, Object manifest) {
|
||||
timelines[index] = sourceTimeline;
|
||||
manifests[index] = manifest;
|
||||
for (int i = 0; i < timelines.length; i++) {
|
||||
if (timelines[i] == null) {
|
||||
for (Timeline timeline : timelines) {
|
||||
if (timeline == null) {
|
||||
// Don't invoke the listener until all sources have timelines.
|
||||
return;
|
||||
}
|
||||
|
|
@ -72,16 +71,6 @@ public final class ConcatenatingMediaSource implements MediaSource {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNewPlayingPeriodIndex(int oldPlayingPeriodIndex, Timeline oldConcatenatedTimeline) {
|
||||
ConcatenatedTimeline oldTimeline = (ConcatenatedTimeline) oldConcatenatedTimeline;
|
||||
int sourceIndex = oldTimeline.getSourceIndexForPeriod(oldPlayingPeriodIndex);
|
||||
int oldFirstPeriodIndex = oldTimeline.getFirstPeriodIndexInSource(sourceIndex);
|
||||
int firstPeriodIndex = timeline.getFirstPeriodIndexInSource(sourceIndex);
|
||||
return firstPeriodIndex + mediaSources[sourceIndex].getNewPlayingPeriodIndex(
|
||||
oldPlayingPeriodIndex - oldFirstPeriodIndex, oldTimeline.timelines[sourceIndex]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void maybeThrowSourceInfoRefreshError() throws IOException {
|
||||
for (MediaSource mediaSource : mediaSources) {
|
||||
|
|
@ -122,22 +111,14 @@ public final class ConcatenatingMediaSource implements MediaSource {
|
|||
private final Timeline[] timelines;
|
||||
private final int[] sourcePeriodOffsets;
|
||||
private final int[] sourceWindowOffsets;
|
||||
private final Window[] windows;
|
||||
|
||||
public ConcatenatedTimeline(Timeline[] timelines) {
|
||||
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];
|
||||
// Offset the windows so they are relative to the source.
|
||||
int sourceWindowCount = timeline.getWindowCount();
|
||||
for (int j = 0; j < sourceWindowCount; j++) {
|
||||
Window sourceWindow = timeline.getWindow(j);
|
||||
concatenatedWindows.add(sourceWindow.copyOffsetByPeriodCount(periodCount));
|
||||
}
|
||||
periodCount += timeline.getPeriodCount();
|
||||
sourcePeriodOffsets[i] = periodCount;
|
||||
windowCount += timeline.getWindowCount();
|
||||
|
|
@ -146,7 +127,6 @@ public final class ConcatenatingMediaSource implements MediaSource {
|
|||
this.timelines = timelines;
|
||||
this.sourcePeriodOffsets = sourcePeriodOffsets;
|
||||
this.sourceWindowOffsets = sourceWindowOffsets;
|
||||
windows = concatenatedWindows.toArray(new Window[concatenatedWindows.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -183,7 +163,7 @@ public final class ConcatenatingMediaSource implements MediaSource {
|
|||
|
||||
@Override
|
||||
public Window getPeriodWindow(int periodIndex) {
|
||||
return windows[getPeriodWindowIndex(periodIndex)];
|
||||
return getWindow(getPeriodWindowIndex(periodIndex));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -199,12 +179,11 @@ public final class ConcatenatingMediaSource implements MediaSource {
|
|||
if (!(id instanceof Pair)) {
|
||||
return NO_PERIOD_INDEX;
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
Pair<Integer, Object> sourceIndexAndPeriodId = (Pair<Integer, Object>) id;
|
||||
Pair sourceIndexAndPeriodId = (Pair) id;
|
||||
if (!(sourceIndexAndPeriodId.first instanceof Integer)) {
|
||||
return NO_PERIOD_INDEX;
|
||||
}
|
||||
int sourceIndex = sourceIndexAndPeriodId.first;
|
||||
int sourceIndex = (int) sourceIndexAndPeriodId.first;
|
||||
Object periodId = sourceIndexAndPeriodId.second;
|
||||
if (sourceIndex < 0 || sourceIndex >= timelines.length) {
|
||||
return NO_PERIOD_INDEX;
|
||||
|
|
@ -216,12 +195,39 @@ public final class ConcatenatingMediaSource implements MediaSource {
|
|||
|
||||
@Override
|
||||
public int getWindowCount() {
|
||||
return windows.length;
|
||||
return sourceWindowOffsets[sourceWindowOffsets.length - 1];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Window getWindow(int windowIndex) {
|
||||
return windows[windowIndex];
|
||||
int sourceIndex = getSourceIndexForWindow(windowIndex);
|
||||
int firstWindowIndexInSource = getFirstWindowIndexInSource(sourceIndex);
|
||||
return timelines[sourceIndex].getWindow(windowIndex - firstWindowIndexInSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWindowFirstPeriodIndex(int windowIndex) {
|
||||
int sourceIndex = getSourceIndexForWindow(windowIndex);
|
||||
int firstPeriodIndexInSource = getFirstPeriodIndexInSource(sourceIndex);
|
||||
int firstWindowIndexInSource = getFirstWindowIndexInSource(sourceIndex);
|
||||
return firstPeriodIndexInSource + timelines[sourceIndex].getWindowFirstPeriodIndex(
|
||||
windowIndex - firstWindowIndexInSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWindowLastPeriodIndex(int windowIndex) {int sourceIndex = getSourceIndexForWindow(windowIndex);
|
||||
int firstPeriodIndexInSource = getFirstPeriodIndexInSource(sourceIndex);
|
||||
int firstWindowIndexInSource = getFirstWindowIndexInSource(sourceIndex);
|
||||
return firstPeriodIndexInSource + timelines[sourceIndex].getWindowLastPeriodIndex(
|
||||
windowIndex - firstWindowIndexInSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getWindowOffsetInFirstPeriodUs(int windowIndex) {
|
||||
int sourceIndex = getSourceIndexForWindow(windowIndex);
|
||||
int firstWindowIndexInSource = getFirstPeriodIndexInSource(sourceIndex);
|
||||
return timelines[sourceIndex].getWindowOffsetInFirstPeriodUs(
|
||||
windowIndex - firstWindowIndexInSource);
|
||||
}
|
||||
|
||||
private int getSourceIndexForPeriod(int periodIndex) {
|
||||
|
|
@ -232,6 +238,14 @@ public final class ConcatenatingMediaSource implements MediaSource {
|
|||
return sourceIndex == 0 ? 0 : sourcePeriodOffsets[sourceIndex - 1];
|
||||
}
|
||||
|
||||
private int getSourceIndexForWindow(int windowIndex) {
|
||||
return Util.binarySearchFloor(sourceWindowOffsets, windowIndex, true, false) + 1;
|
||||
}
|
||||
|
||||
private int getFirstWindowIndexInSource(int sourceIndex) {
|
||||
return sourceIndex == 0 ? 0 : sourceWindowOffsets[sourceIndex - 1];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -139,11 +139,6 @@ public final class ExtractorMediaSource implements MediaSource, MediaSource.List
|
|||
listener.onSourceInfoRefreshed(timeline, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNewPlayingPeriodIndex(int oldPlayingPeriodIndex, Timeline oldTimeline) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void maybeThrowSourceInfoRefreshError() throws IOException {
|
||||
// Do nothing.
|
||||
|
|
|
|||
|
|
@ -47,19 +47,6 @@ public interface MediaSource {
|
|||
*/
|
||||
void prepareSource(Listener listener);
|
||||
|
||||
/**
|
||||
* Returns the period index to play in this source's new timeline, or
|
||||
* {@link Timeline#NO_PERIOD_INDEX} if the player should stop playback. The
|
||||
* {@code oldPlayingPeriodIndex} should be an index of a period in the old timeline that is no
|
||||
* longer present (based on its identifier) in the new timeline.
|
||||
*
|
||||
* @param oldPlayingPeriodIndex The period index that was being played in the old timeline.
|
||||
* @param oldTimeline The old timeline.
|
||||
* @return The new period index to play in this source's new timeline. Playback will resume from
|
||||
* the default start position in the new period index.
|
||||
*/
|
||||
int getNewPlayingPeriodIndex(int oldPlayingPeriodIndex, Timeline oldTimeline);
|
||||
|
||||
/**
|
||||
* Throws any pending error encountered while loading or refreshing source information.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -65,11 +65,6 @@ public final class MergingMediaSource implements MediaSource {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNewPlayingPeriodIndex(int oldPlayingPeriodIndex, Timeline oldTimeline) {
|
||||
return mediaSources[0].getNewPlayingPeriodIndex(oldPlayingPeriodIndex, oldTimeline);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void maybeThrowSourceInfoRefreshError() throws IOException {
|
||||
for (MediaSource mediaSource : mediaSources) {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ public final class SinglePeriodTimeline implements Timeline {
|
|||
|
||||
private static final Object ID = new Object();
|
||||
|
||||
private final long durationUs;
|
||||
private final long offsetInFirstPeriodUs;
|
||||
private final Window window;
|
||||
|
||||
/**
|
||||
|
|
@ -39,18 +39,18 @@ public final class SinglePeriodTimeline implements Timeline {
|
|||
* @param isSeekable Whether seeking is supported within the period.
|
||||
*/
|
||||
public SinglePeriodTimeline(long durationUs, boolean isSeekable) {
|
||||
this(durationUs, Window.createWindowFromZero(durationUs, isSeekable, false /* isDynamic */));
|
||||
this(0, Window.createWindowFromZero(durationUs, isSeekable, false /* isDynamic */));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a timeline with one period of known duration and a window extending from zero to its
|
||||
* duration.
|
||||
*
|
||||
* @param durationUs The duration of the period, in microseconds.
|
||||
* @param offsetInFirstPeriodUs The offset of the start of the window in the period.
|
||||
* @param window The available window within the period.
|
||||
*/
|
||||
public SinglePeriodTimeline(long durationUs, Window window) {
|
||||
this.durationUs = durationUs;
|
||||
public SinglePeriodTimeline(long offsetInFirstPeriodUs, Window window) {
|
||||
this.offsetInFirstPeriodUs = offsetInFirstPeriodUs;
|
||||
this.window = window;
|
||||
}
|
||||
|
||||
|
|
@ -67,13 +67,15 @@ public final class SinglePeriodTimeline implements Timeline {
|
|||
@Override
|
||||
public long getPeriodDurationMs(int periodIndex) {
|
||||
Assertions.checkIndex(periodIndex, 0, 1);
|
||||
return durationUs == C.UNSET_TIME_US ? ExoPlayer.UNKNOWN_TIME : (durationUs / 1000);
|
||||
return window.durationUs == C.UNSET_TIME_US ? ExoPlayer.UNKNOWN_TIME
|
||||
: ((offsetInFirstPeriodUs + window.durationUs) / 1000);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getPeriodDurationUs(int periodIndex) {
|
||||
Assertions.checkIndex(periodIndex, 0, 1);
|
||||
return durationUs;
|
||||
return window.durationUs == C.UNSET_TIME_US ? C.UNSET_TIME_US
|
||||
: (offsetInFirstPeriodUs + window.durationUs);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -110,4 +112,22 @@ public final class SinglePeriodTimeline implements Timeline {
|
|||
return window;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWindowFirstPeriodIndex(int windowIndex) {
|
||||
Assertions.checkIndex(windowIndex, 0, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWindowLastPeriodIndex(int windowIndex) {
|
||||
Assertions.checkIndex(windowIndex, 0, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getWindowOffsetInFirstPeriodUs(int windowIndex) {
|
||||
Assertions.checkIndex(windowIndex, 0, 1);
|
||||
return offsetInFirstPeriodUs;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,11 +89,6 @@ public final class SingleSampleMediaSource implements MediaSource {
|
|||
listener.onSourceInfoRefreshed(timeline, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNewPlayingPeriodIndex(int oldPlayingPeriodIndex, Timeline oldTimeline) {
|
||||
return oldPlayingPeriodIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void maybeThrowSourceInfoRefreshError() throws IOException {
|
||||
// Do nothing.
|
||||
|
|
|
|||
|
|
@ -161,12 +161,6 @@ public final class DashMediaSource implements MediaSource {
|
|||
startLoadingManifest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNewPlayingPeriodIndex(int oldPlayingPeriodIndex, Timeline oldTimeline) {
|
||||
// Seek to the default position, which is the live edge for live sources.
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void maybeThrowSourceInfoRefreshError() throws IOException {
|
||||
loader.maybeThrowError();
|
||||
|
|
@ -381,29 +375,19 @@ public final class DashMediaSource implements MediaSource {
|
|||
for (int i = 0; i < manifest.getPeriodCount() - 1; i++) {
|
||||
windowDurationUs += manifest.getPeriodDurationUs(i);
|
||||
}
|
||||
int defaultInitialPeriodIndex = 0;
|
||||
long defaultInitialTimeUs = 0;
|
||||
if (manifest.dynamic) {
|
||||
defaultInitialPeriodIndex = lastPeriodIndex;
|
||||
long liveEdgeOffsetForManifestMs = liveEdgeOffsetMs;
|
||||
if (liveEdgeOffsetForManifestMs == DEFAULT_LIVE_EDGE_OFFSET_PREFER_MANIFEST_MS) {
|
||||
liveEdgeOffsetForManifestMs = manifest.suggestedPresentationDelay != -1
|
||||
? manifest.suggestedPresentationDelay : DEFAULT_LIVE_EDGE_OFFSET_FIXED_MS;
|
||||
}
|
||||
defaultInitialTimeUs = currentEndTimeUs - (liveEdgeOffsetForManifestMs * 1000);
|
||||
while (defaultInitialTimeUs < 0 && defaultInitialPeriodIndex > 0) {
|
||||
defaultInitialPeriodIndex--;
|
||||
defaultInitialTimeUs += manifest.getPeriodDurationUs(defaultInitialPeriodIndex);
|
||||
}
|
||||
if (defaultInitialPeriodIndex == 0) {
|
||||
defaultInitialTimeUs = Math.max(defaultInitialTimeUs, currentStartTimeUs);
|
||||
}
|
||||
defaultInitialTimeUs = Math.max(0, windowDurationUs - (liveEdgeOffsetForManifestMs * 1000));
|
||||
}
|
||||
window = Window.createWindow(0, currentStartTimeUs, lastPeriodIndex, currentEndTimeUs,
|
||||
windowDurationUs, true /* isSeekable */, manifest.dynamic, defaultInitialPeriodIndex,
|
||||
window = new Window(windowDurationUs, true /* isSeekable */, manifest.dynamic,
|
||||
defaultInitialTimeUs);
|
||||
sourceListener.onSourceInfoRefreshed(new DashTimeline(firstPeriodId, manifest, window),
|
||||
manifest);
|
||||
sourceListener.onSourceInfoRefreshed(new DashTimeline(firstPeriodId, currentStartTimeUs,
|
||||
manifest, window), manifest);
|
||||
}
|
||||
|
||||
private void scheduleManifestRefresh() {
|
||||
|
|
@ -485,11 +469,14 @@ public final class DashMediaSource implements MediaSource {
|
|||
private static final class DashTimeline implements Timeline {
|
||||
|
||||
private final int firstPeriodId;
|
||||
private final long offsetInFirstPeriodUs;
|
||||
private final DashManifest manifest;
|
||||
private final Window window;
|
||||
|
||||
public DashTimeline(int firstPeriodId, DashManifest manifest, Window window) {
|
||||
public DashTimeline(int firstPeriodId, long offsetInFirstPeriodUs, DashManifest manifest,
|
||||
Window window) {
|
||||
this.firstPeriodId = firstPeriodId;
|
||||
this.offsetInFirstPeriodUs = offsetInFirstPeriodUs;
|
||||
this.manifest = manifest;
|
||||
this.window = window;
|
||||
}
|
||||
|
|
@ -554,6 +541,21 @@ public final class DashMediaSource implements MediaSource {
|
|||
return window;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWindowFirstPeriodIndex(int windowIndex) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWindowLastPeriodIndex(int windowIndex) {
|
||||
return manifest.getPeriodCount() - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getWindowOffsetInFirstPeriodUs(int windowIndex) {
|
||||
return offsetInFirstPeriodUs;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final class ManifestCallback implements
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ package com.google.android.exoplayer2.source.hls;
|
|||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener;
|
||||
import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher;
|
||||
import com.google.android.exoplayer2.source.MediaPeriod;
|
||||
|
|
@ -68,11 +67,6 @@ public final class HlsMediaSource implements MediaSource {
|
|||
listener.onSourceInfoRefreshed(new SinglePeriodTimeline(C.UNSET_TIME_US, false), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNewPlayingPeriodIndex(int oldPlayingPeriodIndex, Timeline oldTimeline) {
|
||||
return oldPlayingPeriodIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void maybeThrowSourceInfoRefreshError() {
|
||||
// Do nothing.
|
||||
|
|
|
|||
|
|
@ -108,11 +108,6 @@ public final class SsMediaSource implements MediaSource,
|
|||
startLoadingManifest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNewPlayingPeriodIndex(int oldPlayingPeriodIndex, Timeline oldTimeline) {
|
||||
return oldPlayingPeriodIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void maybeThrowSourceInfoRefreshError() throws IOException {
|
||||
manifestLoader.maybeThrowError();
|
||||
|
|
@ -182,11 +177,10 @@ public final class SsMediaSource implements MediaSource,
|
|||
startTimeUs = Math.max(startTimeUs, endTimeUs - manifest.dvrWindowLengthUs);
|
||||
}
|
||||
long durationUs = endTimeUs - startTimeUs;
|
||||
long defaultInitialStartPositionUs = Math.max(startTimeUs,
|
||||
endTimeUs - (liveEdgeOffsetMs * 1000));
|
||||
Window window = Window.createWindow(0, startTimeUs, 0, endTimeUs, durationUs,
|
||||
true /* isSeekable */, true /* isDynamic */, 0, defaultInitialStartPositionUs);
|
||||
timeline = new SinglePeriodTimeline(endTimeUs, window);
|
||||
long defaultInitialStartPositionUs = Math.max(0, durationUs - (liveEdgeOffsetMs * 1000));
|
||||
Window window = new Window(durationUs, true /* isSeekable */, true /* isDynamic */,
|
||||
defaultInitialStartPositionUs);
|
||||
timeline = new SinglePeriodTimeline(startTimeUs, window);
|
||||
}
|
||||
} else {
|
||||
boolean isSeekable = manifest.durationUs != C.UNSET_TIME_US;
|
||||
|
|
|
|||
Loading…
Reference in a new issue