From 595147de9b4c0cf6ec1d784597457cba1af97507 Mon Sep 17 00:00:00 2001 From: Oliver Woodman Date: Mon, 15 Dec 2014 15:03:10 +0000 Subject: [PATCH] Enforce sliding window of available segments for DASH DVB. --- .../exoplayer/dash/DashChunkSource.java | 48 ++++++++++++------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java b/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java index 8f0dc8b71c..a7bc566889 100644 --- a/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java +++ b/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java @@ -326,13 +326,30 @@ public class DashChunkSource implements ChunkSource { return; } - int lastSegmentNum = segmentIndex.getLastSegmentNum(); - boolean indexUnbounded = lastSegmentNum == DashSegmentIndex.INDEX_UNBOUNDED; + // TODO: Use UtcTimingElement where possible. + long nowUs = System.currentTimeMillis() * 1000; + + int firstAvailableSegmentNum = segmentIndex.getFirstSegmentNum(); + int lastAvailableSegmentNum = segmentIndex.getLastSegmentNum(); + boolean indexUnbounded = lastAvailableSegmentNum == DashSegmentIndex.INDEX_UNBOUNDED; + if (indexUnbounded) { + // The index is itself unbounded. We need to use the current time to calculate the range of + // available segments. + long liveEdgeTimestampUs = nowUs - currentManifest.availabilityStartTime * 1000; + if (currentManifest.timeShiftBufferDepth != -1) { + long bufferDepthUs = currentManifest.timeShiftBufferDepth * 1000; + firstAvailableSegmentNum = Math.max(firstAvailableSegmentNum, + segmentIndex.getSegmentNum(liveEdgeTimestampUs - bufferDepthUs)); + } + // getSegmentNum(liveEdgeTimestampUs) will not be completed yet, so subtract one to get the + // index of the last completed segment. + lastAvailableSegmentNum = segmentIndex.getSegmentNum(liveEdgeTimestampUs) - 1; + } int segmentNum; if (queue.isEmpty()) { if (currentManifest.dynamic) { - seekPositionUs = getLiveSeekPosition(indexUnbounded); + seekPositionUs = getLiveSeekPosition(nowUs, indexUnbounded); } segmentNum = segmentIndex.getSegmentNum(seekPositionUs); } else { @@ -340,20 +357,20 @@ public class DashChunkSource implements ChunkSource { - representationHolder.segmentNumShift; } - // TODO: For unbounded manifests, we need to enforce that we don't try and request chunks - // behind or in front of the live window. if (currentManifest.dynamic) { - if (segmentNum < segmentIndex.getFirstSegmentNum()) { + if (segmentNum < firstAvailableSegmentNum) { // This is before the first chunk in the current manifest. fatalError = new BehindLiveWindowException(); return; - } else if (!indexUnbounded && segmentNum > lastSegmentNum) { - // This is beyond the last chunk in the current manifest. - finishedCurrentManifest = true; + } else if (segmentNum > lastAvailableSegmentNum) { + // This chunk is beyond the last chunk in the current manifest. If the index is bounded + // we'll need to refresh it. If it's unbounded we just need to wait for a while before + // attempting to load the chunk. + finishedCurrentManifest = !indexUnbounded; return; - } else if (!indexUnbounded && segmentNum == lastSegmentNum) { - // This is the last chunk in the current manifest. Mark the manifest as being finished, - // but continue to return the final chunk. + } else if (!indexUnbounded && segmentNum == lastAvailableSegmentNum) { + // This is the last chunk in a dynamic bounded manifest. We'll need to refresh the manifest + // to obtain the next chunk. finishedCurrentManifest = true; } } @@ -457,15 +474,14 @@ public class DashChunkSource implements ChunkSource { * For live playbacks, determines the seek position that snaps playback to be * {@link #liveEdgeLatencyUs} behind the live edge of the current manifest * + * @param nowUs An estimate of the current server time, in microseconds. * @param indexUnbounded True if the segment index for this source is unbounded. False otherwise. * @return The seek position in microseconds. */ - private long getLiveSeekPosition(boolean indexUnbounded) { + private long getLiveSeekPosition(long nowUs, boolean indexUnbounded) { long liveEdgeTimestampUs; if (indexUnbounded) { - // TODO: Use UtcTimingElement where possible. - long nowMs = System.currentTimeMillis(); - liveEdgeTimestampUs = (nowMs - currentManifest.availabilityStartTime) * 1000; + liveEdgeTimestampUs = nowUs - currentManifest.availabilityStartTime * 1000; } else { liveEdgeTimestampUs = Long.MIN_VALUE; for (RepresentationHolder representationHolder : representationHolders.values()) {