diff --git a/library/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java b/library/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java index 3a9e738a12..9340c78b75 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java @@ -57,23 +57,31 @@ public final class DashMediaSource implements MediaSource { * The default minimum number of times to retry loading data prior to failing. */ 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 position returned by {@link #getDefaultStartPosition(int)}) should + * be set to {@link DashManifest#suggestedPresentationDelay} if specified by the manifest, or + * {@link #DEFAULT_LIVE_EDGE_OFFSET_FIXED_MS} otherwise. + */ + public static final long DEFAULT_LIVE_EDGE_OFFSET_PREFER_MANIFEST_MS = -1; + /** + * A fixed default live edge offset (the offset subtracted from the live edge when calculating the + * default position returned by {@link #getDefaultStartPosition(int)}). + */ + public static final long DEFAULT_LIVE_EDGE_OFFSET_FIXED_MS = 30000; /** * The interval in milliseconds between invocations of * {@link MediaSource.Listener#onSourceInfoRefreshed(Timeline, Object)} when the source's * {@link Window} is changing dynamically (for example, for incomplete live streams). */ private static final int NOTIFY_MANIFEST_INTERVAL_MS = 5000; - /** - * The offset in milliseconds subtracted from the live edge position when calculating the default - * position returned by {@link #getDefaultStartPosition(int)}. - */ - private static final long LIVE_EDGE_OFFSET_MS = 30000; 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 EventDispatcher eventDispatcher; private final DashManifestParser manifestParser; private final ManifestCallback manifestCallback; @@ -99,16 +107,18 @@ public final class DashMediaSource implements MediaSource { DashChunkSource.Factory chunkSourceFactory, Handler eventHandler, AdaptiveMediaSourceEventListener eventListener) { this(manifestUri, manifestDataSourceFactory, chunkSourceFactory, - DEFAULT_MIN_LOADABLE_RETRY_COUNT, eventHandler, eventListener); + DEFAULT_MIN_LOADABLE_RETRY_COUNT, DEFAULT_LIVE_EDGE_OFFSET_PREFER_MANIFEST_MS, eventHandler, + eventListener); } public DashMediaSource(Uri manifestUri, DataSource.Factory manifestDataSourceFactory, - DashChunkSource.Factory chunkSourceFactory, int minLoadableRetryCount, + DashChunkSource.Factory chunkSourceFactory, int minLoadableRetryCount, long liveEdgeOffsetMs, Handler eventHandler, AdaptiveMediaSourceEventListener eventListener) { this.manifestUri = manifestUri; this.manifestDataSourceFactory = manifestDataSourceFactory; this.chunkSourceFactory = chunkSourceFactory; this.minLoadableRetryCount = minLoadableRetryCount; + this.liveEdgeOffsetMs = liveEdgeOffsetMs; eventDispatcher = new EventDispatcher(eventHandler, eventListener); manifestParser = new DashManifestParser(generateContentId()); manifestCallback = new ManifestCallback(); @@ -159,7 +169,12 @@ public final class DashMediaSource implements MediaSource { if (index == 0 && manifest.dynamic) { // The stream is live, so return a position a position offset from the live edge. int periodIndex = window.endPeriodIndex; - long positionMs = window.endTimeMs - LIVE_EDGE_OFFSET_MS; + long liveEdgeOffsetForManifest = liveEdgeOffsetMs; + if (liveEdgeOffsetForManifest == DEFAULT_LIVE_EDGE_OFFSET_PREFER_MANIFEST_MS) { + liveEdgeOffsetForManifest = manifest.suggestedPresentationDelay != -1 + ? manifest.suggestedPresentationDelay : DEFAULT_LIVE_EDGE_OFFSET_FIXED_MS; + } + long positionMs = window.endTimeMs - liveEdgeOffsetForManifest; while (positionMs < 0 && periodIndex > window.startPeriodIndex) { periodIndex--; positionMs += manifest.getPeriodDurationMs(periodIndex); diff --git a/library/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifest.java b/library/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifest.java index 4a87445a3f..3d8391d20e 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifest.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifest.java @@ -37,6 +37,8 @@ public class DashManifest { public final long timeShiftBufferDepth; + public final long suggestedPresentationDelay; + public final UtcTimingElement utcTiming; public final Uri location; @@ -44,14 +46,16 @@ public class DashManifest { private final List periods; public DashManifest(long availabilityStartTime, long duration, long minBufferTime, - boolean dynamic, long minUpdatePeriod, long timeShiftBufferDepth, UtcTimingElement utcTiming, - Uri location, List periods) { + boolean dynamic, long minUpdatePeriod, long timeShiftBufferDepth, + long suggestedPresentationDelay, UtcTimingElement utcTiming, Uri location, + List periods) { this.availabilityStartTime = availabilityStartTime; this.duration = duration; this.minBufferTime = minBufferTime; this.dynamic = dynamic; this.minUpdatePeriod = minUpdatePeriod; this.timeShiftBufferDepth = timeShiftBufferDepth; + this.suggestedPresentationDelay = suggestedPresentationDelay; this.utcTiming = utcTiming; this.location = location; this.periods = periods == null ? Collections.emptyList() : periods; diff --git a/library/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java b/library/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java index eff4149d5b..0de53be2fa 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java @@ -106,8 +106,10 @@ public class DashManifestParser extends DefaultHandler long minBufferTimeMs = parseDuration(xpp, "minBufferTime", -1); String typeString = xpp.getAttributeValue(null, "type"); boolean dynamic = typeString != null && typeString.equals("dynamic"); - long minUpdateTimeMs = (dynamic) ? parseDuration(xpp, "minimumUpdatePeriod", -1) : -1; - long timeShiftBufferDepthMs = (dynamic) ? parseDuration(xpp, "timeShiftBufferDepth", -1) : -1; + long minUpdateTimeMs = dynamic ? parseDuration(xpp, "minimumUpdatePeriod", -1) : -1; + long timeShiftBufferDepthMs = dynamic ? parseDuration(xpp, "timeShiftBufferDepth", -1) : -1; + long suggestedPresentationDelayMs = dynamic + ? parseDuration(xpp, "suggestedPresentationDelay", -1) : -1; UtcTimingElement utcTiming = null; Uri location = null; @@ -159,14 +161,17 @@ public class DashManifestParser extends DefaultHandler } return buildMediaPresentationDescription(availabilityStartTime, durationMs, minBufferTimeMs, - dynamic, minUpdateTimeMs, timeShiftBufferDepthMs, utcTiming, location, periods); + dynamic, minUpdateTimeMs, timeShiftBufferDepthMs, suggestedPresentationDelayMs, utcTiming, + location, periods); } protected DashManifest buildMediaPresentationDescription(long availabilityStartTime, long durationMs, long minBufferTimeMs, boolean dynamic, long minUpdateTimeMs, - long timeShiftBufferDepthMs, UtcTimingElement utcTiming, Uri location, List periods) { + long timeShiftBufferDepthMs, long suggestedPresentationDelayMs, UtcTimingElement utcTiming, + Uri location, List periods) { return new DashManifest(availabilityStartTime, durationMs, minBufferTimeMs, - dynamic, minUpdateTimeMs, timeShiftBufferDepthMs, utcTiming, location, periods); + dynamic, minUpdateTimeMs, timeShiftBufferDepthMs, suggestedPresentationDelayMs, utcTiming, + location, periods); } protected UtcTimingElement parseUtcTiming(XmlPullParser xpp) { diff --git a/playbacktests/src/main/java/com/google/android/exoplayer2/playbacktests/gts/DashTest.java b/playbacktests/src/main/java/com/google/android/exoplayer2/playbacktests/gts/DashTest.java index 6d0768f84c..8768431173 100644 --- a/playbacktests/src/main/java/com/google/android/exoplayer2/playbacktests/gts/DashTest.java +++ b/playbacktests/src/main/java/com/google/android/exoplayer2/playbacktests/gts/DashTest.java @@ -743,7 +743,8 @@ public final class DashTest extends ActivityInstrumentationTestCase2