Expose seek window + start position for SmoothStreaming

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=129867969
This commit is contained in:
olly 2016-08-10 08:21:50 -07:00 committed by Oliver Woodman
parent 2a15480102
commit c5a2f9b010
2 changed files with 56 additions and 13 deletions

View File

@ -19,18 +19,29 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.util.Assertions;
/**
* A {@link Timeline} consisting of a single period.
* A {@link Timeline} consisting of a single period and seek window.
*/
public final class SinglePeriodTimeline implements Timeline {
/**
* Returns a new timeline with one period of unknown duration and no seek window.
* Returns a new timeline with one period of unknown duration and an empty seek window.
*
* @param id The identifier for the period.
* @return A new timeline with one period of unknown duration.
*/
public static Timeline createNonFinalTimeline(Object id) {
return new SinglePeriodTimeline(id, false, C.UNSET_TIME_US);
return new SinglePeriodTimeline(id, false, C.UNSET_TIME_US, SeekWindow.UNSEEKABLE);
}
/**
* Returns a new timeline with one period of unknown duration and the specified seek window.
*
* @param id The identifier for the period.
* @param seekWindow The seek window.
* @return A new timeline with one period of unknown duration.
*/
public static Timeline createNonFinalTimeline(Object id, SeekWindow seekWindow) {
return new SinglePeriodTimeline(id, false, C.UNSET_TIME_US, seekWindow);
}
/**
@ -59,14 +70,13 @@ public final class SinglePeriodTimeline implements Timeline {
private final Object id;
private final boolean isFinal;
private final long durationMs;
private final SeekWindow[] seekWindows;
private final SeekWindow seekWindow;
private SinglePeriodTimeline(Object id, boolean isFinal, long durationMs,
SeekWindow... seekWindows) {
private SinglePeriodTimeline(Object id, boolean isFinal, long durationMs, SeekWindow seekWindow) {
this.id = Assertions.checkNotNull(id);
this.isFinal = isFinal;
this.durationMs = durationMs;
this.seekWindows = seekWindows;
this.seekWindow = seekWindow;
}
@Override
@ -99,12 +109,12 @@ public final class SinglePeriodTimeline implements Timeline {
@Override
public int getSeekWindowCount() {
return seekWindows.length;
return 1;
}
@Override
public SeekWindow getSeekWindow(int index) {
return seekWindows[index];
return seekWindow;
}
}

View File

@ -24,9 +24,11 @@ import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener;
import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher;
import com.google.android.exoplayer2.source.MediaPeriod;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.SeekWindow;
import com.google.android.exoplayer2.source.SinglePeriodTimeline;
import com.google.android.exoplayer2.source.Timeline;
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest;
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest.StreamElement;
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifestParser;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.Loader;
@ -45,6 +47,11 @@ public final class SsMediaSource implements MediaSource,
* The default minimum number of times to retry loading data prior to failing.
*/
public static final int DEFAULT_MIN_LOADABLE_RETRY_COUNT = 3;
/**
* The offset in microseconds subtracted from the live edge position when calculating the default
* position returned by {@link #getDefaultStartPosition(int)}.
*/
private static final long LIVE_EDGE_OFFSET_US = 30000000;
private static final int MINIMUM_MANIFEST_REFRESH_PERIOD_MS = 5000;
@ -61,6 +68,7 @@ public final class SsMediaSource implements MediaSource,
private long manifestLoadStartTimestamp;
private SsManifest manifest;
private SeekWindow seekWindow;
private Handler manifestRefreshHandler;
private SsMediaPeriod period;
@ -102,7 +110,14 @@ public final class SsMediaSource implements MediaSource,
@Override
public Position getDefaultStartPosition(int index) {
// TODO: Return the position of the live edge, if applicable.
if (seekWindow == null) {
return null;
}
if (manifest.isLive) {
long startPositionUs = Math.max(seekWindow.startTimeUs,
seekWindow.endTimeUs - LIVE_EDGE_OFFSET_US);
return new Position(0, startPositionUs);
}
return Position.DEFAULT;
}
@ -144,9 +159,27 @@ public final class SsMediaSource implements MediaSource,
} else {
period.updateManifest(manifest);
}
Timeline timeline = manifest.isLive || manifest.durationUs == C.UNSET_TIME_US
? SinglePeriodTimeline.createUnseekableFinalTimeline(this, C.UNSET_TIME_US)
: SinglePeriodTimeline.createSeekableFinalTimeline(this, manifest.durationUs);
Timeline timeline;
if (manifest.isLive) {
long startTimeUs = Long.MAX_VALUE;
for (int i = 0; i < manifest.streamElements.length; i++) {
StreamElement element = manifest.streamElements[i];
if (element.chunkCount > 0) {
startTimeUs = Math.min(startTimeUs, element.getStartTimeUs(0));
}
}
if (startTimeUs == Long.MAX_VALUE) {
timeline = SinglePeriodTimeline.createNonFinalTimeline(this);
} else {
timeline = SinglePeriodTimeline.createNonFinalTimeline(this,
new SeekWindow(0, startTimeUs, 0, startTimeUs + manifest.dvrWindowLengthUs));
}
} else if (manifest.durationUs == C.UNSET_TIME_US) {
timeline = SinglePeriodTimeline.createUnseekableFinalTimeline(this, C.UNSET_TIME_US);
} else {
timeline = SinglePeriodTimeline.createSeekableFinalTimeline(this, manifest.durationUs);
}
seekWindow = timeline.getSeekWindow(0);
sourceListener.onSourceInfoRefreshed(timeline, manifest);
scheduleManifestRefresh();
}