Fix live window calculation and transition from VOD.

Fix the calculation of the seek window for multi-period DASH.

Snap the default initial position back to the start of its segment, to ensure
that the first sample provided when transitioning to a DASH live source is a
key-frame.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=131052912
This commit is contained in:
andrewlewis 2016-08-23 07:21:23 -07:00 committed by Oliver Woodman
parent 458c7bfc05
commit 85915f2ac1
2 changed files with 54 additions and 17 deletions

View File

@ -354,22 +354,33 @@ public final class DashMediaSource implements MediaSource {
manifest.getPeriodDurationUs(0)); manifest.getPeriodDurationUs(0));
PeriodSeekInfo lastPeriodSeekInfo = PeriodSeekInfo.createPeriodSeekInfo( PeriodSeekInfo lastPeriodSeekInfo = PeriodSeekInfo.createPeriodSeekInfo(
manifest.getPeriod(lastPeriodIndex), manifest.getPeriodDurationUs(lastPeriodIndex)); manifest.getPeriod(lastPeriodIndex), manifest.getPeriodDurationUs(lastPeriodIndex));
long currentStartTimeUs; // Get the period-relative start/end times.
long currentEndTimeUs; long currentStartTimeUs = firstPeriodSeekInfo.availableStartTimeUs;
long currentEndTimeUs = lastPeriodSeekInfo.availableEndTimeUs;
if (manifest.dynamic && !lastPeriodSeekInfo.isIndexExplicit) { if (manifest.dynamic && !lastPeriodSeekInfo.isIndexExplicit) {
long minStartPositionUs = firstPeriodSeekInfo.availableStartTimeUs; // The manifest describes an incomplete live stream. Update the start/end times to reflect the
long maxEndPositionUs = lastPeriodSeekInfo.availableEndTimeUs; // live stream duration and the manifest's time shift buffer depth.
long timeShiftBufferDepthUs = manifest.timeShiftBufferDepth == C.TIME_UNSET long liveStreamDurationUs = getNowUnixTimeUs() - manifest.availabilityStartTime * 1000;
? C.TIME_UNSET : (manifest.timeShiftBufferDepth * 1000); long liveStreamEndPositionInLastPeriodUs =
currentEndTimeUs = Math.min(maxEndPositionUs, liveStreamDurationUs - manifest.getPeriod(lastPeriodIndex).startMs * 1000;
getNowUnixTimeUs() - manifest.availabilityStartTime * 1000); currentEndTimeUs = Math.min(liveStreamEndPositionInLastPeriodUs, currentEndTimeUs);
currentStartTimeUs = timeShiftBufferDepthUs == C.TIME_UNSET ? minStartPositionUs if (manifest.timeShiftBufferDepth != C.TIME_UNSET) {
: Math.max(minStartPositionUs, currentEndTimeUs - timeShiftBufferDepthUs); long timeShiftBufferDepthUs = manifest.timeShiftBufferDepth * 1000;
long offsetInPeriodUs = currentEndTimeUs - timeShiftBufferDepthUs;
int periodIndex = lastPeriodIndex;
while (offsetInPeriodUs < 0 && periodIndex > 0) {
offsetInPeriodUs += manifest.getPeriodDurationUs(--periodIndex);
}
if (periodIndex == 0) {
currentStartTimeUs = Math.max(currentStartTimeUs, offsetInPeriodUs);
} else {
// The time shift buffer starts after the earliest period.
// TODO: Does this ever happen?
currentStartTimeUs = manifest.getPeriodDurationUs(0);
}
}
// The window is changing implicitly. Post a simulated manifest refresh to update it. // The window is changing implicitly. Post a simulated manifest refresh to update it.
handler.postDelayed(simulateManifestRefreshRunnable, NOTIFY_MANIFEST_INTERVAL_MS); handler.postDelayed(simulateManifestRefreshRunnable, NOTIFY_MANIFEST_INTERVAL_MS);
} else {
currentStartTimeUs = firstPeriodSeekInfo.availableStartTimeUs;
currentEndTimeUs = lastPeriodSeekInfo.availableEndTimeUs;
} }
long windowDurationUs = currentEndTimeUs - currentStartTimeUs; long windowDurationUs = currentEndTimeUs - currentStartTimeUs;
for (int i = 0; i < manifest.getPeriodCount() - 1; i++) { for (int i = 0; i < manifest.getPeriodCount() - 1; i++) {
@ -382,7 +393,31 @@ public final class DashMediaSource implements MediaSource {
liveEdgeOffsetForManifestMs = manifest.suggestedPresentationDelay != C.TIME_UNSET liveEdgeOffsetForManifestMs = manifest.suggestedPresentationDelay != C.TIME_UNSET
? manifest.suggestedPresentationDelay : DEFAULT_LIVE_EDGE_OFFSET_FIXED_MS; ? manifest.suggestedPresentationDelay : DEFAULT_LIVE_EDGE_OFFSET_FIXED_MS;
} }
defaultInitialTimeUs = Math.max(0, windowDurationUs - (liveEdgeOffsetForManifestMs * 1000)); // Snap the default position to the start of the segment containing it.
long initialTimeOffsetInWindowUs =
Math.max(0, windowDurationUs - (liveEdgeOffsetForManifestMs * 1000));
int periodIndex = 0;
long initialTimeOffsetInPeriodUs = currentStartTimeUs + initialTimeOffsetInWindowUs;
long periodDurationUs = manifest.getPeriodDurationUs(periodIndex);
while (periodIndex < manifest.getPeriodCount() - 1
&& initialTimeOffsetInPeriodUs >= periodDurationUs) {
initialTimeOffsetInPeriodUs -= periodDurationUs;
periodIndex++;
periodDurationUs = manifest.getPeriodDurationUs(periodIndex);
}
Period period = manifest.getPeriod(periodIndex);
int videoAdaptationSetIndex = period.getAdaptationSetIndex(C.TRACK_TYPE_VIDEO);
if (videoAdaptationSetIndex != C.INDEX_UNSET) {
// If there are multiple video adaptation sets with unaligned segments, the initial time may
// 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);
defaultInitialTimeUs =
initialTimeOffsetInWindowUs - initialTimeOffsetInPeriodUs + index.getTimeUs(segmentNum);
} else {
defaultInitialTimeUs = initialTimeOffsetInWindowUs;
}
} }
window = new MediaWindow(windowDurationUs, true /* isSeekable */, manifest.dynamic, window = new MediaWindow(windowDurationUs, true /* isSeekable */, manifest.dynamic,
defaultInitialTimeUs); defaultInitialTimeUs);

