Skip to content

Commit

Permalink
Add parsed essential/supplemental properties to the Representation.
Browse files Browse the repository at this point in the history
We already parse essential and supplemental properties from the
Representation, but don't add them to our Representation class so that
they can be accessed by users.

Issue: #9579
PiperOrigin-RevId: 409961990
  • Loading branch information
tonihei committed Nov 15, 2021
1 parent 1564d53 commit cd857d8
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 28 deletions.
3 changes: 3 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
* Extractors:
* WAV: Add support for RF64 streams
([#9543](https://github.com/google/ExoPlayer/issues/9543).
* DASH:
* Add parsed essential and supplemental properties to the `Representation`
([#9579](https://github.com/google/ExoPlayer/issues/9579)).

### 2.16.0 (2021-11-04)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,8 @@ protected RepresentationInfo parseRepresentation(
drmSchemeType,
drmSchemeDatas,
inbandEventStreams,
essentialProperties,
supplementalProperties,
Representation.REVISION_ID_DEFAULT);
}

Expand Down Expand Up @@ -841,7 +843,10 @@ protected Representation buildRepresentation(
formatBuilder.build(),
representationInfo.baseUrls,
representationInfo.segmentBase,
inbandEventStreams);
inbandEventStreams,
representationInfo.essentialProperties,
representationInfo.supplementalProperties,
/* cacheKey= */ null);
}

