added parsing for program information

This commit is contained in:
Lieblich, Jonathan 2018-10-10 15:34:29 -06:00
parent 7849a5eb52
commit e42786d6f2
6 changed files with 134 additions and 6 deletions

View File

@ -86,12 +86,17 @@ public class DashManifest implements FilterableManifest<DashManifest> {
*/ */
public final Uri location; public final Uri location;
/**
* The ProgramInformation of this manifest.
*/
public final ProgramInformation programInformation;
private final List<Period> periods; private final List<Period> periods;
public DashManifest(long availabilityStartTimeMs, long durationMs, long minBufferTimeMs, public DashManifest(long availabilityStartTimeMs, long durationMs, long minBufferTimeMs,
boolean dynamic, long minUpdatePeriodMs, long timeShiftBufferDepthMs, boolean dynamic, long minUpdatePeriodMs, long timeShiftBufferDepthMs,
long suggestedPresentationDelayMs, long publishTimeMs, UtcTimingElement utcTiming, long suggestedPresentationDelayMs, long publishTimeMs, UtcTimingElement utcTiming,
Uri location, List<Period> periods) { Uri location, ProgramInformation programInformation, List<Period> periods) {
this.availabilityStartTimeMs = availabilityStartTimeMs; this.availabilityStartTimeMs = availabilityStartTimeMs;
this.durationMs = durationMs; this.durationMs = durationMs;
this.minBufferTimeMs = minBufferTimeMs; this.minBufferTimeMs = minBufferTimeMs;
@ -102,6 +107,7 @@ public class DashManifest implements FilterableManifest<DashManifest> {
this.publishTimeMs = publishTimeMs; this.publishTimeMs = publishTimeMs;
this.utcTiming = utcTiming; this.utcTiming = utcTiming;
this.location = location; this.location = location;
this.programInformation = programInformation;
this.periods = periods == null ? Collections.emptyList() : periods; this.periods = periods == null ? Collections.emptyList() : periods;
} }
@ -150,7 +156,7 @@ public class DashManifest implements FilterableManifest<DashManifest> {
long newDuration = durationMs != C.TIME_UNSET ? durationMs - shiftMs : C.TIME_UNSET; long newDuration = durationMs != C.TIME_UNSET ? durationMs - shiftMs : C.TIME_UNSET;
return new DashManifest(availabilityStartTimeMs, newDuration, minBufferTimeMs, dynamic, return new DashManifest(availabilityStartTimeMs, newDuration, minBufferTimeMs, dynamic,
minUpdatePeriodMs, timeShiftBufferDepthMs, suggestedPresentationDelayMs, publishTimeMs, minUpdatePeriodMs, timeShiftBufferDepthMs, suggestedPresentationDelayMs, publishTimeMs,
utcTiming, location, copyPeriods); utcTiming, location, programInformation, copyPeriods);
} }
private static ArrayList<AdaptationSet> copyAdaptationSets( private static ArrayList<AdaptationSet> copyAdaptationSets(

View File

@ -122,6 +122,7 @@ public class DashManifestParser extends DefaultHandler
long publishTimeMs = parseDateTime(xpp, "publishTime", C.TIME_UNSET); long publishTimeMs = parseDateTime(xpp, "publishTime", C.TIME_UNSET);
UtcTimingElement utcTiming = null; UtcTimingElement utcTiming = null;
Uri location = null; Uri location = null;
ProgramInformation programInformation = null;
List<Period> periods = new ArrayList<>(); List<Period> periods = new ArrayList<>();
long nextPeriodStartMs = dynamic ? C.TIME_UNSET : 0; long nextPeriodStartMs = dynamic ? C.TIME_UNSET : 0;
@ -138,6 +139,8 @@ public class DashManifestParser extends DefaultHandler
utcTiming = parseUtcTiming(xpp); utcTiming = parseUtcTiming(xpp);
} else if (XmlPullParserUtil.isStartTag(xpp, "Location")) { } else if (XmlPullParserUtil.isStartTag(xpp, "Location")) {
location = Uri.parse(xpp.nextText()); location = Uri.parse(xpp.nextText());
} else if (XmlPullParserUtil.isStartTag(xpp, "ProgramInformation")) {
programInformation = parseProgramInformation(xpp);
} else if (XmlPullParserUtil.isStartTag(xpp, "Period") && !seenEarlyAccessPeriod) { } else if (XmlPullParserUtil.isStartTag(xpp, "Period") && !seenEarlyAccessPeriod) {
Pair<Period, Long> periodWithDurationMs = parsePeriod(xpp, baseUrl, nextPeriodStartMs); Pair<Period, Long> periodWithDurationMs = parsePeriod(xpp, baseUrl, nextPeriodStartMs);
Period period = periodWithDurationMs.first; Period period = periodWithDurationMs.first;
@ -173,16 +176,16 @@ public class DashManifestParser extends DefaultHandler
return buildMediaPresentationDescription(availabilityStartTime, durationMs, minBufferTimeMs, return buildMediaPresentationDescription(availabilityStartTime, durationMs, minBufferTimeMs,
dynamic, minUpdateTimeMs, timeShiftBufferDepthMs, suggestedPresentationDelayMs, dynamic, minUpdateTimeMs, timeShiftBufferDepthMs, suggestedPresentationDelayMs,
publishTimeMs, utcTiming, location, periods); publishTimeMs, utcTiming, location, programInformation, periods);
} }
protected DashManifest buildMediaPresentationDescription(long availabilityStartTime, protected DashManifest buildMediaPresentationDescription(long availabilityStartTime,
long durationMs, long minBufferTimeMs, boolean dynamic, long minUpdateTimeMs, long durationMs, long minBufferTimeMs, boolean dynamic, long minUpdateTimeMs,
long timeShiftBufferDepthMs, long suggestedPresentationDelayMs, long publishTimeMs, long timeShiftBufferDepthMs, long suggestedPresentationDelayMs, long publishTimeMs,
UtcTimingElement utcTiming, Uri location, List<Period> periods) { UtcTimingElement utcTiming, Uri location, ProgramInformation programInformation, List<Period> periods) {
return new DashManifest(availabilityStartTime, durationMs, minBufferTimeMs, return new DashManifest(availabilityStartTime, durationMs, minBufferTimeMs,
dynamic, minUpdateTimeMs, timeShiftBufferDepthMs, suggestedPresentationDelayMs, dynamic, minUpdateTimeMs, timeShiftBufferDepthMs, suggestedPresentationDelayMs,
publishTimeMs, utcTiming, location, periods); publishTimeMs, utcTiming, location, programInformation, periods);
} }
protected UtcTimingElement parseUtcTiming(XmlPullParser xpp) { protected UtcTimingElement parseUtcTiming(XmlPullParser xpp) {
@ -978,6 +981,43 @@ public class DashManifestParser extends DefaultHandler
return new RangedUri(urlText, rangeStart, rangeLength); return new RangedUri(urlText, rangeStart, rangeLength);
} }
protected ProgramInformation parseProgramInformation(XmlPullParser xpp) throws IOException, XmlPullParserException {
String title = "";
String source = "";
String copyright = "";
List<byte[]> 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. // AudioChannelConfiguration parsing.
protected int parseAudioChannelConfiguration(XmlPullParser xpp) protected int parseAudioChannelConfiguration(XmlPullParser xpp)

View File

@ -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<byte[]> customEvents;
public ProgramInformation(String title, String source, String copyright, List<byte[]> 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<byte[]> 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;
}
}

View File

@ -9,6 +9,9 @@
xmlns="urn:mpeg:DASH:schema:MPD:2011" xmlns="urn:mpeg:DASH:schema:MPD:2011"
xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd" xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd"
yt:earliestMediaSequence="1266404" > yt:earliestMediaSequence="1266404" >
<ProgramInformation>
<scte214:ContentIdentifier type="URN" value="5939026565177792163" />
</ProgramInformation>
<Period start="PT6462826.784S" > <Period start="PT6462826.784S" >
<SegmentList <SegmentList
presentationTimeOffset="34740095" presentationTimeOffset="34740095"

View File

@ -25,6 +25,7 @@ import com.google.android.exoplayer2.testutil.TestUtil;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import org.junit.Test; import org.junit.Test;
@ -152,6 +153,17 @@ public class DashManifestParserTest {
1000000000)); 1000000000));
} }
@Test
public void testParseMediaPresentationDescriptionCanParseProgramInformation() throws IOException {
DashManifestParser parser = new DashManifestParser();
DashManifest mpd = parser.parse(Uri.parse("Https://example.com/test.mpd"),
TestUtil.getInputStream(RuntimeEnvironment.application, SAMPLE_MPD_1));
List<byte[]> list = new ArrayList<>();
list.add("<scte214:ContentIdentifier type=\"URN\" value=\"5939026565177792163\" />".getBytes());
ProgramInformation programInformation = new ProgramInformation("", "", "", list);
assertThat(programInformation).isEqualTo(mpd.programInformation);
}
@Test @Test
public void testParseCea608AccessibilityChannel() { public void testParseCea608AccessibilityChannel() {
assertThat( assertThat(

View File

@ -219,7 +219,7 @@ public class DashManifestTest {
private static DashManifest newDashManifest(int duration, Period... periods) { private static DashManifest newDashManifest(int duration, Period... periods) {
return new DashManifest( 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) { private static Period newPeriod(String id, int startMs, AdaptationSet... adaptationSets) {