diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 2992f9994e..631f0e8bdb 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -4,6 +4,9 @@ * Common Library: * ExoPlayer: + * Add source prefix to all `Format.id` fields generated from + `MergingMediaSource`. This helps to identify which source produced a + `Format` ([#883](https://github.com/androidx/media/issues/883)). * Transformer: * Track Selection: * Extractors: diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MergingMediaPeriod.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MergingMediaPeriod.java index 6e910e4b4a..07ff893bfd 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MergingMediaPeriod.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/MergingMediaPeriod.java @@ -284,7 +284,17 @@ import java.util.List; int periodTrackGroupCount = periodTrackGroups.length; for (int j = 0; j < periodTrackGroupCount; j++) { TrackGroup childTrackGroup = periodTrackGroups.get(j); - TrackGroup mergedTrackGroup = childTrackGroup.copyWithId(i + ":" + childTrackGroup.id); + Format[] mergedFormats = new Format[childTrackGroup.length]; + for (int k = 0; k < childTrackGroup.length; k++) { + Format originalFormat = childTrackGroup.getFormat(k); + mergedFormats[k] = + originalFormat + .buildUpon() + .setId(i + ":" + (originalFormat.id == null ? "" : originalFormat.id)) + .build(); + } + TrackGroup mergedTrackGroup = + new TrackGroup(/* id= */ i + ":" + childTrackGroup.id, mergedFormats); childTrackGroupByMergedTrackGroup.put(mergedTrackGroup, childTrackGroup); trackGroupArray[trackGroupIndex++] = mergedTrackGroup; } @@ -325,7 +335,7 @@ import java.util.List; @Override public Format getFormat(int index) { - return trackSelection.getFormat(index); + return trackGroup.getFormat(trackSelection.getIndexInTrackGroup(index)); } @Override @@ -335,7 +345,7 @@ import java.util.List; @Override public int indexOf(Format format) { - return trackSelection.indexOf(format); + return trackSelection.indexOf(trackGroup.indexOf(format)); } @Override @@ -355,7 +365,7 @@ import java.util.List; @Override public Format getSelectedFormat() { - return trackSelection.getSelectedFormat(); + return trackGroup.getFormat(trackSelection.getSelectedIndexInTrackGroup()); } @Override diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/MergingMediaPeriodTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/MergingMediaPeriodTest.java index 92bd17b894..3f3278f654 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/MergingMediaPeriodTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/MergingMediaPeriodTest.java @@ -61,10 +61,10 @@ public final class MergingMediaPeriodTest { /* timeOffsetUs= */ 0, /* singleSampleTimeUs= */ 0, childFormat21, childFormat22)); assertThat(mergingMediaPeriod.getTrackGroups().length).isEqualTo(4); - assertThat(mergingMediaPeriod.getTrackGroups().get(0).getFormat(0)).isEqualTo(childFormat11); - assertThat(mergingMediaPeriod.getTrackGroups().get(1).getFormat(0)).isEqualTo(childFormat12); - assertThat(mergingMediaPeriod.getTrackGroups().get(2).getFormat(0)).isEqualTo(childFormat21); - assertThat(mergingMediaPeriod.getTrackGroups().get(3).getFormat(0)).isEqualTo(childFormat22); + assertThat(mergingMediaPeriod.getTrackGroups().get(0).getFormat(0).id).isEqualTo("0:1_1"); + assertThat(mergingMediaPeriod.getTrackGroups().get(1).getFormat(0).id).isEqualTo("0:1_2"); + assertThat(mergingMediaPeriod.getTrackGroups().get(2).getFormat(0).id).isEqualTo("1:2_1"); + assertThat(mergingMediaPeriod.getTrackGroups().get(3).getFormat(0).id).isEqualTo("1:2_2"); } @Test @@ -199,6 +199,44 @@ public final class MergingMediaPeriodTest { assertThat(firstSelectionChild2).isEqualTo(secondSelectionChild2); } + @Test + public void selectTracks_forwardsSelectionsWithChildIdsToChildSources() throws Exception { + MergingMediaPeriod mergingMediaPeriod = + prepareMergingPeriod( + /* singleTrackGroup= */ true, + new MergingPeriodDefinition( + /* timeOffsetUs= */ 0, /* singleSampleTimeUs= */ 0, childFormat11, childFormat12), + new MergingPeriodDefinition( + /* timeOffsetUs= */ 0, /* singleSampleTimeUs= */ 0, childFormat21, childFormat22)); + FakeMediaPeriodWithSelectionParameters childMediaPeriod1 = + (FakeMediaPeriodWithSelectionParameters) mergingMediaPeriod.getChildPeriod(0); + FakeMediaPeriodWithSelectionParameters childMediaPeriod2 = + (FakeMediaPeriodWithSelectionParameters) mergingMediaPeriod.getChildPeriod(1); + + TrackGroupArray mergedTrackGroups = mergingMediaPeriod.getTrackGroups(); + ExoTrackSelection[] selectionArray = + new ExoTrackSelection[] { + new FixedTrackSelection(mergedTrackGroups.get(0), /* track= */ 0), + new FixedTrackSelection(mergedTrackGroups.get(1), /* track= */ 1) + }; + + mergingMediaPeriod.selectTracks( + selectionArray, + /* mayRetainStreamFlags= */ new boolean[2], + /* streams= */ new SampleStream[2], + /* streamResetFlags= */ new boolean[2], + /* positionUs= */ 0); + ExoTrackSelection selectionChild1 = childMediaPeriod1.selectTracksSelections[0]; + ExoTrackSelection selectionChild2 = childMediaPeriod2.selectTracksSelections[1]; + + assertThat(selectionChild1.getSelectedFormat()).isEqualTo(childFormat11); + assertThat(selectionChild2.getSelectedFormat()).isEqualTo(childFormat22); + assertThat(selectionChild1.getFormat(/* index= */ 0)).isEqualTo(childFormat11); + assertThat(selectionChild2.getFormat(/* index= */ 0)).isEqualTo(childFormat22); + assertThat(selectionChild1.indexOf(childFormat11)).isEqualTo(0); + assertThat(selectionChild2.indexOf(childFormat22)).isEqualTo(0); + } + // https://github.com/google/ExoPlayer/issues/10930 @Test public void selectTracks_withIdenticalFormats_selectsMatchingPeriod() throws Exception { @@ -234,14 +272,24 @@ public final class MergingMediaPeriodTest { private MergingMediaPeriod prepareMergingPeriod(MergingPeriodDefinition... definitions) throws Exception { + return prepareMergingPeriod(/* singleTrackGroup= */ false, definitions); + } + + private MergingMediaPeriod prepareMergingPeriod( + boolean singleTrackGroup, MergingPeriodDefinition... definitions) throws Exception { MediaPeriod[] mediaPeriods = new MediaPeriod[definitions.length]; long[] timeOffsetsUs = new long[definitions.length]; for (int i = 0; i < definitions.length; i++) { MergingPeriodDefinition definition = definitions[i]; timeOffsetsUs[i] = definition.timeOffsetUs; - TrackGroup[] trackGroups = new TrackGroup[definition.formats.length]; - for (int j = 0; j < definition.formats.length; j++) { - trackGroups[j] = new TrackGroup(definition.formats[j]); + TrackGroup[] trackGroups; + if (singleTrackGroup) { + trackGroups = new TrackGroup[] {new TrackGroup(definition.formats)}; + } else { + trackGroups = new TrackGroup[definition.formats.length]; + for (int j = 0; j < definition.formats.length; j++) { + trackGroups[j] = new TrackGroup(definition.formats[j]); + } } mediaPeriods[i] = new FakeMediaPeriodWithSelectionParameters(