// SegmentBase, SegmentList and SegmentTemplate parsing.
Expand Down Expand Up @@ -1910,6 +1915,8 @@ protected static final class RepresentationInfo {
public final ArrayList<SchemeData> drmSchemeDatas;
public final ArrayList<Descriptor> inbandEventStreams;
public final long revisionId;
public final List<Descriptor> essentialProperties;
public final List<Descriptor> supplementalProperties;

public RepresentationInfo(
Format format,
Expand All @@ -1918,13 +1925,17 @@ public RepresentationInfo(
@Nullable String drmSchemeType,
ArrayList<SchemeData> drmSchemeDatas,
ArrayList<Descriptor> inbandEventStreams,
List<Descriptor> essentialProperties,
List<Descriptor> supplementalProperties,
long revisionId) {
this.format = format;
this.baseUrls = ImmutableList.copyOf(baseUrls);
this.segmentBase = segmentBase;
this.drmSchemeType = drmSchemeType;
this.drmSchemeDatas = drmSchemeDatas;
this.inbandEventStreams = inbandEventStreams;
this.essentialProperties = essentialProperties;
this.supplementalProperties = supplementalProperties;
this.revisionId = revisionId;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ public abstract class Representation {
public final long presentationTimeOffsetUs;
/** The in-band event streams in the representation. May be empty. */
public final List<Descriptor> inbandEventStreams;
/** Essential properties in the representation. May be empty. */
public final List<Descriptor> essentialProperties;
/** Supplemental properties in the adaptation set. May be empty. */
public final List<Descriptor> supplementalProperties;

private final RangedUri initializationUri;

Expand All @@ -64,27 +68,15 @@ public abstract class Representation {
*/
public static Representation newInstance(
long revisionId, Format format, List<BaseUrl> baseUrls, SegmentBase segmentBase) {
return newInstance(revisionId, format, baseUrls, segmentBase, /* inbandEventStreams= */ null);
}

/**
* Constructs a new instance.
*
* @param revisionId Identifies the revision of the content.
* @param format The format of the representation.
* @param baseUrls The list of base URLs of the representation.
* @param segmentBase A segment base element for the representation.
* @param inbandEventStreams The in-band event streams in the representation. May be null.
* @return The constructed instance.
*/
public static Representation newInstance(
long revisionId,
Format format,
List<BaseUrl> baseUrls,
SegmentBase segmentBase,
@Nullable List<Descriptor> inbandEventStreams) {
return newInstance(
revisionId, format, baseUrls, segmentBase, inbandEventStreams, /* cacheKey= */ null);
revisionId,
format,
baseUrls,
segmentBase,
/* inbandEventStreams= */ null,
/* essentialProperties= */ ImmutableList.of(),
/* supplementalProperties= */ ImmutableList.of(),
/* cacheKey= */ null);
}

/**
Expand All @@ -95,6 +87,8 @@ public static Representation newInstance(
* @param baseUrls The list of base URLs of the representation.
* @param segmentBase A segment base element for the representation.
* @param inbandEventStreams The in-band event streams in the representation. May be null.
* @param essentialProperties Essential properties in the representation. May be empty.
* @param supplementalProperties Supplemental properties in the representation. May be empty.
* @param cacheKey An optional key to be returned from {@link #getCacheKey()}, or null. This
* parameter is ignored if {@code segmentBase} consists of multiple segments.
* @return The constructed instance.
Expand All @@ -105,6 +99,8 @@ public static Representation newInstance(
List<BaseUrl> baseUrls,
SegmentBase segmentBase,
@Nullable List<Descriptor> inbandEventStreams,
List<Descriptor> essentialProperties,
List<Descriptor> supplementalProperties,
@Nullable String cacheKey) {
if (segmentBase instanceof SingleSegmentBase) {
return new SingleSegmentRepresentation(
Expand All @@ -113,11 +109,19 @@ public static Representation newInstance(
baseUrls,
(SingleSegmentBase) segmentBase,
inbandEventStreams,
essentialProperties,
supplementalProperties,
cacheKey,
C.LENGTH_UNSET);
/* contentLength= */ C.LENGTH_UNSET);
} else if (segmentBase instanceof MultiSegmentBase) {
return new MultiSegmentRepresentation(
revisionId, format, baseUrls, (MultiSegmentBase) segmentBase, inbandEventStreams);
revisionId,
format,
baseUrls,
(MultiSegmentBase) segmentBase,
inbandEventStreams,
essentialProperties,
supplementalProperties);
} else {
throw new IllegalArgumentException(
"segmentBase must be of type SingleSegmentBase or " + "MultiSegmentBase");
Expand All @@ -129,7 +133,9 @@ private Representation(
Format format,
List<BaseUrl> baseUrls,
SegmentBase segmentBase,
@Nullable List<Descriptor> inbandEventStreams) {
@Nullable List<Descriptor> inbandEventStreams,
List<Descriptor> essentialProperties,
List<Descriptor> supplementalProperties) {
checkArgument(!baseUrls.isEmpty());
this.revisionId = revisionId;
this.format = format;
Expand All @@ -138,6 +144,8 @@ private Representation(
inbandEventStreams == null
? Collections.emptyList()
: Collections.unmodifiableList(inbandEventStreams);
this.essentialProperties = essentialProperties;
this.supplementalProperties = supplementalProperties;
initializationUri = segmentBase.getInitialization(this);
presentationTimeOffsetUs = segmentBase.getPresentationTimeOffsetUs();
}
Expand Down Expand Up @@ -207,7 +215,15 @@ public static SingleSegmentRepresentation newInstance(
new SingleSegmentBase(rangedUri, 1, 0, indexStart, indexEnd - indexStart + 1);
List<BaseUrl> baseUrls = ImmutableList.of(new BaseUrl(uri));
return new SingleSegmentRepresentation(
revisionId, format, baseUrls, segmentBase, inbandEventStreams, cacheKey, contentLength);
revisionId,
format,
baseUrls,
segmentBase,
inbandEventStreams,
/* essentialProperties= */ ImmutableList.of(),
/* supplementalProperties= */ ImmutableList.of(),
cacheKey,
contentLength);
}

/**
Expand All @@ -216,6 +232,8 @@ public static SingleSegmentRepresentation newInstance(
* @param baseUrls The base urls of the representation.
* @param segmentBase The segment base underlying the representation.
* @param inbandEventStreams The in-band event streams in the representation. May be null.
* @param essentialProperties Essential properties in the representation. May be empty.
* @param supplementalProperties Supplemental properties in the representation. May be empty.
* @param cacheKey An optional key to be returned from {@link #getCacheKey()}, or null.
* @param contentLength The content length, or {@link C#LENGTH_UNSET} if unknown.
*/
Expand All @@ -225,9 +243,18 @@ public SingleSegmentRepresentation(
List<BaseUrl> baseUrls,
SingleSegmentBase segmentBase,
@Nullable List<Descriptor> inbandEventStreams,
List<Descriptor> essentialProperties,
List<Descriptor> supplementalProperties,
@Nullable String cacheKey,
long contentLength) {
super(revisionId, format, baseUrls, segmentBase, inbandEventStreams);
super(
revisionId,
format,
baseUrls,
segmentBase,
inbandEventStreams,
essentialProperties,
supplementalProperties);
this.uri = Uri.parse(baseUrls.get(0).url);
this.indexUri = segmentBase.getIndex();
this.cacheKey = cacheKey;
Expand Down Expand Up @@ -271,14 +298,25 @@ public static class MultiSegmentRepresentation extends Representation
* @param baseUrls The base URLs of the representation.
* @param segmentBase The segment base underlying the representation.
* @param inbandEventStreams The in-band event streams in the representation. May be null.
* @param essentialProperties Essential properties in the representation. May be empty.
* @param supplementalProperties Supplemental properties in the representation. May be empty.
*/
public MultiSegmentRepresentation(
long revisionId,
Format format,
List<BaseUrl> baseUrls,
MultiSegmentBase segmentBase,
@Nullable List<Descriptor> inbandEventStreams) {
super(revisionId, format, baseUrls, segmentBase, inbandEventStreams);
@Nullable List<Descriptor> inbandEventStreams,
List<Descriptor> essentialProperties,
List<Descriptor> supplementalProperties) {
super(
revisionId,
format,
baseUrls,
segmentBase,
inbandEventStreams,
essentialProperties,
supplementalProperties);
this.segmentBase = segmentBase;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ public void resolveCacheKey_representationCacheKeyIsNull_resolvesRangedUriWithFi
baseUrls,
new SingleSegmentBase(),
/* inbandEventStreams= */ null,
/* essentialProperties= */ ImmutableList.of(),
/* supplementalProperties= */ ImmutableList.of(),
/* cacheKey= */ null,
/* contentLength= */ 1);
RangedUri rangedUri = new RangedUri("path/to/resource", /* start= */ 0, /* length= */ 1);
Expand All @@ -99,6 +101,8 @@ public void resolveCacheKey_representationCacheKeyDefined_usesRepresentationCach
baseUrls,
new SingleSegmentBase(),
/* inbandEventStreams= */ null,
/* essentialProperties= */ ImmutableList.of(),
/* supplementalProperties= */ ImmutableList.of(),
"cacheKey",
/* contentLength= */ 1);
RangedUri rangedUri = new RangedUri("path/to/resource", /* start= */ 0, /* length= */ 1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.metadata.emsg.EventMessage;
import com.google.android.exoplayer2.source.dash.manifest.Representation.MultiSegmentRepresentation;
import com.google.android.exoplayer2.source.dash.manifest.Representation.SingleSegmentRepresentation;
import com.google.android.exoplayer2.source.dash.manifest.SegmentBase.SegmentTimelineElement;
import com.google.android.exoplayer2.testutil.TestUtil;
import com.google.android.exoplayer2.util.MimeTypes;
Expand Down Expand Up @@ -53,6 +55,8 @@ public class DashManifestParserTest {
private static final String SAMPLE_MPD_ASSET_IDENTIFIER = "media/mpd/sample_mpd_asset_identifier";
private static final String SAMPLE_MPD_TEXT = "media/mpd/sample_mpd_text";
private static final String SAMPLE_MPD_TRICK_PLAY = "media/mpd/sample_mpd_trick_play";
private static final String SAMPLE_MPD_ESSENTIAL_SUPPLEMENTAL_PROPERTIES =
"media/mpd/sample_mpd_essential_supplemental_properties";
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 =
Expand Down Expand Up @@ -504,6 +508,74 @@ public void parsePeriodAssetIdentifier() throws IOException {
assertThat(assetIdentifier.id).isEqualTo("uniqueId");
}

@Test
public void parseEssentialAndSupplementalProperties() throws IOException {
DashManifestParser parser = new DashManifestParser();
DashManifest manifest =
parser.parse(
Uri.parse("https://example.com/test.mpd"),
TestUtil.getInputStream(
ApplicationProvider.getApplicationContext(),
SAMPLE_MPD_ESSENTIAL_SUPPLEMENTAL_PROPERTIES));

// Verify test setup.
assertThat(manifest.getPeriodCount()).isEqualTo(1);
assertThat(manifest.getPeriod(0).adaptationSets).hasSize(1);
AdaptationSet adaptationSet = manifest.getPeriod(0).adaptationSets.get(0);
assertThat(adaptationSet.representations).hasSize(2);
Representation representation0 = adaptationSet.representations.get(0);
Representation representation1 = adaptationSet.representations.get(1);
assertThat(representation0).isInstanceOf(SingleSegmentRepresentation.class);
assertThat(representation1).isInstanceOf(MultiSegmentRepresentation.class);

// Verify parsed properties.
assertThat(adaptationSet.essentialProperties).hasSize(1);
assertThat(adaptationSet.essentialProperties.get(0).schemeIdUri)
.isEqualTo("urn:mpeg:dash:essential-scheme:2050");
assertThat(adaptationSet.essentialProperties.get(0).value).isEqualTo("adaptationEssential");
assertThat(adaptationSet.supplementalProperties).hasSize(1);
assertThat(adaptationSet.supplementalProperties.get(0).schemeIdUri)
.isEqualTo("urn:mpeg:dash:supplemental-scheme:2050");
assertThat(adaptationSet.supplementalProperties.get(0).value)
.isEqualTo("adaptationSupplemental");

assertThat(representation0.essentialProperties).hasSize(2);
assertThat(representation0.essentialProperties.get(0).schemeIdUri)
.isEqualTo("urn:mpeg:dash:essential-scheme:2050");
assertThat(representation0.essentialProperties.get(0).value).isEqualTo("adaptationEssential");
assertThat(representation0.essentialProperties.get(1).schemeIdUri)
.isEqualTo("urn:mpeg:dash:essential-scheme:2050");
assertThat(representation0.essentialProperties.get(1).value)
.isEqualTo("representationEssential");
assertThat(representation0.supplementalProperties).hasSize(2);
assertThat(representation0.supplementalProperties.get(0).schemeIdUri)
.isEqualTo("urn:mpeg:dash:supplemental-scheme:2050");
assertThat(representation0.supplementalProperties.get(0).value)
.isEqualTo("adaptationSupplemental");
assertThat(representation0.supplementalProperties.get(1).schemeIdUri)
.isEqualTo("urn:mpeg:dash:supplemental-scheme:2050");
assertThat(representation0.supplementalProperties.get(1).value)
.isEqualTo("representationSupplemental");

assertThat(representation1.essentialProperties).hasSize(2);
assertThat(representation0.essentialProperties.get(0).schemeIdUri)
.isEqualTo("urn:mpeg:dash:essential-scheme:2050");
assertThat(representation0.essentialProperties.get(0).value).isEqualTo("adaptationEssential");
assertThat(representation1.essentialProperties.get(1).schemeIdUri)
.isEqualTo("urn:mpeg:dash:essential-scheme:2050");
assertThat(representation1.essentialProperties.get(1).value)
.isEqualTo("representationEssential");
assertThat(representation1.supplementalProperties).hasSize(2);
assertThat(representation0.supplementalProperties.get(0).schemeIdUri)
.isEqualTo("urn:mpeg:dash:supplemental-scheme:2050");
assertThat(representation0.supplementalProperties.get(0).value)
.isEqualTo("adaptationSupplemental");
assertThat(representation1.supplementalProperties.get(1).schemeIdUri)
.isEqualTo("urn:mpeg:dash:supplemental-scheme:2050");
assertThat(representation1.supplementalProperties.get(1).value)
.isEqualTo("representationSupplemental");
}

@Test
public void availabilityTimeOffset_staticManifest_setToTimeUnset() throws IOException {
DashManifestParser parser = new DashManifestParser();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<MPD availabilityStartTime="2014-06-19T23:07:42" type="dynamic">
<Period start="PT7462826.784S" id="0">
<AdaptationSet id="0" mimeType="video/mp4">
<SupplementalProperty
schemeIdUri="urn:mpeg:dash:supplemental-scheme:2050"
value="adaptationSupplemental"/>
<EssentialProperty
schemeIdUri="urn:mpeg:dash:essential-scheme:2050"
value="adaptationEssential"/>
<Representation id="singleSegment">
<SegmentBase/>
<SupplementalProperty
schemeIdUri="urn:mpeg:dash:supplemental-scheme:2050"
value="representationSupplemental"/>
<EssentialProperty
schemeIdUri="urn:mpeg:dash:essential-scheme:2050"
value="representationEssential"/>
</Representation>
<Representation id="multiSegment">
<SegmentList/>
<SupplementalProperty
schemeIdUri="urn:mpeg:dash:supplemental-scheme:2050"
value="representationSupplemental"/>
<EssentialProperty
schemeIdUri="urn:mpeg:dash:essential-scheme:2050"
value="representationEssential"/>
</Representation>
</AdaptationSet>
</Period>
</MPD>

0 comments on commit cd857d8

Please sign in to comment.