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));
PeriodSeekInfo lastPeriodSeekInfo = PeriodSeekInfo.createPeriodSeekInfo(
manifest.getPeriod(lastPeriodIndex), manifest.getPeriodDurationUs(lastPeriodIndex));
long currentStartTimeUs;
long currentEndTimeUs;
// Get the period-relative start/end times.
long currentStartTimeUs = firstPeriodSeekInfo.availableStartTimeUs;
long currentEndTimeUs = lastPeriodSeekInfo.availableEndTimeUs;
if (manifest.dynamic && !lastPeriodSeekInfo.isIndexExplicit) {
long minStartPositionUs = firstPeriodSeekInfo.availableStartTimeUs;
long maxEndPositionUs = lastPeriodSeekInfo.availableEndTimeUs;
long timeShiftBufferDepthUs = manifest.timeShiftBufferDepth == C.TIME_UNSET
? C.TIME_UNSET : (manifest.timeShiftBufferDepth * 1000);
currentEndTimeUs = Math.min(maxEndPositionUs,
getNowUnixTimeUs() - manifest.availabilityStartTime * 1000);
currentStartTimeUs = timeShiftBufferDepthUs == C.TIME_UNSET ? minStartPositionUs
: Math.max(minStartPositionUs, currentEndTimeUs - timeShiftBufferDepthUs);
// The manifest describes an incomplete live stream. Update the start/end times to reflect the
// live stream duration and the manifest's time shift buffer depth.
long liveStreamDurationUs = getNowUnixTimeUs() - manifest.availabilityStartTime * 1000;
long liveStreamEndPositionInLastPeriodUs =
liveStreamDurationUs - manifest.getPeriod(lastPeriodIndex).startMs * 1000;
currentEndTimeUs = Math.min(liveStreamEndPositionInLastPeriodUs, currentEndTimeUs);
if (manifest.timeShiftBufferDepth != C.TIME_UNSET) {
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.
handler.postDelayed(simulateManifestRefreshRunnable, NOTIFY_MANIFEST_INTERVAL_MS);
} else {
currentStartTimeUs = firstPeriodSeekInfo.availableStartTimeUs;
currentEndTimeUs = lastPeriodSeekInfo.availableEndTimeUs;
}
long windowDurationUs = currentEndTimeUs - currentStartTimeUs;
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
? 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,
defaultInitialTimeUs);

View File

@ -186,15 +186,17 @@ public class DefaultDashChunkSource implements DashChunkSource {
if (indexUnbounded) {
// The index is itself unbounded. We need to use the current time to calculate the range of
// 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) {
long bufferDepthUs = manifest.timeShiftBufferDepth * 1000;
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
// index of the last completed segment.
lastAvailableSegmentNum = representationHolder.getSegmentNum(liveEdgeTimestampUs) - 1;
lastAvailableSegmentNum = representationHolder.getSegmentNum(liveEdgeTimeInPeriodUs) - 1;
}
int segmentNum;