From e42786d6f2e0a6ece1c2e26f9e62e2e8638d1ecc Mon Sep 17 00:00:00 2001 From: "Lieblich, Jonathan" Date: Wed, 10 Oct 2018 15:34:29 -0600 Subject: [PATCH 1/5] added parsing for program information --- .../source/dash/manifest/DashManifest.java | 10 ++- .../dash/manifest/DashManifestParser.java | 46 ++++++++++++- .../dash/manifest/ProgramInformation.java | 67 +++++++++++++++++++ library/dash/src/test/assets/sample_mpd_1 | 3 + .../dash/manifest/DashManifestParserTest.java | 12 ++++ .../dash/manifest/DashManifestTest.java | 2 +- 6 files changed, 134 insertions(+), 6 deletions(-) create mode 100644 library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/ProgramInformation.java diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifest.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifest.java index 1fdb137be9..6446909808 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifest.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifest.java @@ -86,12 +86,17 @@ public class DashManifest implements FilterableManifest { */ public final Uri location; + /** + * The ProgramInformation of this manifest. + */ + public final ProgramInformation programInformation; + private final List periods; public DashManifest(long availabilityStartTimeMs, long durationMs, long minBufferTimeMs, boolean dynamic, long minUpdatePeriodMs, long timeShiftBufferDepthMs, long suggestedPresentationDelayMs, long publishTimeMs, UtcTimingElement utcTiming, - Uri location, List periods) { + Uri location, ProgramInformation programInformation, List periods) { this.availabilityStartTimeMs = availabilityStartTimeMs; this.durationMs = durationMs; this.minBufferTimeMs = minBufferTimeMs; @@ -102,6 +107,7 @@ public class DashManifest implements FilterableManifest { this.publishTimeMs = publishTimeMs; this.utcTiming = utcTiming; this.location = location; + this.programInformation = programInformation; this.periods = periods == null ? Collections.emptyList() : periods; } @@ -150,7 +156,7 @@ public class DashManifest implements FilterableManifest { long newDuration = durationMs != C.TIME_UNSET ? durationMs - shiftMs : C.TIME_UNSET; return new DashManifest(availabilityStartTimeMs, newDuration, minBufferTimeMs, dynamic, minUpdatePeriodMs, timeShiftBufferDepthMs, suggestedPresentationDelayMs, publishTimeMs, - utcTiming, location, copyPeriods); + utcTiming, location, programInformation, copyPeriods); } private static ArrayList copyAdaptationSets( 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 153856af8c..ce629f6509 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 @@ -122,6 +122,7 @@ public class DashManifestParser extends DefaultHandler long publishTimeMs = parseDateTime(xpp, "publishTime", C.TIME_UNSET); UtcTimingElement utcTiming = null; Uri location = null; + ProgramInformation programInformation = null; List periods = new ArrayList<>(); long nextPeriodStartMs = dynamic ? C.TIME_UNSET : 0; @@ -138,6 +139,8 @@ public class DashManifestParser extends DefaultHandler utcTiming = parseUtcTiming(xpp); } else if (XmlPullParserUtil.isStartTag(xpp, "Location")) { location = Uri.parse(xpp.nextText()); + } else if (XmlPullParserUtil.isStartTag(xpp, "ProgramInformation")) { + programInformation = parseProgramInformation(xpp); } else if (XmlPullParserUtil.isStartTag(xpp, "Period") && !seenEarlyAccessPeriod) { Pair periodWithDurationMs = parsePeriod(xpp, baseUrl, nextPeriodStartMs); Period period = periodWithDurationMs.first; @@ -173,16 +176,16 @@ public class DashManifestParser extends DefaultHandler return buildMediaPresentationDescription(availabilityStartTime, durationMs, minBufferTimeMs, dynamic, minUpdateTimeMs, timeShiftBufferDepthMs, suggestedPresentationDelayMs, - publishTimeMs, utcTiming, location, periods); + publishTimeMs, utcTiming, location, programInformation, periods); } protected DashManifest buildMediaPresentationDescription(long availabilityStartTime, long durationMs, long minBufferTimeMs, boolean dynamic, long minUpdateTimeMs, long timeShiftBufferDepthMs, long suggestedPresentationDelayMs, long publishTimeMs, - UtcTimingElement utcTiming, Uri location, List periods) { + UtcTimingElement utcTiming, Uri location, ProgramInformation programInformation, List periods) { return new DashManifest(availabilityStartTime, durationMs, minBufferTimeMs, dynamic, minUpdateTimeMs, timeShiftBufferDepthMs, suggestedPresentationDelayMs, - publishTimeMs, utcTiming, location, periods); + publishTimeMs, utcTiming, location, programInformation, periods); } protected UtcTimingElement parseUtcTiming(XmlPullParser xpp) { @@ -978,6 +981,43 @@ public class DashManifestParser extends DefaultHandler return new RangedUri(urlText, rangeStart, rangeLength); } + protected ProgramInformation parseProgramInformation(XmlPullParser xpp) throws IOException, XmlPullParserException { + String title = ""; + String source = ""; + String copyright = ""; + List customEvents = new ArrayList<>(); + do { + xpp.next(); + if (XmlPullParserUtil.isStartTag(xpp, "Title")) { + title = xpp.getText(); + } else if (XmlPullParserUtil.isStartTag(xpp, "Source")) { + source = xpp.getText(); + } else if (XmlPullParserUtil.isStartTag(xpp, "Copyright")) { + copyright = xpp.getText(); + } else { + byte[] customElement = parseCustomElement(xpp, new ByteArrayOutputStream(512)); + if (customElement.length > 0) { + customEvents.add(customElement); + } + } + } while (!XmlPullParserUtil.isEndTag(xpp, "ProgramInformation")); + return new ProgramInformation(title, source, copyright, customEvents); + } + + private byte[] parseCustomElement(XmlPullParser xpp, ByteArrayOutputStream outputStream) throws IOException, XmlPullParserException { + XmlSerializer serializer = Xml.newSerializer(); + serializer.setOutput(outputStream, C.UTF8_NAME); + if (xpp.getEventType() == XmlPullParser.START_TAG) { + serializer.startTag(xpp.getNamespace(), xpp.getName()); + for (int i = 0; i < xpp.getAttributeCount(); i++) { + serializer.attribute(xpp.getAttributeNamespace(i), xpp.getAttributeName(i), xpp.getAttributeValue(i)); + } + serializer.endTag(xpp.getNamespace(), xpp.getName()); + } + serializer.flush(); + return outputStream.toByteArray(); + } + // AudioChannelConfiguration parsing. protected int parseAudioChannelConfiguration(XmlPullParser xpp) diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/ProgramInformation.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/ProgramInformation.java new file mode 100644 index 0000000000..7d162336bc --- /dev/null +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/ProgramInformation.java @@ -0,0 +1,67 @@ +package com.google.android.exoplayer2.source.dash.manifest; + +import java.util.Arrays; +import java.util.List; + +public class ProgramInformation { + /** + * The title for the media presentation. + */ + public final String title; + + /** + * Information about the original source of the media presentation. + */ + public final String source; + + /** + * A copyright statement for the media presentation. + */ + public final String copyright; + + /** + * A list of custom elements. + */ + public final List customEvents; + + public ProgramInformation(String title, String source, String copyright, List customEvents) { + this.title = title; + this.source = source; + this.copyright = copyright; + this.customEvents = customEvents; + } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + (title != null ? title.hashCode() : 0); + result = 31 * result + (source != null ? source.hashCode() : 0); + result = 31 * result + (copyright != null ? copyright.hashCode() : 0); + for (int i = 0; i < customEvents.size(); i++) { + result = 31 * result + Arrays.hashCode(customEvents.get(i)); + } + return result; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof ProgramInformation + && ((ProgramInformation)obj).title.equals(this.title) + && ((ProgramInformation)obj).source.equals(this.source) + && ((ProgramInformation)obj).copyright.equals(this.copyright) + && validateEvents(((ProgramInformation)obj).customEvents); + } + + private boolean validateEvents(List customEvents) { + for (int i = 0; i < customEvents.size() && i < this.customEvents.size(); i++) { + byte[] comparator = customEvents.get(i); + byte[] current = this.customEvents.get(i); + for (int j = 0; j < comparator.length && j < current.length; j++) { + if (current[j] != comparator[j]) { + return false; + } + } + } + return true; + } +} \ No newline at end of file diff --git a/library/dash/src/test/assets/sample_mpd_1 b/library/dash/src/test/assets/sample_mpd_1 index 07bcdd4f50..e75c1234c0 100644 --- a/library/dash/src/test/assets/sample_mpd_1 +++ b/library/dash/src/test/assets/sample_mpd_1 @@ -9,6 +9,9 @@ xmlns="urn:mpeg:DASH:schema:MPD:2011" xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd" yt:earliestMediaSequence="1266404" > + + + list = new ArrayList<>(); + list.add("".getBytes()); + ProgramInformation programInformation = new ProgramInformation("", "", "", list); + assertThat(programInformation).isEqualTo(mpd.programInformation); + } + @Test public void testParseCea608AccessibilityChannel() { assertThat( diff --git a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestTest.java index 94f7a28a73..17a96fded3 100644 --- a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestTest.java +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestTest.java @@ -219,7 +219,7 @@ public class DashManifestTest { private static DashManifest newDashManifest(int duration, Period... periods) { return new DashManifest( - 0, duration, 1, false, 2, 3, 4, 12345, DUMMY_UTC_TIMING, Uri.EMPTY, Arrays.asList(periods)); + 0, duration, 1, false, 2, 3, 4, 12345, DUMMY_UTC_TIMING, Uri.EMPTY, null, Arrays.asList(periods)); } private static Period newPeriod(String id, int startMs, AdaptationSet... adaptationSets) { From ce0b5e4c445b96ca4a62bab722f34a29c844d9e7 Mon Sep 17 00:00:00 2001 From: "Lieblich, Jonathan" Date: Thu, 11 Oct 2018 16:25:21 -0600 Subject: [PATCH 2/5] removed custom element parsing --- .../dash/manifest/DashManifestParser.java | 28 ++------- .../dash/manifest/ProgramInformation.java | 62 +++++-------------- 2 files changed, 21 insertions(+), 69 deletions(-) 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 ce629f6509..1bbcab690b 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 @@ -982,10 +982,9 @@ public class DashManifestParser extends DefaultHandler } protected ProgramInformation parseProgramInformation(XmlPullParser xpp) throws IOException, XmlPullParserException { - String title = ""; - String source = ""; - String copyright = ""; - List customEvents = new ArrayList<>(); + String title = null; + String source = null; + String copyright = null; do { xpp.next(); if (XmlPullParserUtil.isStartTag(xpp, "Title")) { @@ -994,28 +993,9 @@ public class DashManifestParser extends DefaultHandler source = xpp.getText(); } else if (XmlPullParserUtil.isStartTag(xpp, "Copyright")) { copyright = xpp.getText(); - } else { - byte[] customElement = parseCustomElement(xpp, new ByteArrayOutputStream(512)); - if (customElement.length > 0) { - customEvents.add(customElement); - } } } while (!XmlPullParserUtil.isEndTag(xpp, "ProgramInformation")); - return new ProgramInformation(title, source, copyright, customEvents); - } - - private byte[] parseCustomElement(XmlPullParser xpp, ByteArrayOutputStream outputStream) throws IOException, XmlPullParserException { - XmlSerializer serializer = Xml.newSerializer(); - serializer.setOutput(outputStream, C.UTF8_NAME); - if (xpp.getEventType() == XmlPullParser.START_TAG) { - serializer.startTag(xpp.getNamespace(), xpp.getName()); - for (int i = 0; i < xpp.getAttributeCount(); i++) { - serializer.attribute(xpp.getAttributeNamespace(i), xpp.getAttributeName(i), xpp.getAttributeValue(i)); - } - serializer.endTag(xpp.getNamespace(), xpp.getName()); - } - serializer.flush(); - return outputStream.toByteArray(); + return new ProgramInformation(title, source, copyright); } // AudioChannelConfiguration parsing. diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/ProgramInformation.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/ProgramInformation.java index 7d162336bc..faf938e3df 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/ProgramInformation.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/ProgramInformation.java @@ -1,8 +1,20 @@ +/* + * Copyright (C) 2018 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 java.util.Arrays; -import java.util.List; - public class ProgramInformation { /** * The title for the media presentation. @@ -19,49 +31,9 @@ public class ProgramInformation { */ public final String copyright; - /** - * A list of custom elements. - */ - public final List customEvents; - - public ProgramInformation(String title, String source, String copyright, List customEvents) { + public ProgramInformation(String title, String source, String copyright) { this.title = title; this.source = source; this.copyright = copyright; - this.customEvents = customEvents; } - - @Override - public int hashCode() { - int result = 17; - result = 31 * result + (title != null ? title.hashCode() : 0); - result = 31 * result + (source != null ? source.hashCode() : 0); - result = 31 * result + (copyright != null ? copyright.hashCode() : 0); - for (int i = 0; i < customEvents.size(); i++) { - result = 31 * result + Arrays.hashCode(customEvents.get(i)); - } - return result; - } - - @Override - public boolean equals(Object obj) { - return obj instanceof ProgramInformation - && ((ProgramInformation)obj).title.equals(this.title) - && ((ProgramInformation)obj).source.equals(this.source) - && ((ProgramInformation)obj).copyright.equals(this.copyright) - && validateEvents(((ProgramInformation)obj).customEvents); - } - - private boolean validateEvents(List customEvents) { - for (int i = 0; i < customEvents.size() && i < this.customEvents.size(); i++) { - byte[] comparator = customEvents.get(i); - byte[] current = this.customEvents.get(i); - for (int j = 0; j < comparator.length && j < current.length; j++) { - if (current[j] != comparator[j]) { - return false; - } - } - } - return true; - } -} \ No newline at end of file +} From 6cbac152c9da36cddec56d6722666abd1721ba73 Mon Sep 17 00:00:00 2001 From: "Lieblich, Jonathan" Date: Thu, 11 Oct 2018 17:09:00 -0600 Subject: [PATCH 3/5] added attribute parsin --- .../dash/manifest/DashManifestParser.java | 10 +++-- .../dash/manifest/ProgramInformation.java | 40 ++++++++++++++++++- library/dash/src/test/assets/sample_mpd_1 | 7 +++- .../dash/manifest/DashManifestParserTest.java | 5 +-- 4 files changed, 52 insertions(+), 10 deletions(-) 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 1bbcab690b..9938f8564c 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 @@ -985,17 +985,19 @@ public class DashManifestParser extends DefaultHandler String title = null; String source = null; String copyright = null; + String moreInformationURL = parseString(xpp, "moreInformationURL", null); + String lang = parseString(xpp, "lang", null); do { xpp.next(); if (XmlPullParserUtil.isStartTag(xpp, "Title")) { - title = xpp.getText(); + title = xpp.nextText(); } else if (XmlPullParserUtil.isStartTag(xpp, "Source")) { - source = xpp.getText(); + source = xpp.nextText(); } else if (XmlPullParserUtil.isStartTag(xpp, "Copyright")) { - copyright = xpp.getText(); + copyright = xpp.nextText(); } } while (!XmlPullParserUtil.isEndTag(xpp, "ProgramInformation")); - return new ProgramInformation(title, source, copyright); + return new ProgramInformation(title, source, copyright, moreInformationURL, lang); } // AudioChannelConfiguration parsing. diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/ProgramInformation.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/ProgramInformation.java index faf938e3df..d3fc909ef8 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/ProgramInformation.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/ProgramInformation.java @@ -15,6 +15,8 @@ */ package com.google.android.exoplayer2.source.dash.manifest; +import com.google.android.exoplayer2.util.Util; + public class ProgramInformation { /** * The title for the media presentation. @@ -31,9 +33,45 @@ public class ProgramInformation { */ public final String copyright; - public ProgramInformation(String title, String source, String copyright) { + /** + * A URL that provides more information about the media presentation. + */ + public final String moreInformationURL; + + /** + * Declares the language code(s) for this ProgramInformation. + */ + public final String lang; + + public ProgramInformation(String title, String source, String copyright, String moreInformationURL, String lang) { this.title = title; this.source = source; this.copyright = copyright; + this.moreInformationURL = moreInformationURL; + this.lang = lang; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ProgramInformation)) { + return false; + } + ProgramInformation other = (ProgramInformation) obj; + return Util.areEqual(this.title, other.title) + && Util.areEqual(this.source, other.source) + && Util.areEqual(this.copyright, other.copyright) + && Util.areEqual(this.moreInformationURL, other.moreInformationURL) + && Util.areEqual(this.lang, other.lang); + } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + (title != null ? title.hashCode() : 0); + result = 31 * result + (source != null ? source.hashCode() : 0); + result = 31 * result + (copyright != null ? copyright.hashCode() : 0); + result = 31 * result + (moreInformationURL != null ? moreInformationURL.hashCode() : 0); + result = 31 * result + (lang != null ? lang.hashCode() : 0); + return result; } } diff --git a/library/dash/src/test/assets/sample_mpd_1 b/library/dash/src/test/assets/sample_mpd_1 index e75c1234c0..ccd3ab4dd6 100644 --- a/library/dash/src/test/assets/sample_mpd_1 +++ b/library/dash/src/test/assets/sample_mpd_1 @@ -9,8 +9,11 @@ xmlns="urn:mpeg:DASH:schema:MPD:2011" xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd" yt:earliestMediaSequence="1266404" > - - + + MediaTitle + MediaSource + MediaCopyright list = new ArrayList<>(); - list.add("".getBytes()); - ProgramInformation programInformation = new ProgramInformation("", "", "", list); + ProgramInformation programInformation = new ProgramInformation("MediaTitle", "MediaSource", + "MediaCopyright", "www.example.com", "enUs"); assertThat(programInformation).isEqualTo(mpd.programInformation); } From ced302fd83c24258ee43932c68fa3245ea6ac789 Mon Sep 17 00:00:00 2001 From: "Lieblich, Jonathan" Date: Fri, 12 Oct 2018 11:00:17 -0600 Subject: [PATCH 4/5] added javadoc for ProgramInformation --- .../exoplayer2/source/dash/manifest/ProgramInformation.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/ProgramInformation.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/ProgramInformation.java index d3fc909ef8..71b4af3a21 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/ProgramInformation.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/ProgramInformation.java @@ -17,6 +17,9 @@ package com.google.android.exoplayer2.source.dash.manifest; import com.google.android.exoplayer2.util.Util; +/** + * A parsed ProgramInformation element. + */ public class ProgramInformation { /** * The title for the media presentation. From 0af9ccc99090f87a509965805d343a426f439b16 Mon Sep 17 00:00:00 2001 From: "Lieblich, Jonathan" Date: Mon, 15 Oct 2018 15:37:42 -0600 Subject: [PATCH 5/5] added handling of unexpected tags --- .../exoplayer2/source/dash/manifest/DashManifestParser.java | 2 ++ 1 file changed, 2 insertions(+) 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 aacafc5a40..d048e22b33 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 @@ -1015,6 +1015,8 @@ public class DashManifestParser extends DefaultHandler source = xpp.nextText(); } else if (XmlPullParserUtil.isStartTag(xpp, "Copyright")) { copyright = xpp.nextText(); + } else { + maybeSkipTag(xpp); } } while (!XmlPullParserUtil.isEndTag(xpp, "ProgramInformation")); return new ProgramInformation(title, source, copyright, moreInformationURL, lang);