diff --git a/RELEASENOTES.md b/RELEASENOTES.md index dacaa8d161..28dbd82d0b 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -76,6 +76,7 @@ MediaSources factories. * Downloads: Merge downloads in `SegmentDownloader` to improve overall download speed ([#5978](https://github.com/google/ExoPlayer/issues/5978)). +* DASH: Parse period `AssetIdentifier` elements. * MP3: Add `IndexSeeker` for accurate seeks in VBR streams ([#6787](https://github.com/google/ExoPlayer/issues/6787)). This seeker is enabled by passing `FLAG_ENABLE_INDEX_SEEKING` to the `Mp3Extractor`. It may diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java index 85470b07b5..4420a0f985 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java @@ -222,10 +222,11 @@ public class DashManifestParser extends DefaultHandler protected Pair parsePeriod(XmlPullParser xpp, String baseUrl, long defaultStartMs) throws XmlPullParserException, IOException { - String id = xpp.getAttributeValue(null, "id"); + @Nullable String id = xpp.getAttributeValue(null, "id"); long startMs = parseDuration(xpp, "start", defaultStartMs); long durationMs = parseDuration(xpp, "duration", C.TIME_UNSET); - SegmentBase segmentBase = null; + @Nullable SegmentBase segmentBase = null; + @Nullable Descriptor assetIdentifier = null; List adaptationSets = new ArrayList<>(); List eventStreams = new ArrayList<>(); boolean seenFirstBaseUrl = false; @@ -246,17 +247,24 @@ public class DashManifestParser extends DefaultHandler segmentBase = parseSegmentList(xpp, null, durationMs); } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentTemplate")) { segmentBase = parseSegmentTemplate(xpp, null, Collections.emptyList(), durationMs); + } else if (XmlPullParserUtil.isStartTag(xpp, "AssetIdentifier")) { + assetIdentifier = parseDescriptor(xpp, "AssetIdentifier"); } else { maybeSkipTag(xpp); } } while (!XmlPullParserUtil.isEndTag(xpp, "Period")); - return Pair.create(buildPeriod(id, startMs, adaptationSets, eventStreams), durationMs); + return Pair.create( + buildPeriod(id, startMs, adaptationSets, eventStreams, assetIdentifier), durationMs); } - protected Period buildPeriod(String id, long startMs, List adaptationSets, - List eventStreams) { - return new Period(id, startMs, adaptationSets, eventStreams); + protected Period buildPeriod( + @Nullable String id, + long startMs, + List adaptationSets, + List eventStreams, + @Nullable Descriptor assetIdentifier) { + return new Period(id, startMs, adaptationSets, eventStreams, assetIdentifier); } // AdaptationSet parsing. diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/Period.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/Period.java index 18614ca4b0..b472aed50c 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/Period.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/Period.java @@ -45,13 +45,16 @@ public class Period { */ public final List eventStreams; + /** The asset identifier for this period, if one exists */ + @Nullable public final Descriptor assetIdentifier; + /** * @param id The period identifier. May be null. * @param startMs The start time of the period in milliseconds. * @param adaptationSets The adaptation sets belonging to the period. */ public Period(@Nullable String id, long startMs, List adaptationSets) { - this(id, startMs, adaptationSets, Collections.emptyList()); + this(id, startMs, adaptationSets, Collections.emptyList(), /* assetIdentifier= */ null); } /** @@ -62,10 +65,27 @@ public class Period { */ public Period(@Nullable String id, long startMs, List adaptationSets, List eventStreams) { + this(id, startMs, adaptationSets, eventStreams, /* assetIdentifier= */ null); + } + + /** + * @param id The period identifier. May be null. + * @param startMs The start time of the period in milliseconds. + * @param adaptationSets The adaptation sets belonging to the period. + * @param eventStreams The {@link EventStream}s belonging to the period. + * @param assetIdentifier The asset identifier for this period + */ + public Period( + @Nullable String id, + long startMs, + List adaptationSets, + List eventStreams, + @Nullable Descriptor assetIdentifier) { this.id = id; this.startMs = startMs; this.adaptationSets = Collections.unmodifiableList(adaptationSets); this.eventStreams = Collections.unmodifiableList(eventStreams); + this.assetIdentifier = assetIdentifier; } /** diff --git a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParserTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParserTest.java index e7963c5c48..55b3f971f5 100644 --- a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParserTest.java +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParserTest.java @@ -18,6 +18,7 @@ package com.google.android.exoplayer2.source.dash.manifest; import static com.google.common.truth.Truth.assertThat; import android.net.Uri; +import androidx.annotation.Nullable; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.C; @@ -45,6 +46,7 @@ public class DashManifestParserTest { private static final String SAMPLE_MPD_SEGMENT_TEMPLATE = "mpd/sample_mpd_segment_template"; private static final String SAMPLE_MPD_EVENT_STREAM = "mpd/sample_mpd_event_stream"; private static final String SAMPLE_MPD_LABELS = "mpd/sample_mpd_labels"; + private static final String SAMPLE_MPD_ASSET_IDENTIFIER = "mpd/sample_mpd_asset_identifier"; private static final String NEXT_TAG_NAME = "Next"; private static final String NEXT_TAG = "<" + NEXT_TAG_NAME + "/>"; @@ -377,6 +379,27 @@ public class DashManifestParserTest { .isEqualTo(Format.NO_VALUE); } + @Test + public void parsePeriodAssetIdentifier() throws IOException { + DashManifestParser parser = new DashManifestParser(); + DashManifest mpd = + parser.parse( + Uri.parse("https://example.com/test.mpd"), + TestUtil.getInputStream( + ApplicationProvider.getApplicationContext(), SAMPLE_MPD_ASSET_IDENTIFIER)); + + assertThat(mpd.getPeriodCount()).isEqualTo(1); + + Period period = mpd.getPeriod(0); + assertThat(period).isNotNull(); + @Nullable Descriptor assetIdentifier = period.assetIdentifier; + assertThat(assetIdentifier).isNotNull(); + + assertThat(assetIdentifier.schemeIdUri).isEqualTo("urn:org:dashif:asset-id:2013"); + assertThat(assetIdentifier.value).isEqualTo("md:cid:EIDR:10.5240%2f0EFB-02CD-126E-8092-1E49-W"); + assertThat(assetIdentifier.id).isEqualTo("uniqueId"); + } + private static List buildCea608AccessibilityDescriptors(String value) { return Collections.singletonList(new Descriptor("urn:scte:dash:cc:cea-608:2015", value, null)); } diff --git a/testdata/src/test/assets/mpd/sample_mpd_asset_identifier b/testdata/src/test/assets/mpd/sample_mpd_asset_identifier new file mode 100644 index 0000000000..ff5bc874b9 --- /dev/null +++ b/testdata/src/test/assets/mpd/sample_mpd_asset_identifier @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + http://www.dummy.url/ + + + + + + http://www.dummy.url/ + + + +