diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/MediaPeriod.java b/library/core/src/main/java/com/google/android/exoplayer2/source/MediaPeriod.java index 532131ba7d..b40bbb35d1 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/MediaPeriod.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/MediaPeriod.java @@ -87,18 +87,18 @@ public interface MediaPeriod extends SequenceableLoader { TrackGroupArray getTrackGroups(); /** - * Returns a list of {@link StreamKey stream keys} which allow to filter the media in this period - * to load only the parts needed to play the provided {@link TrackSelection}. + * Returns a list of {@link StreamKey StreamKeys} which allow to filter the media in this period + * to load only the parts needed to play the provided {@link TrackSelection TrackSelections}. * *

This method is only called after the period has been prepared. * - * @param trackSelection The {@link TrackSelection} describing the tracks for which stream keys - * are requested. - * @return The corresponding {@link StreamKey stream keys} for the selected tracks, or an empty + * @param trackSelections The {@link TrackSelection TrackSelections} describing the tracks for + * which stream keys are requested. + * @return The corresponding {@link StreamKey StreamKeys} for the selected tracks, or an empty * list if filtering is not possible and the entire media needs to be loaded to play the * selected tracks. */ - default List getStreamKeys(TrackSelection trackSelection) { + default List getStreamKeys(List trackSelections) { return Collections.emptyList(); } diff --git a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriod.java b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriod.java index 8798ea09b2..ae6b60183c 100644 --- a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriod.java +++ b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriod.java @@ -144,11 +144,14 @@ import java.util.List; } @Override - public List getStreamKeys(TrackSelection trackSelection) { - List streamKeys = new ArrayList<>(trackSelection.length()); - int streamElementIndex = trackGroups.indexOf(trackSelection.getTrackGroup()); - for (int i = 0; i < trackSelection.length(); i++) { - streamKeys.add(new StreamKey(streamElementIndex, trackSelection.getIndexInTrackGroup(i))); + public List getStreamKeys(List trackSelections) { + List streamKeys = new ArrayList<>(); + for (int selectionIndex = 0; selectionIndex < trackSelections.size(); selectionIndex++) { + TrackSelection trackSelection = trackSelections.get(selectionIndex); + int streamElementIndex = trackGroups.indexOf(trackSelection.getTrackGroup()); + for (int i = 0; i < trackSelection.length(); i++) { + streamKeys.add(new StreamKey(streamElementIndex, trackSelection.getIndexInTrackGroup(i))); + } } return streamKeys; } diff --git a/library/smoothstreaming/src/test/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriodTest.java b/library/smoothstreaming/src/test/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriodTest.java index f4feef3949..54de4badbd 100644 --- a/library/smoothstreaming/src/test/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriodTest.java +++ b/library/smoothstreaming/src/test/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriodTest.java @@ -61,7 +61,7 @@ public class SsMediaPeriodTest { createStreamElement( /* name= */ "text", C.TRACK_TYPE_TEXT, createTextFormat(/* language= */ "eng"))); FilterableManifestMediaPeriodFactory mediaPeriodFactory = - manifest -> + (manifest, periodIndex) -> new SsMediaPeriod( manifest, mock(SsChunkSource.Factory.class), @@ -77,7 +77,7 @@ public class SsMediaPeriodTest { mock(Allocator.class)); MediaPeriodAsserts.assertGetStreamKeysAndManifestFilterIntegration( - mediaPeriodFactory, testManifest); + mediaPeriodFactory, testManifest, /* periodIndex= */ 0); } private static Format createVideoFormat(int bitrate) { diff --git a/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/MediaPeriodAsserts.java b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/MediaPeriodAsserts.java index 48b9128caf..5235163684 100644 --- a/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/MediaPeriodAsserts.java +++ b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/MediaPeriodAsserts.java @@ -30,6 +30,8 @@ import com.google.android.exoplayer2.trackselection.BaseTrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.util.ConditionVariable; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.concurrent.atomic.AtomicReference; @@ -45,53 +47,81 @@ public final class MediaPeriodAsserts { public interface FilterableManifestMediaPeriodFactory> { /** Returns media period based on the provided filterable manifest. */ - MediaPeriod createMediaPeriod(T manifest); + MediaPeriod createMediaPeriod(T manifest, int periodIndex); } private MediaPeriodAsserts() {} /** - * Asserts that the values returns by {@link MediaPeriod#getStreamKeys(TrackSelection)} are - * compatible with a {@link FilterableManifest} using these stream keys. + * Asserts that the values returns by {@link MediaPeriod#getStreamKeys(List)} are compatible with + * a {@link FilterableManifest} using these stream keys. * * @param mediaPeriodFactory A factory to create a {@link MediaPeriod} based on a manifest. * @param manifest The manifest which is to be tested. + * @param periodIndex The index of period in the manifest. */ public static > void assertGetStreamKeysAndManifestFilterIntegration( - FilterableManifestMediaPeriodFactory mediaPeriodFactory, T manifest) { - MediaPeriod mediaPeriod = mediaPeriodFactory.createMediaPeriod(manifest); + FilterableManifestMediaPeriodFactory mediaPeriodFactory, T manifest, int periodIndex) { + MediaPeriod mediaPeriod = mediaPeriodFactory.createMediaPeriod(manifest, periodIndex); TrackGroupArray trackGroupArray = getTrackGroups(mediaPeriod); + // Create test vector of query test selections: + // - One selection with one track per group, two tracks or all tracks. + // - Two selections with tracks from multiple groups, or tracks from a single group. + // - Multiple selections with tracks from all groups. + List> testSelections = new ArrayList<>(); for (int i = 0; i < trackGroupArray.length; i++) { TrackGroup trackGroup = trackGroupArray.get(i); - - // For each track group, create various test selections. - List testSelections = new ArrayList<>(); for (int j = 0; j < trackGroup.length; j++) { - testSelections.add(new TestTrackSelection(trackGroup, j)); + testSelections.add(Collections.singletonList(new TestTrackSelection(trackGroup, j))); } if (trackGroup.length > 1) { - testSelections.add(new TestTrackSelection(trackGroup, 0, 1)); + testSelections.add(Collections.singletonList(new TestTrackSelection(trackGroup, 0, 1))); + testSelections.add( + Arrays.asList( + new TrackSelection[] { + new TestTrackSelection(trackGroup, 0), new TestTrackSelection(trackGroup, 1) + })); } if (trackGroup.length > 2) { int[] allTracks = new int[trackGroup.length]; for (int j = 0; j < trackGroup.length; j++) { allTracks[j] = j; } - testSelections.add(new TestTrackSelection(trackGroup, allTracks)); + testSelections.add( + Collections.singletonList(new TestTrackSelection(trackGroup, allTracks))); } + } + if (trackGroupArray.length > 1) { + testSelections.add( + Arrays.asList( + new TrackSelection[] { + new TestTrackSelection(trackGroupArray.get(0), 0), + new TestTrackSelection(trackGroupArray.get(1), 0) + })); + } + if (trackGroupArray.length > 2) { + List selectionsFromAllGroups = new ArrayList<>(); + for (int i = 0; i < trackGroupArray.length; i++) { + selectionsFromAllGroups.add(new TestTrackSelection(trackGroupArray.get(i), 0)); + } + testSelections.add(selectionsFromAllGroups); + } - // Get stream keys for each selection and check that the resulting filtered manifest includes - // at least the same subset of tracks. - for (TrackSelection testSelection : testSelections) { - List streamKeys = mediaPeriod.getStreamKeys(testSelection); - T filteredManifest = manifest.copy(streamKeys); - MediaPeriod filteredMediaPeriod = mediaPeriodFactory.createMediaPeriod(filteredManifest); - TrackGroupArray filteredTrackGroupArray = getTrackGroups(filteredMediaPeriod); - Format[] expectedFormats = new Format[testSelection.length()]; - for (int k = 0; k < testSelection.length(); k++) { - expectedFormats[k] = testSelection.getFormat(k); + // Verify for each case that stream keys can be used to create filtered tracks which still + // contain at least all requested formats. + for (List testSelection : testSelections) { + List streamKeys = mediaPeriod.getStreamKeys(testSelection); + T filteredManifest = manifest.copy(streamKeys); + // The filtered manifest should only have one period left. + MediaPeriod filteredMediaPeriod = + mediaPeriodFactory.createMediaPeriod(filteredManifest, /* periodIndex= */ 0); + TrackGroupArray filteredTrackGroupArray = getTrackGroups(filteredMediaPeriod); + for (TrackSelection trackSelection : testSelection) { + Format[] expectedFormats = new Format[trackSelection.length()]; + for (int k = 0; k < trackSelection.length(); k++) { + expectedFormats[k] = trackSelection.getFormat(k); } assertOneTrackGroupContainsFormats(filteredTrackGroupArray, expectedFormats); }