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:
olly 2016-08-18 11:07:57 -07:00 committed by Oliver Woodman
parent eff82e920e
commit 83e466d129
13 changed files with 219 additions and 263 deletions

View file

@ -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();

View file

@ -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);

View file

@ -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);
}

View file

@ -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 + "]";
}
}

View file

@ -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];
}
}
}

View file

@ -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.

View file

@ -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.
*/

View file

@ -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) {

View file

@ -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;
}
}

View file

@ -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.

View file

@ -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

View file

@ -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.

View file

@ -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;