mirror of
https://github.com/samsonjs/media.git
synced 2026-04-01 10:35:48 +00:00
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
This commit is contained in:
parent
f74e0eb992
commit
92bf8e918c
4 changed files with 67 additions and 34 deletions
|
|
@ -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}.
|
||||
*
|
||||
* <p>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<StreamKey> getStreamKeys(TrackSelection trackSelection) {
|
||||
default List<StreamKey> getStreamKeys(List<TrackSelection> trackSelections) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -144,11 +144,14 @@ import java.util.List;
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<StreamKey> getStreamKeys(TrackSelection trackSelection) {
|
||||
List<StreamKey> 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<StreamKey> getStreamKeys(List<TrackSelection> trackSelections) {
|
||||
List<StreamKey> 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ public class SsMediaPeriodTest {
|
|||
createStreamElement(
|
||||
/* name= */ "text", C.TRACK_TYPE_TEXT, createTextFormat(/* language= */ "eng")));
|
||||
FilterableManifestMediaPeriodFactory<SsManifest> 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) {
|
||||
|
|
|
|||
|
|
@ -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<T extends FilterableManifest<T>> {
|
||||
|
||||
/** 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 <T extends FilterableManifest<T>>
|
||||
void assertGetStreamKeysAndManifestFilterIntegration(
|
||||
FilterableManifestMediaPeriodFactory<T> mediaPeriodFactory, T manifest) {
|
||||
MediaPeriod mediaPeriod = mediaPeriodFactory.createMediaPeriod(manifest);
|
||||
FilterableManifestMediaPeriodFactory<T> 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<List<TrackSelection>> 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<TrackSelection> 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<TrackSelection> 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<StreamKey> 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<TrackSelection> testSelection : testSelections) {
|
||||
List<StreamKey> 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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue