Update DefaultTrackSelector to use more conditions when selecting audio track

Update the audio track selection logic in DefaultTrackSelector:

- When forcing lowest bitrate, use bitrate as tie-breaker when track scores are
the same, prefer the lower bitrate.
- Otherwise, use one of the following values as tie-breaker in order:
  - ChannelCount
  - SampleRate
  - BitRate
If the format being checked is within renderer's capabilities, select it if it
has higher tie-break value, else, select it if it has lower tie-break value.

If all tie-break values are the same, prefer the already selected track.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=171803092
This commit is contained in:
hoangtc 2017-10-11 03:54:15 -07:00 committed by Oliver Woodman
parent 5d4fa335f9
commit 763f663d01
2 changed files with 762 additions and 30 deletions

View File

@ -762,10 +762,9 @@ public class DefaultTrackSelector extends MappingTrackSelector {
protected TrackSelection selectAudioTrack(TrackGroupArray groups, int[][] formatSupport,
Parameters params, TrackSelection.Factory adaptiveTrackSelectionFactory)
throws ExoPlaybackException {
int selectedGroupIndex = C.INDEX_UNSET;
int selectedTrackIndex = C.INDEX_UNSET;
int selectedTrackScore = 0;
int selectedBitrate = Format.NO_VALUE;
int selectedGroupIndex = C.INDEX_UNSET;
AudioTrackScore selectedTrackScore = null;
for (int groupIndex = 0; groupIndex < groups.length; groupIndex++) {
TrackGroup trackGroup = groups.get(groupIndex);
int[] trackFormatSupport = formatSupport[groupIndex];
@ -773,15 +772,12 @@ public class DefaultTrackSelector extends MappingTrackSelector {
if (isSupported(trackFormatSupport[trackIndex],
params.exceedRendererCapabilitiesIfNecessary)) {
Format format = trackGroup.getFormat(trackIndex);
int trackScore = getAudioTrackScore(trackFormatSupport[trackIndex],
params.preferredAudioLanguage, format);
if (trackScore > selectedTrackScore
|| (trackScore == selectedTrackScore && params.forceLowestBitrate
&& compareFormatValues(format.bitrate, selectedBitrate) < 0)) {
AudioTrackScore trackScore =
new AudioTrackScore(format, params, trackFormatSupport[trackIndex]);
if (selectedTrackScore == null || trackScore.compareTo(selectedTrackScore) > 0) {
selectedGroupIndex = groupIndex;
selectedTrackIndex = trackIndex;
selectedTrackScore = trackScore;
selectedBitrate = format.bitrate;
}
}
}
@ -804,27 +800,6 @@ public class DefaultTrackSelector extends MappingTrackSelector {
return new FixedTrackSelection(selectedGroup, selectedTrackIndex);
}
private static int getAudioTrackScore(int formatSupport, String preferredLanguage,
Format format) {
boolean isDefault = (format.selectionFlags & C.SELECTION_FLAG_DEFAULT) != 0;
int trackScore;
if (formatHasLanguage(format, preferredLanguage)) {
if (isDefault) {
trackScore = 4;
} else {
trackScore = 3;
}
} else if (isDefault) {
trackScore = 2;
} else {
trackScore = 1;
}
if (isSupported(formatSupport, false)) {
trackScore += WITHIN_RENDERER_CAPABILITIES_BONUS;
}
return trackScore;
}
private static int[] getAdaptiveAudioTracks(TrackGroup group, int[] formatSupport,
boolean allowMixedMimeTypes) {
int selectedConfigurationTrackCount = 0;
@ -1090,6 +1065,103 @@ public class DefaultTrackSelector extends MappingTrackSelector {
}
}
/**
* 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 final Parameters parameters;
private final int withinRendererCapabilitiesScore;
private final int matchLanguageScore;
private final int defaultSelectionFlagScore;
private final int channelCount;
private final int sampleRate;
private final int bitrate;
public AudioTrackScore(Format format, Parameters parameters, int formatSupport) {
this.parameters = parameters;
withinRendererCapabilitiesScore = isSupported(formatSupport, false) ? 1 : 0;
matchLanguageScore = formatHasLanguage(format, parameters.preferredAudioLanguage) ? 1 : 0;
defaultSelectionFlagScore = (format.selectionFlags & C.SELECTION_FLAG_DEFAULT) != 0 ? 1 : 0;
channelCount = format.channelCount;
sampleRate = format.sampleRate;
bitrate = format.bitrate;
}
/**
* Compares the score of the current track format with another {@link AudioTrackScore}.
*
* @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 negative integer if this score is worse than the other.
*/
@Override
public int compareTo(AudioTrackScore other) {
if (this.withinRendererCapabilitiesScore != other.withinRendererCapabilitiesScore) {
return compareInts(this.withinRendererCapabilitiesScore,
other.withinRendererCapabilitiesScore);
} else if (this.matchLanguageScore != other.matchLanguageScore) {
return compareInts(this.matchLanguageScore, other.matchLanguageScore);
} else if (this.defaultSelectionFlagScore != other.defaultSelectionFlagScore) {
return compareInts(this.defaultSelectionFlagScore, other.defaultSelectionFlagScore);
} else if (parameters.forceLowestBitrate) {
return compareInts(other.bitrate, this.bitrate);
} else {
// If the format are within renderer capabilities, prefer higher values of channel count,
// sample rate and bit rate in that order. Otherwise, prefer lower values.
int resultSign = withinRendererCapabilitiesScore == 1 ? 1 : -1;
if (this.channelCount != other.channelCount) {
return resultSign * compareInts(this.channelCount, other.channelCount);
} else if (this.sampleRate != other.sampleRate) {
return resultSign * compareInts(this.sampleRate, other.sampleRate);
}
return resultSign * compareInts(this.bitrate, other.bitrate);
}
}
@Override
public boolean equals(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;
}
}
/**
* Compares two integers in a safe way and avoiding potential overflow.
*
* @param first The first value.
* @param second The second value.
* @return A negative integer if the first value is less than the second. Zero if they are equal.
* A positive integer if the first value is greater than the second.
*/
private static int compareInts(int first, int second) {
return first > second ? 1 : (second > first ? -1 : 0);
}
private static final class AudioConfigurationTuple {
public final int channelCount;

View File

@ -0,0 +1,660 @@
package com.google.android.exoplayer2.trackselection;
import static com.google.android.exoplayer2.RendererCapabilities.FORMAT_EXCEEDS_CAPABILITIES;
import static com.google.android.exoplayer2.RendererCapabilities.FORMAT_HANDLED;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.MockitoAnnotations.initMocks;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.RendererCapabilities;
import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.Parameters;
import com.google.android.exoplayer2.trackselection.TrackSelector.InvalidationListener;
import com.google.android.exoplayer2.util.MimeTypes;
import java.util.HashMap;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
/**
* Unit tests for {@link DefaultTrackSelector}.
*/
@RunWith(RobolectricTestRunner.class)
@Config(sdk = Config.TARGET_SDK, manifest = Config.NONE)
public final class DefaultTrackSelectorTest {
private static final Parameters DEFAULT_PARAMETERS = new Parameters();
private static final RendererCapabilities ALL_AUDIO_FORMAT_SUPPORTED_RENDERER_CAPABILITIES =
new FakeRendererCapabilities(C.TRACK_TYPE_AUDIO);
private static final RendererCapabilities ALL_AUDIO_FORMAT_EXCEEDED_RENDERER_CAPABILITIES =
new FakeRendererCapabilities(C.TRACK_TYPE_AUDIO, FORMAT_EXCEEDS_CAPABILITIES);
@Mock
private InvalidationListener invalidationListener;
private DefaultTrackSelector trackSelector;
@Before
public void setUp() {
initMocks(this);
trackSelector = new DefaultTrackSelector();
}
/**
* Tests that track selector will not call
* {@link InvalidationListener#onTrackSelectionsInvalidated()} when it's set with default
* values of {@link Parameters}.
*/
@Test
public void testSetParameterWithDefaultParametersDoesNotNotifyInvalidationListener()
throws Exception {
trackSelector.init(invalidationListener);
trackSelector.setParameters(DEFAULT_PARAMETERS);
verify(invalidationListener, never()).onTrackSelectionsInvalidated();
}
/**
* Tests that track selector will call {@link InvalidationListener#onTrackSelectionsInvalidated()}
* when it's set with non-default values of {@link Parameters}.
*/
@Test
public void testSetParameterWithNonDefaultParameterNotifyInvalidationListener()
throws Exception {
Parameters parameters = DEFAULT_PARAMETERS.withPreferredAudioLanguage("en");
trackSelector.init(invalidationListener);
trackSelector.setParameters(parameters);
verify(invalidationListener).onTrackSelectionsInvalidated();
}
/**
* Tests that track selector will not call
* {@link InvalidationListener#onTrackSelectionsInvalidated()} again when it's set with
* the same values of {@link Parameters}.
*/
@Test
public void testSetParameterWithSameParametersDoesNotNotifyInvalidationListenerAgain()
throws Exception {
Parameters parameters = DEFAULT_PARAMETERS.withPreferredAudioLanguage("en");
trackSelector.init(invalidationListener);
trackSelector.setParameters(parameters);
trackSelector.setParameters(parameters);
verify(invalidationListener, times(1)).onTrackSelectionsInvalidated();
}
/**
* Tests that track selector will select audio track with {@link C#SELECTION_FLAG_DEFAULT}
* given default values of {@link Parameters}.
*/
@Test
public void testSelectTracksSelectTrackWithSelectionFlag() throws Exception {
Format audioFormat =
Format.createAudioSampleFormat("audio", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE,
Format.NO_VALUE, 2, 44100, null, null, 0, null);
Format formatWithSelectionFlag =
Format.createAudioSampleFormat("audio", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE,
Format.NO_VALUE, 2, 44100, null, null, C.SELECTION_FLAG_DEFAULT, null);
TrackSelectorResult result = trackSelector.selectTracks(
new RendererCapabilities[] {ALL_AUDIO_FORMAT_SUPPORTED_RENDERER_CAPABILITIES},
singleTrackGroup(formatWithSelectionFlag, audioFormat));
assertThat(result.selections.get(0).getSelectedFormat()).isEqualTo(formatWithSelectionFlag);
}
/**
* Tests that track selector will select audio track with language that match preferred language
* given by {@link Parameters}.
*/
@Test
public void testSelectTracksSelectPreferredAudioLanguage()
throws Exception {
Parameters parameters = DEFAULT_PARAMETERS.withPreferredAudioLanguage("en");
trackSelector.setParameters(parameters);
Format frAudioFormat =
Format.createAudioSampleFormat("audio", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE,
Format.NO_VALUE, 2, 44100, null, null, 0, "fr");
Format enAudioFormat =
Format.createAudioSampleFormat("audio", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE,
Format.NO_VALUE, 2, 44100, null, null, 0, "en");
TrackSelectorResult result = trackSelector.selectTracks(
new RendererCapabilities[] {ALL_AUDIO_FORMAT_SUPPORTED_RENDERER_CAPABILITIES},
singleTrackGroup(frAudioFormat, enAudioFormat));
assertThat(result.selections.get(0).getSelectedFormat()).isEqualTo(enAudioFormat);
}
/**
* Tests that track selector will prefer selecting audio track with language that match preferred
* language given by {@link Parameters} over track with {@link C#SELECTION_FLAG_DEFAULT}.
*/
@Test
public void testSelectTracksSelectPreferredAudioLanguageOverSelectionFlag()
throws Exception {
Parameters parameters = DEFAULT_PARAMETERS.withPreferredAudioLanguage("en");
trackSelector.setParameters(parameters);
Format frAudioFormat =
Format.createAudioSampleFormat("audio", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE,
Format.NO_VALUE, 2, 44100, null, null, C.SELECTION_FLAG_DEFAULT, "fr");
Format enAudioFormat =
Format.createAudioSampleFormat("audio", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE,
Format.NO_VALUE, 2, 44100, null, null, 0, "en");
TrackSelectorResult result = trackSelector.selectTracks(
new RendererCapabilities[] {ALL_AUDIO_FORMAT_SUPPORTED_RENDERER_CAPABILITIES},
singleTrackGroup(frAudioFormat, enAudioFormat));
assertThat(result.selections.get(0).getSelectedFormat()).isEqualTo(enAudioFormat);
}
/**
* Tests that track selector will prefer tracks that are within renderer's capabilities over
* track that exceed renderer's capabilities.
*/
@Test
public void testSelectTracksPreferTrackWithinCapabilities()
throws Exception {
Format supportedFormat =
Format.createAudioSampleFormat("supportedFormat", MimeTypes.AUDIO_AAC, null,
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<>();
mappedCapabilities.put(supportedFormat.id, FORMAT_HANDLED);
mappedCapabilities.put(exceededFormat.id, FORMAT_EXCEEDS_CAPABILITIES);
RendererCapabilities mappedAudioRendererCapabilities =
new FakeMappedRendererCapabilities(C.TRACK_TYPE_AUDIO, mappedCapabilities);
TrackSelectorResult result = trackSelector.selectTracks(
new RendererCapabilities[] {mappedAudioRendererCapabilities},
singleTrackGroup(exceededFormat, supportedFormat));
assertThat(result.selections.get(0).getSelectedFormat()).isEqualTo(supportedFormat);
}
/**
* Tests that track selector will select a track that exceeds the renderer's capabilities when
* there are no other choice, given the default {@link Parameters}.
*/
@Test
public void testSelectTracksWithNoTrackWithinCapabilitiesSelectExceededCapabilityTrack()
throws Exception {
Format audioFormat =
Format.createAudioSampleFormat("audio", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE,
Format.NO_VALUE, 2, 44100, null, null, 0, null);
TrackSelectorResult result = trackSelector.selectTracks(
new RendererCapabilities[] {ALL_AUDIO_FORMAT_EXCEEDED_RENDERER_CAPABILITIES},
singleTrackGroup(audioFormat));
assertThat(result.selections.get(0).getSelectedFormat()).isEqualTo(audioFormat);
}
/**
* Tests that track selector will return a null track selection for a renderer when
* all tracks exceed that renderer's capabilities when {@link Parameters} does not allow
* exceeding-capabilities tracks.
*/
@Test
public void testSelectTracksWithNoTrackWithinCapabilitiesAndSetByParamsReturnNoSelection()
throws Exception {
Parameters parameters = DEFAULT_PARAMETERS.withExceedRendererCapabilitiesIfNecessary(false);
trackSelector.setParameters(parameters);
Format audioFormat =
Format.createAudioSampleFormat("audio", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE,
Format.NO_VALUE, 2, 44100, null, null, 0, null);
TrackSelectorResult result = trackSelector.selectTracks(
new RendererCapabilities[] {ALL_AUDIO_FORMAT_EXCEEDED_RENDERER_CAPABILITIES},
singleTrackGroup(audioFormat));
assertThat(result.selections.get(0)).isNull();
}
/**
* Tests that track selector will prefer tracks that are within renderer's capabilities over
* tracks that have {@link C#SELECTION_FLAG_DEFAULT} but exceed renderer's capabilities.
*/
@Test
public void testSelectTracksPreferTrackWithinCapabilitiesOverSelectionFlag()
throws Exception {
Format supportedFormat =
Format.createAudioSampleFormat("supportedFormat", MimeTypes.AUDIO_AAC, null,
Format.NO_VALUE, Format.NO_VALUE, 2, 44100, null, null, 0, null);
Format exceededWithSelectionFlagFormat =
Format.createAudioSampleFormat("exceededFormat", MimeTypes.AUDIO_AAC, null,
Format.NO_VALUE, Format.NO_VALUE, 2, 44100, null, null, C.SELECTION_FLAG_DEFAULT, null);
Map<String, Integer> mappedCapabilities = new HashMap<>();
mappedCapabilities.put(supportedFormat.id, FORMAT_HANDLED);
mappedCapabilities.put(exceededWithSelectionFlagFormat.id, FORMAT_EXCEEDS_CAPABILITIES);
RendererCapabilities mappedAudioRendererCapabilities =
new FakeMappedRendererCapabilities(C.TRACK_TYPE_AUDIO, mappedCapabilities);
TrackSelectorResult result = trackSelector.selectTracks(
new RendererCapabilities[] {mappedAudioRendererCapabilities},
singleTrackGroup(exceededWithSelectionFlagFormat, supportedFormat));
assertThat(result.selections.get(0).getSelectedFormat()).isEqualTo(supportedFormat);
}
/**
* Tests that track selector will prefer tracks that are within renderer's capabilities over
* track that have language matching preferred audio given by {@link Parameters} but exceed
* renderer's capabilities.
*/
@Test
public void testSelectTracksPreferTrackWithinCapabilitiesOverPreferredLanguage()
throws Exception {
Parameters parameters = DEFAULT_PARAMETERS.withPreferredAudioLanguage("en");
trackSelector.setParameters(parameters);
Format supportedFrFormat =
Format.createAudioSampleFormat("supportedFormat", MimeTypes.AUDIO_AAC, null,
Format.NO_VALUE, Format.NO_VALUE, 2, 44100, null, null, 0, "fr");
Format exceededEnFormat =
Format.createAudioSampleFormat("exceededFormat", MimeTypes.AUDIO_AAC, null,
Format.NO_VALUE, Format.NO_VALUE, 2, 44100, null, null, 0, "en");
Map<String, Integer> mappedCapabilities = new HashMap<>();
mappedCapabilities.put(exceededEnFormat.id, FORMAT_EXCEEDS_CAPABILITIES);
mappedCapabilities.put(supportedFrFormat.id, FORMAT_HANDLED);
RendererCapabilities mappedAudioRendererCapabilities =
new FakeMappedRendererCapabilities(C.TRACK_TYPE_AUDIO, mappedCapabilities);
TrackSelectorResult result = trackSelector.selectTracks(
new RendererCapabilities[] {mappedAudioRendererCapabilities},
singleTrackGroup(exceededEnFormat, supportedFrFormat));
assertThat(result.selections.get(0).getSelectedFormat()).isEqualTo(supportedFrFormat);
}
/**
* Tests that track selector will prefer tracks that are within renderer's capabilities over
* track that have both language matching preferred audio given by {@link Parameters} and
* {@link C#SELECTION_FLAG_DEFAULT}, but exceed renderer's capabilities.
*/
@Test
public void testSelectTracksPreferTrackWithinCapabilitiesOverSelectionFlagAndPreferredLanguage()
throws Exception {
Parameters parameters = DEFAULT_PARAMETERS.withPreferredAudioLanguage("en");
trackSelector.setParameters(parameters);
Format supportedFrFormat =
Format.createAudioSampleFormat("supportedFormat", MimeTypes.AUDIO_AAC, null,
Format.NO_VALUE, Format.NO_VALUE, 2, 44100, null, null, 0, "fr");
Format exceededDefaultSelectionEnFormat =
Format.createAudioSampleFormat("exceededFormat", MimeTypes.AUDIO_AAC, null,
Format.NO_VALUE, Format.NO_VALUE, 2, 44100, null, null, C.SELECTION_FLAG_DEFAULT, "en");
Map<String, Integer> mappedCapabilities = new HashMap<>();
mappedCapabilities.put(exceededDefaultSelectionEnFormat.id, FORMAT_EXCEEDS_CAPABILITIES);
mappedCapabilities.put(supportedFrFormat.id, FORMAT_HANDLED);
RendererCapabilities mappedAudioRendererCapabilities =
new FakeMappedRendererCapabilities(C.TRACK_TYPE_AUDIO, mappedCapabilities);
TrackSelectorResult result = trackSelector.selectTracks(
new RendererCapabilities[] {mappedAudioRendererCapabilities},
singleTrackGroup(exceededDefaultSelectionEnFormat, supportedFrFormat));
assertThat(result.selections.get(0).getSelectedFormat()).isEqualTo(supportedFrFormat);
}
/**
* Tests that track selector will select audio tracks with higher num channel when other factors
* are the same, and tracks are within renderer's capabilities.
*/
@Test
public void testSelectTracksWithinCapabilitiesSelectHigherNumChannel()
throws Exception {
Format lowerChannelFormat =
Format.createAudioSampleFormat("audioFormat", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE,
Format.NO_VALUE, 2, 44100, null, null, 0, null);
Format higherChannelFormat =
Format.createAudioSampleFormat("audioFormat", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE,
Format.NO_VALUE, 6, 44100, null, null, 0, null);
TrackSelectorResult result = trackSelector.selectTracks(
new RendererCapabilities[] {ALL_AUDIO_FORMAT_SUPPORTED_RENDERER_CAPABILITIES},
singleTrackGroup(higherChannelFormat, lowerChannelFormat));
assertThat(result.selections.get(0).getSelectedFormat()).isEqualTo(higherChannelFormat);
}
/**
* Tests that track selector will select audio tracks with higher sample rate when other factors
* are the same, and tracks are within renderer's capabilities.
*/
@Test
public void testSelectTracksWithinCapabilitiesSelectHigherSampleRate()
throws Exception {
Format higherSampleRateFormat =
Format.createAudioSampleFormat("audioFormat", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE,
Format.NO_VALUE, 2, 44100, null, null, 0, null);
Format lowerSampleRateFormat =
Format.createAudioSampleFormat("audioFormat", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE,
Format.NO_VALUE, 2, 22050, null, null, 0, null);
TrackSelectorResult result = trackSelector.selectTracks(
new RendererCapabilities[] {ALL_AUDIO_FORMAT_SUPPORTED_RENDERER_CAPABILITIES},
singleTrackGroup(higherSampleRateFormat, lowerSampleRateFormat));
assertThat(result.selections.get(0).getSelectedFormat()).isEqualTo(higherSampleRateFormat);
}
/**
* Tests that track selector will select audio tracks with higher bit-rate when other factors
* are the same, and tracks are within renderer's capabilities.
*/
@Test
public void testSelectTracksWithinCapabilitiesSelectHigherBitrate()
throws Exception {
Format lowerBitrateFormat =
Format.createAudioSampleFormat("audioFormat", MimeTypes.AUDIO_AAC, null, 15000,
Format.NO_VALUE, 2, 44100, null, null, 0, null);
Format higherBitrateFormat =
Format.createAudioSampleFormat("audioFormat", MimeTypes.AUDIO_AAC, null, 30000,
Format.NO_VALUE, 2, 44100, null, null, 0, null);
TrackSelectorResult result = trackSelector.selectTracks(
new RendererCapabilities[] {ALL_AUDIO_FORMAT_SUPPORTED_RENDERER_CAPABILITIES},
singleTrackGroup(lowerBitrateFormat, higherBitrateFormat));
assertThat(result.selections.get(0).getSelectedFormat()).isEqualTo(higherBitrateFormat);
}
/**
* Tests that track selector will prefer audio tracks with higher channel count over tracks with
* higher sample rate when other factors are the same, and tracks are within renderer's
* capabilities.
*/
@Test
public void testSelectTracksPreferHigherNumChannelBeforeSampleRate()
throws Exception {
Format lowerChannelHigherSampleRateFormat =
Format.createAudioSampleFormat("audioFormat", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE,
Format.NO_VALUE, 2, 44100, null, null, 0, null);
Format higherChannelLowerSampleRateFormat =
Format.createAudioSampleFormat("audioFormat", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE,
Format.NO_VALUE, 6, 22050, null, null, 0, null);
TrackSelectorResult result = trackSelector.selectTracks(
new RendererCapabilities[] {ALL_AUDIO_FORMAT_SUPPORTED_RENDERER_CAPABILITIES},
singleTrackGroup(higherChannelLowerSampleRateFormat, lowerChannelHigherSampleRateFormat));
assertThat(result.selections.get(0).getSelectedFormat())
.isEqualTo(higherChannelLowerSampleRateFormat);
}
/**
* Tests that track selector will prefer audio tracks with higher sample rate over tracks with
* higher bitrate when other factors are the same, and tracks are within renderer's
* capabilities.
*/
@Test
public void testSelectTracksPreferHigherSampleRateBeforeBitrate()
throws Exception {
Format higherSampleRateLowerBitrateFormat =
Format.createAudioSampleFormat("audioFormat", MimeTypes.AUDIO_AAC, null, 15000,
Format.NO_VALUE, 2, 44100, null, null, 0, null);
Format lowerSampleRateHigherBitrateFormat =
Format.createAudioSampleFormat("audioFormat", MimeTypes.AUDIO_AAC, null, 30000,
Format.NO_VALUE, 2, 22050, null, null, 0, null);
TrackSelectorResult result = trackSelector.selectTracks(
new RendererCapabilities[] {ALL_AUDIO_FORMAT_SUPPORTED_RENDERER_CAPABILITIES},
singleTrackGroup(higherSampleRateLowerBitrateFormat, lowerSampleRateHigherBitrateFormat));
assertThat(result.selections.get(0).getSelectedFormat())
.isEqualTo(higherSampleRateLowerBitrateFormat);
}
/**
* Tests that track selector will select audio tracks with lower num channel when other factors
* are the same, and tracks exceed renderer's capabilities.
*/
@Test
public void testSelectTracksExceedingCapabilitiesSelectLowerNumChannel()
throws Exception {
Format lowerChannelFormat =
Format.createAudioSampleFormat("audioFormat", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE,
Format.NO_VALUE, 2, 44100, null, null, 0, null);
Format higherChannelFormat =
Format.createAudioSampleFormat("audioFormat", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE,
Format.NO_VALUE, 6, 44100, null, null, 0, null);
TrackSelectorResult result = trackSelector.selectTracks(
new RendererCapabilities[] {ALL_AUDIO_FORMAT_EXCEEDED_RENDERER_CAPABILITIES},
singleTrackGroup(higherChannelFormat, lowerChannelFormat));
assertThat(result.selections.get(0).getSelectedFormat()).isEqualTo(lowerChannelFormat);
}
/**
* Tests that track selector will select audio tracks with lower sample rate when other factors
* are the same, and tracks exceed renderer's capabilities.
*/
@Test
public void testSelectTracksExceedingCapabilitiesSelectLowerSampleRate()
throws Exception {
Format lowerSampleRateFormat =
Format.createAudioSampleFormat("audioFormat", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE,
Format.NO_VALUE, 2, 22050, null, null, 0, null);
Format higherSampleRateFormat =
Format.createAudioSampleFormat("audioFormat", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE,
Format.NO_VALUE, 2, 44100, null, null, 0, null);
TrackSelectorResult result = trackSelector.selectTracks(
new RendererCapabilities[] {ALL_AUDIO_FORMAT_EXCEEDED_RENDERER_CAPABILITIES},
singleTrackGroup(higherSampleRateFormat, lowerSampleRateFormat));
assertThat(result.selections.get(0).getSelectedFormat()).isEqualTo(lowerSampleRateFormat);
}
/**
* Tests that track selector will select audio tracks with lower bit-rate when other factors
* are the same, and tracks exceed renderer's capabilities.
*/
@Test
public void testSelectTracksExceedingCapabilitiesSelectLowerBitrate()
throws Exception {
Format lowerBitrateFormat =
Format.createAudioSampleFormat("audioFormat", MimeTypes.AUDIO_AAC, null, 15000,
Format.NO_VALUE, 2, 44100, null, null, 0, null);
Format higherBitrateFormat =
Format.createAudioSampleFormat("audioFormat", MimeTypes.AUDIO_AAC, null, 30000,
Format.NO_VALUE, 2, 44100, null, null, 0, null);
TrackSelectorResult result = trackSelector.selectTracks(
new RendererCapabilities[] {ALL_AUDIO_FORMAT_EXCEEDED_RENDERER_CAPABILITIES},
singleTrackGroup(lowerBitrateFormat, higherBitrateFormat));
assertThat(result.selections.get(0).getSelectedFormat()).isEqualTo(lowerBitrateFormat);
}
/**
* Tests that track selector will prefer audio tracks with lower channel count over tracks with
* lower sample rate when other factors are the same, and tracks are within renderer's
* capabilities.
*/
@Test
public void testSelectTracksExceedingCapabilitiesPreferLowerNumChannelBeforeSampleRate()
throws Exception {
Format lowerChannelHigherSampleRateFormat =
Format.createAudioSampleFormat("audioFormat", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE,
Format.NO_VALUE, 2, 44100, null, null, 0, null);
Format higherChannelLowerSampleRateFormat =
Format.createAudioSampleFormat("audioFormat", MimeTypes.AUDIO_AAC, null, Format.NO_VALUE,
Format.NO_VALUE, 6, 22050, null, null, 0, null);
TrackSelectorResult result = trackSelector.selectTracks(
new RendererCapabilities[] {ALL_AUDIO_FORMAT_EXCEEDED_RENDERER_CAPABILITIES},
singleTrackGroup(higherChannelLowerSampleRateFormat, lowerChannelHigherSampleRateFormat));
assertThat(result.selections.get(0).getSelectedFormat())
.isEqualTo(lowerChannelHigherSampleRateFormat);
}
/**
* Tests that track selector will prefer audio tracks with lower sample rate over tracks with
* lower bitrate when other factors are the same, and tracks are within renderer's
* capabilities.
*/
@Test
public void testSelectTracksExceedingCapabilitiesPreferLowerSampleRateBeforeBitrate()
throws Exception {
Format higherSampleRateLowerBitrateFormat =
Format.createAudioSampleFormat("audioFormat", MimeTypes.AUDIO_AAC, null, 15000,
Format.NO_VALUE, 2, 44100, null, null, 0, null);
Format lowerSampleRateHigherBitrateFormat =
Format.createAudioSampleFormat("audioFormat", MimeTypes.AUDIO_AAC, null, 30000,
Format.NO_VALUE, 2, 22050, null, null, 0, null);
TrackSelectorResult result = trackSelector.selectTracks(
new RendererCapabilities[] {ALL_AUDIO_FORMAT_EXCEEDED_RENDERER_CAPABILITIES},
singleTrackGroup(higherSampleRateLowerBitrateFormat, lowerSampleRateHigherBitrateFormat));
assertThat(result.selections.get(0).getSelectedFormat())
.isEqualTo(lowerSampleRateHigherBitrateFormat);
}
/**
* Tests that track selector will select audio tracks with lower bitrate when {@link Parameters}
* indicate lowest bitrate preference, even when tracks are within capabilities.
*/
@Test
public void testSelectTracksWithinCapabilitiesAndForceLowestBitrateSelectLowerBitrate()
throws Exception {
Parameters parameters = DEFAULT_PARAMETERS.withForceLowestBitrate(true);
trackSelector.setParameters(parameters);
Format lowerBitrateFormat =
Format.createAudioSampleFormat("audioFormat", MimeTypes.AUDIO_AAC, null, 15000,
Format.NO_VALUE, 2, 44100, null, null, 0, null);
Format higherBitrateFormat =
Format.createAudioSampleFormat("audioFormat", MimeTypes.AUDIO_AAC, null, 30000,
Format.NO_VALUE, 2, 44100, null, null, 0, null);
TrackSelectorResult result = trackSelector.selectTracks(
new RendererCapabilities[] {ALL_AUDIO_FORMAT_SUPPORTED_RENDERER_CAPABILITIES},
singleTrackGroup(lowerBitrateFormat, higherBitrateFormat));
assertThat(result.selections.get(0).getSelectedFormat()).isEqualTo(lowerBitrateFormat);
}
private static TrackGroupArray singleTrackGroup(Format... formats) {
return new TrackGroupArray(new TrackGroup(formats));
}
/**
* A {@link RendererCapabilities} that advertises support for all formats of a given type using
* a provided support value. For any format that does not have the given track type,
* {@link #supportsFormat(Format)} will return {@link #FORMAT_UNSUPPORTED_TYPE}.
*/
private static final class FakeRendererCapabilities implements RendererCapabilities {
private final int trackType;
private final int supportValue;
/**
* Returns {@link FakeRendererCapabilities} that advertises adaptive support for all
* tracks of the given type.
*
* @param trackType the track type of all formats that this renderer capabilities advertises
* support for.
*/
FakeRendererCapabilities(int trackType) {
this(trackType, FORMAT_HANDLED | ADAPTIVE_SEAMLESS);
}
/**
* Returns {@link FakeRendererCapabilities} that advertises support level using given value
* for all tracks of the given type.
*
* @param trackType the track type of all formats that this renderer capabilities advertises
* support for.
* @param supportValue the support level value that will be returned for formats with
* the given type.
*/
FakeRendererCapabilities(int trackType, int supportValue) {
this.trackType = trackType;
this.supportValue = supportValue;
}
@Override
public int getTrackType() {
return trackType;
}
@Override
public int supportsFormat(Format format) throws ExoPlaybackException {
return MimeTypes.getTrackType(format.sampleMimeType) == trackType
? (supportValue) : FORMAT_UNSUPPORTED_TYPE;
}
@Override
public int supportsMixedMimeTypeAdaptation() throws ExoPlaybackException {
return ADAPTIVE_SEAMLESS;
}
}
/**
* A {@link RendererCapabilities} that advertises support for different formats using a mapping
* between format ID and format-support value.
*/
private static final class FakeMappedRendererCapabilities implements RendererCapabilities {
private final int trackType;
private final Map<String, Integer> formatToCapability;
/**
* Returns {@link FakeRendererCapabilities} that advertises support level using the given
* mapping between format ID and format-support value.
*
* @param trackType the track type to be returned for {@link #getTrackType()}
* @param formatToCapability a map of (format id, support level) that will be used to return
* support level for any given format. For any format that's not in the map,
* {@link #supportsFormat(Format)} will return {@link #FORMAT_UNSUPPORTED_TYPE}.
*/
FakeMappedRendererCapabilities(int trackType, Map<String, Integer> formatToCapability) {
this.trackType = trackType;
this.formatToCapability = new HashMap<>(formatToCapability);
}
@Override
public int getTrackType() {
return trackType;
}
@Override
public int supportsFormat(Format format) throws ExoPlaybackException {
return format.id != null && formatToCapability.containsKey(format.id)
? formatToCapability.get(format.id)
: FORMAT_UNSUPPORTED_TYPE;
}
@Override
public int supportsMixedMimeTypeAdaptation() throws ExoPlaybackException {
return ADAPTIVE_SEAMLESS;
}
}
}