View File

@ -186,15 +186,17 @@ public class DefaultDashChunkSource implements DashChunkSource {
if (indexUnbounded) { if (indexUnbounded) {
// The index is itself unbounded. We need to use the current time to calculate the range of // The index is itself unbounded. We need to use the current time to calculate the range of
// available segments. // available segments.
long liveEdgeTimestampUs = nowUs - manifest.availabilityStartTime * 1000; long liveEdgeTimeUs = nowUs - manifest.availabilityStartTime * 1000;
long periodStartUs = manifest.getPeriod(periodIndex).startMs * 1000;
long liveEdgeTimeInPeriodUs = liveEdgeTimeUs - periodStartUs;
if (manifest.timeShiftBufferDepth != C.TIME_UNSET) { if (manifest.timeShiftBufferDepth != C.TIME_UNSET) {
long bufferDepthUs = manifest.timeShiftBufferDepth * 1000; long bufferDepthUs = manifest.timeShiftBufferDepth * 1000;
firstAvailableSegmentNum = Math.max(firstAvailableSegmentNum, firstAvailableSegmentNum = Math.max(firstAvailableSegmentNum,
representationHolder.getSegmentNum(liveEdgeTimestampUs - bufferDepthUs)); representationHolder.getSegmentNum(liveEdgeTimeInPeriodUs - bufferDepthUs));
} }
// getSegmentNum(liveEdgeTimestampUs) will not be completed yet, so subtract one to get the // getSegmentNum(liveEdgeTimestampUs) will not be completed yet, so subtract one to get the
// index of the last completed segment. // index of the last completed segment.
lastAvailableSegmentNum = representationHolder.getSegmentNum(liveEdgeTimestampUs) - 1; lastAvailableSegmentNum = representationHolder.getSegmentNum(liveEdgeTimeInPeriodUs) - 1;
} }
int segmentNum; int segmentNum;