mirror of
https://github.com/androidx/media.git
synced 2025-05-18 13:09:56 +08:00
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:
parent
341e66ceec
commit
b48b618bce
@ -29,7 +29,7 @@ public final class BaseUrl {
|
||||
/** The URL. */
|
||||
public final String url;
|
||||
/** The service location. */
|
||||
@Nullable public final String serviceLocation;
|
||||
public final String serviceLocation;
|
||||
/** The priority. */
|
||||
public final int priority;
|
||||
/** The weight. */
|
||||
@ -44,7 +44,7 @@ public final class BaseUrl {
|
||||
}
|
||||
|
||||
/** 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.serviceLocation = serviceLocation;
|
||||
this.priority = priority;
|
||||
|
@ -43,6 +43,7 @@ import com.google.android.exoplayer2.util.XmlPullParserUtil;
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@ -99,8 +100,9 @@ public class DashManifestParser extends DefaultHandler
|
||||
xpp.setInput(inputStream, null);
|
||||
int eventType = xpp.next();
|
||||
if (eventType != XmlPullParser.START_TAG || !"MPD".equals(xpp.getName())) {
|
||||
throw new ParserException(
|
||||
"inputStream does not contain a valid media presentation description");
|
||||
throw ParserException.createForMalformedManifest(
|
||||
"inputStream does not contain a valid media presentation description",
|
||||
/* cause= */ null);
|
||||
}
|
||||
return parseMediaPresentationDescription(xpp, new BaseUrl(uri.toString()));
|
||||
} catch (XmlPullParserException e) {
|
||||
@ -108,8 +110,8 @@ public class DashManifestParser extends DefaultHandler
|
||||
}
|
||||
}
|
||||
|
||||
protected DashManifest parseMediaPresentationDescription(XmlPullParser xpp, BaseUrl baseUrl)
|
||||
throws XmlPullParserException, IOException {
|
||||
protected DashManifest parseMediaPresentationDescription(
|
||||
XmlPullParser xpp, BaseUrl documentBaseUrl) throws XmlPullParserException, IOException {
|
||||
long availabilityStartTime = parseDateTime(xpp, "availabilityStartTime", C.TIME_UNSET);
|
||||
long durationMs = parseDuration(xpp, "mediaPresentationDuration", C.TIME_UNSET);
|
||||
long minBufferTimeMs = parseDuration(xpp, "minBufferTime", C.TIME_UNSET);
|
||||
@ -127,8 +129,10 @@ public class DashManifestParser extends DefaultHandler
|
||||
Uri location = null;
|
||||
ServiceDescriptionElement serviceDescription = null;
|
||||
long baseUrlAvailabilityTimeOffsetUs = dynamic ? 0 : C.TIME_UNSET;
|
||||
ArrayList<BaseUrl> parentBaseUrls = Lists.newArrayList(documentBaseUrl);
|
||||
|
||||
List<Period> periods = new ArrayList<>();
|
||||
ArrayList<BaseUrl> baseUrls = new ArrayList<>();
|
||||
long nextPeriodStartMs = dynamic ? C.TIME_UNSET : 0;
|
||||
boolean seenEarlyAccessPeriod = false;
|
||||
boolean seenFirstBaseUrl = false;
|
||||
@ -138,9 +142,9 @@ public class DashManifestParser extends DefaultHandler
|
||||
if (!seenFirstBaseUrl) {
|
||||
baseUrlAvailabilityTimeOffsetUs =
|
||||
parseAvailabilityTimeOffsetUs(xpp, baseUrlAvailabilityTimeOffsetUs);
|
||||
baseUrl = parseBaseUrl(xpp, baseUrl);
|
||||
seenFirstBaseUrl = true;
|
||||
}
|
||||
baseUrls.addAll(parseBaseUrl(xpp, parentBaseUrls));
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "ProgramInformation")) {
|
||||
programInformation = parseProgramInformation(xpp);
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "UTCTiming")) {
|
||||
@ -153,7 +157,7 @@ public class DashManifestParser extends DefaultHandler
|
||||
Pair<Period, Long> periodWithDurationMs =
|
||||
parsePeriod(
|
||||
xpp,
|
||||
baseUrl,
|
||||
!baseUrls.isEmpty() ? baseUrls : parentBaseUrls,
|
||||
nextPeriodStartMs,
|
||||
baseUrlAvailabilityTimeOffsetUs,
|
||||
availabilityStartTime,
|
||||
@ -271,7 +275,7 @@ public class DashManifestParser extends DefaultHandler
|
||||
|
||||
protected Pair<Period, Long> parsePeriod(
|
||||
XmlPullParser xpp,
|
||||
BaseUrl baseUrl,
|
||||
List<BaseUrl> parentBaseUrls,
|
||||
long defaultStartMs,
|
||||
long baseUrlAvailabilityTimeOffsetUs,
|
||||
long availabilityStartTimeMs,
|
||||
@ -286,6 +290,7 @@ public class DashManifestParser extends DefaultHandler
|
||||
@Nullable Descriptor assetIdentifier = null;
|
||||
List<AdaptationSet> adaptationSets = new ArrayList<>();
|
||||
List<EventStream> eventStreams = new ArrayList<>();
|
||||
ArrayList<BaseUrl> baseUrls = new ArrayList<>();
|
||||
boolean seenFirstBaseUrl = false;
|
||||
long segmentBaseAvailabilityTimeOffsetUs = C.TIME_UNSET;
|
||||
do {
|
||||
@ -294,14 +299,14 @@ public class DashManifestParser extends DefaultHandler
|
||||
if (!seenFirstBaseUrl) {
|
||||
baseUrlAvailabilityTimeOffsetUs =
|
||||
parseAvailabilityTimeOffsetUs(xpp, baseUrlAvailabilityTimeOffsetUs);
|
||||
baseUrl = parseBaseUrl(xpp, baseUrl);
|
||||
seenFirstBaseUrl = true;
|
||||
}
|
||||
baseUrls.addAll(parseBaseUrl(xpp, parentBaseUrls));
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "AdaptationSet")) {
|
||||
adaptationSets.add(
|
||||
parseAdaptationSet(
|
||||
xpp,
|
||||
baseUrl,
|
||||
!baseUrls.isEmpty() ? baseUrls : parentBaseUrls,
|
||||
segmentBase,
|
||||
durationMs,
|
||||
baseUrlAvailabilityTimeOffsetUs,
|
||||
@ -361,7 +366,7 @@ public class DashManifestParser extends DefaultHandler
|
||||
|
||||
protected AdaptationSet parseAdaptationSet(
|
||||
XmlPullParser xpp,
|
||||
BaseUrl baseUrl,
|
||||
List<BaseUrl> parentBaseUrls,
|
||||
@Nullable SegmentBase segmentBase,
|
||||
long periodDurationMs,
|
||||
long baseUrlAvailabilityTimeOffsetUs,
|
||||
@ -389,6 +394,7 @@ public class DashManifestParser extends DefaultHandler
|
||||
ArrayList<Descriptor> essentialProperties = new ArrayList<>();
|
||||
ArrayList<Descriptor> supplementalProperties = new ArrayList<>();
|
||||
List<RepresentationInfo> representationInfos = new ArrayList<>();
|
||||
ArrayList<BaseUrl> baseUrls = new ArrayList<>();
|
||||
|
||||
boolean seenFirstBaseUrl = false;
|
||||
do {
|
||||
@ -397,9 +403,9 @@ public class DashManifestParser extends DefaultHandler
|
||||
if (!seenFirstBaseUrl) {
|
||||
baseUrlAvailabilityTimeOffsetUs =
|
||||
parseAvailabilityTimeOffsetUs(xpp, baseUrlAvailabilityTimeOffsetUs);
|
||||
baseUrl = parseBaseUrl(xpp, baseUrl);
|
||||
seenFirstBaseUrl = true;
|
||||
}
|
||||
baseUrls.addAll(parseBaseUrl(xpp, parentBaseUrls));
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "ContentProtection")) {
|
||||
Pair<String, SchemeData> contentProtection = parseContentProtection(xpp);
|
||||
if (contentProtection.first != null) {
|
||||
@ -425,7 +431,7 @@ public class DashManifestParser extends DefaultHandler
|
||||
RepresentationInfo representationInfo =
|
||||
parseRepresentation(
|
||||
xpp,
|
||||
baseUrl,
|
||||
!baseUrls.isEmpty() ? baseUrls : parentBaseUrls,
|
||||
mimeType,
|
||||
codecs,
|
||||
width,
|
||||
@ -625,7 +631,7 @@ public class DashManifestParser extends DefaultHandler
|
||||
|
||||
protected RepresentationInfo parseRepresentation(
|
||||
XmlPullParser xpp,
|
||||
BaseUrl baseUrl,
|
||||
List<BaseUrl> parentBaseUrls,
|
||||
@Nullable String adaptationSetMimeType,
|
||||
@Nullable String adaptationSetCodecs,
|
||||
int adaptationSetWidth,
|
||||
@ -661,6 +667,7 @@ public class DashManifestParser extends DefaultHandler
|
||||
ArrayList<Descriptor> essentialProperties = new ArrayList<>(adaptationSetEssentialProperties);
|
||||
ArrayList<Descriptor> supplementalProperties =
|
||||
new ArrayList<>(adaptationSetSupplementalProperties);
|
||||
ArrayList<BaseUrl> baseUrls = new ArrayList<>();
|
||||
|
||||
boolean seenFirstBaseUrl = false;
|
||||
do {
|
||||
@ -669,9 +676,9 @@ public class DashManifestParser extends DefaultHandler
|
||||
if (!seenFirstBaseUrl) {
|
||||
baseUrlAvailabilityTimeOffsetUs =
|
||||
parseAvailabilityTimeOffsetUs(xpp, baseUrlAvailabilityTimeOffsetUs);
|
||||
baseUrl = parseBaseUrl(xpp, baseUrl);
|
||||
seenFirstBaseUrl = true;
|
||||
}
|
||||
baseUrls.addAll(parseBaseUrl(xpp, parentBaseUrls));
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "AudioChannelConfiguration")) {
|
||||
audioChannels = parseAudioChannelConfiguration(xpp);
|
||||
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentBase")) {
|
||||
@ -740,7 +747,7 @@ public class DashManifestParser extends DefaultHandler
|
||||
|
||||
return new RepresentationInfo(
|
||||
format,
|
||||
baseUrl,
|
||||
!baseUrls.isEmpty() ? baseUrls : parentBaseUrls,
|
||||
segmentBase,
|
||||
drmSchemeType,
|
||||
drmSchemeDatas,
|
||||
@ -829,7 +836,7 @@ public class DashManifestParser extends DefaultHandler
|
||||
return Representation.newInstance(
|
||||
representationInfo.revisionId,
|
||||
formatBuilder.build(),
|
||||
ImmutableList.of(representationInfo.baseUrl),
|
||||
representationInfo.baseUrls,
|
||||
representationInfo.segmentBase,
|
||||
inbandEventStreams);
|
||||
}
|
||||
@ -1355,12 +1362,12 @@ public class DashManifestParser extends DefaultHandler
|
||||
* Parses a BaseURL element.
|
||||
*
|
||||
* @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 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 {
|
||||
@Nullable String priorityValue = xpp.getAttributeValue(null, "dvb:priority");
|
||||
int priority =
|
||||
@ -1372,13 +1379,21 @@ public class DashManifestParser extends DefaultHandler
|
||||
if (serviceLocation == null) {
|
||||
serviceLocation = baseUrl;
|
||||
}
|
||||
if (!UriUtil.isAbsolute(baseUrl)) {
|
||||
baseUrl = UriUtil.resolve(parentBaseUrl.url, baseUrl);
|
||||
if (UriUtil.isAbsolute(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;
|
||||
weight = parentBaseUrl.weight;
|
||||
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 {
|
||||
|
||||
public final Format format;
|
||||
public final BaseUrl baseUrl;
|
||||
public final ImmutableList<BaseUrl> baseUrls;
|
||||
public final SegmentBase segmentBase;
|
||||
@Nullable public final String drmSchemeType;
|
||||
public final ArrayList<SchemeData> drmSchemeDatas;
|
||||
@ -1891,14 +1906,14 @@ public class DashManifestParser extends DefaultHandler
|
||||
|
||||
public RepresentationInfo(
|
||||
Format format,
|
||||
BaseUrl baseUrl,
|
||||
List<BaseUrl> baseUrls,
|
||||
SegmentBase segmentBase,
|
||||
@Nullable String drmSchemeType,
|
||||
ArrayList<SchemeData> drmSchemeDatas,
|
||||
ArrayList<Descriptor> inbandEventStreams,
|
||||
long revisionId) {
|
||||
this.format = format;
|
||||
this.baseUrl = baseUrl;
|
||||
this.baseUrls = ImmutableList.copyOf(baseUrls);
|
||||
this.segmentBase = segmentBase;
|
||||
this.drmSchemeType = drmSchemeType;
|
||||
this.drmSchemeDatas = drmSchemeDatas;
|
||||
|
@ -29,6 +29,7 @@ import com.google.android.exoplayer2.testutil.TestUtil;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
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_AVAILABILITY_TIME_OFFSET_BASE_URL =
|
||||
"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 =
|
||||
"media/mpd/sample_mpd_availabilityTimeOffset_segmentTemplate";
|
||||
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");
|
||||
}
|
||||
|
||||
@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
|
||||
public void serviceDescriptionElement_allValuesSet() throws IOException {
|
||||
DashManifestParser parser = new DashManifestParser();
|
||||
|
32
testdata/src/test/assets/media/mpd/sample_mpd_multiple_baseUrls
vendored
Normal file
32
testdata/src/test/assets/media/mpd/sample_mpd_multiple_baseUrls
vendored
Normal 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>
|
Loading…
x
Reference in New Issue
Block a user