From ea29c71d94cb013115b3bff26e971f693770bdde Mon Sep 17 00:00:00 2001 From: Oliver Woodman Date: Thu, 28 May 2015 17:11:28 +0100 Subject: [PATCH] Temporarily revert bounded live seeking. It's causing issues in its current form. --- .../demo/player/DashRendererBuilder.java | 8 +- .../exoplayer/demo/player/DemoPlayer.java | 10 +- .../exoplayer/dash/DashChunkSource.java | 192 +++----- .../exoplayer/dash/DashChunkSourceTest.java | 449 +++++------------- 4 files changed, 193 insertions(+), 466 deletions(-) diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/player/DashRendererBuilder.java b/demo/src/main/java/com/google/android/exoplayer/demo/player/DashRendererBuilder.java index bfd203c5fa..b875ac9ab7 100644 --- a/demo/src/main/java/com/google/android/exoplayer/demo/player/DashRendererBuilder.java +++ b/demo/src/main/java/com/google/android/exoplayer/demo/player/DashRendererBuilder.java @@ -234,8 +234,7 @@ public class DashRendererBuilder implements RendererBuilder, DataSource videoDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent); ChunkSource videoChunkSource = new DashChunkSource(manifestFetcher, videoAdaptationSetIndex, videoRepresentationIndices, videoDataSource, - new AdaptiveEvaluator(bandwidthMeter), LIVE_EDGE_LATENCY_MS, elapsedRealtimeOffset, - mainHandler, player); + new AdaptiveEvaluator(bandwidthMeter), LIVE_EDGE_LATENCY_MS, elapsedRealtimeOffset); ChunkSampleSource videoSampleSource = new ChunkSampleSource(videoChunkSource, loadControl, VIDEO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, true, mainHandler, player, DemoPlayer.TYPE_VIDEO); @@ -259,7 +258,7 @@ public class DashRendererBuilder implements RendererBuilder, format.audioSamplingRate + "Hz)"); audioChunkSourceList.add(new DashChunkSource(manifestFetcher, audioAdaptationSetIndex, new int[] {i}, audioDataSource, audioEvaluator, LIVE_EDGE_LATENCY_MS, - elapsedRealtimeOffset, mainHandler, player)); + elapsedRealtimeOffset)); codecs.add(format.codecs); } @@ -316,8 +315,7 @@ public class DashRendererBuilder implements RendererBuilder, Representation representation = representations.get(j); textTrackNameList.add(representation.format.id); textChunkSourceList.add(new DashChunkSource(manifestFetcher, i, new int[] {j}, - textDataSource, textEvaluator, LIVE_EDGE_LATENCY_MS, elapsedRealtimeOffset, - mainHandler, player)); + textDataSource, textEvaluator, LIVE_EDGE_LATENCY_MS, elapsedRealtimeOffset)); } } } diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/player/DemoPlayer.java b/demo/src/main/java/com/google/android/exoplayer/demo/player/DemoPlayer.java index 8b7c85c1ed..b475915d6a 100644 --- a/demo/src/main/java/com/google/android/exoplayer/demo/player/DemoPlayer.java +++ b/demo/src/main/java/com/google/android/exoplayer/demo/player/DemoPlayer.java @@ -27,7 +27,6 @@ import com.google.android.exoplayer.audio.AudioTrack; import com.google.android.exoplayer.chunk.ChunkSampleSource; import com.google.android.exoplayer.chunk.Format; import com.google.android.exoplayer.chunk.MultiTrackChunkSource; -import com.google.android.exoplayer.dash.DashChunkSource; import com.google.android.exoplayer.drm.StreamingDrmSessionManager; import com.google.android.exoplayer.hls.HlsSampleSource; import com.google.android.exoplayer.metadata.MetadataTrackRenderer; @@ -55,7 +54,7 @@ import java.util.concurrent.CopyOnWriteArrayList; public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventListener, HlsSampleSource.EventListener, DefaultBandwidthMeter.EventListener, MediaCodecVideoTrackRenderer.EventListener, MediaCodecAudioTrackRenderer.EventListener, - StreamingDrmSessionManager.EventListener, DashChunkSource.EventListener, TextRenderer { + StreamingDrmSessionManager.EventListener, TextRenderer { /** * Builds renderers for the player. @@ -516,13 +515,6 @@ public class DemoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventLi processCues(cues); } - @Override - public void onSeekRangeChanged(TimeRange seekRange) { - if (infoListener != null) { - infoListener.onSeekRangeChanged(seekRange); - } - } - /* package */ MetadataTrackRenderer.MetadataRenderer> getId3MetadataRenderer() { return new MetadataTrackRenderer.MetadataRenderer>() { 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 76b40483d8..08068f1a63 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 @@ -18,7 +18,6 @@ package com.google.android.exoplayer.dash; import com.google.android.exoplayer.BehindLiveWindowException; import com.google.android.exoplayer.C; import com.google.android.exoplayer.MediaFormat; -import com.google.android.exoplayer.TimeRange; import com.google.android.exoplayer.TrackInfo; import com.google.android.exoplayer.TrackRenderer; import com.google.android.exoplayer.chunk.Chunk; @@ -51,8 +50,6 @@ import com.google.android.exoplayer.util.ManifestFetcher; import com.google.android.exoplayer.util.MimeTypes; import com.google.android.exoplayer.util.SystemClock; -import android.os.Handler; - import java.io.IOException; import java.util.Arrays; import java.util.Collections; @@ -66,20 +63,6 @@ import java.util.List; */ public class DashChunkSource implements ChunkSource { - /** - * Interface definition for a callback to be notified of {@link DashChunkSource} events. - */ - public interface EventListener { - - /** - * Invoked when the available seek range of the stream has changed. - * - * @param seekRange The range which specifies available content that can be seeked to. - */ - public void onSeekRangeChanged(TimeRange seekRange); - - } - /** * Thrown when an AdaptationSet is missing from the MPD. */ @@ -96,9 +79,6 @@ public class DashChunkSource implements ChunkSource { */ public static final int USE_ALL_TRACKS = -1; - private final Handler eventHandler; - private final EventListener eventListener; - private final TrackInfo trackInfo; private final DataSource dataSource; private final FormatEvaluator formatEvaluator; @@ -121,11 +101,6 @@ public class DashChunkSource implements ChunkSource { private boolean finishedCurrentManifest; private DrmInitData drmInitData; - private TimeRange seekRange; - private long[] seekRangeValues; - private int firstAvailableSegmentNum; - private int lastAvailableSegmentNum; - private boolean lastChunkWasInitialization; private IOException fatalError; @@ -167,7 +142,7 @@ public class DashChunkSource implements ChunkSource { public DashChunkSource(MediaPresentationDescription manifest, int adaptationSetIndex, int[] representationIndices, DataSource dataSource, FormatEvaluator formatEvaluator) { this(null, manifest, adaptationSetIndex, representationIndices, dataSource, formatEvaluator, - new SystemClock(), 0, 0, null, null); + new SystemClock(), 0, 0); } /** @@ -192,24 +167,19 @@ public class DashChunkSource implements ChunkSource { * @param elapsedRealtimeOffsetMs If known, an estimate of the instantaneous difference between * server-side unix time and {@link SystemClock#elapsedRealtime()} in milliseconds, specified * as the server's unix time minus the local elapsed time. It unknown, set to 0. - * @param eventHandler A handler to use when delivering events to {@code EventListener}. May be - * null if delivery of events is not required. - * @param eventListener A listener of events. May be null if delivery of events is not required. */ public DashChunkSource(ManifestFetcher manifestFetcher, int adaptationSetIndex, int[] representationIndices, DataSource dataSource, - FormatEvaluator formatEvaluator, long liveEdgeLatencyMs, long elapsedRealtimeOffsetMs, - Handler eventHandler, EventListener eventListener) { + FormatEvaluator formatEvaluator, long liveEdgeLatencyMs, long elapsedRealtimeOffsetMs) { this(manifestFetcher, manifestFetcher.getManifest(), adaptationSetIndex, representationIndices, dataSource, formatEvaluator, new SystemClock(), liveEdgeLatencyMs * 1000, - elapsedRealtimeOffsetMs * 1000, eventHandler, eventListener); + elapsedRealtimeOffsetMs * 1000); } /* package */ DashChunkSource(ManifestFetcher manifestFetcher, MediaPresentationDescription initialManifest, int adaptationSetIndex, int[] representationIndices, DataSource dataSource, FormatEvaluator formatEvaluator, - Clock systemClock, long liveEdgeLatencyUs, long elapsedRealtimeOffsetUs, - Handler eventHandler, EventListener eventListener) { + Clock systemClock, long liveEdgeLatencyUs, long elapsedRealtimeOffsetUs) { this.manifestFetcher = manifestFetcher; this.currentManifest = initialManifest; this.adaptationSetIndex = adaptationSetIndex; @@ -219,11 +189,8 @@ public class DashChunkSource implements ChunkSource { this.systemClock = systemClock; this.liveEdgeLatencyUs = liveEdgeLatencyUs; this.elapsedRealtimeOffsetUs = elapsedRealtimeOffsetUs; - this.eventHandler = eventHandler; - this.eventListener = eventListener; this.evaluation = new Evaluation(); this.headerBuilder = new StringBuilder(); - this.seekRangeValues = new long[2]; drmInitData = getDrmInitData(currentManifest, adaptationSetIndex); Representation[] representations = getFilteredRepresentations(currentManifest, @@ -262,11 +229,6 @@ public class DashChunkSource implements ChunkSource { return trackInfo; } - // VisibleForTesting - /* package */ TimeRange getSeekRange() { - return seekRange; - } - @Override public void enable() { fatalError = null; @@ -274,16 +236,6 @@ public class DashChunkSource implements ChunkSource { if (manifestFetcher != null) { manifestFetcher.enable(); } - DashSegmentIndex segmentIndex = - representationHolders.get(formats[0].id).representation.getIndex(); - if (segmentIndex == null) { - seekRange = new TimeRange(TimeRange.TYPE_SNAPSHOT, 0, currentManifest.duration * 1000); - notifySeekRangeChanged(seekRange); - } else { - long nowUs = getNowUs(); - updateAvailableSegmentBounds(segmentIndex, nowUs); - updateSeekRange(segmentIndex, nowUs); - } } @Override @@ -292,7 +244,6 @@ public class DashChunkSource implements ChunkSource { if (manifestFetcher != null) { manifestFetcher.disable(); } - seekRange = null; } @Override @@ -334,10 +285,6 @@ public class DashChunkSource implements ChunkSource { } currentManifest = newManifest; finishedCurrentManifest = false; - - long nowUs = getNowUs(); - updateAvailableSegmentBounds(newRepresentations[0].getIndex(), nowUs); - updateSeekRange(newRepresentations[0].getIndex(), nowUs); } // TODO: This is a temporary hack to avoid constantly refreshing the MPD in cases where @@ -407,21 +354,36 @@ public class DashChunkSource implements ChunkSource { return; } + long nowUs; + if (elapsedRealtimeOffsetUs != 0) { + nowUs = (systemClock.elapsedRealtime() * 1000) + elapsedRealtimeOffsetUs; + } else { + 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; - boolean indexUnbounded = segmentIndex.getLastSegmentNum() == DashSegmentIndex.INDEX_UNBOUNDED; if (queue.isEmpty()) { if (currentManifest.dynamic) { - seekRangeValues = seekRange.getCurrentBoundsUs(seekRangeValues); - seekPositionUs = Math.max(seekPositionUs, seekRangeValues[0]); - seekPositionUs = Math.min(seekPositionUs, seekRangeValues[1]); + seekPositionUs = getLiveSeekPosition(nowUs, indexUnbounded, segmentIndex.isExplicit()); } segmentNum = segmentIndex.getSegmentNum(seekPositionUs); - - // if the index is unbounded then the result of getSegmentNum isn't clamped to ensure that - // it doesn't exceed the last available segment. Clamp it here. - if (indexUnbounded) { - segmentNum = Math.min(segmentNum, lastAvailableSegmentNum); - } } else { MediaChunk previous = queue.get(out.queueSize - 1); segmentNum = previous.isLastChunk ? -1 @@ -490,59 +452,6 @@ public class DashChunkSource implements ChunkSource { // Do nothing. } - private void updateAvailableSegmentBounds(DashSegmentIndex segmentIndex, long nowUs) { - int indexFirstAvailableSegmentNum = segmentIndex.getFirstSegmentNum(); - int indexLastAvailableSegmentNum = segmentIndex.getLastSegmentNum(); - if (indexLastAvailableSegmentNum == DashSegmentIndex.INDEX_UNBOUNDED) { - // 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; - indexFirstAvailableSegmentNum = Math.max(indexFirstAvailableSegmentNum, - segmentIndex.getSegmentNum(liveEdgeTimestampUs - bufferDepthUs)); - } - // getSegmentNum(liveEdgeTimestampUs) will not be completed yet, so subtract one to get the - // index of the last completed segment. - indexLastAvailableSegmentNum = segmentIndex.getSegmentNum(liveEdgeTimestampUs) - 1; - } - firstAvailableSegmentNum = indexFirstAvailableSegmentNum; - lastAvailableSegmentNum = indexLastAvailableSegmentNum; - } - - private void updateSeekRange(DashSegmentIndex segmentIndex, long nowUs) { - long earliestSeekPosition = segmentIndex.getTimeUs(firstAvailableSegmentNum); - long latestSeekPosition = segmentIndex.getTimeUs(lastAvailableSegmentNum) - + segmentIndex.getDurationUs(lastAvailableSegmentNum); - if (currentManifest.dynamic) { - long liveEdgeTimestampUs; - if (segmentIndex.getLastSegmentNum() == DashSegmentIndex.INDEX_UNBOUNDED) { - liveEdgeTimestampUs = nowUs - currentManifest.availabilityStartTime * 1000; - } else { - liveEdgeTimestampUs = segmentIndex.getTimeUs(segmentIndex.getLastSegmentNum()) - + segmentIndex.getDurationUs(segmentIndex.getLastSegmentNum()); - if (!segmentIndex.isExplicit()) { - // Some segments defined by the index may not be available yet. Bound the calculated live - // edge based on the elapsed time since the manifest became available. - liveEdgeTimestampUs = Math.min(liveEdgeTimestampUs, - nowUs - currentManifest.availabilityStartTime * 1000); - } - } - - // it's possible that the live edge latency actually puts our latest position before - // the earliest position in the case of a DVR-like stream that's just starting up, so - // in that case just return the earliest position instead - latestSeekPosition = Math.max(earliestSeekPosition, liveEdgeTimestampUs - liveEdgeLatencyUs); - } - - TimeRange newSeekRange = new TimeRange(TimeRange.TYPE_SNAPSHOT, earliestSeekPosition, - latestSeekPosition); - if (seekRange == null || !seekRange.equals(newSeekRange)) { - seekRange = newSeekRange; - notifySeekRangeChanged(seekRange); - } - } - private static boolean mimeTypeIsWebm(String mimeType) { return mimeType.startsWith(MimeTypes.VIDEO_WEBM) || mimeType.startsWith(MimeTypes.AUDIO_WEBM); } @@ -603,12 +512,36 @@ public class DashChunkSource implements ChunkSource { } } - private long getNowUs() { - if (elapsedRealtimeOffsetUs != 0) { - return (systemClock.elapsedRealtime() * 1000) + elapsedRealtimeOffsetUs; + /** + * 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. + * @param indexExplicit True if the segment index is explicit. False otherwise. + * @return The seek position in microseconds. + */ + private long getLiveSeekPosition(long nowUs, boolean indexUnbounded, boolean indexExplicit) { + long liveEdgeTimestampUs; + if (indexUnbounded) { + liveEdgeTimestampUs = nowUs - currentManifest.availabilityStartTime * 1000; } else { - return System.currentTimeMillis() * 1000; + liveEdgeTimestampUs = Long.MIN_VALUE; + for (RepresentationHolder representationHolder : representationHolders.values()) { + DashSegmentIndex segmentIndex = representationHolder.segmentIndex; + int lastSegmentNum = segmentIndex.getLastSegmentNum(); + long indexLiveEdgeTimestampUs = segmentIndex.getTimeUs(lastSegmentNum) + + segmentIndex.getDurationUs(lastSegmentNum); + liveEdgeTimestampUs = Math.max(liveEdgeTimestampUs, indexLiveEdgeTimestampUs); + } + if (!indexExplicit) { + // Some segments defined by the index may not be available yet. Bound the calculated live + // edge based on the elapsed time since the manifest became available. + liveEdgeTimestampUs = Math.min(liveEdgeTimestampUs, + nowUs - currentManifest.availabilityStartTime * 1000); + } } + return liveEdgeTimestampUs - liveEdgeLatencyUs; } private static Representation[] getFilteredRepresentations(MediaPresentationDescription manifest, @@ -659,17 +592,6 @@ public class DashChunkSource implements ChunkSource { Collections.singletonList(period)); } - private void notifySeekRangeChanged(final TimeRange seekRange) { - if (eventHandler != null && eventListener != null) { - eventHandler.post(new Runnable() { - @Override - public void run() { - eventListener.onSeekRangeChanged(seekRange); - } - }); - } - } - private static class RepresentationHolder { public final Representation representation; diff --git a/library/src/test/java/com/google/android/exoplayer/dash/DashChunkSourceTest.java b/library/src/test/java/com/google/android/exoplayer/dash/DashChunkSourceTest.java index 8270620985..1cdfa1efed 100644 --- a/library/src/test/java/com/google/android/exoplayer/dash/DashChunkSourceTest.java +++ b/library/src/test/java/com/google/android/exoplayer/dash/DashChunkSourceTest.java @@ -15,11 +15,9 @@ */ package com.google.android.exoplayer.dash; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import com.google.android.exoplayer.MediaFormat; -import com.google.android.exoplayer.TimeRange; import com.google.android.exoplayer.TrackRenderer; import com.google.android.exoplayer.chunk.ChunkOperationHolder; import com.google.android.exoplayer.chunk.Format; @@ -57,19 +55,12 @@ public class DashChunkSourceTest extends InstrumentationTestCase { private static final FormatEvaluator EVALUATOR = new FixedEvaluator(); - private static final long VOD_DURATION = 30000; - - private static final long LIVE_SEGMENT_COUNT = 5; - private static final long LIVE_SEGMENT_DURATION_MS = 1000; - private static final long LIVE_TIMESHIFT_BUFFER_DEPTH_MS = - LIVE_SEGMENT_COUNT * LIVE_SEGMENT_DURATION_MS; - - private static final long AVAILABILITY_START_TIME_MS = 60000; - private static final long AVAILABILITY_REALTIME_OFFSET_MS = 1000; - private static final long AVAILABILITY_CURRENT_TIME_MS = - AVAILABILITY_START_TIME_MS + LIVE_TIMESHIFT_BUFFER_DEPTH_MS - AVAILABILITY_REALTIME_OFFSET_MS; - - private static final long LIVE_SEEK_BEYOND_EDGE_MS = 60000; + private static final long AVAILABILITY_START_TIME = 0; + private static final long AVAILABILITY_LATENCY = 5000; + private static final long AVAILABILITY_REALTIME_OFFSET = 1000; + private static final long AVAILABILITY_CURRENT_TIME = + AVAILABILITY_START_TIME + AVAILABILITY_LATENCY - AVAILABILITY_REALTIME_OFFSET; + private static final FakeClock AVAILABILITY_CLOCK = new FakeClock(AVAILABILITY_CURRENT_TIME); private static final int TALL_HEIGHT = 200; private static final int WIDE_WIDTH = 400; @@ -99,21 +90,6 @@ public class DashChunkSourceTest extends InstrumentationTestCase { assertEquals(TALL_HEIGHT, out.getMaxVideoHeight()); } - public void testGetSeekRangeOnVod() { - DashChunkSource chunkSource = new DashChunkSource(generateVodMpd(), AdaptationSet.TYPE_VIDEO, - null, null, mock(FormatEvaluator.class)); - chunkSource.enable(); - TimeRange seekRange = chunkSource.getSeekRange(); - - long[] seekRangeValuesUs = seekRange.getCurrentBoundsUs(null); - assertEquals(0, seekRangeValuesUs[0]); - assertEquals(VOD_DURATION * 1000, seekRangeValuesUs[1]); - - long[] seekRangeValuesMs = seekRange.getCurrentBoundsMs(null); - assertEquals(0, seekRangeValuesMs[0]); - assertEquals(VOD_DURATION, seekRangeValuesMs[1]); - } - public void testMaxVideoDimensionsLegacy() { SingleSegmentBase segmentBase1 = new SingleSegmentBase("https://example.com/1.mp4"); Representation representation1 = @@ -131,254 +107,147 @@ public class DashChunkSourceTest extends InstrumentationTestCase { assertEquals(TALL_HEIGHT, out.getMaxVideoHeight()); } - public void testLiveEdgeNoLatency() { - long startTimeMs = 0; - long liveEdgeLatencyMs = 0; - long seekPositionMs = LIVE_SEEK_BEYOND_EDGE_MS; - long seekRangeStartMs = 0; - long seekRangeEndMs = LIVE_SEGMENT_COUNT * LIVE_SEGMENT_DURATION_MS - liveEdgeLatencyMs; - long chunkStartTimeMs = 4000; - long chunkEndTimeMs = 5000; + public void testLiveEdgeNoLatencyWithTimeline() { + DashChunkSource chunkSource = setupLiveEdgeTimelineTest(0L); + List queue = new ArrayList<>(); + ChunkOperationHolder out = new ChunkOperationHolder(); + chunkSource.getChunkOperation(queue, 0, 0, out); - checkLiveEdgeLatencyWithTimeline(startTimeMs, liveEdgeLatencyMs, seekPositionMs, - seekRangeStartMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); - checkLiveEdgeLatencyWithTemplateAndUnlimitedTimeshift(startTimeMs, liveEdgeLatencyMs, - seekPositionMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); - checkLiveEdgeLatencyWithTemplateAndLimitedTimeshift(startTimeMs, liveEdgeLatencyMs, - seekPositionMs, seekRangeStartMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); + assertEquals(4000000L, ((MediaChunk) out.chunk).startTimeUs); + assertEquals(5000000L, ((MediaChunk) out.chunk).endTimeUs); } - public void testLiveEdgeAlmostNoLatency() { - long startTimeMs = 0; - long liveEdgeLatencyMs = 1; - long seekPositionMs = LIVE_SEEK_BEYOND_EDGE_MS; - long seekRangeStartMs = 0; - long seekRangeEndMs = LIVE_SEGMENT_COUNT * LIVE_SEGMENT_DURATION_MS - liveEdgeLatencyMs; - long chunkStartTimeMs = 4000; - long chunkEndTimeMs = 5000; + public void testLiveEdge500msLatencyWithTimeline() { + DashChunkSource chunkSource = setupLiveEdgeTimelineTest(500L); + List queue = new ArrayList<>(); + ChunkOperationHolder out = new ChunkOperationHolder(); + chunkSource.getChunkOperation(queue, 0, 0, out); - checkLiveEdgeLatencyWithTimeline(startTimeMs, liveEdgeLatencyMs, seekPositionMs, - seekRangeStartMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); - checkLiveEdgeLatencyWithTemplateAndUnlimitedTimeshift(startTimeMs, liveEdgeLatencyMs, - seekPositionMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); - checkLiveEdgeLatencyWithTemplateAndLimitedTimeshift(startTimeMs, liveEdgeLatencyMs, - seekPositionMs, seekRangeStartMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); + assertEquals(4000000L, ((MediaChunk) out.chunk).startTimeUs); + assertEquals(5000000L, ((MediaChunk) out.chunk).endTimeUs); } - public void testLiveEdge500msLatency() { - long startTimeMs = 0; - long liveEdgeLatencyMs = 500; - long seekPositionMs = LIVE_SEEK_BEYOND_EDGE_MS; - long seekRangeStartMs = 0; - long seekRangeEndMs = LIVE_SEGMENT_COUNT * LIVE_SEGMENT_DURATION_MS - liveEdgeLatencyMs; - long chunkStartTimeMs = 4000; - long chunkEndTimeMs = 5000; + public void testLiveEdge1000msLatencyWithTimeline() { + DashChunkSource chunkSource = setupLiveEdgeTimelineTest(1000L); + List queue = new ArrayList<>(); + ChunkOperationHolder out = new ChunkOperationHolder(); + chunkSource.getChunkOperation(queue, 0, 0, out); - checkLiveEdgeLatencyWithTimeline(startTimeMs, liveEdgeLatencyMs, seekPositionMs, - seekRangeStartMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); - checkLiveEdgeLatencyWithTemplateAndUnlimitedTimeshift(startTimeMs, liveEdgeLatencyMs, - seekPositionMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); - checkLiveEdgeLatencyWithTemplateAndLimitedTimeshift(startTimeMs, liveEdgeLatencyMs, - seekPositionMs, seekRangeStartMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); + assertEquals(4000000L, ((MediaChunk) out.chunk).startTimeUs); + assertEquals(5000000L, ((MediaChunk) out.chunk).endTimeUs); } - public void testLiveEdge1000msLatency() { - long startTimeMs = 0; - long liveEdgeLatencyMs = 1000; - long seekPositionMs = LIVE_SEEK_BEYOND_EDGE_MS; - long seekRangeStartMs = 0; - long seekRangeEndMs = LIVE_SEGMENT_COUNT * LIVE_SEGMENT_DURATION_MS - liveEdgeLatencyMs; - long chunkStartTimeMs = 4000; - long chunkEndTimeMs = 5000; + public void testLiveEdge1001msLatencyWithTimeline() { + DashChunkSource chunkSource = setupLiveEdgeTimelineTest(1001L); + List queue = new ArrayList<>(); + ChunkOperationHolder out = new ChunkOperationHolder(); + chunkSource.getChunkOperation(queue, 0, 0, out); - checkLiveEdgeLatencyWithTimeline(startTimeMs, liveEdgeLatencyMs, seekPositionMs, - seekRangeStartMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); - checkLiveEdgeLatencyWithTemplateAndUnlimitedTimeshift(startTimeMs, liveEdgeLatencyMs, - seekPositionMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); - checkLiveEdgeLatencyWithTemplateAndLimitedTimeshift(startTimeMs, liveEdgeLatencyMs, - seekPositionMs, seekRangeStartMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); + assertEquals(3000000L, ((MediaChunk) out.chunk).startTimeUs); + assertEquals(4000000L, ((MediaChunk) out.chunk).endTimeUs); } - public void testLiveEdge1001msLatency() { - long startTimeMs = 0; - long liveEdgeLatencyMs = 1001; - long seekPositionMs = LIVE_SEEK_BEYOND_EDGE_MS; - long seekRangeStartMs = 0; - long seekRangeEndMs = LIVE_SEGMENT_COUNT * LIVE_SEGMENT_DURATION_MS - liveEdgeLatencyMs; - long chunkStartTimeMs = 3000; - long chunkEndTimeMs = 4000; + public void testLiveEdge2500msLatencyWithTimeline() { + DashChunkSource chunkSource = setupLiveEdgeTimelineTest(2500L); + List queue = new ArrayList<>(); + ChunkOperationHolder out = new ChunkOperationHolder(); + chunkSource.getChunkOperation(queue, 0, 0, out); - checkLiveEdgeLatencyWithTimeline(startTimeMs, liveEdgeLatencyMs, seekPositionMs, - seekRangeStartMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); - checkLiveEdgeLatencyWithTemplateAndUnlimitedTimeshift(startTimeMs, liveEdgeLatencyMs, - seekPositionMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); - checkLiveEdgeLatencyWithTemplateAndLimitedTimeshift(startTimeMs, liveEdgeLatencyMs, - seekPositionMs, seekRangeStartMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); + assertEquals(2000000L, ((MediaChunk) out.chunk).startTimeUs); + assertEquals(3000000L, ((MediaChunk) out.chunk).endTimeUs); } - public void testLiveEdge2500msLatency() { - long startTimeMs = 0; - long liveEdgeLatencyMs = 2500; - long seekPositionMs = LIVE_SEEK_BEYOND_EDGE_MS; - long seekRangeStartMs = 0; - long seekRangeEndMs = LIVE_SEGMENT_COUNT * LIVE_SEGMENT_DURATION_MS - liveEdgeLatencyMs; - long chunkStartTimeMs = 2000; - long chunkEndTimeMs = 3000; + public void testLiveEdgeVeryHighLatencyWithTimeline() { + DashChunkSource chunkSource = setupLiveEdgeTimelineTest(10000L); + List queue = new ArrayList<>(); + ChunkOperationHolder out = new ChunkOperationHolder(); + chunkSource.getChunkOperation(queue, 0, 0, out); - checkLiveEdgeLatencyWithTimeline(startTimeMs, liveEdgeLatencyMs, seekPositionMs, - seekRangeStartMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); - checkLiveEdgeLatencyWithTemplateAndUnlimitedTimeshift(startTimeMs, liveEdgeLatencyMs, - seekPositionMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); - checkLiveEdgeLatencyWithTemplateAndLimitedTimeshift(startTimeMs, liveEdgeLatencyMs, - seekPositionMs, seekRangeStartMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); + assertEquals(0L, ((MediaChunk) out.chunk).startTimeUs); + assertEquals(1000000L, ((MediaChunk) out.chunk).endTimeUs); } - public void testLiveEdgeVeryHighLatency() { - long startTimeMs = 0; - long liveEdgeLatencyMs = 10000; - long seekPositionMs = LIVE_SEEK_BEYOND_EDGE_MS; - long seekRangeStartMs = 0; - long seekRangeEndMs = 0; - long chunkStartTimeMs = 0; - long chunkEndTimeMs = 1000; + public void testLiveEdgeNoLatencyWithTemplate() { + DashChunkSource chunkSource = setupLiveEdgeTemplateTest(0L); + List queue = new ArrayList<>(); + ChunkOperationHolder out = new ChunkOperationHolder(); + chunkSource.getChunkOperation(queue, 0, 0, out); - checkLiveEdgeLatencyWithTimeline(startTimeMs, liveEdgeLatencyMs, seekPositionMs, - seekRangeStartMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); - checkLiveEdgeLatencyWithTemplateAndUnlimitedTimeshift(startTimeMs, liveEdgeLatencyMs, - seekPositionMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); - checkLiveEdgeLatencyWithTemplateAndLimitedTimeshift(startTimeMs, liveEdgeLatencyMs, - seekPositionMs, seekRangeStartMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); + // this should actually return the "5th" segment, but it currently returns the "6th", which + // doesn't actually exist yet; this will be resolved in a subsequent cl (cl/87518875). + //assertEquals(4000000L, ((MediaChunk) out.chunk).startTimeUs); + //assertEquals(5000000L, ((MediaChunk) out.chunk).endTimeUs); } - public void testLiveEdgeNoLatencyInProgress() { - long startTimeMs = 3000; - long liveEdgeLatencyMs = 0; - long seekPositionMs = LIVE_SEEK_BEYOND_EDGE_MS; - long seekRangeStartMs = 3000; - long seekRangeEndMs = 3000 + LIVE_SEGMENT_COUNT * LIVE_SEGMENT_DURATION_MS - liveEdgeLatencyMs; - long chunkStartTimeMs = 7000; - long chunkEndTimeMs = 8000; + public void testLiveEdgeAlmostNoLatencyWithTemplate() { + DashChunkSource chunkSource = setupLiveEdgeTemplateTest(1L); + List queue = new ArrayList<>(); + ChunkOperationHolder out = new ChunkOperationHolder(); + chunkSource.getChunkOperation(queue, 0, 0, out); - checkLiveEdgeLatencyWithTimeline(startTimeMs, liveEdgeLatencyMs, seekPositionMs, - seekRangeStartMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); - checkLiveEdgeLatencyWithTemplateAndUnlimitedTimeshift(startTimeMs, liveEdgeLatencyMs, - seekPositionMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); - checkLiveEdgeLatencyWithTemplateAndLimitedTimeshift(startTimeMs, liveEdgeLatencyMs, - seekPositionMs, seekRangeStartMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); + assertEquals(4000000L, ((MediaChunk) out.chunk).startTimeUs); + assertEquals(5000000L, ((MediaChunk) out.chunk).endTimeUs); } - public void testLiveEdgeAlmostNoLatencyInProgress() { - long startTimeMs = 3000; - long liveEdgeLatencyMs = 1; - long seekPositionMs = LIVE_SEEK_BEYOND_EDGE_MS; - long seekRangeStartMs = 3000; - long seekRangeEndMs = 3000 + LIVE_SEGMENT_COUNT * LIVE_SEGMENT_DURATION_MS - liveEdgeLatencyMs; - long chunkStartTimeMs = 7000; - long chunkEndTimeMs = 8000; + public void testLiveEdge500msLatencyWithTemplate() { + DashChunkSource chunkSource = setupLiveEdgeTemplateTest(500L); + List queue = new ArrayList<>(); + ChunkOperationHolder out = new ChunkOperationHolder(); + chunkSource.getChunkOperation(queue, 0, 0, out); - checkLiveEdgeLatencyWithTimeline(startTimeMs, liveEdgeLatencyMs, seekPositionMs, - seekRangeStartMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); - checkLiveEdgeLatencyWithTemplateAndUnlimitedTimeshift(startTimeMs, liveEdgeLatencyMs, - seekPositionMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); - checkLiveEdgeLatencyWithTemplateAndLimitedTimeshift(startTimeMs, liveEdgeLatencyMs, - seekPositionMs, seekRangeStartMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); + assertEquals(4000000L, ((MediaChunk) out.chunk).startTimeUs); + assertEquals(5000000L, ((MediaChunk) out.chunk).endTimeUs); } - public void testLiveEdge500msLatencyInProgress() { - long startTimeMs = 3000; - long liveEdgeLatencyMs = 500; - long seekPositionMs = LIVE_SEEK_BEYOND_EDGE_MS; - long seekRangeStartMs = 3000; - long seekRangeEndMs = 3000 + LIVE_SEGMENT_COUNT * LIVE_SEGMENT_DURATION_MS - liveEdgeLatencyMs; - long chunkStartTimeMs = 7000; - long chunkEndTimeMs = 8000; + public void testLiveEdge1000msLatencyWithTemplate() { + DashChunkSource chunkSource = setupLiveEdgeTemplateTest(1000L); + List queue = new ArrayList<>(); + ChunkOperationHolder out = new ChunkOperationHolder(); + chunkSource.getChunkOperation(queue, 0, 0, out); - checkLiveEdgeLatencyWithTimeline(startTimeMs, liveEdgeLatencyMs, seekPositionMs, - seekRangeStartMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); - checkLiveEdgeLatencyWithTemplateAndUnlimitedTimeshift(startTimeMs, liveEdgeLatencyMs, - seekPositionMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); - checkLiveEdgeLatencyWithTemplateAndLimitedTimeshift(startTimeMs, liveEdgeLatencyMs, - seekPositionMs, seekRangeStartMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); + assertEquals(4000000L, ((MediaChunk) out.chunk).startTimeUs); + assertEquals(5000000L, ((MediaChunk) out.chunk).endTimeUs); } - public void testLiveEdge1000msLatencyInProgress() { - long startTimeMs = 3000; - long liveEdgeLatencyMs = 1000; - long seekPositionMs = LIVE_SEEK_BEYOND_EDGE_MS; - long seekRangeStartMs = 3000; - long seekRangeEndMs = 3000 + LIVE_SEGMENT_COUNT * LIVE_SEGMENT_DURATION_MS - liveEdgeLatencyMs; - long chunkStartTimeMs = 7000; - long chunkEndTimeMs = 8000; + public void testLiveEdge1001msLatencyWithTemplate() { + DashChunkSource chunkSource = setupLiveEdgeTemplateTest(1001L); + List queue = new ArrayList<>(); + ChunkOperationHolder out = new ChunkOperationHolder(); + chunkSource.getChunkOperation(queue, 0, 0, out); - checkLiveEdgeLatencyWithTimeline(startTimeMs, liveEdgeLatencyMs, seekPositionMs, - seekRangeStartMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); - checkLiveEdgeLatencyWithTemplateAndUnlimitedTimeshift(startTimeMs, liveEdgeLatencyMs, - seekPositionMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); - checkLiveEdgeLatencyWithTemplateAndLimitedTimeshift(startTimeMs, liveEdgeLatencyMs, - seekPositionMs, seekRangeStartMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); + assertEquals(3000000L, ((MediaChunk) out.chunk).startTimeUs); + assertEquals(4000000L, ((MediaChunk) out.chunk).endTimeUs); } - public void testLiveEdge1001msLatencyInProgress() { - long startTimeMs = 3000; - long liveEdgeLatencyMs = 1001; - long seekPositionMs = LIVE_SEEK_BEYOND_EDGE_MS; - long seekRangeStartMs = 3000; - long seekRangeEndMs = 3000 + LIVE_SEGMENT_COUNT * LIVE_SEGMENT_DURATION_MS - liveEdgeLatencyMs; - long chunkStartTimeMs = 6000; - long chunkEndTimeMs = 7000; + public void testLiveEdge2500msLatencyWithTemplate() { + DashChunkSource chunkSource = setupLiveEdgeTemplateTest(2500L); + List queue = new ArrayList<>(); + ChunkOperationHolder out = new ChunkOperationHolder(); + chunkSource.getChunkOperation(queue, 0, 0, out); - checkLiveEdgeLatencyWithTimeline(startTimeMs, liveEdgeLatencyMs, seekPositionMs, - seekRangeStartMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); - checkLiveEdgeLatencyWithTemplateAndUnlimitedTimeshift(startTimeMs, liveEdgeLatencyMs, - seekPositionMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); - checkLiveEdgeLatencyWithTemplateAndLimitedTimeshift(startTimeMs, liveEdgeLatencyMs, - seekPositionMs, seekRangeStartMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); + assertEquals(2000000L, ((MediaChunk) out.chunk).startTimeUs); + assertEquals(3000000L, ((MediaChunk) out.chunk).endTimeUs); } - public void testLiveEdge2500msLatencyInProgress() { - long startTimeMs = 3000; - long liveEdgeLatencyMs = 2500; - long seekPositionMs = LIVE_SEEK_BEYOND_EDGE_MS; - long seekRangeStartMs = 3000; - long seekRangeEndMs = 3000 + LIVE_SEGMENT_COUNT * LIVE_SEGMENT_DURATION_MS - liveEdgeLatencyMs; - long chunkStartTimeMs = 5000; - long chunkEndTimeMs = 6000; + public void testLiveEdgeVeryHighLatencyWithTemplate() { + DashChunkSource chunkSource = setupLiveEdgeTemplateTest(10000L); + List queue = new ArrayList<>(); + ChunkOperationHolder out = new ChunkOperationHolder(); + chunkSource.getChunkOperation(queue, 0, 0, out); - checkLiveEdgeLatencyWithTimeline(startTimeMs, liveEdgeLatencyMs, seekPositionMs, - seekRangeStartMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); - checkLiveEdgeLatencyWithTemplateAndUnlimitedTimeshift(startTimeMs, liveEdgeLatencyMs, - seekPositionMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); - checkLiveEdgeLatencyWithTemplateAndLimitedTimeshift(startTimeMs, liveEdgeLatencyMs, - seekPositionMs, seekRangeStartMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); - } - - public void testLiveEdgeVeryHighLatencyInProgress() { - long startTimeMs = 3000; - long liveEdgeLatencyMs = 10000; - long seekPositionMs = LIVE_SEEK_BEYOND_EDGE_MS; - long seekRangeStartMs = 3000; - long seekRangeEndMs = 3000; - long chunkStartTimeMs = 3000; - long chunkEndTimeMs = 4000; - - checkLiveEdgeLatencyWithTimeline(startTimeMs, liveEdgeLatencyMs, seekPositionMs, - seekRangeStartMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); - checkLiveEdgeLatencyWithTemplateAndUnlimitedTimeshift(startTimeMs, liveEdgeLatencyMs, - seekPositionMs, 0, 0, 1000); - checkLiveEdgeLatencyWithTemplateAndLimitedTimeshift(startTimeMs, liveEdgeLatencyMs, - seekPositionMs, seekRangeStartMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs); + assertEquals(0L, ((MediaChunk) out.chunk).startTimeUs); + assertEquals(1000000L, ((MediaChunk) out.chunk).endTimeUs); } private static MediaPresentationDescription generateMpd(boolean live, - List representations, boolean limitTimeshiftBuffer) { + List representations) { Representation firstRepresentation = representations.get(0); AdaptationSet adaptationSet = new AdaptationSet(0, AdaptationSet.TYPE_UNKNOWN, representations); Period period = new Period(null, firstRepresentation.periodStartMs, firstRepresentation.periodDurationMs, Collections.singletonList(adaptationSet)); long duration = (live) ? TrackRenderer.UNKNOWN_TIME_US : firstRepresentation.periodDurationMs - firstRepresentation.periodStartMs; - return new MediaPresentationDescription(AVAILABILITY_START_TIME_MS, duration, -1, live, -1, - (limitTimeshiftBuffer) ? LIVE_TIMESHIFT_BUFFER_DEPTH_MS : -1, + return new MediaPresentationDescription(AVAILABILITY_START_TIME, duration, -1, live, -1, -1, null, Collections.singletonList(period)); } @@ -387,126 +256,72 @@ public class DashChunkSourceTest extends InstrumentationTestCase { SingleSegmentBase segmentBase1 = new SingleSegmentBase("https://example.com/1.mp4"); Representation representation1 = - Representation.newInstance(0, VOD_DURATION, null, 0, TALL_VIDEO, segmentBase1); + Representation.newInstance(0, 0, null, 0, TALL_VIDEO, segmentBase1); representations.add(representation1); SingleSegmentBase segmentBase2 = new SingleSegmentBase("https://example.com/2.mp4"); Representation representation2 = - Representation.newInstance(0, VOD_DURATION, null, 0, WIDE_VIDEO, segmentBase2); + Representation.newInstance(0, 0, null, 0, WIDE_VIDEO, segmentBase2); representations.add(representation2); - return generateMpd(false, representations, false); + return generateMpd(false, representations); } - private static MediaPresentationDescription generateLiveMpdWithTimeline(long startTime) { + private static MediaPresentationDescription generateLiveMpdWithTimeline() { List representations = new ArrayList<>(); List segmentTimeline = new ArrayList<>(); + segmentTimeline.add(new SegmentTimelineElement(0L, 1000L)); + segmentTimeline.add(new SegmentTimelineElement(1000L, 1000L)); + segmentTimeline.add(new SegmentTimelineElement(2000L, 1000L)); + segmentTimeline.add(new SegmentTimelineElement(3000L, 1000L)); + segmentTimeline.add(new SegmentTimelineElement(4000L, 1000L)); List mediaSegments = new ArrayList<>(); - long byteStart = 0; - for (int i = 0; i < LIVE_SEGMENT_COUNT; i++) { - segmentTimeline.add(new SegmentTimelineElement(startTime, LIVE_SEGMENT_DURATION_MS)); - mediaSegments.add(new RangedUri("", "", byteStart, 500L)); - startTime += LIVE_SEGMENT_DURATION_MS; - byteStart += 500; - } + mediaSegments.add(new RangedUri("", "", 0L, 500L)); + mediaSegments.add(new RangedUri("", "", 500L, 500L)); + mediaSegments.add(new RangedUri("", "", 1000L, 500L)); + mediaSegments.add(new RangedUri("", "", 1500L, 500L)); + mediaSegments.add(new RangedUri("", "", 2000L, 500L)); MultiSegmentBase segmentBase = new SegmentList(null, 1000, 0, - TrackRenderer.UNKNOWN_TIME_US, 0, TrackRenderer.UNKNOWN_TIME_US, segmentTimeline, + TrackRenderer.UNKNOWN_TIME_US, 1, TrackRenderer.UNKNOWN_TIME_US, segmentTimeline, mediaSegments); - Representation representation = Representation.newInstance(startTime, - TrackRenderer.UNKNOWN_TIME_US, null, 0, REGULAR_VIDEO, segmentBase); + Representation representation = Representation.newInstance(0, TrackRenderer.UNKNOWN_TIME_US, + null, 0, REGULAR_VIDEO, segmentBase); representations.add(representation); - return generateMpd(true, representations, false); + return generateMpd(true, representations); } - private static MediaPresentationDescription generateLiveMpdWithTemplate( - boolean limitTimeshiftBuffer) { + private static MediaPresentationDescription generateLiveMpdWithTemplate() { List representations = new ArrayList<>(); UrlTemplate initializationTemplate = null; UrlTemplate mediaTemplate = UrlTemplate.compile("$RepresentationID$/$Number$"); MultiSegmentBase segmentBase = new SegmentTemplate(null, 1000, 0, - TrackRenderer.UNKNOWN_TIME_US, 0, LIVE_SEGMENT_DURATION_MS, null, + TrackRenderer.UNKNOWN_TIME_US, 1, 1000, null, initializationTemplate, mediaTemplate, "http://www.youtube.com"); Representation representation = Representation.newInstance(0, TrackRenderer.UNKNOWN_TIME_US, null, 0, REGULAR_VIDEO, segmentBase); representations.add(representation); - return generateMpd(true, representations, limitTimeshiftBuffer); + return generateMpd(true, representations); } - private DashChunkSource setupLiveEdgeTimelineTest(long startTime, long liveEdgeLatencyMs) { - MediaPresentationDescription manifest = generateLiveMpdWithTimeline(startTime); + private DashChunkSource setupLiveEdgeTimelineTest(long liveEdgeLatencyMs) { + MediaPresentationDescription manifest = generateLiveMpdWithTimeline(); when(mockManifestFetcher.getManifest()).thenReturn(manifest); - DashChunkSource chunkSource = new DashChunkSource(mockManifestFetcher, manifest, - AdaptationSet.TYPE_VIDEO, null, mockDataSource, EVALUATOR, - new FakeClock(AVAILABILITY_CURRENT_TIME_MS + startTime), liveEdgeLatencyMs * 1000, - AVAILABILITY_REALTIME_OFFSET_MS * 1000, null, null); - chunkSource.enable(); - return chunkSource; + return new DashChunkSource(mockManifestFetcher, manifest, AdaptationSet.TYPE_VIDEO, null, + mockDataSource, EVALUATOR, AVAILABILITY_CLOCK, liveEdgeLatencyMs * 1000, + AVAILABILITY_REALTIME_OFFSET * 1000); } - private DashChunkSource setupLiveEdgeTemplateTest(long startTime, long liveEdgeLatencyMs, - boolean limitTimeshiftBuffer) { - MediaPresentationDescription manifest = generateLiveMpdWithTemplate(limitTimeshiftBuffer); + private DashChunkSource setupLiveEdgeTemplateTest(long liveEdgeLatencyMs) { + MediaPresentationDescription manifest = generateLiveMpdWithTemplate(); when(mockManifestFetcher.getManifest()).thenReturn(manifest); - DashChunkSource chunkSource = new DashChunkSource(mockManifestFetcher, manifest, - AdaptationSet.TYPE_VIDEO, null, mockDataSource, EVALUATOR, - new FakeClock(AVAILABILITY_CURRENT_TIME_MS + startTime), liveEdgeLatencyMs * 1000, - AVAILABILITY_REALTIME_OFFSET_MS * 1000, null, null); - chunkSource.enable(); - return chunkSource; - } - - private void checkLiveEdgeLatencyWithTimeline(long startTimeMs, long liveEdgeLatencyMs, - long seekPositionMs, long seekRangeStartMs, long seekRangeEndMs, long chunkStartTimeMs, - long chunkEndTimeMs) { - DashChunkSource chunkSource = setupLiveEdgeTimelineTest(startTimeMs, liveEdgeLatencyMs); - List queue = new ArrayList<>(); - ChunkOperationHolder out = new ChunkOperationHolder(); - chunkSource.getChunkOperation(queue, seekPositionMs * 1000, 0, out); - TimeRange seekRange = chunkSource.getSeekRange(); - - assertNotNull(out.chunk); - long[] seekRangeValuesUs = seekRange.getCurrentBoundsUs(null); - assertEquals(seekRangeStartMs * 1000, seekRangeValuesUs[0]); - assertEquals(seekRangeEndMs * 1000, seekRangeValuesUs[1]); - assertEquals(chunkStartTimeMs * 1000, ((MediaChunk) out.chunk).startTimeUs); - assertEquals(chunkEndTimeMs * 1000, ((MediaChunk) out.chunk).endTimeUs); - } - - private void checkLiveEdgeLatencyWithTemplate(long startTimeMs, long liveEdgeLatencyMs, - long seekPositionMs, long seekRangeStartMs, long seekRangeEndMs, long chunkStartTimeMs, - long chunkEndTimeMs, boolean limitTimeshiftBuffer) { - DashChunkSource chunkSource = setupLiveEdgeTemplateTest(startTimeMs, liveEdgeLatencyMs, - limitTimeshiftBuffer); - List queue = new ArrayList<>(); - ChunkOperationHolder out = new ChunkOperationHolder(); - chunkSource.getChunkOperation(queue, seekPositionMs * 1000, 0, out); - TimeRange seekRange = chunkSource.getSeekRange(); - - assertNotNull(out.chunk); - long[] seekRangeValuesUs = seekRange.getCurrentBoundsUs(null); - assertEquals(seekRangeStartMs * 1000, seekRangeValuesUs[0]); - assertEquals(seekRangeEndMs * 1000, seekRangeValuesUs[1]); - assertEquals(chunkStartTimeMs * 1000, ((MediaChunk) out.chunk).startTimeUs); - assertEquals(chunkEndTimeMs * 1000, ((MediaChunk) out.chunk).endTimeUs); - } - - private void checkLiveEdgeLatencyWithTemplateAndUnlimitedTimeshift(long startTimeMs, - long liveEdgeLatencyMs, long seekPositionMs, long seekRangeEndMs, - long chunkStartTimeMs, long chunkEndTimeMs) { - checkLiveEdgeLatencyWithTemplate(startTimeMs, liveEdgeLatencyMs, seekPositionMs, 0, - seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs, false); - } - - private void checkLiveEdgeLatencyWithTemplateAndLimitedTimeshift(long startTimeMs, - long liveEdgeLatencyMs, long seekPositionMs, long seekRangeStartMs, long seekRangeEndMs, - long chunkStartTimeMs, long chunkEndTimeMs) { - checkLiveEdgeLatencyWithTemplate(startTimeMs, liveEdgeLatencyMs, seekPositionMs, - seekRangeStartMs, seekRangeEndMs, chunkStartTimeMs, chunkEndTimeMs, true); + return new DashChunkSource(mockManifestFetcher, manifest, AdaptationSet.TYPE_VIDEO, null, + mockDataSource, EVALUATOR, AVAILABILITY_CLOCK, liveEdgeLatencyMs * 1000, + AVAILABILITY_REALTIME_OFFSET * 1000); } }