mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Fix audio and text track selection in the multi-renderer case
If we can select a track that has a strictly higher score than a selection already made for a renderer of the same type, we should prefer it. Issue: #4711 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=212835895
This commit is contained in:
parent
3e4115ff3c
commit
6f6f381beb
3 changed files with 195 additions and 103 deletions
|
|
@ -124,6 +124,9 @@
|
||||||
* Fix bugs reporting events for multi-period media sources
|
* Fix bugs reporting events for multi-period media sources
|
||||||
([#4492](https://github.com/google/ExoPlayer/issues/4492) and
|
([#4492](https://github.com/google/ExoPlayer/issues/4492) and
|
||||||
[#4634](https://github.com/google/ExoPlayer/issues/4634)).
|
[#4634](https://github.com/google/ExoPlayer/issues/4634)).
|
||||||
|
* Fix issue where the preferred audio or text track would not be selected if
|
||||||
|
mapped onto a secondary renderer of the corresponding type
|
||||||
|
([#4711](http://github.com/google/ExoPlayer/issues/4711)).
|
||||||
* Fix issue where errors of upcoming playlist items are thrown too early
|
* Fix issue where errors of upcoming playlist items are thrown too early
|
||||||
([#4661](https://github.com/google/ExoPlayer/issues/4661)).
|
([#4661](https://github.com/google/ExoPlayer/issues/4661)).
|
||||||
* Allow edit lists which do not start with a sync sample.
|
* Allow edit lists which do not start with a sync sample.
|
||||||
|
|
|
||||||
|
|
@ -1318,8 +1318,10 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean selectedAudioTracks = false;
|
AudioTrackScore selectedAudioTrackScore = null;
|
||||||
boolean selectedTextTracks = false;
|
int selectedAudioRendererIndex = C.INDEX_UNSET;
|
||||||
|
int selectedTextTrackScore = Integer.MIN_VALUE;
|
||||||
|
int selectedTextRendererIndex = C.INDEX_UNSET;
|
||||||
for (int i = 0; i < rendererCount; i++) {
|
for (int i = 0; i < rendererCount; i++) {
|
||||||
int trackType = mappedTrackInfo.getRendererType(i);
|
int trackType = mappedTrackInfo.getRendererType(i);
|
||||||
switch (trackType) {
|
switch (trackType) {
|
||||||
|
|
@ -1327,23 +1329,38 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
||||||
// Already done. Do nothing.
|
// Already done. Do nothing.
|
||||||
break;
|
break;
|
||||||
case C.TRACK_TYPE_AUDIO:
|
case C.TRACK_TYPE_AUDIO:
|
||||||
if (!selectedAudioTracks) {
|
Pair<TrackSelection, AudioTrackScore> audioSelection =
|
||||||
rendererTrackSelections[i] =
|
selectAudioTrack(
|
||||||
selectAudioTrack(
|
mappedTrackInfo.getTrackGroups(i),
|
||||||
mappedTrackInfo.getTrackGroups(i),
|
rendererFormatSupports[i],
|
||||||
rendererFormatSupports[i],
|
rendererMixedMimeTypeAdaptationSupports[i],
|
||||||
rendererMixedMimeTypeAdaptationSupports[i],
|
params,
|
||||||
params,
|
seenVideoRendererWithMappedTracks ? null : adaptiveTrackSelectionFactory);
|
||||||
seenVideoRendererWithMappedTracks ? null : adaptiveTrackSelectionFactory);
|
if (audioSelection != null
|
||||||
selectedAudioTracks = rendererTrackSelections[i] != null;
|
&& (selectedAudioTrackScore == null
|
||||||
|
|| audioSelection.second.compareTo(selectedAudioTrackScore) > 0)) {
|
||||||
|
if (selectedAudioRendererIndex != C.INDEX_UNSET) {
|
||||||
|
// We've already made a selection for another audio renderer, but it had a lower
|
||||||
|
// score. Clear the selection for that renderer.
|
||||||
|
rendererTrackSelections[selectedAudioRendererIndex] = null;
|
||||||
|
}
|
||||||
|
rendererTrackSelections[i] = audioSelection.first;
|
||||||
|
selectedAudioTrackScore = audioSelection.second;
|
||||||
|
selectedAudioRendererIndex = i;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case C.TRACK_TYPE_TEXT:
|
case C.TRACK_TYPE_TEXT:
|
||||||
if (!selectedTextTracks) {
|
Pair<TrackSelection, Integer> textSelection =
|
||||||
rendererTrackSelections[i] =
|
selectTextTrack(mappedTrackInfo.getTrackGroups(i), rendererFormatSupports[i], params);
|
||||||
selectTextTrack(
|
if (textSelection != null && textSelection.second > selectedTextTrackScore) {
|
||||||
mappedTrackInfo.getTrackGroups(i), rendererFormatSupports[i], params);
|
if (selectedTextRendererIndex != C.INDEX_UNSET) {
|
||||||
selectedTextTracks = rendererTrackSelections[i] != null;
|
// We've already made a selection for another text renderer, but it had a lower score.
|
||||||
|
// Clear the selection for that renderer.
|
||||||
|
rendererTrackSelections[selectedTextRendererIndex] = null;
|
||||||
|
}
|
||||||
|
rendererTrackSelections[i] = textSelection.first;
|
||||||
|
selectedTextTrackScore = textSelection.second;
|
||||||
|
selectedTextRendererIndex = i;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
@ -1599,10 +1616,11 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
||||||
* @param params The selector's current constraint parameters.
|
* @param params The selector's current constraint parameters.
|
||||||
* @param adaptiveTrackSelectionFactory A factory for generating adaptive track selections, or
|
* @param adaptiveTrackSelectionFactory A factory for generating adaptive track selections, or
|
||||||
* null if a fixed track selection is required.
|
* null if a fixed track selection is required.
|
||||||
* @return The {@link TrackSelection} for the renderer, or null if no selection was made.
|
* @return The {@link TrackSelection} and corresponding {@link AudioTrackScore}, or null if no
|
||||||
|
* selection was made.
|
||||||
* @throws ExoPlaybackException If an error occurs while selecting the tracks.
|
* @throws ExoPlaybackException If an error occurs while selecting the tracks.
|
||||||
*/
|
*/
|
||||||
protected @Nullable TrackSelection selectAudioTrack(
|
protected @Nullable Pair<TrackSelection, AudioTrackScore> selectAudioTrack(
|
||||||
TrackGroupArray groups,
|
TrackGroupArray groups,
|
||||||
int[][] formatSupports,
|
int[][] formatSupports,
|
||||||
int mixedMimeTypeAdaptationSupports,
|
int mixedMimeTypeAdaptationSupports,
|
||||||
|
|
@ -1635,6 +1653,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
||||||
}
|
}
|
||||||
|
|
||||||
TrackGroup selectedGroup = groups.get(selectedGroupIndex);
|
TrackGroup selectedGroup = groups.get(selectedGroupIndex);
|
||||||
|
|
||||||
|
TrackSelection selection = null;
|
||||||
if (!params.forceHighestSupportedBitrate
|
if (!params.forceHighestSupportedBitrate
|
||||||
&& !params.forceLowestBitrate
|
&& !params.forceLowestBitrate
|
||||||
&& adaptiveTrackSelectionFactory != null) {
|
&& adaptiveTrackSelectionFactory != null) {
|
||||||
|
|
@ -1643,11 +1663,17 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
||||||
getAdaptiveAudioTracks(
|
getAdaptiveAudioTracks(
|
||||||
selectedGroup, formatSupports[selectedGroupIndex], params.allowMixedMimeAdaptiveness);
|
selectedGroup, formatSupports[selectedGroupIndex], params.allowMixedMimeAdaptiveness);
|
||||||
if (adaptiveTracks.length > 0) {
|
if (adaptiveTracks.length > 0) {
|
||||||
return adaptiveTrackSelectionFactory
|
selection =
|
||||||
.createTrackSelection(selectedGroup, getBandwidthMeter(), adaptiveTracks);
|
adaptiveTrackSelectionFactory.createTrackSelection(
|
||||||
|
selectedGroup, getBandwidthMeter(), adaptiveTracks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new FixedTrackSelection(selectedGroup, selectedTrackIndex);
|
if (selection == null) {
|
||||||
|
// We didn't make an adaptive selection, so make a fixed one instead.
|
||||||
|
selection = new FixedTrackSelection(selectedGroup, selectedTrackIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Pair.create(selection, Assertions.checkNotNull(selectedTrackScore));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int[] getAdaptiveAudioTracks(TrackGroup group, int[] formatSupport,
|
private static int[] getAdaptiveAudioTracks(TrackGroup group, int[] formatSupport,
|
||||||
|
|
@ -1712,10 +1738,11 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
||||||
* @param formatSupport The result of {@link RendererCapabilities#supportsFormat} for each mapped
|
* @param formatSupport The result of {@link RendererCapabilities#supportsFormat} for each mapped
|
||||||
* track, indexed by track group index and track index (in that order).
|
* track, indexed by track group index and track index (in that order).
|
||||||
* @param params The selector's current constraint parameters.
|
* @param params The selector's current constraint parameters.
|
||||||
* @return The {@link TrackSelection} for the renderer, or null if no selection was made.
|
* @return The {@link TrackSelection} and corresponding track score, or null if no selection was
|
||||||
|
* made.
|
||||||
* @throws ExoPlaybackException If an error occurs while selecting the tracks.
|
* @throws ExoPlaybackException If an error occurs while selecting the tracks.
|
||||||
*/
|
*/
|
||||||
protected @Nullable TrackSelection selectTextTrack(
|
protected @Nullable Pair<TrackSelection, Integer> selectTextTrack(
|
||||||
TrackGroupArray groups, int[][] formatSupport, Parameters params)
|
TrackGroupArray groups, int[][] formatSupport, Parameters params)
|
||||||
throws ExoPlaybackException {
|
throws ExoPlaybackException {
|
||||||
TrackGroup selectedGroup = null;
|
TrackGroup selectedGroup = null;
|
||||||
|
|
@ -1770,8 +1797,10 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return selectedGroup == null ? null
|
return selectedGroup == null
|
||||||
: new FixedTrackSelection(selectedGroup, selectedTrackIndex);
|
? null
|
||||||
|
: Pair.create(
|
||||||
|
new FixedTrackSelection(selectedGroup, selectedTrackIndex), selectedTrackScore);
|
||||||
}
|
}
|
||||||
|
|
||||||
// General track selection methods.
|
// General track selection methods.
|
||||||
|
|
@ -2032,12 +2061,9 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Represents how well an audio track matches the selection {@link Parameters}. */
|
||||||
* A representation of how well a track fits with our track selection {@link Parameters}.
|
|
||||||
*
|
|
||||||
* <p>This is used to rank different audio tracks relatively with each other.
|
|
||||||
*/
|
|
||||||
private static final class AudioTrackScore implements Comparable<AudioTrackScore> {
|
private static final class AudioTrackScore implements Comparable<AudioTrackScore> {
|
||||||
|
|
||||||
private final Parameters parameters;
|
private final Parameters parameters;
|
||||||
private final int withinRendererCapabilitiesScore;
|
private final int withinRendererCapabilitiesScore;
|
||||||
private final int matchLanguageScore;
|
private final int matchLanguageScore;
|
||||||
|
|
@ -2057,7 +2083,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compares the score of the current track format with another {@link AudioTrackScore}.
|
* Compares this score with another.
|
||||||
*
|
*
|
||||||
* @param other The other score to compare to.
|
* @param other The other score to compare to.
|
||||||
* @return A positive integer if this score is better than the other. Zero if they are equal. A
|
* @return A positive integer if this score is better than the other. Zero if they are equal. A
|
||||||
|
|
@ -2086,35 +2112,6 @@ public class DefaultTrackSelector extends MappingTrackSelector {
|
||||||
return resultSign * compareInts(this.bitrate, other.bitrate);
|
return resultSign * compareInts(this.bitrate, other.bitrate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(@Nullable Object o) {
|
|
||||||
if (this == o) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (o == null || getClass() != o.getClass()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioTrackScore that = (AudioTrackScore) o;
|
|
||||||
|
|
||||||
return withinRendererCapabilitiesScore == that.withinRendererCapabilitiesScore
|
|
||||||
&& matchLanguageScore == that.matchLanguageScore
|
|
||||||
&& defaultSelectionFlagScore == that.defaultSelectionFlagScore
|
|
||||||
&& channelCount == that.channelCount && sampleRate == that.sampleRate
|
|
||||||
&& bitrate == that.bitrate;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
int result = withinRendererCapabilitiesScore;
|
|
||||||
result = 31 * result + matchLanguageScore;
|
|
||||||
result = 31 * result + defaultSelectionFlagScore;
|
|
||||||
result = 31 * result + channelCount;
|
|
||||||
result = 31 * result + sampleRate;
|
|
||||||
result = 31 * result + bitrate;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ package com.google.android.exoplayer2.trackselection;
|
||||||
|
|
||||||
import static com.google.android.exoplayer2.RendererCapabilities.FORMAT_EXCEEDS_CAPABILITIES;
|
import static com.google.android.exoplayer2.RendererCapabilities.FORMAT_EXCEEDS_CAPABILITIES;
|
||||||
import static com.google.android.exoplayer2.RendererCapabilities.FORMAT_HANDLED;
|
import static com.google.android.exoplayer2.RendererCapabilities.FORMAT_HANDLED;
|
||||||
|
import static com.google.android.exoplayer2.RendererCapabilities.FORMAT_UNSUPPORTED_SUBTYPE;
|
||||||
import static com.google.android.exoplayer2.RendererConfiguration.DEFAULT;
|
import static com.google.android.exoplayer2.RendererConfiguration.DEFAULT;
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static org.mockito.Matchers.any;
|
import static org.mockito.Matchers.any;
|
||||||
|
|
@ -76,31 +77,8 @@ public final class DefaultTrackSelectorTest {
|
||||||
private static final RendererCapabilities[] RENDERER_CAPABILITIES_WITH_NO_SAMPLE_RENDERER =
|
private static final RendererCapabilities[] RENDERER_CAPABILITIES_WITH_NO_SAMPLE_RENDERER =
|
||||||
new RendererCapabilities[] {VIDEO_CAPABILITIES, NO_SAMPLE_CAPABILITIES};
|
new RendererCapabilities[] {VIDEO_CAPABILITIES, NO_SAMPLE_CAPABILITIES};
|
||||||
|
|
||||||
private static final Format VIDEO_FORMAT =
|
private static final Format VIDEO_FORMAT = buildVideoFormat("video");
|
||||||
Format.createVideoSampleFormat(
|
private static final Format AUDIO_FORMAT = buildAudioFormat("audio");
|
||||||
"video",
|
|
||||||
MimeTypes.VIDEO_H264,
|
|
||||||
null,
|
|
||||||
Format.NO_VALUE,
|
|
||||||
Format.NO_VALUE,
|
|
||||||
1024,
|
|
||||||
768,
|
|
||||||
Format.NO_VALUE,
|
|
||||||
null,
|
|
||||||
null);
|
|
||||||
private static final Format AUDIO_FORMAT =
|
|
||||||
Format.createAudioSampleFormat(
|
|
||||||
"audio",
|
|
||||||
MimeTypes.AUDIO_AAC,
|
|
||||||
null,
|
|
||||||
Format.NO_VALUE,
|
|
||||||
Format.NO_VALUE,
|
|
||||||
2,
|
|
||||||
44100,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
0,
|
|
||||||
null);
|
|
||||||
private static final TrackGroup VIDEO_TRACK_GROUP = new TrackGroup(VIDEO_FORMAT);
|
private static final TrackGroup VIDEO_TRACK_GROUP = new TrackGroup(VIDEO_FORMAT);
|
||||||
private static final TrackGroup AUDIO_TRACK_GROUP = new TrackGroup(AUDIO_FORMAT);
|
private static final TrackGroup AUDIO_TRACK_GROUP = new TrackGroup(AUDIO_FORMAT);
|
||||||
private static final TrackGroupArray TRACK_GROUPS =
|
private static final TrackGroupArray TRACK_GROUPS =
|
||||||
|
|
@ -339,12 +317,9 @@ public final class DefaultTrackSelectorTest {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testSelectTracksSelectTrackWithSelectionFlag() throws Exception {
|
public void testSelectTracksSelectTrackWithSelectionFlag() throws Exception {
|
||||||
Format audioFormat =
|
Format audioFormat = buildAudioFormat("audio", /* language= */ null, /* selectionFlags= */ 0);
|
||||||
Format.createAudioSampleFormat("audio", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE,
|
|
||||||
Format.NO_VALUE, 2, 44100, null, null, 0, null);
|
|
||||||
Format formatWithSelectionFlag =
|
Format formatWithSelectionFlag =
|
||||||
Format.createAudioSampleFormat("audio", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE,
|
buildAudioFormat("audio", /* language= */ null, C.SELECTION_FLAG_DEFAULT);
|
||||||
Format.NO_VALUE, 2, 44100, null, null, C.SELECTION_FLAG_DEFAULT, null);
|
|
||||||
|
|
||||||
TrackSelectorResult result = trackSelector.selectTracks(
|
TrackSelectorResult result = trackSelector.selectTracks(
|
||||||
new RendererCapabilities[] {ALL_AUDIO_FORMAT_SUPPORTED_RENDERER_CAPABILITIES},
|
new RendererCapabilities[] {ALL_AUDIO_FORMAT_SUPPORTED_RENDERER_CAPABILITIES},
|
||||||
|
|
@ -405,12 +380,8 @@ public final class DefaultTrackSelectorTest {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testSelectTracksPreferTrackWithinCapabilities() throws Exception {
|
public void testSelectTracksPreferTrackWithinCapabilities() throws Exception {
|
||||||
Format supportedFormat =
|
Format supportedFormat = buildAudioFormat("supportedFormat");
|
||||||
Format.createAudioSampleFormat("supportedFormat", MimeTypes.AUDIO_AAC, null,
|
Format exceededFormat = buildAudioFormat("exceededFormat");
|
||||||
Format.NO_VALUE, Format.NO_VALUE, 2, 44100, null, null, 0, null);
|
|
||||||
Format exceededFormat =
|
|
||||||
Format.createAudioSampleFormat("exceededFormat", MimeTypes.AUDIO_AAC, null,
|
|
||||||
Format.NO_VALUE, Format.NO_VALUE, 2, 44100, null, null, 0, null);
|
|
||||||
|
|
||||||
Map<String, Integer> mappedCapabilities = new HashMap<>();
|
Map<String, Integer> mappedCapabilities = new HashMap<>();
|
||||||
mappedCapabilities.put(supportedFormat.id, FORMAT_HANDLED);
|
mappedCapabilities.put(supportedFormat.id, FORMAT_HANDLED);
|
||||||
|
|
@ -781,10 +752,8 @@ public final class DefaultTrackSelectorTest {
|
||||||
RendererCapabilities[] textRendererCapabilities =
|
RendererCapabilities[] textRendererCapabilities =
|
||||||
new RendererCapabilities[] {ALL_TEXT_FORMAT_SUPPORTED_RENDERER_CAPABILITIES};
|
new RendererCapabilities[] {ALL_TEXT_FORMAT_SUPPORTED_RENDERER_CAPABILITIES};
|
||||||
|
|
||||||
TrackSelectorResult result;
|
|
||||||
|
|
||||||
// There is no text language preference, the first track flagged as default should be selected.
|
// There is no text language preference, the first track flagged as default should be selected.
|
||||||
result =
|
TrackSelectorResult result =
|
||||||
trackSelector.selectTracks(
|
trackSelector.selectTracks(
|
||||||
textRendererCapabilities, wrapFormats(forcedOnly, forcedDefault, defaultOnly, noFlag));
|
textRendererCapabilities, wrapFormats(forcedOnly, forcedDefault, defaultOnly, noFlag));
|
||||||
assertThat(result.selections.get(0).getFormat(0)).isSameAs(forcedDefault);
|
assertThat(result.selections.get(0).getFormat(0)).isSameAs(forcedDefault);
|
||||||
|
|
@ -878,10 +847,10 @@ public final class DefaultTrackSelectorTest {
|
||||||
RendererCapabilities[] textRendererCapabilites =
|
RendererCapabilities[] textRendererCapabilites =
|
||||||
new RendererCapabilities[] {ALL_TEXT_FORMAT_SUPPORTED_RENDERER_CAPABILITIES};
|
new RendererCapabilities[] {ALL_TEXT_FORMAT_SUPPORTED_RENDERER_CAPABILITIES};
|
||||||
|
|
||||||
TrackSelectorResult result;
|
TrackSelectorResult result =
|
||||||
|
trackSelector.selectTracks(
|
||||||
result = trackSelector.selectTracks(textRendererCapabilites,
|
textRendererCapabilites,
|
||||||
wrapFormats(spanish, german, undeterminedUnd, undeterminedNull));
|
wrapFormats(spanish, german, undeterminedUnd, undeterminedNull));
|
||||||
assertThat(result.selections.get(0)).isNull();
|
assertThat(result.selections.get(0)).isNull();
|
||||||
|
|
||||||
trackSelector.setParameters(
|
trackSelector.setParameters(
|
||||||
|
|
@ -913,6 +882,48 @@ public final class DefaultTrackSelectorTest {
|
||||||
assertThat(result.selections.get(0)).isNull();
|
assertThat(result.selections.get(0)).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Tests audio track selection when there are multiple audio renderers. */
|
||||||
|
@Test
|
||||||
|
public void testSelectPreferredTextTrackMultipleRenderers() throws Exception {
|
||||||
|
Format english = buildTextFormat("en", "en");
|
||||||
|
Format german = buildTextFormat("de", "de");
|
||||||
|
|
||||||
|
// First renderer handles english.
|
||||||
|
Map<String, Integer> firstRendererMappedCapabilities = new HashMap<>();
|
||||||
|
firstRendererMappedCapabilities.put(english.id, FORMAT_HANDLED);
|
||||||
|
firstRendererMappedCapabilities.put(german.id, FORMAT_UNSUPPORTED_SUBTYPE);
|
||||||
|
RendererCapabilities firstRendererCapabilities =
|
||||||
|
new FakeMappedRendererCapabilities(C.TRACK_TYPE_TEXT, firstRendererMappedCapabilities);
|
||||||
|
|
||||||
|
// Second renderer handles german.
|
||||||
|
Map<String, Integer> secondRendererMappedCapabilities = new HashMap<>();
|
||||||
|
secondRendererMappedCapabilities.put(english.id, FORMAT_UNSUPPORTED_SUBTYPE);
|
||||||
|
secondRendererMappedCapabilities.put(german.id, FORMAT_HANDLED);
|
||||||
|
RendererCapabilities secondRendererCapabilities =
|
||||||
|
new FakeMappedRendererCapabilities(C.TRACK_TYPE_TEXT, secondRendererMappedCapabilities);
|
||||||
|
|
||||||
|
RendererCapabilities[] rendererCapabilities =
|
||||||
|
new RendererCapabilities[] {firstRendererCapabilities, secondRendererCapabilities};
|
||||||
|
|
||||||
|
// Without an explicit language preference, nothing should be selected.
|
||||||
|
TrackSelectorResult result =
|
||||||
|
trackSelector.selectTracks(rendererCapabilities, wrapFormats(english, german));
|
||||||
|
assertThat(result.selections.get(0)).isNull();
|
||||||
|
assertThat(result.selections.get(1)).isNull();
|
||||||
|
|
||||||
|
// Explicit language preference for english. First renderer should be used.
|
||||||
|
trackSelector.setParameters(trackSelector.buildUponParameters().setPreferredTextLanguage("en"));
|
||||||
|
result = trackSelector.selectTracks(rendererCapabilities, wrapFormats(english, german));
|
||||||
|
assertThat(result.selections.get(0).getFormat(0)).isSameAs(english);
|
||||||
|
assertThat(result.selections.get(1)).isNull();
|
||||||
|
|
||||||
|
// Explicit language preference for German. Second renderer should be used.
|
||||||
|
trackSelector.setParameters(trackSelector.buildUponParameters().setPreferredTextLanguage("de"));
|
||||||
|
result = trackSelector.selectTracks(rendererCapabilities, wrapFormats(english, german));
|
||||||
|
assertThat(result.selections.get(0)).isNull();
|
||||||
|
assertThat(result.selections.get(1).getFormat(0)).isSameAs(german);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests that track selector will select audio tracks with lower bitrate when {@link Parameters}
|
* Tests that track selector will select audio tracks with lower bitrate when {@link Parameters}
|
||||||
* indicate lowest bitrate preference, even when tracks are within capabilities.
|
* indicate lowest bitrate preference, even when tracks are within capabilities.
|
||||||
|
|
@ -987,6 +998,50 @@ public final class DefaultTrackSelectorTest {
|
||||||
.createTrackSelection(trackGroupArray.get(0), bandwidthMeter, 1, 2);
|
.createTrackSelection(trackGroupArray.get(0), bandwidthMeter, 1, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Tests audio track selection when there are multiple audio renderers. */
|
||||||
|
@Test
|
||||||
|
public void testSelectPreferredAudioTrackMultipleRenderers() throws Exception {
|
||||||
|
Format english = buildAudioFormat("en", "en");
|
||||||
|
Format german = buildAudioFormat("de", "de");
|
||||||
|
|
||||||
|
// First renderer handles english.
|
||||||
|
Map<String, Integer> firstRendererMappedCapabilities = new HashMap<>();
|
||||||
|
firstRendererMappedCapabilities.put(english.id, FORMAT_HANDLED);
|
||||||
|
firstRendererMappedCapabilities.put(german.id, FORMAT_UNSUPPORTED_SUBTYPE);
|
||||||
|
RendererCapabilities firstRendererCapabilities =
|
||||||
|
new FakeMappedRendererCapabilities(C.TRACK_TYPE_AUDIO, firstRendererMappedCapabilities);
|
||||||
|
|
||||||
|
// Second renderer handles german.
|
||||||
|
Map<String, Integer> secondRendererMappedCapabilities = new HashMap<>();
|
||||||
|
secondRendererMappedCapabilities.put(english.id, FORMAT_UNSUPPORTED_SUBTYPE);
|
||||||
|
secondRendererMappedCapabilities.put(german.id, FORMAT_HANDLED);
|
||||||
|
RendererCapabilities secondRendererCapabilities =
|
||||||
|
new FakeMappedRendererCapabilities(C.TRACK_TYPE_AUDIO, secondRendererMappedCapabilities);
|
||||||
|
|
||||||
|
RendererCapabilities[] rendererCapabilities =
|
||||||
|
new RendererCapabilities[] {firstRendererCapabilities, secondRendererCapabilities};
|
||||||
|
|
||||||
|
// Without an explicit language preference, prefer the first renderer.
|
||||||
|
TrackSelectorResult result =
|
||||||
|
trackSelector.selectTracks(rendererCapabilities, wrapFormats(english, german));
|
||||||
|
assertThat(result.selections.get(0).getFormat(0)).isSameAs(english);
|
||||||
|
assertThat(result.selections.get(1)).isNull();
|
||||||
|
|
||||||
|
// Explicit language preference for english. First renderer should be used.
|
||||||
|
trackSelector.setParameters(
|
||||||
|
trackSelector.buildUponParameters().setPreferredAudioLanguage("en"));
|
||||||
|
result = trackSelector.selectTracks(rendererCapabilities, wrapFormats(english, german));
|
||||||
|
assertThat(result.selections.get(0).getFormat(0)).isSameAs(english);
|
||||||
|
assertThat(result.selections.get(1)).isNull();
|
||||||
|
|
||||||
|
// Explicit language preference for German. Second renderer should be used.
|
||||||
|
trackSelector.setParameters(
|
||||||
|
trackSelector.buildUponParameters().setPreferredAudioLanguage("de"));
|
||||||
|
result = trackSelector.selectTracks(rendererCapabilities, wrapFormats(english, german));
|
||||||
|
assertThat(result.selections.get(0)).isNull();
|
||||||
|
assertThat(result.selections.get(1).getFormat(0)).isSameAs(german);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSelectTracksWithMultipleVideoTracksReturnsAdaptiveTrackSelection()
|
public void testSelectTracksWithMultipleVideoTracksReturnsAdaptiveTrackSelection()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
@ -1057,6 +1112,43 @@ public final class DefaultTrackSelectorTest {
|
||||||
return new TrackGroupArray(trackGroups);
|
return new TrackGroupArray(trackGroups);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Format buildVideoFormat(String id) {
|
||||||
|
return Format.createVideoSampleFormat(
|
||||||
|
id,
|
||||||
|
MimeTypes.VIDEO_H264,
|
||||||
|
null,
|
||||||
|
Format.NO_VALUE,
|
||||||
|
Format.NO_VALUE,
|
||||||
|
1024,
|
||||||
|
768,
|
||||||
|
Format.NO_VALUE,
|
||||||
|
null,
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Format buildAudioFormat(String id) {
|
||||||
|
return buildAudioFormat(id, /* language= */ null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Format buildAudioFormat(String id, String language) {
|
||||||
|
return buildAudioFormat(id, language, /* selectionFlags= */ 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Format buildAudioFormat(String id, String language, int selectionFlags) {
|
||||||
|
return Format.createAudioSampleFormat(
|
||||||
|
id,
|
||||||
|
MimeTypes.AUDIO_AAC,
|
||||||
|
/* codecs= */ null,
|
||||||
|
/* bitrate= */ Format.NO_VALUE,
|
||||||
|
/* maxInputSize= */ Format.NO_VALUE,
|
||||||
|
/* channelCount= */ 2,
|
||||||
|
/* sampleRate= */ 44100,
|
||||||
|
/* initializationData= */ null,
|
||||||
|
/* drmInitData= */ null,
|
||||||
|
selectionFlags,
|
||||||
|
language);
|
||||||
|
}
|
||||||
|
|
||||||
private static Format buildTextFormat(String id, String language) {
|
private static Format buildTextFormat(String id, String language) {
|
||||||
return buildTextFormat(id, language, /* selectionFlags= */ 0);
|
return buildTextFormat(id, language, /* selectionFlags= */ 0);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue