Inherit parent properties for manifests with dvb profile only

Issue: google/ExoPlayer#9856
PiperOrigin-RevId: 421842579
This commit is contained in:
bachinger 2022-01-14 17:18:17 +00:00 committed by Ian Baker
parent 4ab10484ad
commit c6e5ace310
11 changed files with 182 additions and 35 deletions

View File

@ -68,7 +68,9 @@ public final class BaseUrlExclusionList {
public void exclude(BaseUrl baseUrlToExclude, long exclusionDurationMs) {
long excludeUntilMs = SystemClock.elapsedRealtime() + exclusionDurationMs;
addExclusion(baseUrlToExclude.serviceLocation, excludeUntilMs, excludedServiceLocations);
addExclusion(baseUrlToExclude.priority, excludeUntilMs, excludedPriorities);
if (baseUrlToExclude.priority != BaseUrl.PRIORITY_UNSET) {
addExclusion(baseUrlToExclude.priority, excludeUntilMs, excludedPriorities);
}
}
/**

View File

@ -23,10 +23,12 @@ import com.google.common.base.Objects;
@UnstableApi
public final class BaseUrl {
/** The default priority. */
public static final int DEFAULT_PRIORITY = 1;
/** The default weight. */
public static final int DEFAULT_WEIGHT = 1;
/** The default priority. */
public static final int DEFAULT_DVB_PRIORITY = 1;
/** Constant representing an unset priority in a manifest that does not declare a DVB profile. */
public static final int PRIORITY_UNSET = Integer.MIN_VALUE;
/** The URL. */
public final String url;
@ -38,11 +40,11 @@ public final class BaseUrl {
public final int weight;
/**
* Creates an instance with {@link #DEFAULT_PRIORITY default priority}, {@link #DEFAULT_WEIGHT
* Creates an instance with {@link #PRIORITY_UNSET an unset priority}, {@link #DEFAULT_WEIGHT
* default weight} and using the URL as the service location.
*/
public BaseUrl(String url) {
this(url, /* serviceLocation= */ url, DEFAULT_PRIORITY, DEFAULT_WEIGHT);
this(url, /* serviceLocation= */ url, PRIORITY_UNSET, DEFAULT_WEIGHT);
}
/** Creates an instance. */

View File

@ -15,6 +15,10 @@
*/
package androidx.media3.exoplayer.dash.manifest;
import static androidx.media3.exoplayer.dash.manifest.BaseUrl.DEFAULT_DVB_PRIORITY;
import static androidx.media3.exoplayer.dash.manifest.BaseUrl.DEFAULT_WEIGHT;
import static androidx.media3.exoplayer.dash.manifest.BaseUrl.PRIORITY_UNSET;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Base64;
@ -105,14 +109,16 @@ public class DashManifestParser extends DefaultHandler
"inputStream does not contain a valid media presentation description",
/* cause= */ null);
}
return parseMediaPresentationDescription(xpp, new BaseUrl(uri.toString()));
return parseMediaPresentationDescription(xpp, uri);
} catch (XmlPullParserException e) {
throw ParserException.createForMalformedManifest(/* message= */ null, /* cause= */ e);
}
}
protected DashManifest parseMediaPresentationDescription(
XmlPullParser xpp, BaseUrl documentBaseUrl) throws XmlPullParserException, IOException {
protected DashManifest parseMediaPresentationDescription(XmlPullParser xpp, Uri documentBaseUri)
throws XmlPullParserException, IOException {
boolean dvbProfileDeclared =
isDvbProfileDeclared(parseProfiles(xpp, "profiles", new String[0]));
long availabilityStartTime = parseDateTime(xpp, "availabilityStartTime", C.TIME_UNSET);
long durationMs = parseDuration(xpp, "mediaPresentationDuration", C.TIME_UNSET);
long minBufferTimeMs = parseDuration(xpp, "minBufferTime", C.TIME_UNSET);
@ -130,6 +136,12 @@ public class DashManifestParser extends DefaultHandler
Uri location = null;
ServiceDescriptionElement serviceDescription = null;
long baseUrlAvailabilityTimeOffsetUs = dynamic ? 0 : C.TIME_UNSET;
BaseUrl documentBaseUrl =
new BaseUrl(
documentBaseUri.toString(),
/* serviceLocation= */ documentBaseUri.toString(),
dvbProfileDeclared ? DEFAULT_DVB_PRIORITY : PRIORITY_UNSET,
DEFAULT_WEIGHT);
ArrayList<BaseUrl> parentBaseUrls = Lists.newArrayList(documentBaseUrl);
List<Period> periods = new ArrayList<>();
@ -145,7 +157,7 @@ public class DashManifestParser extends DefaultHandler
parseAvailabilityTimeOffsetUs(xpp, baseUrlAvailabilityTimeOffsetUs);
seenFirstBaseUrl = true;
}
baseUrls.addAll(parseBaseUrl(xpp, parentBaseUrls));
baseUrls.addAll(parseBaseUrl(xpp, parentBaseUrls, dvbProfileDeclared));
} else if (XmlPullParserUtil.isStartTag(xpp, "ProgramInformation")) {
programInformation = parseProgramInformation(xpp);
} else if (XmlPullParserUtil.isStartTag(xpp, "UTCTiming")) {
@ -162,7 +174,8 @@ public class DashManifestParser extends DefaultHandler
nextPeriodStartMs,
baseUrlAvailabilityTimeOffsetUs,
availabilityStartTime,
timeShiftBufferDepthMs);
timeShiftBufferDepthMs,
dvbProfileDeclared);
Period period = periodWithDurationMs.first;
if (period.startMs == C.TIME_UNSET) {
if (dynamic) {
@ -282,7 +295,8 @@ public class DashManifestParser extends DefaultHandler
long defaultStartMs,
long baseUrlAvailabilityTimeOffsetUs,
long availabilityStartTimeMs,
long timeShiftBufferDepthMs)
long timeShiftBufferDepthMs,
boolean dvbProfileDeclared)
throws XmlPullParserException, IOException {
@Nullable String id = xpp.getAttributeValue(null, "id");
long startMs = parseDuration(xpp, "start", defaultStartMs);
@ -304,7 +318,7 @@ public class DashManifestParser extends DefaultHandler
parseAvailabilityTimeOffsetUs(xpp, baseUrlAvailabilityTimeOffsetUs);
seenFirstBaseUrl = true;
}
baseUrls.addAll(parseBaseUrl(xpp, parentBaseUrls));
baseUrls.addAll(parseBaseUrl(xpp, parentBaseUrls, dvbProfileDeclared));
} else if (XmlPullParserUtil.isStartTag(xpp, "AdaptationSet")) {
adaptationSets.add(
parseAdaptationSet(
@ -315,7 +329,8 @@ public class DashManifestParser extends DefaultHandler
baseUrlAvailabilityTimeOffsetUs,
segmentBaseAvailabilityTimeOffsetUs,
periodStartUnixTimeMs,
timeShiftBufferDepthMs));
timeShiftBufferDepthMs,
dvbProfileDeclared));
} else if (XmlPullParserUtil.isStartTag(xpp, "EventStream")) {
eventStreams.add(parseEventStream(xpp));
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentBase")) {
@ -375,7 +390,8 @@ public class DashManifestParser extends DefaultHandler
long baseUrlAvailabilityTimeOffsetUs,
long segmentBaseAvailabilityTimeOffsetUs,
long periodStartUnixTimeMs,
long timeShiftBufferDepthMs)
long timeShiftBufferDepthMs,
boolean dvbProfileDeclared)
throws XmlPullParserException, IOException {
int id = parseInt(xpp, "id", AdaptationSet.ID_UNSET);
@C.TrackType int contentType = parseContentType(xpp);
@ -408,7 +424,7 @@ public class DashManifestParser extends DefaultHandler
parseAvailabilityTimeOffsetUs(xpp, baseUrlAvailabilityTimeOffsetUs);
seenFirstBaseUrl = true;
}
baseUrls.addAll(parseBaseUrl(xpp, parentBaseUrls));
baseUrls.addAll(parseBaseUrl(xpp, parentBaseUrls, dvbProfileDeclared));
} else if (XmlPullParserUtil.isStartTag(xpp, "ContentProtection")) {
Pair<String, SchemeData> contentProtection = parseContentProtection(xpp);
if (contentProtection.first != null) {
@ -452,7 +468,8 @@ public class DashManifestParser extends DefaultHandler
periodDurationMs,
baseUrlAvailabilityTimeOffsetUs,
segmentBaseAvailabilityTimeOffsetUs,
timeShiftBufferDepthMs);
timeShiftBufferDepthMs,
dvbProfileDeclared);
contentType =
checkContentTypeConsistency(
contentType, MimeTypes.getTrackType(representationInfo.format.sampleMimeType));
@ -652,7 +669,8 @@ public class DashManifestParser extends DefaultHandler
long periodDurationMs,
long baseUrlAvailabilityTimeOffsetUs,
long segmentBaseAvailabilityTimeOffsetUs,
long timeShiftBufferDepthMs)
long timeShiftBufferDepthMs,
boolean dvbProfileDeclared)
throws XmlPullParserException, IOException {
String id = xpp.getAttributeValue(null, "id");
int bandwidth = parseInt(xpp, "bandwidth", Format.NO_VALUE);
@ -681,7 +699,7 @@ public class DashManifestParser extends DefaultHandler
parseAvailabilityTimeOffsetUs(xpp, baseUrlAvailabilityTimeOffsetUs);
seenFirstBaseUrl = true;
}
baseUrls.addAll(parseBaseUrl(xpp, parentBaseUrls));
baseUrls.addAll(parseBaseUrl(xpp, parentBaseUrls, dvbProfileDeclared));
} else if (XmlPullParserUtil.isStartTag(xpp, "AudioChannelConfiguration")) {
audioChannels = parseAudioChannelConfiguration(xpp);
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentBase")) {
@ -1373,35 +1391,42 @@ public class DashManifestParser extends DefaultHandler
*
* @param xpp The parser from which to read.
* @param parentBaseUrls The parent base URLs for resolving the parsed URLs.
* @param dvbProfileDeclared Whether the dvb profile is declared.
* @throws XmlPullParserException If an error occurs parsing the element.
* @throws IOException If an error occurs reading the element.
* @return The list of parsed and resolved URLs.
*/
protected List<BaseUrl> parseBaseUrl(XmlPullParser xpp, List<BaseUrl> parentBaseUrls)
protected List<BaseUrl> parseBaseUrl(
XmlPullParser xpp, List<BaseUrl> parentBaseUrls, boolean dvbProfileDeclared)
throws XmlPullParserException, IOException {
@Nullable String priorityValue = xpp.getAttributeValue(null, "dvb:priority");
int priority =
priorityValue != null ? Integer.parseInt(priorityValue) : BaseUrl.DEFAULT_PRIORITY;
priorityValue != null
? Integer.parseInt(priorityValue)
: (dvbProfileDeclared ? DEFAULT_DVB_PRIORITY : PRIORITY_UNSET);
@Nullable String weightValue = xpp.getAttributeValue(null, "dvb:weight");
int weight = weightValue != null ? Integer.parseInt(weightValue) : BaseUrl.DEFAULT_WEIGHT;
int weight = weightValue != null ? Integer.parseInt(weightValue) : DEFAULT_WEIGHT;
@Nullable String serviceLocation = xpp.getAttributeValue(null, "serviceLocation");
String baseUrl = parseText(xpp, "BaseURL");
if (serviceLocation == null) {
serviceLocation = baseUrl;
}
if (UriUtil.isAbsolute(baseUrl)) {
if (serviceLocation == null) {
serviceLocation = 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));
String resolvedBaseUri = UriUtil.resolve(parentBaseUrl.url, baseUrl);
String resolvedServiceLocation = serviceLocation == null ? resolvedBaseUri : serviceLocation;
if (dvbProfileDeclared) {
// Inherit parent properties only if dvb profile is declared.
priority = parentBaseUrl.priority;
weight = parentBaseUrl.weight;
resolvedServiceLocation = parentBaseUrl.serviceLocation;
}
baseUrls.add(new BaseUrl(resolvedBaseUri, resolvedServiceLocation, priority, weight));
}
return baseUrls;
}
@ -1583,6 +1608,14 @@ public class DashManifestParser extends DefaultHandler
}
}
protected String[] parseProfiles(XmlPullParser xpp, String attributeName, String[] defaultValue) {
@Nullable String attributeValue = xpp.getAttributeValue(/* namespace= */ null, attributeName);
if (attributeValue == null) {
return defaultValue;
}
return attributeValue.split(",");
}
// Utility methods.
/**
@ -1909,6 +1942,15 @@ public class DashManifestParser extends DefaultHandler
return availabilityTimeOffsetUs;
}
private boolean isDvbProfileDeclared(String[] profiles) {
for (String profile : profiles) {
if (profile.startsWith("urn:dvb:dash:profile:dvb-dash:")) {
return true;
}
}
return false;
}
/** A parsed Representation element. */
protected static final class RepresentationInfo {

View File

@ -215,7 +215,7 @@ public abstract class Representation {
new RangedUri(null, initializationStart, initializationEnd - initializationStart + 1);
SingleSegmentBase segmentBase =
new SingleSegmentBase(rangedUri, 1, 0, indexStart, indexEnd - indexStart + 1);
List<BaseUrl> baseUrls = ImmutableList.of(new BaseUrl(uri));
ImmutableList<BaseUrl> baseUrls = ImmutableList.of(new BaseUrl(uri));
return new SingleSegmentRepresentation(
revisionId,
format,

View File

@ -15,6 +15,8 @@
*/
package androidx.media3.exoplayer.dash;
import static androidx.media3.exoplayer.dash.manifest.BaseUrl.DEFAULT_DVB_PRIORITY;
import static androidx.media3.exoplayer.dash.manifest.BaseUrl.DEFAULT_WEIGHT;
import static androidx.media3.exoplayer.upstream.DefaultLoadErrorHandlingPolicy.DEFAULT_LOCATION_EXCLUSION_MS;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
@ -173,6 +175,32 @@ public class BaseUrlExclusionListTest {
assertThat(baseUrlExclusionList.getPriorityCountAfterExclusion(baseUrls)).isEqualTo(2);
}
@Test
public void selectBaseUrl_priorityUnset_isNotExcluded() {
BaseUrlExclusionList baseUrlExclusionList = new BaseUrlExclusionList();
ImmutableList<BaseUrl> baseUrls =
ImmutableList.of(
new BaseUrl(
/* url= */ "a-1",
/* serviceLocation= */ "a",
BaseUrl.PRIORITY_UNSET,
/* weight= */ 1),
new BaseUrl(
/* url= */ "a-2",
/* serviceLocation= */ "a",
BaseUrl.PRIORITY_UNSET,
/* weight= */ 1),
new BaseUrl(
/* url= */ "b",
/* serviceLocation= */ "b",
BaseUrl.PRIORITY_UNSET,
/* weight= */ 1));
baseUrlExclusionList.exclude(baseUrls.get(0), 10_000);
assertThat(baseUrlExclusionList.selectBaseUrl(baseUrls).serviceLocation).isEqualTo("b");
}
@Test
public void selectBaseUrl_emptyBaseUrlList_selectionIsNull() {
BaseUrlExclusionList baseUrlExclusionList = new BaseUrlExclusionList();
@ -183,7 +211,8 @@ public class BaseUrlExclusionListTest {
@Test
public void reset_dropsAllExclusions() {
BaseUrlExclusionList baseUrlExclusionList = new BaseUrlExclusionList();
List<BaseUrl> baseUrls = ImmutableList.of(new BaseUrl("a"));
ImmutableList<BaseUrl> baseUrls =
ImmutableList.of(new BaseUrl("a", "a", DEFAULT_DVB_PRIORITY, DEFAULT_WEIGHT));
baseUrlExclusionList.exclude(baseUrls.get(0), 5000);
baseUrlExclusionList.reset();

View File

@ -61,6 +61,10 @@ public class DashManifestParserTest {
"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_RELATIVE_BASE_URLS_DVB_PROFILE_NOT_DECLARED =
"media/mpd/sample_mpd_relative_baseUrls_dvb_profile_not_declared";
private static final String SAMPLE_MPD_RELATIVE_BASE_URLS_DVB_PROFILE_DECLARED =
"media/mpd/sample_mpd_relative_baseUrls_dvb_profile_declared";
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 =
@ -748,6 +752,41 @@ public class DashManifestParserTest {
assertThat(textBaseUrls.get(0).serviceLocation).isEqualTo("e");
}
@Test
public void baseUrl_relativeBaseUrlsNoDvbNamespace_hasDifferentPrioritiesAndServiceLocation()
throws IOException {
DashManifestParser parser = new DashManifestParser();
DashManifest manifest =
parser.parse(
Uri.parse("https://example.com/test.mpd"),
TestUtil.getInputStream(
ApplicationProvider.getApplicationContext(),
SAMPLE_MPD_RELATIVE_BASE_URLS_DVB_PROFILE_NOT_DECLARED));
ImmutableList<BaseUrl> baseUrls =
manifest.getPeriod(0).adaptationSets.get(0).representations.get(0).baseUrls;
assertThat(baseUrls.get(0).priority).isEqualTo(BaseUrl.PRIORITY_UNSET);
assertThat(baseUrls.get(1).priority).isEqualTo(BaseUrl.PRIORITY_UNSET);
assertThat(baseUrls.get(0).serviceLocation).isNotEqualTo(baseUrls.get(1).serviceLocation);
}
@Test
public void baseUrl_relativeBaseUrlsWithDvbNamespace_inheritsPrioritiesAndServiceLocation()
throws IOException {
DashManifestParser parser = new DashManifestParser();
DashManifest manifest =
parser.parse(
Uri.parse("https://example.com/test.mpd"),
TestUtil.getInputStream(
ApplicationProvider.getApplicationContext(),
SAMPLE_MPD_RELATIVE_BASE_URLS_DVB_PROFILE_DECLARED));
ImmutableList<BaseUrl> baseUrls =
manifest.getPeriod(0).adaptationSets.get(0).representations.get(0).baseUrls;
assertThat(baseUrls.get(0).priority).isEqualTo(baseUrls.get(1).priority);
assertThat(baseUrls.get(0).serviceLocation).isEqualTo(baseUrls.get(1).serviceLocation);
}
@Test
public void serviceDescriptionElement_allValuesSet() throws IOException {
DashManifestParser parser = new DashManifestParser();

View File

@ -3,7 +3,7 @@
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"
profiles="urn:mpeg:dash:profile:isoff-main:2011,urn:dvb:dash:profile:dvb-dash:2014"
type="dynamic"
availabilityStartTime="2016-10-14T17:00:17">
<BaseURL>http://video.com/baseUrl</BaseURL>

View File

@ -3,7 +3,7 @@
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"
profiles="urn:mpeg:dash:profile:isoff-main:2011,urn:dvb:dash:profile:dvb-dash:2014"
type="dynamic"
availabilityStartTime="2016-10-14T17:00:17">
<BaseURL serviceLocation="a" dvb:priority="1" dvb:weight="1">http://video.com/baseUrl/a/</BaseURL>

View File

@ -0,0 +1,17 @@
<?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,urn:dvb:dash:profile:dvb-dash:2014"
type="dynamic"
availabilityStartTime="2016-10-14T17:00:17">
<Period start="PT0.000S">
<BaseURL>//anotherhost.com/some/url/1/</BaseURL>
<BaseURL>//anotherhost.com/some/url/2/</BaseURL>
<AdaptationSet contentType="audio">
<SegmentTemplate/>
<Representation/>
</AdaptationSet>
</Period>
</MPD>

View File

@ -0,0 +1,16 @@
<?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"
profiles="urn:mpeg:dash:profile:isoff-main:2011"
type="dynamic"
availabilityStartTime="2016-10-14T17:00:17">
<Period start="PT0.000S">
<BaseURL>//anotherhost.com/some/url/1/</BaseURL>
<BaseURL>//anotherhost.com/some/url/2/</BaseURL>
<AdaptationSet contentType="audio">
<SegmentTemplate/>
<Representation/>
</AdaptationSet>
</Period>
</MPD>

View File

@ -4,7 +4,7 @@
xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011"
xmlns:dvb="urn:dvb:dash:dash-extensions:2014-1"
minBufferTime="PT1S"
profiles="urn:mpeg:dash:profile:isoff-main:2011"
profiles="urn:mpeg:dash:profile:isoff-main:2011,urn:dvb:dash:profile:dvb-dash:2014"
type="static"
mediaPresentationDuration="PT904S">
<BaseURL serviceLocation="a" dvb:priority="1" dvb:weight="1">http://video.com/baseUrl/a/</BaseURL>