From e42786d6f2e0a6ece1c2e26f9e62e2e8638d1ecc Mon Sep 17 00:00:00 2001 From: "Lieblich, Jonathan" Date: Wed, 10 Oct 2018 15:34:29 -0600 Subject: [PATCH] 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) {