From 44d6b1a2710965225943d941cb3c4150f4fc82a9 Mon Sep 17 00:00:00 2001 From: olly Date: Wed, 4 Jan 2017 07:57:13 -0800 Subject: [PATCH] Add some initial plumbing for DASH EMSG support Issue: #2176 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=143554094 --- .../source/dash/manifest/AdaptationSet.java | 34 +++++++++- .../dash/manifest/DashManifestParser.java | 64 +++++++++++++++++-- .../dash/manifest/InbandEventStream.java | 51 +++++++++++++++ 3 files changed, 141 insertions(+), 8 deletions(-) create mode 100644 library/src/main/java/com/google/android/exoplayer2/source/dash/manifest/InbandEventStream.java 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 44da52f52c..30649dcbe2 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 @@ -23,18 +23,48 @@ import java.util.List; */ public class AdaptationSet { - public static final int UNSET_ID = -1; + /** + * Value of {@link #id} indicating no value is set.= + */ + public static final int ID_UNSET = -1; + /** + * 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. + */ public final int id; + /** + * The type of the adaptation set. One of the {@link com.google.android.exoplayer2.C} + * {@code TRACK_TYPE_*} constants. + */ public final int type; + /** + * The {@link Representation}s in the adaptation set. + */ public final List representations; - public AdaptationSet(int id, int type, 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) { 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 7e2ce0de1d..51a7531b10 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 @@ -227,7 +227,7 @@ public class DashManifestParser extends DefaultHandler protected AdaptationSet parseAdaptationSet(XmlPullParser xpp, String baseUrl, SegmentBase segmentBase) throws XmlPullParserException, IOException { - int id = parseInt(xpp, "id", AdaptationSet.UNSET_ID); + int id = parseInt(xpp, "id", AdaptationSet.ID_UNSET); int contentType = parseContentType(xpp); String mimeType = xpp.getAttributeValue(null, "mimeType"); @@ -241,6 +241,8 @@ public class DashManifestParser extends DefaultHandler int accessibilityChannel = Format.NO_VALUE; ArrayList drmSchemeDatas = new ArrayList<>(); List representationInfos = new ArrayList<>(); + List adaptationSetInbandEventStreams = new ArrayList<>(); + List commonRepresentationInbandEventStreams = null; boolean seenFirstBaseUrl = false; do { @@ -265,6 +267,22 @@ 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, "AudioChannelConfiguration")) { audioChannels = parseAudioChannelConfiguration(xpp); } else if (XmlPullParserUtil.isStartTag(xpp, "Accessibility")) { @@ -275,23 +293,34 @@ public class DashManifestParser extends DefaultHandler segmentBase = parseSegmentList(xpp, (SegmentList) segmentBase); } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentTemplate")) { segmentBase = parseSegmentTemplate(xpp, (SegmentTemplate) segmentBase); + } else if (XmlPullParserUtil.isStartTag(xpp, "InbandEventStream")) { + adaptationSetInbandEventStreams.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)); } - return buildAdaptationSet(id, contentType, representations); + return buildAdaptationSet(id, contentType, representations, adaptationSetInbandEventStreams); } protected AdaptationSet buildAdaptationSet(int id, int contentType, - List representations) { - return new AdaptationSet(id, contentType, representations); + List representations, List inbandEventStreams) { + return new AdaptationSet(id, contentType, representations, inbandEventStreams); } protected int parseContentType(XmlPullParser xpp) { @@ -355,6 +384,24 @@ public class DashManifestParser extends DefaultHandler } } + /** + * Parses an InbandEventStream element. + * + * @param xpp The parser from which to read. + * @throws XmlPullParserException If an error occurs parsing the element. + * @throws IOException If an error occurs reading the element. + * @return {@link InbandEventStream} parsed from the element. + */ + protected InbandEventStream parseInbandEventStream(XmlPullParser xpp) + throws XmlPullParserException, IOException { + String schemeIdUri = parseString(xpp, "schemeIdUri", null); + String value = parseString(xpp, "value", null); + do { + xpp.next(); + } while (!XmlPullParserUtil.isEndTag(xpp, "InbandEventStream")); + return new InbandEventStream(schemeIdUri, value); + } + /** * Parses children of AdaptationSet elements not specifically parsed elsewhere. * @@ -386,6 +433,7 @@ public class DashManifestParser extends DefaultHandler int audioChannels = adaptationSetAudioChannels; int audioSamplingRate = parseInt(xpp, "audioSamplingRate", adaptationSetAudioSamplingRate); ArrayList drmSchemeDatas = new ArrayList<>(); + ArrayList inbandEventStreams = new ArrayList<>(); boolean seenFirstBaseUrl = false; do { @@ -408,6 +456,8 @@ public class DashManifestParser extends DefaultHandler if (contentProtection != null) { drmSchemeDatas.add(contentProtection); } + } else if (XmlPullParserUtil.isStartTag(xpp, "InbandEventStream")) { + inbandEventStreams.add(parseInbandEventStream(xpp)); } } while (!XmlPullParserUtil.isEndTag(xpp, "Representation")); @@ -416,7 +466,7 @@ public class DashManifestParser extends DefaultHandler codecs); segmentBase = segmentBase != null ? segmentBase : new SingleSegmentBase(); - return new RepresentationInfo(format, baseUrl, segmentBase, drmSchemeDatas); + return new RepresentationInfo(format, baseUrl, segmentBase, drmSchemeDatas, inbandEventStreams); } protected Format buildFormat(String id, String containerMimeType, int width, int height, @@ -850,13 +900,15 @@ public class DashManifestParser extends DefaultHandler public final String baseUrl; public final SegmentBase segmentBase; public final ArrayList drmSchemeDatas; + public final ArrayList inbandEventStreams; public RepresentationInfo(Format format, String baseUrl, SegmentBase segmentBase, - ArrayList drmSchemeDatas) { + ArrayList drmSchemeDatas, ArrayList inbandEventStreams) { this.format = format; this.baseUrl = baseUrl; this.segmentBase = segmentBase; this.drmSchemeDatas = drmSchemeDatas; + this.inbandEventStreams = inbandEventStreams; } } diff --git a/library/src/main/java/com/google/android/exoplayer2/source/dash/manifest/InbandEventStream.java b/library/src/main/java/com/google/android/exoplayer2/source/dash/manifest/InbandEventStream.java new file mode 100644 index 0000000000..2f24603598 --- /dev/null +++ b/library/src/main/java/com/google/android/exoplayer2/source/dash/manifest/InbandEventStream.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer2.source.dash.manifest; + +import com.google.android.exoplayer2.util.Util; + +/** + * Represents a DASH in-band event stream. + */ +public class InbandEventStream { + + public final String schemeIdUri; + public final String value; + + public InbandEventStream(String schemeIdUri, String value) { + this.schemeIdUri = schemeIdUri; + this.value = value; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + InbandEventStream other = (InbandEventStream) obj; + return Util.areEqual(schemeIdUri, other.schemeIdUri) && Util.areEqual(value, other.value); + } + + @Override + public int hashCode() { + return 31 * (schemeIdUri != null ? schemeIdUri.hashCode() : 0) + + (value != null ? value.hashCode() : 0); + } + +}