Parse multiple BaseURL elements

After this change, multiple BaseURL elements are parsed, but the player still only uses the first BaseURL element appearing in the manifest and its corresponding availabilityTimeOffsetUs.

PiperOrigin-RevId: 380775256
This commit is contained in:
bachinger 2021-06-22 11:54:53 +01:00 committed by Oliver Woodman
parent 341e66ceec
commit b48b618bce
4 changed files with 125 additions and 27 deletions

View file

@ -29,7 +29,7 @@ public final class BaseUrl {
/** The URL. */ /** The URL. */
public final String url; public final String url;
/** The service location. */ /** The service location. */
@Nullable public final String serviceLocation; public final String serviceLocation;
/** The priority. */ /** The priority. */
public final int priority; public final int priority;
/** The weight. */ /** The weight. */
@ -44,7 +44,7 @@ public final class BaseUrl {
} }
/** Creates an instance. */ /** Creates an instance. */
public BaseUrl(String url, @Nullable String serviceLocation, int priority, int weight) { public BaseUrl(String url, String serviceLocation, int priority, int weight) {
this.url = url; this.url = url;
this.serviceLocation = serviceLocation; this.serviceLocation = serviceLocation;
this.priority = priority; this.priority = priority;

View file

@ -43,6 +43,7 @@ import com.google.android.exoplayer2.util.XmlPullParserUtil;
import com.google.common.base.Ascii; import com.google.common.base.Ascii;
import com.google.common.base.Charsets; import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -99,8 +100,9 @@ public class DashManifestParser extends DefaultHandler
xpp.setInput(inputStream, null); xpp.setInput(inputStream, null);
int eventType = xpp.next(); int eventType = xpp.next();
if (eventType != XmlPullParser.START_TAG || !"MPD".equals(xpp.getName())) { if (eventType != XmlPullParser.START_TAG || !"MPD".equals(xpp.getName())) {
throw new ParserException( throw ParserException.createForMalformedManifest(
"inputStream does not contain a valid media presentation description"); "inputStream does not contain a valid media presentation description",
/* cause= */ null);
} }
return parseMediaPresentationDescription(xpp, new BaseUrl(uri.toString())); return parseMediaPresentationDescription(xpp, new BaseUrl(uri.toString()));
} catch (XmlPullParserException e) { } catch (XmlPullParserException e) {
@ -108,8 +110,8 @@ public class DashManifestParser extends DefaultHandler
} }
} }
protected DashManifest parseMediaPresentationDescription(XmlPullParser xpp, BaseUrl baseUrl) protected DashManifest parseMediaPresentationDescription(
throws XmlPullParserException, IOException { XmlPullParser xpp, BaseUrl documentBaseUrl) throws XmlPullParserException, IOException {
long availabilityStartTime = parseDateTime(xpp, "availabilityStartTime", C.TIME_UNSET); long availabilityStartTime = parseDateTime(xpp, "availabilityStartTime", C.TIME_UNSET);
long durationMs = parseDuration(xpp, "mediaPresentationDuration", C.TIME_UNSET); long durationMs = parseDuration(xpp, "mediaPresentationDuration", C.TIME_UNSET);
long minBufferTimeMs = parseDuration(xpp, "minBufferTime", C.TIME_UNSET); long minBufferTimeMs = parseDuration(xpp, "minBufferTime", C.TIME_UNSET);
@ -127,8 +129,10 @@ public class DashManifestParser extends DefaultHandler
Uri location = null; Uri location = null;
ServiceDescriptionElement serviceDescription = null; ServiceDescriptionElement serviceDescription = null;
long baseUrlAvailabilityTimeOffsetUs = dynamic ? 0 : C.TIME_UNSET; long baseUrlAvailabilityTimeOffsetUs = dynamic ? 0 : C.TIME_UNSET;
ArrayList<BaseUrl> parentBaseUrls = Lists.newArrayList(documentBaseUrl);
List<Period> periods = new ArrayList<>(); List<Period> periods = new ArrayList<>();
ArrayList<BaseUrl> baseUrls = new ArrayList<>();
long nextPeriodStartMs = dynamic ? C.TIME_UNSET : 0; long nextPeriodStartMs = dynamic ? C.TIME_UNSET : 0;
boolean seenEarlyAccessPeriod = false; boolean seenEarlyAccessPeriod = false;
boolean seenFirstBaseUrl = false; boolean seenFirstBaseUrl = false;
@ -138,9 +142,9 @@ public class DashManifestParser extends DefaultHandler
if (!seenFirstBaseUrl) { if (!seenFirstBaseUrl) {
baseUrlAvailabilityTimeOffsetUs = baseUrlAvailabilityTimeOffsetUs =
parseAvailabilityTimeOffsetUs(xpp, baseUrlAvailabilityTimeOffsetUs); parseAvailabilityTimeOffsetUs(xpp, baseUrlAvailabilityTimeOffsetUs);
baseUrl = parseBaseUrl(xpp, baseUrl);
seenFirstBaseUrl = true; seenFirstBaseUrl = true;
} }
baseUrls.addAll(parseBaseUrl(xpp, parentBaseUrls));
} else if (XmlPullParserUtil.isStartTag(xpp, "ProgramInformation")) { } else if (XmlPullParserUtil.isStartTag(xpp, "ProgramInformation")) {
programInformation = parseProgramInformation(xpp); programInformation = parseProgramInformation(xpp);
} else if (XmlPullParserUtil.isStartTag(xpp, "UTCTiming")) { } else if (XmlPullParserUtil.isStartTag(xpp, "UTCTiming")) {
@ -153,7 +157,7 @@ public class DashManifestParser extends DefaultHandler
Pair<Period, Long> periodWithDurationMs = Pair<Period, Long> periodWithDurationMs =
parsePeriod( parsePeriod(
xpp, xpp,
baseUrl, !baseUrls.isEmpty() ? baseUrls : parentBaseUrls,
nextPeriodStartMs, nextPeriodStartMs,
baseUrlAvailabilityTimeOffsetUs, baseUrlAvailabilityTimeOffsetUs,
availabilityStartTime, availabilityStartTime,
@ -271,7 +275,7 @@ public class DashManifestParser extends DefaultHandler
protected Pair<Period, Long> parsePeriod( protected Pair<Period, Long> parsePeriod(
XmlPullParser xpp, XmlPullParser xpp,
BaseUrl baseUrl, List<BaseUrl> parentBaseUrls,
long defaultStartMs, long defaultStartMs,
long baseUrlAvailabilityTimeOffsetUs, long baseUrlAvailabilityTimeOffsetUs,
long availabilityStartTimeMs, long availabilityStartTimeMs,
@ -286,6 +290,7 @@ public class DashManifestParser extends DefaultHandler
@Nullable Descriptor assetIdentifier = null; @Nullable Descriptor assetIdentifier = null;
List<AdaptationSet> adaptationSets = new ArrayList<>(); List<AdaptationSet> adaptationSets = new ArrayList<>();
List<EventStream> eventStreams = new ArrayList<>(); List<EventStream> eventStreams = new ArrayList<>();
ArrayList<BaseUrl> baseUrls = new ArrayList<>();
boolean seenFirstBaseUrl = false; boolean seenFirstBaseUrl = false;
long segmentBaseAvailabilityTimeOffsetUs = C.TIME_UNSET; long segmentBaseAvailabilityTimeOffsetUs = C.TIME_UNSET;
do { do {
@ -294,14 +299,14 @@ public class DashManifestParser extends DefaultHandler
if (!seenFirstBaseUrl) { if (!seenFirstBaseUrl) {
baseUrlAvailabilityTimeOffsetUs = baseUrlAvailabilityTimeOffsetUs =
parseAvailabilityTimeOffsetUs(xpp, baseUrlAvailabilityTimeOffsetUs); parseAvailabilityTimeOffsetUs(xpp, baseUrlAvailabilityTimeOffsetUs);
baseUrl = parseBaseUrl(xpp, baseUrl);
seenFirstBaseUrl = true; seenFirstBaseUrl = true;
} }
baseUrls.addAll(parseBaseUrl(xpp, parentBaseUrls));
} else if (XmlPullParserUtil.isStartTag(xpp, "AdaptationSet")) { } else if (XmlPullParserUtil.isStartTag(xpp, "AdaptationSet")) {
adaptationSets.add( adaptationSets.add(
parseAdaptationSet( parseAdaptationSet(
xpp, xpp,
baseUrl, !baseUrls.isEmpty() ? baseUrls : parentBaseUrls,
segmentBase, segmentBase,
durationMs, durationMs,
baseUrlAvailabilityTimeOffsetUs, baseUrlAvailabilityTimeOffsetUs,
@ -361,7 +366,7 @@ public class DashManifestParser extends DefaultHandler
protected AdaptationSet parseAdaptationSet( protected AdaptationSet parseAdaptationSet(
XmlPullParser xpp, XmlPullParser xpp,
BaseUrl baseUrl, List<BaseUrl> parentBaseUrls,
@Nullable SegmentBase segmentBase, @Nullable SegmentBase segmentBase,
long periodDurationMs, long periodDurationMs,
long baseUrlAvailabilityTimeOffsetUs, long baseUrlAvailabilityTimeOffsetUs,
@ -389,6 +394,7 @@ public class DashManifestParser extends DefaultHandler
ArrayList<Descriptor> essentialProperties = new ArrayList<>(); ArrayList<Descriptor> essentialProperties = new ArrayList<>();
ArrayList<Descriptor> supplementalProperties = new ArrayList<>(); ArrayList<Descriptor> supplementalProperties = new ArrayList<>();
List<RepresentationInfo> representationInfos = new ArrayList<>(); List<RepresentationInfo> representationInfos = new ArrayList<>();
ArrayList<BaseUrl> baseUrls = new ArrayList<>();
boolean seenFirstBaseUrl = false; boolean seenFirstBaseUrl = false;
do { do {
@ -397,9 +403,9 @@ public class DashManifestParser extends DefaultHandler
if (!seenFirstBaseUrl) { if (!seenFirstBaseUrl) {
baseUrlAvailabilityTimeOffsetUs = baseUrlAvailabilityTimeOffsetUs =
parseAvailabilityTimeOffsetUs(xpp, baseUrlAvailabilityTimeOffsetUs); parseAvailabilityTimeOffsetUs(xpp, baseUrlAvailabilityTimeOffsetUs);
baseUrl = parseBaseUrl(xpp, baseUrl);
seenFirstBaseUrl = true; seenFirstBaseUrl = true;
} }
baseUrls.addAll(parseBaseUrl(xpp, parentBaseUrls));
} else if (XmlPullParserUtil.isStartTag(xpp, "ContentProtection")) { } else if (XmlPullParserUtil.isStartTag(xpp, "ContentProtection")) {
Pair<String, SchemeData> contentProtection = parseContentProtection(xpp); Pair<String, SchemeData> contentProtection = parseContentProtection(xpp);
if (contentProtection.first != null) { if (contentProtection.first != null) {
@ -425,7 +431,7 @@ public class DashManifestParser extends DefaultHandler
RepresentationInfo representationInfo = RepresentationInfo representationInfo =
parseRepresentation( parseRepresentation(
xpp, xpp,
baseUrl, !baseUrls.isEmpty() ? baseUrls : parentBaseUrls,
mimeType, mimeType,
codecs, codecs,
width, width,
@ -625,7 +631,7 @@ public class DashManifestParser extends DefaultHandler
protected RepresentationInfo parseRepresentation( protected RepresentationInfo parseRepresentation(
XmlPullParser xpp, XmlPullParser xpp,
BaseUrl baseUrl, List<BaseUrl> parentBaseUrls,
@Nullable String adaptationSetMimeType, @Nullable String adaptationSetMimeType,
@Nullable String adaptationSetCodecs, @Nullable String adaptationSetCodecs,
int adaptationSetWidth, int adaptationSetWidth,
@ -661,6 +667,7 @@ public class DashManifestParser extends DefaultHandler
ArrayList<Descriptor> essentialProperties = new ArrayList<>(adaptationSetEssentialProperties); ArrayList<Descriptor> essentialProperties = new ArrayList<>(adaptationSetEssentialProperties);
ArrayList<Descriptor> supplementalProperties = ArrayList<Descriptor> supplementalProperties =
new ArrayList<>(adaptationSetSupplementalProperties); new ArrayList<>(adaptationSetSupplementalProperties);
ArrayList<BaseUrl> baseUrls = new ArrayList<>();
boolean seenFirstBaseUrl = false; boolean seenFirstBaseUrl = false;
do { do {
@ -669,9 +676,9 @@ public class DashManifestParser extends DefaultHandler
if (!seenFirstBaseUrl) { if (!seenFirstBaseUrl) {
baseUrlAvailabilityTimeOffsetUs = baseUrlAvailabilityTimeOffsetUs =
parseAvailabilityTimeOffsetUs(xpp, baseUrlAvailabilityTimeOffsetUs); parseAvailabilityTimeOffsetUs(xpp, baseUrlAvailabilityTimeOffsetUs);
baseUrl = parseBaseUrl(xpp, baseUrl);
seenFirstBaseUrl = true; seenFirstBaseUrl = true;
} }
baseUrls.addAll(parseBaseUrl(xpp, parentBaseUrls));
} else if (XmlPullParserUtil.isStartTag(xpp, "AudioChannelConfiguration")) { } else if (XmlPullParserUtil.isStartTag(xpp, "AudioChannelConfiguration")) {
audioChannels = parseAudioChannelConfiguration(xpp); audioChannels = parseAudioChannelConfiguration(xpp);
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentBase")) { } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentBase")) {
@ -740,7 +747,7 @@ public class DashManifestParser extends DefaultHandler
return new RepresentationInfo( return new RepresentationInfo(
format, format,
baseUrl, !baseUrls.isEmpty() ? baseUrls : parentBaseUrls,
segmentBase, segmentBase,
drmSchemeType, drmSchemeType,
drmSchemeDatas, drmSchemeDatas,
@ -829,7 +836,7 @@ public class DashManifestParser extends DefaultHandler
return Representation.newInstance( return Representation.newInstance(
representationInfo.revisionId, representationInfo.revisionId,
formatBuilder.build(), formatBuilder.build(),
ImmutableList.of(representationInfo.baseUrl), representationInfo.baseUrls,
representationInfo.segmentBase, representationInfo.segmentBase,
inbandEventStreams); inbandEventStreams);
} }
@ -1355,12 +1362,12 @@ public class DashManifestParser extends DefaultHandler
* Parses a BaseURL element. * Parses a BaseURL element.
* *
* @param xpp The parser from which to read. * @param xpp The parser from which to read.
* @param parentBaseUrl A base URL for resolving the parsed URL. * @param parentBaseUrls The parent base URLs for resolving the parsed URLs.
* @throws XmlPullParserException If an error occurs parsing the element. * @throws XmlPullParserException If an error occurs parsing the element.
* @throws IOException If an error occurs reading the element. * @throws IOException If an error occurs reading the element.
* @return The parsed and resolved URL. * @return The list of parsed and resolved URLs.
*/ */
protected BaseUrl parseBaseUrl(XmlPullParser xpp, BaseUrl parentBaseUrl) protected List<BaseUrl> parseBaseUrl(XmlPullParser xpp, List<BaseUrl> parentBaseUrls)
throws XmlPullParserException, IOException { throws XmlPullParserException, IOException {
@Nullable String priorityValue = xpp.getAttributeValue(null, "dvb:priority"); @Nullable String priorityValue = xpp.getAttributeValue(null, "dvb:priority");
int priority = int priority =
@ -1372,13 +1379,21 @@ public class DashManifestParser extends DefaultHandler
if (serviceLocation == null) { if (serviceLocation == null) {
serviceLocation = baseUrl; serviceLocation = baseUrl;
} }
if (!UriUtil.isAbsolute(baseUrl)) { if (UriUtil.isAbsolute(baseUrl)) {
baseUrl = UriUtil.resolve(parentBaseUrl.url, baseUrl); return Lists.newArrayList(new BaseUrl(baseUrl, serviceLocation, priority, weight));
}
List<BaseUrl> baseUrls = new ArrayList<>();
for (int i = 0; i < parentBaseUrls.size(); i++) {
BaseUrl parentBaseUrl = parentBaseUrls.get(i);
priority = parentBaseUrl.priority; priority = parentBaseUrl.priority;
weight = parentBaseUrl.weight; weight = parentBaseUrl.weight;
serviceLocation = parentBaseUrl.serviceLocation; serviceLocation = parentBaseUrl.serviceLocation;
baseUrls.add(
new BaseUrl(
UriUtil.resolve(parentBaseUrl.url, baseUrl), serviceLocation, priority, weight));
} }
return new BaseUrl(baseUrl, serviceLocation, priority, weight); return baseUrls;
} }
/** /**
@ -1882,7 +1897,7 @@ public class DashManifestParser extends DefaultHandler
protected static final class RepresentationInfo { protected static final class RepresentationInfo {
public final Format format; public final Format format;
public final BaseUrl baseUrl; public final ImmutableList<BaseUrl> baseUrls;
public final SegmentBase segmentBase; public final SegmentBase segmentBase;
@Nullable public final String drmSchemeType; @Nullable public final String drmSchemeType;
public final ArrayList<SchemeData> drmSchemeDatas; public final ArrayList<SchemeData> drmSchemeDatas;
@ -1891,14 +1906,14 @@ public class DashManifestParser extends DefaultHandler
public RepresentationInfo( public RepresentationInfo(
Format format, Format format,
BaseUrl baseUrl, List<BaseUrl> baseUrls,
SegmentBase segmentBase, SegmentBase segmentBase,
@Nullable String drmSchemeType, @Nullable String drmSchemeType,
ArrayList<SchemeData> drmSchemeDatas, ArrayList<SchemeData> drmSchemeDatas,
ArrayList<Descriptor> inbandEventStreams, ArrayList<Descriptor> inbandEventStreams,
long revisionId) { long revisionId) {
this.format = format; this.format = format;
this.baseUrl = baseUrl; this.baseUrls = ImmutableList.copyOf(baseUrls);
this.segmentBase = segmentBase; this.segmentBase = segmentBase;
this.drmSchemeType = drmSchemeType; this.drmSchemeType = drmSchemeType;
this.drmSchemeDatas = drmSchemeDatas; this.drmSchemeDatas = drmSchemeDatas;

View file

@ -29,6 +29,7 @@ import com.google.android.exoplayer2.testutil.TestUtil;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import com.google.common.base.Charsets; import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableList;
import java.io.IOException; import java.io.IOException;
import java.io.StringReader; import java.io.StringReader;
import java.util.Collections; import java.util.Collections;
@ -55,6 +56,8 @@ public class DashManifestParserTest {
private static final String SAMPLE_MPD_TRICK_PLAY = "media/mpd/sample_mpd_trick_play"; private static final String SAMPLE_MPD_TRICK_PLAY = "media/mpd/sample_mpd_trick_play";
private static final String SAMPLE_MPD_AVAILABILITY_TIME_OFFSET_BASE_URL = private static final String SAMPLE_MPD_AVAILABILITY_TIME_OFFSET_BASE_URL =
"media/mpd/sample_mpd_availabilityTimeOffset_baseUrl"; "media/mpd/sample_mpd_availabilityTimeOffset_baseUrl";
private static final String SAMPLE_MPD_MULTIPLE_BASE_URLS =
"media/mpd/sample_mpd_multiple_baseUrls";
private static final String SAMPLE_MPD_AVAILABILITY_TIME_OFFSET_SEGMENT_TEMPLATE = private static final String SAMPLE_MPD_AVAILABILITY_TIME_OFFSET_SEGMENT_TEMPLATE =
"media/mpd/sample_mpd_availabilityTimeOffset_segmentTemplate"; "media/mpd/sample_mpd_availabilityTimeOffset_segmentTemplate";
private static final String SAMPLE_MPD_AVAILABILITY_TIME_OFFSET_SEGMENT_LIST = private static final String SAMPLE_MPD_AVAILABILITY_TIME_OFFSET_SEGMENT_LIST =
@ -601,6 +604,54 @@ public class DashManifestParserTest {
.isEqualTo("http://video-foo.com/baseUrl/representation3"); .isEqualTo("http://video-foo.com/baseUrl/representation3");
} }
@Test
public void baseUrl_multipleBaseUrls_correctParsingAndUnfolding() throws IOException {
DashManifestParser parser = new DashManifestParser();
DashManifest manifest =
parser.parse(
Uri.parse("https://example.com/test.mpd"),
TestUtil.getInputStream(
ApplicationProvider.getApplicationContext(), SAMPLE_MPD_MULTIPLE_BASE_URLS));
ImmutableList<BaseUrl> audioBaseUrls =
manifest.getPeriod(0).adaptationSets.get(0).representations.get(0).baseUrls;
assertThat(audioBaseUrls).hasSize(6);
assertThat(audioBaseUrls.get(0).url).endsWith("/baseUrl/a/media/audio");
assertThat(audioBaseUrls.get(1).url).endsWith("/baseUrl/b/media/audio");
assertThat(audioBaseUrls.get(2).url).endsWith("/baseUrl/c/media/audio");
assertThat(audioBaseUrls.get(3).url).endsWith("/baseUrl/a/files/audio");
assertThat(audioBaseUrls.get(4).url).endsWith("/baseUrl/b/files/audio");
assertThat(audioBaseUrls.get(5).url).endsWith("/baseUrl/c/files/audio");
assertThat(audioBaseUrls.get(0).serviceLocation).isEqualTo("a");
assertThat(audioBaseUrls.get(1).serviceLocation).isEqualTo("b");
assertThat(audioBaseUrls.get(2).serviceLocation).isEqualTo("c");
assertThat(audioBaseUrls.get(3).serviceLocation).isEqualTo("a");
assertThat(audioBaseUrls.get(4).serviceLocation).isEqualTo("b");
assertThat(audioBaseUrls.get(5).serviceLocation).isEqualTo("c");
ImmutableList<BaseUrl> videoBaseUrls =
manifest.getPeriod(0).adaptationSets.get(1).representations.get(0).baseUrls;
assertThat(videoBaseUrls).hasSize(7);
assertThat(videoBaseUrls.get(0).url).endsWith("/baseUrl/a/media/video");
assertThat(videoBaseUrls.get(1).url).endsWith("/baseUrl/b/media/video");
assertThat(videoBaseUrls.get(2).url).endsWith("/baseUrl/c/media/video");
assertThat(videoBaseUrls.get(3).url).endsWith("/baseUrl/a/files/video");
assertThat(videoBaseUrls.get(4).url).endsWith("/baseUrl/b/files/video");
assertThat(videoBaseUrls.get(5).url).endsWith("/baseUrl/c/files/video");
assertThat(videoBaseUrls.get(6).url).endsWith("/baseUrl/d/alternative/");
assertThat(videoBaseUrls.get(0).serviceLocation).isEqualTo("a");
assertThat(videoBaseUrls.get(1).serviceLocation).isEqualTo("b");
assertThat(videoBaseUrls.get(2).serviceLocation).isEqualTo("c");
assertThat(videoBaseUrls.get(3).serviceLocation).isEqualTo("a");
assertThat(videoBaseUrls.get(4).serviceLocation).isEqualTo("b");
assertThat(videoBaseUrls.get(5).serviceLocation).isEqualTo("c");
assertThat(videoBaseUrls.get(6).serviceLocation).isEqualTo("d");
ImmutableList<BaseUrl> textBaseUrls =
manifest.getPeriod(0).adaptationSets.get(2).representations.get(0).baseUrls;
assertThat(textBaseUrls).hasSize(1);
assertThat(textBaseUrls.get(0).url).endsWith("/baseUrl/e/text/");
assertThat(textBaseUrls.get(0).serviceLocation).isEqualTo("e");
}
@Test @Test
public void serviceDescriptionElement_allValuesSet() throws IOException { public void serviceDescriptionElement_allValuesSet() throws IOException {
DashManifestParser parser = new DashManifestParser(); DashManifestParser parser = new DashManifestParser();

View file

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="urn:mpeg:DASH:schema:MPD:2011"
xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd"
xmlns:dvb="urn:dvb:dash:dash-extensions:2014-1"
profiles="urn:mpeg:dash:profile:isoff-main:2011"
type="dynamic"
availabilityStartTime="2016-10-14T17:00:17">
<BaseURL serviceLocation="a" dvb:priority="1" dvb:weight="1">http://video.com/baseUrl/a/</BaseURL>
<BaseURL serviceLocation="b" dvb:priority="1" dvb:weight="2">http://video.com/baseUrl/b/</BaseURL>
<BaseURL serviceLocation="c" dvb:priority="1" dvb:weight="3">http://video.com/baseUrl/c/</BaseURL>
<Period start="PT0.000S">
<BaseURL>media/</BaseURL>
<BaseURL>files/</BaseURL>
<AdaptationSet contentType="audio">
<BaseURL>audio</BaseURL>
<SegmentTemplate/>
<Representation/>
</AdaptationSet>
<AdaptationSet contentType="video">
<BaseURL>video</BaseURL>
<BaseURL serviceLocation="d" dvb:priority="1" dvb:weight="4">http://video.com/baseUrl/d/alternative/</BaseURL>
<SegmentTemplate/>
<Representation/>
</AdaptationSet>
<AdaptationSet contentType="text">
<BaseURL serviceLocation="e" dvb:priority="1" dvb:weight="1">http://video.com/baseUrl/e/text/</BaseURL>
<SegmentTemplate/>
<Representation/>
</AdaptationSet>
</Period>
</MPD>