From 24b3bf21b8a465099b8f4d8075507293ec73d96c Mon Sep 17 00:00:00 2001 From: tonihei Date: Thu, 13 Mar 2025 10:50:13 -0700 Subject: [PATCH] Check language/role flags before merging adaptation sets The spec technically allows to mark adaptation sets with the switching property if they allow seamless switching with non-overlapping segments. This is typically only used for compatible content (e.g. different codecs), but the spec allows it to be used for other content as well (e.g. different languages, roles). ExoPlayer's concept of a TrackGroup only allows formats with the same language and role flags to be merged, so we should check that before merging. Issue: androidx/media#2222 PiperOrigin-RevId: 736564055 (cherry picked from commit d37f05238a2d8b45ea2e5f4ac026084b917f30df) --- RELEASENOTES.md | 4 ++ .../exoplayer/dash/DashMediaPeriod.java | 19 ++++++++- .../exoplayer/dash/DashMediaPeriodTest.java | 25 +++++++++++ ...sample_mpd_switching_property_incompatible | 42 +++++++++++++++++++ 4 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 libraries/test_data/src/test/assets/media/mpd/sample_mpd_switching_property_incompatible diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 4bc6724999..2729f710e2 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -11,6 +11,10 @@ * UI: * Add `PlaybackSpeedState` class and the corresponding `rememberPlaybackSpeedState` Composable to `media3-ui-compose` module. +* DASH extension: + * Fix issue where adaptation sets marked with `adaptation-set-switching` + but different languages or role flags are merged together + ([#2222](https://github.com/androidx/media/issues/2222)). ## 1.6 diff --git a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaPeriod.java b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaPeriod.java index 8ae8dbc367..3d07a2efd0 100644 --- a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaPeriod.java +++ b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaPeriod.java @@ -72,6 +72,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.List; +import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -624,7 +625,9 @@ import java.util.regex.Pattern; @Nullable Integer otherAdaptationSetIndex = adaptationSetIdToIndex.get(Long.parseLong(adaptationSetId)); - if (otherAdaptationSetIndex != null) { + if (otherAdaptationSetIndex != null + && canMergeAdaptationSets( + adaptationSet, adaptationSets.get(otherAdaptationSetIndex))) { mergedGroupIndex = min(mergedGroupIndex, otherAdaptationSetIndex); } } @@ -650,6 +653,20 @@ import java.util.regex.Pattern; return groupedAdaptationSetIndices; } + private static boolean canMergeAdaptationSets( + AdaptationSet adaptationSet1, AdaptationSet adaptationSet2) { + if (adaptationSet1.type != adaptationSet2.type) { + return false; + } + if (adaptationSet1.representations.isEmpty() || adaptationSet2.representations.isEmpty()) { + return true; + } + Format format1 = adaptationSet1.representations.get(0).format; + Format format2 = adaptationSet2.representations.get(0).format; + return Objects.equals(format1.language, format2.language) + && format1.roleFlags == format2.roleFlags; + } + /** * Iterates through list of primary track groups and identifies embedded tracks. * diff --git a/libraries/exoplayer_dash/src/test/java/androidx/media3/exoplayer/dash/DashMediaPeriodTest.java b/libraries/exoplayer_dash/src/test/java/androidx/media3/exoplayer/dash/DashMediaPeriodTest.java index eba317eb50..8ae40cd615 100644 --- a/libraries/exoplayer_dash/src/test/java/androidx/media3/exoplayer/dash/DashMediaPeriodTest.java +++ b/libraries/exoplayer_dash/src/test/java/androidx/media3/exoplayer/dash/DashMediaPeriodTest.java @@ -94,6 +94,31 @@ public final class DashMediaPeriodTest { MediaPeriodAsserts.assertTrackGroups(dashMediaPeriod, expectedTrackGroups); } + @Test + public void + adaptationSetSwitchingProperty_withIncompatibleFormats_mergesOnlyCompatibleTrackGroups() + throws IOException { + DashManifest manifest = parseManifest("media/mpd/sample_mpd_switching_property_incompatible"); + DashMediaPeriod dashMediaPeriod = createDashMediaPeriod(manifest, /* periodIndex= */ 0); + List adaptationSets = manifest.getPeriod(0).adaptationSets; + + // Only the two matching pairs of adaptation sets should be merged. + TrackGroupArray expectedTrackGroups = + new TrackGroupArray( + new TrackGroup(/* id= */ "0", adaptationSets.get(0).representations.get(0).format), + new TrackGroup( + /* id= */ "1", + adaptationSets.get(1).representations.get(0).format, + adaptationSets.get(3).representations.get(0).format), + new TrackGroup( + /* id= */ "2", + adaptationSets.get(2).representations.get(0).format, + adaptationSets.get(4).representations.get(0).format), + new TrackGroup(/* id= */ "5", adaptationSets.get(5).representations.get(0).format)); + + MediaPeriodAsserts.assertTrackGroups(dashMediaPeriod, expectedTrackGroups); + } + @Test public void trickPlayProperty_mergesTrackGroups() throws IOException { DashManifest manifest = parseManifest("media/mpd/sample_mpd_trick_play_property"); diff --git a/libraries/test_data/src/test/assets/media/mpd/sample_mpd_switching_property_incompatible b/libraries/test_data/src/test/assets/media/mpd/sample_mpd_switching_property_incompatible new file mode 100644 index 0000000000..7438d47015 --- /dev/null +++ b/libraries/test_data/src/test/assets/media/mpd/sample_mpd_switching_property_incompatible @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +