diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java index 64ec1adb43..8bf142f397 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java @@ -42,6 +42,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.UUID; import java.util.regex.Matcher; @@ -242,7 +243,7 @@ public class DashManifestParser extends DefaultHandler } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentList")) { segmentBase = parseSegmentList(xpp, null); } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentTemplate")) { - segmentBase = parseSegmentTemplate(xpp, null); + segmentBase = parseSegmentTemplate(xpp, null, Collections.emptyList()); } else { maybeSkipTag(xpp); } @@ -323,6 +324,7 @@ public class DashManifestParser extends DefaultHandler language, roleDescriptors, accessibilityDescriptors, + supplementalProperties, segmentBase); contentType = checkContentTypeConsistency(contentType, getContentType(representationInfo.format)); @@ -332,7 +334,8 @@ public class DashManifestParser extends DefaultHandler } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentList")) { segmentBase = parseSegmentList(xpp, (SegmentList) segmentBase); } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentTemplate")) { - segmentBase = parseSegmentTemplate(xpp, (SegmentTemplate) segmentBase); + segmentBase = + parseSegmentTemplate(xpp, (SegmentTemplate) segmentBase, supplementalProperties); } else if (XmlPullParserUtil.isStartTag(xpp, "InbandEventStream")) { inbandEventStreams.add(parseDescriptor(xpp, "InbandEventStream")); } else if (XmlPullParserUtil.isStartTag(xpp)) { @@ -485,6 +488,7 @@ public class DashManifestParser extends DefaultHandler String adaptationSetLanguage, List adaptationSetRoleDescriptors, List adaptationSetAccessibilityDescriptors, + List adaptationSetSupplementalProperties, SegmentBase segmentBase) throws XmlPullParserException, IOException { String id = xpp.getAttributeValue(null, "id"); @@ -517,7 +521,9 @@ public class DashManifestParser extends DefaultHandler } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentList")) { segmentBase = parseSegmentList(xpp, (SegmentList) segmentBase); } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentTemplate")) { - segmentBase = parseSegmentTemplate(xpp, (SegmentTemplate) segmentBase); + segmentBase = + parseSegmentTemplate( + xpp, (SegmentTemplate) segmentBase, adaptationSetSupplementalProperties); } else if (XmlPullParserUtil.isStartTag(xpp, "ContentProtection")) { Pair contentProtection = parseContentProtection(xpp); if (contentProtection.first != null) { @@ -756,13 +762,19 @@ public class DashManifestParser extends DefaultHandler startNumber, duration, timeline, segments); } - protected SegmentTemplate parseSegmentTemplate(XmlPullParser xpp, SegmentTemplate parent) + protected SegmentTemplate parseSegmentTemplate( + XmlPullParser xpp, + SegmentTemplate parent, + List adaptationSetSupplementalProperties) throws XmlPullParserException, IOException { long timescale = parseLong(xpp, "timescale", parent != null ? parent.timescale : 1); long presentationTimeOffset = parseLong(xpp, "presentationTimeOffset", parent != null ? parent.presentationTimeOffset : 0); long duration = parseLong(xpp, "duration", parent != null ? parent.duration : C.TIME_UNSET); long startNumber = parseLong(xpp, "startNumber", parent != null ? parent.startNumber : 1); + long endNumber = + parseLastSegmentNumberSupplementalProperty(adaptationSetSupplementalProperties); + UrlTemplate mediaTemplate = parseUrlTemplate(xpp, "media", parent != null ? parent.mediaTemplate : null); UrlTemplate initializationTemplate = parseUrlTemplate(xpp, "initialization", @@ -787,8 +799,16 @@ public class DashManifestParser extends DefaultHandler timeline = timeline != null ? timeline : parent.segmentTimeline; } - return buildSegmentTemplate(initialization, timescale, presentationTimeOffset, - startNumber, duration, timeline, initializationTemplate, mediaTemplate); + return buildSegmentTemplate( + initialization, + timescale, + presentationTimeOffset, + startNumber, + endNumber, + duration, + timeline, + initializationTemplate, + mediaTemplate); } protected SegmentTemplate buildSegmentTemplate( @@ -796,12 +816,21 @@ public class DashManifestParser extends DefaultHandler long timescale, long presentationTimeOffset, long startNumber, + long endNumber, long duration, List timeline, UrlTemplate initializationTemplate, UrlTemplate mediaTemplate) { - return new SegmentTemplate(initialization, timescale, presentationTimeOffset, - startNumber, duration, timeline, initializationTemplate, mediaTemplate); + return new SegmentTemplate( + initialization, + timescale, + presentationTimeOffset, + startNumber, + endNumber, + duration, + timeline, + initializationTemplate, + mediaTemplate); } /** @@ -1438,6 +1467,18 @@ public class DashManifestParser extends DefaultHandler } } + protected static long parseLastSegmentNumberSupplementalProperty( + List supplementalProperties) { + for (int i = 0; i < supplementalProperties.size(); i++) { + Descriptor descriptor = supplementalProperties.get(i); + if ("http://dashif.org/guidelines/last-segment-number" + .equalsIgnoreCase(descriptor.schemeIdUri)) { + return Long.parseLong(descriptor.value); + } + } + return C.INDEX_UNSET; + } + /** A parsed Representation element. */ protected static final class RepresentationInfo { diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/SegmentBase.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/SegmentBase.java index f033232590..ba4faafd95 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/SegmentBase.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/SegmentBase.java @@ -277,6 +277,7 @@ public abstract class SegmentBase { /* package */ final UrlTemplate initializationTemplate; /* package */ final UrlTemplate mediaTemplate; + /* package */ final long endNumber; /** * @param initialization A {@link RangedUri} corresponding to initialization data, if such data @@ -286,6 +287,9 @@ public abstract class SegmentBase { * @param presentationTimeOffset The presentation time offset. The value in seconds is the * division of this value and {@code timescale}. * @param startNumber The sequence number of the first segment. + * @param endNumber The sequence number of the last segment as specified by the + * SupplementalProperty with schemeIdUri="http://dashif.org/guidelines/last-segment-number", + * or {@link C#INDEX_UNSET}. * @param duration The duration of each segment in the case of fixed duration segments. The * value in seconds is the division of this value and {@code timescale}. If {@code * segmentTimeline} is non-null then this parameter is ignored. @@ -302,14 +306,21 @@ public abstract class SegmentBase { long timescale, long presentationTimeOffset, long startNumber, + long endNumber, long duration, List segmentTimeline, UrlTemplate initializationTemplate, UrlTemplate mediaTemplate) { - super(initialization, timescale, presentationTimeOffset, startNumber, - duration, segmentTimeline); + super( + initialization, + timescale, + presentationTimeOffset, + startNumber, + duration, + segmentTimeline); this.initializationTemplate = initializationTemplate; this.mediaTemplate = mediaTemplate; + this.endNumber = endNumber; } @Override @@ -340,6 +351,8 @@ public abstract class SegmentBase { public int getSegmentCount(long periodDurationUs) { if (segmentTimeline != null) { return segmentTimeline.size(); + } else if (endNumber != C.INDEX_UNSET) { + return (int) (endNumber - startNumber + 1); } else if (periodDurationUs != C.TIME_UNSET) { long durationUs = (duration * C.MICROS_PER_SECOND) / timescale; return (int) Util.ceilDivide(periodDurationUs, durationUs); @@ -347,7 +360,6 @@ public abstract class SegmentBase { return DashSegmentIndex.INDEX_UNBOUNDED; } } - } /**