From 92bf8e918cc183cfaf2c6c93ff4533583c51c107 Mon Sep 17 00:00:00 2001 From: tonihei Date: Tue, 29 Jan 2019 12:57:22 +0000 Subject: [PATCH] Change getStreamKeys to take a list of TrackSelections. Converting a single track selection to stream keys is only possible if the output is independent from other track selections being made. This is not the case for DASH and HLS embedded track groups which should select the already selected primary track if possible (and thus needs to know whether a primary track group is selected). Also, update the test method to take a period index. PiperOrigin-RevId: 231385490 --- .../exoplayer2/source/MediaPeriod.java | 12 ++-- .../source/smoothstreaming/SsMediaPeriod.java | 13 ++-- .../smoothstreaming/SsMediaPeriodTest.java | 4 +- .../testutil/MediaPeriodAsserts.java | 72 +++++++++++++------ 4 files changed, 67 insertions(+), 34 deletions(-) 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); }