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; 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 { 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. * @param id The identifier for the period.
* @return A new timeline with one period of unknown duration. * @return A new timeline with one period of unknown duration.
*/ */
public static Timeline createNonFinalTimeline(Object id) { 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 Object id;
private final boolean isFinal; private final boolean isFinal;
private final long durationMs; private final long durationMs;
private final SeekWindow[] seekWindows; private final SeekWindow seekWindow;
private SinglePeriodTimeline(Object id, boolean isFinal, long durationMs, private SinglePeriodTimeline(Object id, boolean isFinal, long durationMs, SeekWindow seekWindow) {
SeekWindow... seekWindows) {
this.id = Assertions.checkNotNull(id); this.id = Assertions.checkNotNull(id);
this.isFinal = isFinal; this.isFinal = isFinal;
this.durationMs = durationMs; this.durationMs = durationMs;
this.seekWindows = seekWindows; this.seekWindow = seekWindow;
} }
@Override @Override
@ -99,12 +109,12 @@ public final class SinglePeriodTimeline implements Timeline {
@Override @Override
public int getSeekWindowCount() { public int getSeekWindowCount() {
return seekWindows.length; return 1;
} }
@Override @Override
public SeekWindow getSeekWindow(int index) { 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.AdaptiveMediaSourceEventListener.EventDispatcher;
import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaPeriod;
import com.google.android.exoplayer2.source.MediaSource; 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.SinglePeriodTimeline;
import com.google.android.exoplayer2.source.Timeline; 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;
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest.StreamElement;
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifestParser; import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifestParser;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.Loader; 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. * The default minimum number of times to retry loading data prior to failing.
*/ */
public static final int DEFAULT_MIN_LOADABLE_RETRY_COUNT = 3; 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; private static final int MINIMUM_MANIFEST_REFRESH_PERIOD_MS = 5000;
@ -61,6 +68,7 @@ public final class SsMediaSource implements MediaSource,
private long manifestLoadStartTimestamp; private long manifestLoadStartTimestamp;
private SsManifest manifest; private SsManifest manifest;
private SeekWindow seekWindow;
private Handler manifestRefreshHandler; private Handler manifestRefreshHandler;
private SsMediaPeriod period; private SsMediaPeriod period;
@ -102,7 +110,14 @@ public final class SsMediaSource implements MediaSource,
@Override @Override
public Position getDefaultStartPosition(int index) { 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; return Position.DEFAULT;
} }
@ -144,9 +159,27 @@ public final class SsMediaSource implements MediaSource,
} else { } else {
period.updateManifest(manifest); period.updateManifest(manifest);
} }
Timeline timeline = manifest.isLive || manifest.durationUs == C.UNSET_TIME_US Timeline timeline;
? SinglePeriodTimeline.createUnseekableFinalTimeline(this, C.UNSET_TIME_US) if (manifest.isLive) {
: SinglePeriodTimeline.createSeekableFinalTimeline(this, manifest.durationUs); 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); sourceListener.onSourceInfoRefreshed(timeline, manifest);
scheduleManifestRefresh(); scheduleManifestRefresh();
} }