Move default start position into Window

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=130610236
This commit is contained in:
olly 2016-08-18 01:11:10 -07:00 committed by Oliver Woodman
parent 3e0bae616f
commit b2a28a140c
10 changed files with 111 additions and 176 deletions

View File

@ -495,14 +495,12 @@ import java.io.IOException;
private void seekToInternal(int periodIndex, long positionUs) throws ExoPlaybackException {
try {
if (positionUs == C.UNSET_TIME_US && mediaSource != null) {
MediaSource.Position defaultStartPosition =
mediaSource.getDefaultStartPosition(periodIndex);
if (defaultStartPosition != null) {
// We know the default position so seek to it now.
periodIndex = defaultStartPosition.periodIndex;
positionUs = defaultStartPosition.positionUs;
}
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;
}
if (periodIndex == playbackInfo.periodIndex
@ -805,22 +803,18 @@ import java.io.IOException;
return;
}
// Release all loaded periods and seek to the new playing period index.
// Release all loaded periods.
releasePeriodsFrom(playingPeriod);
playingPeriod = null;
readingPeriod = null;
loadingPeriod = null;
MediaSource.Position defaultStartPosition =
mediaSource.getDefaultStartPosition(newPlayingPeriodIndex);
long newPlayingPositionUs;
if (defaultStartPosition != null) {
newPlayingPeriodIndex = defaultStartPosition.periodIndex;
newPlayingPositionUs = seekToPeriodPosition(defaultStartPosition.periodIndex,
defaultStartPosition.positionUs);
} else {
newPlayingPositionUs = seekToPeriodPosition(newPlayingPeriodIndex, C.UNSET_TIME_US);
}
// 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();
return;
@ -901,33 +895,29 @@ import java.io.IOException;
return;
}
// Update the loading period.
if (loadingPeriod == null || (loadingPeriod.isFullyBuffered() && !loadingPeriod.isLast
&& bufferAheadPeriodCount < MAXIMUM_BUFFER_AHEAD_PERIODS)) {
int periodIndex = loadingPeriod == null ? playbackInfo.periodIndex : loadingPeriod.index + 1;
long startPositionUs = playbackInfo.positionUs;
if (loadingPeriod != null || startPositionUs == C.UNSET_TIME_US) {
// We are starting to load the next period or seeking to the default position, so request a
// period and position from the source.
MediaSource.Position defaultStartPosition =
mediaSource.getDefaultStartPosition(periodIndex);
if (defaultStartPosition != null) {
periodIndex = defaultStartPosition.periodIndex;
startPositionUs = defaultStartPosition.positionUs;
} else {
startPositionUs = C.UNSET_TIME_US;
}
}
if (periodIndex >= timeline.getPeriodCount()) {
// This period is not available yet.
// We don't have a loading period or it's fully loaded, so try and create the next one.
int newLoadingPeriodIndex = loadingPeriod == null ? playbackInfo.periodIndex
: loadingPeriod.index + 1;
if (newLoadingPeriodIndex >= timeline.getPeriodCount()) {
// The period is not available yet.
mediaSource.maybeThrowSourceInfoRefreshError();
} else if (startPositionUs != C.UNSET_TIME_US) {
MediaPeriod mediaPeriod = mediaSource.createPeriod(periodIndex, this,
} else {
Window window = timeline.getPeriodWindow(newLoadingPeriodIndex);
long startPositionUs = loadingPeriod == null ? playbackInfo.positionUs : C.UNSET_TIME_US;
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;
}
MediaPeriod mediaPeriod = mediaSource.createPeriod(newLoadingPeriodIndex, this,
loadControl.getAllocator(), startPositionUs);
Period newPeriod = new Period(renderers, rendererCapabilities, trackSelector, mediaSource,
mediaPeriod, timeline.getPeriodId(periodIndex), startPositionUs);
newPeriod.setIndex(timeline, periodIndex);
mediaPeriod, timeline.getPeriodId(newLoadingPeriodIndex), startPositionUs);
newPeriod.setIndex(timeline, newLoadingPeriodIndex);
if (loadingPeriod != null) {
loadingPeriod.setNextPeriod(newPeriod);
newPeriod.offsetUs = loadingPeriod.offsetUs

View File

@ -21,8 +21,8 @@ package com.google.android.exoplayer2;
public final class Window {
/**
* Creates a new {@link Window} containing times from zero up to {@code durationUs} in the first
* period.
* 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.
*
* @param durationUs The duration of the window, in microseconds.
* @param isSeekable Whether seeking is supported within the window.
@ -30,7 +30,27 @@ public final class Window {
*/
public static Window createWindowFromZero(long durationUs, boolean isSeekable,
boolean isDynamic) {
return createWindow(0, 0, 0, durationUs, durationUs, isSeekable, isDynamic);
return createWindow(0, 0, 0, durationUs, durationUs, isSeekable, isDynamic, 0, 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.
*/
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);
}
/**
@ -45,13 +65,18 @@ public final class Window {
* @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.
*/
public static Window createWindow(int startPeriodIndex, long startTimeUs,
int endPeriodIndex, long endTimeUs, long durationUs, boolean isSeekable, boolean isDynamic) {
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);
isSeekable, isDynamic, defaultInitialPeriodIndex, defaultInitialTimeUs / 1000);
}
/**
@ -84,9 +109,19 @@ 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) {
long durationMs, boolean isSeekable, boolean isDynamic, int defaultInitialPeriodIndex,
long defaultInitialTimeMs) {
this.startPeriodIndex = startPeriodIndex;
this.startTimeMs = startTimeMs;
this.endPeriodIndex = endPeriodIndex;
@ -94,6 +129,8 @@ public final class Window {
this.durationMs = durationMs;
this.isSeekable = isSeekable;
this.isDynamic = isDynamic;
this.defaultInitialPeriodIndex = defaultInitialPeriodIndex;
this.defaultInitialTimeMs = defaultInitialTimeMs;
}
/**
@ -105,7 +142,8 @@ public final class Window {
*/
public Window copyOffsetByPeriodCount(int periodCount) {
return new Window(startPeriodIndex + periodCount, startTimeMs, endPeriodIndex + periodCount,
endTimeMs, durationMs, isSeekable, isDynamic);
endTimeMs, durationMs, isSeekable, isDynamic, defaultInitialPeriodIndex + periodCount,
defaultInitialTimeMs);
}
@Override
@ -115,7 +153,10 @@ public final class Window {
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;
return result;
}
@ -134,7 +175,9 @@ public final class Window {
&& other.endTimeMs == endTimeMs
&& other.durationMs == durationMs
&& other.isSeekable == isSeekable
&& other.isDynamic == isDynamic;
&& other.isDynamic == isDynamic
&& other.defaultInitialPeriodIndex == defaultInitialPeriodIndex
&& other.defaultInitialTimeMs == defaultInitialTimeMs;
}
@Override

View File

@ -82,16 +82,6 @@ public final class ConcatenatingMediaSource implements MediaSource {
oldPlayingPeriodIndex - oldFirstPeriodIndex, oldTimeline.timelines[sourceIndex]);
}
@Override
public Position getDefaultStartPosition(int index) {
int sourceIndex = timeline.getSourceIndexForPeriod(index);
int sourceFirstPeriodIndex = timeline.getFirstPeriodIndexInSource(sourceIndex);
Position defaultStartPosition =
mediaSources[sourceIndex].getDefaultStartPosition(index - sourceFirstPeriodIndex);
return new Position(defaultStartPosition.periodIndex + sourceFirstPeriodIndex,
defaultStartPosition.positionUs);
}
@Override
public void maybeThrowSourceInfoRefreshError() throws IOException {
for (MediaSource mediaSource : mediaSources) {

View File

@ -144,11 +144,6 @@ public final class ExtractorMediaSource implements MediaSource, MediaSource.List
return 0;
}
@Override
public Position getDefaultStartPosition(int index) {
return Position.DEFAULT;
}
@Override
public void maybeThrowSourceInfoRefreshError() throws IOException {
// Do nothing.

View File

@ -40,39 +40,6 @@ public interface MediaSource {
}
/**
* A position in the timeline.
*/
final class Position {
/**
* A start position at the earliest time in the first period.
*/
public static final Position DEFAULT = new Position(0, 0);
/**
* The index of the period containing the timeline position.
*/
public final int periodIndex;
/**
* The position in microseconds within the period.
*/
public final long positionUs;
/**
* Creates a new timeline position.
*
* @param periodIndex The index of the period containing the timeline position.
* @param positionUs The position in microseconds within the period.
*/
public Position(int periodIndex, long positionUs) {
this.periodIndex = periodIndex;
this.positionUs = positionUs;
}
}
/**
* Starts preparation of the source.
*
@ -93,20 +60,6 @@ public interface MediaSource {
*/
int getNewPlayingPeriodIndex(int oldPlayingPeriodIndex, Timeline oldTimeline);
/**
* Returns the default {@link Position} that the player should play when when starting to play the
* period at {@code index}, or {@code null} if the default position is not yet known.
* <p>
* For example, sources can return a {@link Position} with the passed period {@code index} to play
* the period at {@code index} immediately after the period at {@code index - 1}. Sources
* providing multi-period live streams may return the index and position of the live edge when
* passed {@code index == 0} to play from the live edge.
*
* @param index The index of the requested period.
* @return The default start position.
*/
Position getDefaultStartPosition(int index);
/**
* Throws any pending error encountered while loading or refreshing source information.
*/

View File

@ -70,11 +70,6 @@ public final class MergingMediaSource implements MediaSource {
return mediaSources[0].getNewPlayingPeriodIndex(oldPlayingPeriodIndex, oldTimeline);
}
@Override
public Position getDefaultStartPosition(int index) {
return mediaSources[0].getDefaultStartPosition(index);
}
@Override
public void maybeThrowSourceInfoRefreshError() throws IOException {
for (MediaSource mediaSource : mediaSources) {

View File

@ -94,11 +94,6 @@ public final class SingleSampleMediaSource implements MediaSource {
return oldPlayingPeriodIndex;
}
@Override
public Position getDefaultStartPosition(int index) {
return Position.DEFAULT;
}
@Override
public void maybeThrowSourceInfoRefreshError() throws IOException {
// Do nothing.

View File

@ -59,14 +59,14 @@ public final class DashMediaSource implements MediaSource {
public static final int DEFAULT_MIN_LOADABLE_RETRY_COUNT = 3;
/**
* A constant indicating that the live edge offset (the offset subtracted from the live edge
* when calculating the default position returned by {@link #getDefaultStartPosition(int)}) should
* be set to {@link DashManifest#suggestedPresentationDelay} if specified by the manifest, or
* when calculating the default initial playback position) should be set to
* {@link DashManifest#suggestedPresentationDelay} if specified by the manifest, or
* {@link #DEFAULT_LIVE_EDGE_OFFSET_FIXED_MS} otherwise.
*/
public static final long DEFAULT_LIVE_EDGE_OFFSET_PREFER_MANIFEST_MS = -1;
/**
* A fixed default live edge offset (the offset subtracted from the live edge when calculating the
* default position returned by {@link #getDefaultStartPosition(int)}).
* default initial playback position).
*/
public static final long DEFAULT_LIVE_EDGE_OFFSET_FIXED_MS = 30000;
/**
@ -167,32 +167,6 @@ public final class DashMediaSource implements MediaSource {
return 0;
}
@Override
public Position getDefaultStartPosition(int index) {
if (window == null) {
return null;
}
if (index == 0 && manifest.dynamic) {
// The stream is live, so return a position a position offset from the live edge.
int periodIndex = window.endPeriodIndex;
long liveEdgeOffsetForManifest = liveEdgeOffsetMs;
if (liveEdgeOffsetForManifest == DEFAULT_LIVE_EDGE_OFFSET_PREFER_MANIFEST_MS) {
liveEdgeOffsetForManifest = manifest.suggestedPresentationDelay != -1
? manifest.suggestedPresentationDelay : DEFAULT_LIVE_EDGE_OFFSET_FIXED_MS;
}
long positionMs = window.endTimeMs - liveEdgeOffsetForManifest;
while (positionMs < 0 && periodIndex > window.startPeriodIndex) {
periodIndex--;
positionMs += manifest.getPeriodDurationMs(periodIndex);
}
positionMs = Math.max(positionMs,
periodIndex == window.startPeriodIndex ? window.startTimeMs : 0);
return new Position(periodIndex, positionMs * 1000);
}
return new Position(index, 0);
}
@Override
public void maybeThrowSourceInfoRefreshError() throws IOException {
loader.maybeThrowError();
@ -405,8 +379,27 @@ 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);
}
}
window = Window.createWindow(0, currentStartTimeUs, lastPeriodIndex, currentEndTimeUs,
windowDurationUs, true /* isSeekable */, manifest.dynamic);
windowDurationUs, true /* isSeekable */, manifest.dynamic, defaultInitialPeriodIndex,
defaultInitialTimeUs);
sourceListener.onSourceInfoRefreshed(new DashTimeline(firstPeriodId, manifest, window),
manifest);
}

View File

@ -73,12 +73,6 @@ public final class HlsMediaSource implements MediaSource {
return oldPlayingPeriodIndex;
}
@Override
public Position getDefaultStartPosition(int index) {
// TODO: Return the position of the live edge, if applicable.
return Position.DEFAULT;
}
@Override
public void maybeThrowSourceInfoRefreshError() {
// Do nothing.

View File

@ -52,7 +52,7 @@ public final class SsMediaSource implements MediaSource,
public static final int DEFAULT_MIN_LOADABLE_RETRY_COUNT = 3;
/**
* A default live edge offset (the offset subtracted from the live edge when calculating the
* default position returned by {@link #getDefaultStartPosition(int)}).
* default initial playback position.
*/
public static final long DEFAULT_LIVE_EDGE_OFFSET_MS = 30000;
@ -73,7 +73,6 @@ public final class SsMediaSource implements MediaSource,
private long manifestLoadStartTimestamp;
private SsManifest manifest;
private Window window;
private Handler manifestRefreshHandler;
@ -114,19 +113,6 @@ public final class SsMediaSource implements MediaSource,
return oldPlayingPeriodIndex;
}
@Override
public Position getDefaultStartPosition(int index) {
if (window == null) {
return null;
}
if (manifest.isLive) {
long startPositionUs = Math.max(window.startTimeMs,
window.endTimeMs - liveEdgeOffsetMs) * 1000;
return new Position(0, startPositionUs);
}
return Position.DEFAULT;
}
@Override
public void maybeThrowSourceInfoRefreshError() throws IOException {
manifestLoader.maybeThrowError();
@ -196,15 +182,16 @@ 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 */);
true /* isSeekable */, true /* isDynamic */, 0, defaultInitialStartPositionUs);
timeline = new SinglePeriodTimeline(endTimeUs, window);
}
} else {
boolean isSeekable = manifest.durationUs != C.UNSET_TIME_US;
timeline = new SinglePeriodTimeline(manifest.durationUs, isSeekable);
}
window = timeline.getWindow(0);
sourceListener.onSourceInfoRefreshed(timeline, manifest);
scheduleManifestRefresh();
}