diff --git a/library/src/androidTest/java/com/google/android/exoplayer2/drm/OfflineLicenseHelperTest.java b/library/src/androidTest/java/com/google/android/exoplayer2/drm/OfflineLicenseHelperTest.java index 0342e37bd6..c7ebb22d9a 100644 --- a/library/src/androidTest/java/com/google/android/exoplayer2/drm/OfflineLicenseHelperTest.java +++ b/library/src/androidTest/java/com/google/android/exoplayer2/drm/OfflineLicenseHelperTest.java @@ -27,14 +27,12 @@ import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.drm.DrmInitData.SchemeData; import com.google.android.exoplayer2.source.dash.manifest.AdaptationSet; import com.google.android.exoplayer2.source.dash.manifest.DashManifest; -import com.google.android.exoplayer2.source.dash.manifest.InbandEventStream; import com.google.android.exoplayer2.source.dash.manifest.Period; import com.google.android.exoplayer2.source.dash.manifest.Representation; import com.google.android.exoplayer2.source.dash.manifest.SegmentBase.SingleSegmentBase; import com.google.android.exoplayer2.testutil.TestUtil; import com.google.android.exoplayer2.upstream.HttpDataSource; import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import org.mockito.Mock; @@ -205,18 +203,17 @@ public class OfflineLicenseHelperTest extends InstrumentationTestCase { private static DashManifest newDashManifestWithAllElements() { return newDashManifest(newPeriods(newAdaptationSets(newRepresentations(newDrmInitData())))); } - + private static DashManifest newDashManifest(Period... periods) { return new DashManifest(0, 0, 0, false, 0, 0, 0, null, null, Arrays.asList(periods)); } - + private static Period newPeriods(AdaptationSet... adaptationSets) { return new Period("", 0, Arrays.asList(adaptationSets)); } private static AdaptationSet newAdaptationSets(Representation... representations) { - return new AdaptationSet(0, C.TRACK_TYPE_VIDEO, Arrays.asList(representations), - Collections.emptyList()); + return new AdaptationSet(0, C.TRACK_TYPE_VIDEO, Arrays.asList(representations)); } private static Representation newRepresentations(DrmInitData drmInitData) { @@ -225,7 +222,7 @@ public class OfflineLicenseHelperTest extends InstrumentationTestCase { } private static DrmInitData newDrmInitData() { - return new DrmInitData(new SchemeData(C.WIDEVINE_UUID, "mimeType", + return new DrmInitData(new SchemeData(C.WIDEVINE_UUID, "mimeType", new byte[]{1, 4, 7, 0, 3, 6})); } diff --git a/library/src/main/java/com/google/android/exoplayer2/source/dash/manifest/AdaptationSet.java b/library/src/main/java/com/google/android/exoplayer2/source/dash/manifest/AdaptationSet.java index 30649dcbe2..c4a4a4446b 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/dash/manifest/AdaptationSet.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/dash/manifest/AdaptationSet.java @@ -45,26 +45,17 @@ public class AdaptationSet { */ public final List representations; - /** - * The {@link InbandEventStream}s contained by all {@link Representation}s in the adaptation set. - */ - public final List inbandEventStreams; - /** * @param id A non-negative identifier for the adaptation set that's unique in the scope of its * containing period, or {@link #ID_UNSET} if not specified. * @param type The type of the adaptation set. One of the {@link com.google.android.exoplayer2.C} * {@code TRACK_TYPE_*} constants. * @param representations The {@link Representation}s in the adaptation set. - * @param inbandEventStreams The {@link InbandEventStream}s contained by all - * {@link Representation}s in the adaptation set. */ - public AdaptationSet(int id, int type, List representations, - List inbandEventStreams) { + public AdaptationSet(int id, int type, List representations) { this.id = id; this.type = type; this.representations = Collections.unmodifiableList(representations); - this.inbandEventStreams = Collections.unmodifiableList(inbandEventStreams); } } 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 8bbf8f6ccf..a9dc0a8665 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 @@ -240,9 +240,8 @@ public class DashManifestParser extends DefaultHandler String language = xpp.getAttributeValue(null, "lang"); int accessibilityChannel = Format.NO_VALUE; ArrayList drmSchemeDatas = new ArrayList<>(); + ArrayList inbandEventStreams = new ArrayList<>(); List representationInfos = new ArrayList<>(); - List adaptationSetInbandEventStreams = new ArrayList<>(); - List commonRepresentationInbandEventStreams = null; @C.SelectionFlags int selectionFlags = 0; boolean seenFirstBaseUrl = false; @@ -274,22 +273,6 @@ public class DashManifestParser extends DefaultHandler contentType = checkContentTypeConsistency(contentType, getContentType(representationInfo.format)); representationInfos.add(representationInfo); - // Initialize or update InbandEventStream elements defined in all child Representations. - List inbandEventStreams = representationInfo.inbandEventStreams; - if (commonRepresentationInbandEventStreams == null) { - // Initialize with the elements defined in this representation. - commonRepresentationInbandEventStreams = new ArrayList<>(inbandEventStreams); - } else { - // Remove elements that are not also defined in this representation. - for (int i = commonRepresentationInbandEventStreams.size() - 1; i >= 0; i--) { - InbandEventStream inbandEventStream = commonRepresentationInbandEventStreams.get(i); - if (!inbandEventStreams.contains(inbandEventStream)) { - Log.w(TAG, "Ignoring InbandEventStream element not defined on all Representations: " - + inbandEventStream); - commonRepresentationInbandEventStreams.remove(i); - } - } - } } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentBase")) { segmentBase = parseSegmentBase(xpp, (SingleSegmentBase) segmentBase); } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentList")) { @@ -297,33 +280,25 @@ public class DashManifestParser extends DefaultHandler } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentTemplate")) { segmentBase = parseSegmentTemplate(xpp, (SegmentTemplate) segmentBase); } else if (XmlPullParserUtil.isStartTag(xpp, "InbandEventStream")) { - adaptationSetInbandEventStreams.add(parseInbandEventStream(xpp)); + inbandEventStreams.add(parseInbandEventStream(xpp)); } else if (XmlPullParserUtil.isStartTag(xpp)) { parseAdaptationSetChild(xpp); } } while (!XmlPullParserUtil.isEndTag(xpp, "AdaptationSet")); - // Pull up InbandEventStream elements defined in all child Representations. - for (int i = 0; i < commonRepresentationInbandEventStreams.size(); i++) { - InbandEventStream inbandEventStream = commonRepresentationInbandEventStreams.get(i); - if (!adaptationSetInbandEventStreams.contains(inbandEventStream)) { - adaptationSetInbandEventStreams.add(inbandEventStream); - } - } - // Build the representations. List representations = new ArrayList<>(representationInfos.size()); for (int i = 0; i < representationInfos.size(); i++) { representations.add(buildRepresentation(representationInfos.get(i), contentId, - drmSchemeDatas)); + drmSchemeDatas, inbandEventStreams)); } - return buildAdaptationSet(id, contentType, representations, adaptationSetInbandEventStreams); + return buildAdaptationSet(id, contentType, representations); } protected AdaptationSet buildAdaptationSet(int id, int contentType, - List representations, List inbandEventStreams) { - return new AdaptationSet(id, contentType, representations, inbandEventStreams); + List representations) { + return new AdaptationSet(id, contentType, representations); } protected int parseContentType(XmlPullParser xpp) { @@ -510,15 +485,18 @@ public class DashManifestParser extends DefaultHandler } protected Representation buildRepresentation(RepresentationInfo representationInfo, - String contentId, ArrayList extraDrmSchemeDatas) { + String contentId, ArrayList extraDrmSchemeDatas, + ArrayList extraInbandEventStreams) { Format format = representationInfo.format; ArrayList drmSchemeDatas = representationInfo.drmSchemeDatas; drmSchemeDatas.addAll(extraDrmSchemeDatas); if (!drmSchemeDatas.isEmpty()) { format = format.copyWithDrmInitData(new DrmInitData(drmSchemeDatas)); } + ArrayList inbandEventStremas = representationInfo.inbandEventStreams; + inbandEventStremas.addAll(extraInbandEventStreams); return Representation.newInstance(contentId, Representation.REVISION_ID_DEFAULT, format, - representationInfo.baseUrl, representationInfo.segmentBase); + representationInfo.baseUrl, representationInfo.segmentBase, inbandEventStremas); } // SegmentBase, SegmentList and SegmentTemplate parsing. diff --git a/library/src/main/java/com/google/android/exoplayer2/source/dash/manifest/Representation.java b/library/src/main/java/com/google/android/exoplayer2/source/dash/manifest/Representation.java index f52727c1a8..cdf84f5f71 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/dash/manifest/Representation.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/dash/manifest/Representation.java @@ -21,6 +21,8 @@ import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.source.dash.DashSegmentIndex; import com.google.android.exoplayer2.source.dash.manifest.SegmentBase.MultiSegmentBase; import com.google.android.exoplayer2.source.dash.manifest.SegmentBase.SingleSegmentBase; +import java.util.Collections; +import java.util.List; /** * A DASH representation. @@ -60,6 +62,10 @@ public abstract class Representation { * The offset of the presentation timestamps in the media stream relative to media time. */ public final long presentationTimeOffsetUs; + /** + * The {@link InbandEventStream}s in the representation. Never null, but may be empty. + */ + public final List inbandEventStreams; private final RangedUri initializationUri; @@ -78,6 +84,23 @@ public abstract class Representation { return newInstance(contentId, revisionId, format, baseUrl, segmentBase, null); } + /** + * Constructs a new instance. + * + * @param contentId Identifies the piece of content to which this representation belongs. + * @param revisionId Identifies the revision of the content. + * @param format The format of the representation. + * @param baseUrl The base URL. + * @param segmentBase A segment base element for the representation. + * @param inbandEventStreams The {@link InbandEventStream}s in the representation. May be null. + * @return The constructed instance. + */ + public static Representation newInstance(String contentId, long revisionId, Format format, + String baseUrl, SegmentBase segmentBase, List inbandEventStreams) { + return newInstance(contentId, revisionId, format, baseUrl, segmentBase, inbandEventStreams, + null); + } + /** * Constructs a new instance. * @@ -86,18 +109,20 @@ public abstract class Representation { * @param format The format of the representation. * @param baseUrl The base URL of the representation. * @param segmentBase A segment base element for the representation. + * @param inbandEventStreams The {@link InbandEventStream}s in the representation. May be null. * @param customCacheKey A custom value to be returned from {@link #getCacheKey()}, or null. This * parameter is ignored if {@code segmentBase} consists of multiple segments. * @return The constructed instance. */ public static Representation newInstance(String contentId, long revisionId, Format format, - String baseUrl, SegmentBase segmentBase, String customCacheKey) { + String baseUrl, SegmentBase segmentBase, List inbandEventStreams, + String customCacheKey) { if (segmentBase instanceof SingleSegmentBase) { return new SingleSegmentRepresentation(contentId, revisionId, format, baseUrl, - (SingleSegmentBase) segmentBase, customCacheKey, C.LENGTH_UNSET); + (SingleSegmentBase) segmentBase, inbandEventStreams, customCacheKey, C.LENGTH_UNSET); } else if (segmentBase instanceof MultiSegmentBase) { return new MultiSegmentRepresentation(contentId, revisionId, format, baseUrl, - (MultiSegmentBase) segmentBase); + (MultiSegmentBase) segmentBase, inbandEventStreams); } else { throw new IllegalArgumentException("segmentBase must be of type SingleSegmentBase or " + "MultiSegmentBase"); @@ -105,11 +130,14 @@ public abstract class Representation { } private Representation(String contentId, long revisionId, Format format, String baseUrl, - SegmentBase segmentBase) { + SegmentBase segmentBase, List inbandEventStreams) { this.contentId = contentId; this.revisionId = revisionId; this.format = format; this.baseUrl = baseUrl; + this.inbandEventStreams = inbandEventStreams == null + ? Collections.emptyList() + : Collections.unmodifiableList(inbandEventStreams); initializationUri = segmentBase.getInitialization(this); presentationTimeOffsetUs = segmentBase.getPresentationTimeOffsetUs(); } @@ -167,18 +195,20 @@ public abstract class Representation { * @param initializationEnd The offset of the last byte of initialization data. * @param indexStart The offset of the first byte of index data. * @param indexEnd The offset of the last byte of index data. + * @param inbandEventStreams The {@link InbandEventStream}s in the representation. May be null. * @param customCacheKey A custom value to be returned from {@link #getCacheKey()}, or null. * @param contentLength The content length, or {@link C#LENGTH_UNSET} if unknown. */ public static SingleSegmentRepresentation newInstance(String contentId, long revisionId, Format format, String uri, long initializationStart, long initializationEnd, - long indexStart, long indexEnd, String customCacheKey, long contentLength) { + long indexStart, long indexEnd, List inbandEventStreams, + String customCacheKey, long contentLength) { RangedUri rangedUri = new RangedUri(null, initializationStart, initializationEnd - initializationStart + 1); SingleSegmentBase segmentBase = new SingleSegmentBase(rangedUri, 1, 0, indexStart, indexEnd - indexStart + 1); return new SingleSegmentRepresentation(contentId, revisionId, - format, uri, segmentBase, customCacheKey, contentLength); + format, uri, segmentBase, inbandEventStreams, customCacheKey, contentLength); } /** @@ -187,12 +217,14 @@ public abstract class Representation { * @param format The format of the representation. * @param baseUrl The base URL of the representation. * @param segmentBase The segment base underlying the representation. + * @param inbandEventStreams The {@link InbandEventStream}s in the representation. May be null. * @param customCacheKey A custom value to be returned from {@link #getCacheKey()}, or null. * @param contentLength The content length, or {@link C#LENGTH_UNSET} if unknown. */ public SingleSegmentRepresentation(String contentId, long revisionId, Format format, - String baseUrl, SingleSegmentBase segmentBase, String customCacheKey, long contentLength) { - super(contentId, revisionId, format, baseUrl, segmentBase); + String baseUrl, SingleSegmentBase segmentBase, List inbandEventStreams, + String customCacheKey, long contentLength) { + super(contentId, revisionId, format, baseUrl, segmentBase, inbandEventStreams); this.uri = Uri.parse(baseUrl); this.indexUri = segmentBase.getIndex(); this.cacheKey = customCacheKey != null ? customCacheKey @@ -235,10 +267,11 @@ public abstract class Representation { * @param format The format of the representation. * @param baseUrl The base URL of the representation. * @param segmentBase The segment base underlying the representation. + * @param inbandEventStreams The {@link InbandEventStream}s in the representation. May be null. */ public MultiSegmentRepresentation(String contentId, long revisionId, Format format, - String baseUrl, MultiSegmentBase segmentBase) { - super(contentId, revisionId, format, baseUrl, segmentBase); + String baseUrl, MultiSegmentBase segmentBase, List inbandEventStreams) { + super(contentId, revisionId, format, baseUrl, segmentBase, inbandEventStreams); this.segmentBase = segmentBase; }