Better selection of default start position for live
If the live window has a small duration, we currently end up setting the default start position to be right at the start of the window. This increases the chance of a BehindLiveWindowException. With this change we impose a minimum 5s gap between the start of the window and the default start position. If the window is *really* small (<10s) then doing this would push the default start position too close to the end of the window. We don't have much time to play with in either direction in this case, so we put the default start position in the middle of the window and hope for the best. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=132054802
This commit is contained in:
parent
06a644eccd
commit
f8c05ebd93
@ -57,30 +57,35 @@ 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 initial playback position) should be set to
|
||||
* A constant indicating that the presentation delay for live streams should be set to
|
||||
* {@link DashManifest#suggestedPresentationDelay} if specified by the manifest, or
|
||||
* {@link #DEFAULT_LIVE_EDGE_OFFSET_FIXED_MS} otherwise.
|
||||
* {@link #DEFAULT_LIVE_PRESENTATION_DELAY_FIXED_MS} otherwise. The presentation delay is the
|
||||
* duration by which the default start position precedes the end of the live window.
|
||||
*/
|
||||
public static final long DEFAULT_LIVE_EDGE_OFFSET_PREFER_MANIFEST_MS = -1;
|
||||
public static final long DEFAULT_LIVE_PRESENTATION_DELAY_PREFER_MANIFEST_MS = -1;
|
||||
/**
|
||||
* A fixed default live edge offset (the offset subtracted from the live edge when calculating the
|
||||
* default initial playback position).
|
||||
* A fixed default presentation delay for live streams. The presentation delay is the duration
|
||||
* by which the default start position precedes the end of the live window.
|
||||
*/
|
||||
public static final long DEFAULT_LIVE_EDGE_OFFSET_FIXED_MS = 30000;
|
||||
public static final long DEFAULT_LIVE_PRESENTATION_DELAY_FIXED_MS = 30000;
|
||||
|
||||
/**
|
||||
* The interval in milliseconds between invocations of
|
||||
* {@link MediaSource.Listener#onSourceInfoRefreshed(Timeline, Object)} when the source's
|
||||
* {@link Timeline} is changing dynamically (for example, for incomplete live streams).
|
||||
*/
|
||||
private static final int NOTIFY_MANIFEST_INTERVAL_MS = 5000;
|
||||
/**
|
||||
* The minimum default start position for live streams, relative to the start of the live window.
|
||||
*/
|
||||
private static final long MIN_LIVE_DEFAULT_START_POSITION_US = 5000000;
|
||||
|
||||
private static final String TAG = "DashMediaSource";
|
||||
|
||||
private final DataSource.Factory manifestDataSourceFactory;
|
||||
private final DashChunkSource.Factory chunkSourceFactory;
|
||||
private final int minLoadableRetryCount;
|
||||
private final long liveEdgeOffsetMs;
|
||||
private final long livePresentationDelayMs;
|
||||
private final EventDispatcher eventDispatcher;
|
||||
private final DashManifestParser manifestParser;
|
||||
private final ManifestCallback manifestCallback;
|
||||
@ -106,18 +111,19 @@ public final class DashMediaSource implements MediaSource {
|
||||
DashChunkSource.Factory chunkSourceFactory, Handler eventHandler,
|
||||
AdaptiveMediaSourceEventListener eventListener) {
|
||||
this(manifestUri, manifestDataSourceFactory, chunkSourceFactory,
|
||||
DEFAULT_MIN_LOADABLE_RETRY_COUNT, DEFAULT_LIVE_EDGE_OFFSET_PREFER_MANIFEST_MS, eventHandler,
|
||||
eventListener);
|
||||
DEFAULT_MIN_LOADABLE_RETRY_COUNT, DEFAULT_LIVE_PRESENTATION_DELAY_PREFER_MANIFEST_MS,
|
||||
eventHandler, eventListener);
|
||||
}
|
||||
|
||||
public DashMediaSource(Uri manifestUri, DataSource.Factory manifestDataSourceFactory,
|
||||
DashChunkSource.Factory chunkSourceFactory, int minLoadableRetryCount, long liveEdgeOffsetMs,
|
||||
Handler eventHandler, AdaptiveMediaSourceEventListener eventListener) {
|
||||
DashChunkSource.Factory chunkSourceFactory, int minLoadableRetryCount,
|
||||
long livePresentationDelayMs, Handler eventHandler,
|
||||
AdaptiveMediaSourceEventListener eventListener) {
|
||||
this.manifestUri = manifestUri;
|
||||
this.manifestDataSourceFactory = manifestDataSourceFactory;
|
||||
this.chunkSourceFactory = chunkSourceFactory;
|
||||
this.minLoadableRetryCount = minLoadableRetryCount;
|
||||
this.liveEdgeOffsetMs = liveEdgeOffsetMs;
|
||||
this.livePresentationDelayMs = livePresentationDelayMs;
|
||||
eventDispatcher = new EventDispatcher(eventHandler, eventListener);
|
||||
manifestParser = new DashManifestParser(generateContentId());
|
||||
manifestCallback = new ManifestCallback();
|
||||
@ -386,20 +392,26 @@ public final class DashMediaSource implements MediaSource {
|
||||
}
|
||||
long windowDefaultStartPositionUs = 0;
|
||||
if (manifest.dynamic) {
|
||||
long liveEdgeOffsetForManifestMs = liveEdgeOffsetMs;
|
||||
if (liveEdgeOffsetForManifestMs == DEFAULT_LIVE_EDGE_OFFSET_PREFER_MANIFEST_MS) {
|
||||
liveEdgeOffsetForManifestMs = manifest.suggestedPresentationDelay != C.TIME_UNSET
|
||||
? manifest.suggestedPresentationDelay : DEFAULT_LIVE_EDGE_OFFSET_FIXED_MS;
|
||||
long presentationDelayForManifestMs = livePresentationDelayMs;
|
||||
if (presentationDelayForManifestMs == DEFAULT_LIVE_PRESENTATION_DELAY_PREFER_MANIFEST_MS) {
|
||||
presentationDelayForManifestMs = manifest.suggestedPresentationDelay != C.TIME_UNSET
|
||||
? manifest.suggestedPresentationDelay : DEFAULT_LIVE_PRESENTATION_DELAY_FIXED_MS;
|
||||
}
|
||||
// Snap the default position to the start of the segment containing it.
|
||||
long initialTimeOffsetInWindowUs =
|
||||
Math.max(0, windowDurationUs - C.msToUs(liveEdgeOffsetForManifestMs));
|
||||
long defaultStartPositionUs = windowDurationUs - C.msToUs(presentationDelayForManifestMs);
|
||||
if (defaultStartPositionUs < MIN_LIVE_DEFAULT_START_POSITION_US) {
|
||||
// The default start position is too close to the start of the live window. Set it to the
|
||||
// minimum default start position provided the window is at least twice as big. Else set
|
||||
// it to the middle of the window.
|
||||
defaultStartPositionUs = Math.min(MIN_LIVE_DEFAULT_START_POSITION_US, windowDurationUs / 2);
|
||||
}
|
||||
|
||||
int periodIndex = 0;
|
||||
long initialTimeOffsetInPeriodUs = currentStartTimeUs + initialTimeOffsetInWindowUs;
|
||||
long defaultStartPositionInPeriodUs = currentStartTimeUs + defaultStartPositionUs;
|
||||
long periodDurationUs = manifest.getPeriodDurationUs(periodIndex);
|
||||
while (periodIndex < manifest.getPeriodCount() - 1
|
||||
&& initialTimeOffsetInPeriodUs >= periodDurationUs) {
|
||||
initialTimeOffsetInPeriodUs -= periodDurationUs;
|
||||
&& defaultStartPositionInPeriodUs >= periodDurationUs) {
|
||||
defaultStartPositionInPeriodUs -= periodDurationUs;
|
||||
periodIndex++;
|
||||
periodDurationUs = manifest.getPeriodDurationUs(periodIndex);
|
||||
}
|
||||
@ -410,11 +422,11 @@ public final class DashMediaSource implements MediaSource {
|
||||
// not correspond to the start of a segment in both, but this is an edge case.
|
||||
DashSegmentIndex index =
|
||||
period.adaptationSets.get(videoAdaptationSetIndex).representations.get(0).getIndex();
|
||||
int segmentNum = index.getSegmentNum(initialTimeOffsetInPeriodUs, periodDurationUs);
|
||||
int segmentNum = index.getSegmentNum(defaultStartPositionInPeriodUs, periodDurationUs);
|
||||
windowDefaultStartPositionUs =
|
||||
initialTimeOffsetInWindowUs - initialTimeOffsetInPeriodUs + index.getTimeUs(segmentNum);
|
||||
defaultStartPositionUs - defaultStartPositionInPeriodUs + index.getTimeUs(segmentNum);
|
||||
} else {
|
||||
windowDefaultStartPositionUs = initialTimeOffsetInWindowUs;
|
||||
windowDefaultStartPositionUs = defaultStartPositionUs;
|
||||
}
|
||||
}
|
||||
long windowStartTimeMs = manifest.availabilityStartTime
|
||||
|
@ -50,18 +50,25 @@ 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 initial playback position.
|
||||
* The default presentation delay for live streams. The presentation delay is the duration by
|
||||
* which the default start position precedes the end of the live window.
|
||||
*/
|
||||
public static final long DEFAULT_LIVE_EDGE_OFFSET_MS = 30000;
|
||||
public static final long DEFAULT_LIVE_PRESENTATION_DELAY_MS = 30000;
|
||||
|
||||
/**
|
||||
* The minimum period between manifest refreshes.
|
||||
*/
|
||||
private static final int MINIMUM_MANIFEST_REFRESH_PERIOD_MS = 5000;
|
||||
/**
|
||||
* The minimum default start position for live streams, relative to the start of the live window.
|
||||
*/
|
||||
private static final long MIN_LIVE_DEFAULT_START_POSITION_US = 5000000;
|
||||
|
||||
private final Uri manifestUri;
|
||||
private final DataSource.Factory dataSourceFactory;
|
||||
private final SsChunkSource.Factory chunkSourceFactory;
|
||||
private final int minLoadableRetryCount;
|
||||
private final long liveEdgeOffsetMs;
|
||||
private final long livePresentationDelayMs;
|
||||
private final EventDispatcher eventDispatcher;
|
||||
private final SsManifestParser manifestParser;
|
||||
private final ArrayList<SsMediaPeriod> mediaPeriods;
|
||||
@ -79,18 +86,20 @@ public final class SsMediaSource implements MediaSource,
|
||||
SsChunkSource.Factory chunkSourceFactory, Handler eventHandler,
|
||||
AdaptiveMediaSourceEventListener eventListener) {
|
||||
this(manifestUri, manifestDataSourceFactory, chunkSourceFactory,
|
||||
DEFAULT_MIN_LOADABLE_RETRY_COUNT, DEFAULT_LIVE_EDGE_OFFSET_MS, eventHandler, eventListener);
|
||||
DEFAULT_MIN_LOADABLE_RETRY_COUNT, DEFAULT_LIVE_PRESENTATION_DELAY_MS, eventHandler,
|
||||
eventListener);
|
||||
}
|
||||
|
||||
public SsMediaSource(Uri manifestUri, DataSource.Factory dataSourceFactory,
|
||||
SsChunkSource.Factory chunkSourceFactory, int minLoadableRetryCount,
|
||||
long liveEdgeOffsetMs, Handler eventHandler, AdaptiveMediaSourceEventListener eventListener) {
|
||||
long livePresentationDelayMs, Handler eventHandler,
|
||||
AdaptiveMediaSourceEventListener eventListener) {
|
||||
this.manifestUri = Util.toLowerInvariant(manifestUri.getLastPathSegment()).equals("manifest")
|
||||
? manifestUri : Uri.withAppendedPath(manifestUri, "Manifest");
|
||||
this.dataSourceFactory = dataSourceFactory;
|
||||
this.chunkSourceFactory = chunkSourceFactory;
|
||||
this.minLoadableRetryCount = minLoadableRetryCount;
|
||||
this.liveEdgeOffsetMs = liveEdgeOffsetMs;
|
||||
this.livePresentationDelayMs = livePresentationDelayMs;
|
||||
this.eventDispatcher = new EventDispatcher(eventHandler, eventListener);
|
||||
manifestParser = new SsManifestParser();
|
||||
mediaPeriods = new ArrayList<>();
|
||||
@ -176,7 +185,13 @@ public final class SsMediaSource implements MediaSource,
|
||||
startTimeUs = Math.max(startTimeUs, endTimeUs - manifest.dvrWindowLengthUs);
|
||||
}
|
||||
long durationUs = endTimeUs - startTimeUs;
|
||||
long defaultStartPositionUs = Math.max(0, durationUs - (liveEdgeOffsetMs * 1000));
|
||||
long defaultStartPositionUs = durationUs - C.msToUs(livePresentationDelayMs);
|
||||
if (defaultStartPositionUs < MIN_LIVE_DEFAULT_START_POSITION_US) {
|
||||
// The default start position is too close to the start of the live window. Set it to the
|
||||
// minimum default start position provided the window is at least twice as big. Else set
|
||||
// it to the middle of the window.
|
||||
defaultStartPositionUs = Math.min(MIN_LIVE_DEFAULT_START_POSITION_US, durationUs / 2);
|
||||
}
|
||||
timeline = new SinglePeriodTimeline(C.TIME_UNSET, durationUs, startTimeUs,
|
||||
defaultStartPositionUs, true /* isSeekable */, true /* isDynamic */);
|
||||
}
|
||||
|
@ -737,8 +737,7 @@ public final class DashTest extends ActivityInstrumentationTestCase2<HostActivit
|
||||
DefaultDashChunkSource.Factory chunkSourceFactory = new DefaultDashChunkSource.Factory(
|
||||
mediaDataSourceFactory);
|
||||
return new DashMediaSource(manifestUri, manifestDataSourceFactory, chunkSourceFactory,
|
||||
MIN_LOADABLE_RETRY_COUNT, DashMediaSource.DEFAULT_LIVE_EDGE_OFFSET_PREFER_MANIFEST_MS,
|
||||
null, null);
|
||||
MIN_LOADABLE_RETRY_COUNT, 0 /* livePresentationDelayMs */, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
Loading…
x
Reference in New Issue
Block a user