Commit 50ddfccd by tonihei Committed by Oliver Woodman

Use consistent scoring style in DefaultTrackSelector.

We currently mix point-based systems with Comparable-based systems.
This switches all scoring to using Comparable and modernizes it by
using ComparisonChain.

Using Comparator chains is more maintainable than point systems because
the reader doesn't have to think about all combinatorial combinations
of points.

PiperOrigin-RevId: 322766278
parent d2517884
...@@ -38,6 +38,8 @@ import com.google.android.exoplayer2.source.TrackGroup; ...@@ -38,6 +38,8 @@ import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.Ordering;
import com.google.common.primitives.Ints; import com.google.common.primitives.Ints;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
...@@ -1488,12 +1490,15 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -1488,12 +1490,15 @@ public class DefaultTrackSelector extends MappingTrackSelector {
*/ */
private static final float FRACTION_TO_CONSIDER_FULLSCREEN = 0.98f; private static final float FRACTION_TO_CONSIDER_FULLSCREEN = 0.98f;
private static final int[] NO_TRACKS = new int[0]; private static final int[] NO_TRACKS = new int[0];
/** Ordering of two format values. A known value is considered greater than Format#NO_VALUE. */
// Constants that are added to a track's score when certain conditions are met. Higher scoring private static final Ordering<Integer> FORMAT_VALUE_ORDERING =
// tracks are selected over those with lower scores. Ordering.from(
private static final int WITHIN_RENDERER_CAPABILITIES_BONUS = 1000; (first, second) ->
private static final int SATISFIES_MIN_VIDEO_CONSTRAINTS_BONUS = 1; first == Format.NO_VALUE
private static final int SATISFIES_MAX_VIDEO_CONSTRAINTS_BONUS = 2; ? (second == Format.NO_VALUE ? 0 : -1)
: (second == Format.NO_VALUE ? 1 : (first - second)));
/** Ordering where all elements are equal. */
private static final Ordering<Integer> NO_ORDER = Ordering.from((first, second) -> 0);
private final TrackSelection.Factory trackSelectionFactory; private final TrackSelection.Factory trackSelectionFactory;
private final AtomicReference<Parameters> parametersReference; private final AtomicReference<Parameters> parametersReference;
...@@ -1690,13 +1695,14 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -1690,13 +1695,14 @@ public class DefaultTrackSelector extends MappingTrackSelector {
} }
} }
AudioTrackScore selectedAudioTrackScore = null; @Nullable AudioTrackScore selectedAudioTrackScore = null;
String selectedAudioLanguage = null; @Nullable String selectedAudioLanguage = null;
int selectedAudioRendererIndex = C.INDEX_UNSET; int selectedAudioRendererIndex = C.INDEX_UNSET;
for (int i = 0; i < rendererCount; i++) { for (int i = 0; i < rendererCount; i++) {
if (C.TRACK_TYPE_AUDIO == mappedTrackInfo.getRendererType(i)) { if (C.TRACK_TYPE_AUDIO == mappedTrackInfo.getRendererType(i)) {
boolean enableAdaptiveTrackSelection = boolean enableAdaptiveTrackSelection =
allowMultipleAdaptiveSelections || !seenVideoRendererWithMappedTracks; allowMultipleAdaptiveSelections || !seenVideoRendererWithMappedTracks;
@Nullable
Pair<TrackSelection.Definition, AudioTrackScore> audioSelection = Pair<TrackSelection.Definition, AudioTrackScore> audioSelection =
selectAudioTrack( selectAudioTrack(
mappedTrackInfo.getTrackGroups(i), mappedTrackInfo.getTrackGroups(i),
...@@ -1722,7 +1728,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -1722,7 +1728,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
} }
} }
TextTrackScore selectedTextTrackScore = null; @Nullable TextTrackScore selectedTextTrackScore = null;
int selectedTextRendererIndex = C.INDEX_UNSET; 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);
...@@ -1732,6 +1738,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -1732,6 +1738,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
// Already done. Do nothing. // Already done. Do nothing.
break; break;
case C.TRACK_TYPE_TEXT: case C.TRACK_TYPE_TEXT:
@Nullable
Pair<TrackSelection.Definition, TextTrackScore> textSelection = Pair<TrackSelection.Definition, TextTrackScore> textSelection =
selectTextTrack( selectTextTrack(
mappedTrackInfo.getTrackGroups(i), mappedTrackInfo.getTrackGroups(i),
...@@ -2019,15 +2026,17 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -2019,15 +2026,17 @@ public class DefaultTrackSelector extends MappingTrackSelector {
@Nullable @Nullable
private static TrackSelection.Definition selectFixedVideoTrack( private static TrackSelection.Definition selectFixedVideoTrack(
TrackGroupArray groups, @Capabilities int[][] formatSupports, Parameters params) { TrackGroupArray groups, @Capabilities int[][] formatSupports, Parameters params) {
int selectedTrackIndex = C.INDEX_UNSET;
@Nullable TrackGroup selectedGroup = null; @Nullable TrackGroup selectedGroup = null;
int selectedTrackIndex = 0; @Nullable VideoTrackScore selectedTrackScore = null;
int selectedTrackScore = 0;
int selectedBitrate = Format.NO_VALUE;
int selectedPixelCount = Format.NO_VALUE;
for (int groupIndex = 0; groupIndex < groups.length; groupIndex++) { for (int groupIndex = 0; groupIndex < groups.length; groupIndex++) {
TrackGroup trackGroup = groups.get(groupIndex); TrackGroup trackGroup = groups.get(groupIndex);
List<Integer> selectedTrackIndices = getViewportFilteredTrackIndices(trackGroup, List<Integer> viewportFilteredTrackIndices =
params.viewportWidth, params.viewportHeight, params.viewportOrientationMayChange); getViewportFilteredTrackIndices(
trackGroup,
params.viewportWidth,
params.viewportHeight,
params.viewportOrientationMayChange);
@Capabilities int[] trackFormatSupport = formatSupports[groupIndex]; @Capabilities int[] trackFormatSupport = formatSupports[groupIndex];
for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) { for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
Format format = trackGroup.getFormat(trackIndex); Format format = trackGroup.getFormat(trackIndex);
...@@ -2037,82 +2046,25 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -2037,82 +2046,25 @@ public class DefaultTrackSelector extends MappingTrackSelector {
} }
if (isSupported(trackFormatSupport[trackIndex], if (isSupported(trackFormatSupport[trackIndex],
params.exceedRendererCapabilitiesIfNecessary)) { params.exceedRendererCapabilitiesIfNecessary)) {
boolean satisfiesMaxConstraints = VideoTrackScore trackScore =
selectedTrackIndices.contains(trackIndex) new VideoTrackScore(
&& (format.width == Format.NO_VALUE || format.width <= params.maxVideoWidth) format,
&& (format.height == Format.NO_VALUE || format.height <= params.maxVideoHeight) params,
&& (format.frameRate == Format.NO_VALUE trackFormatSupport[trackIndex],
|| format.frameRate <= params.maxVideoFrameRate) viewportFilteredTrackIndices.contains(trackIndex));
&& (format.bitrate == Format.NO_VALUE if (!trackScore.isWithinMaxConstraints && !params.exceedVideoConstraintsIfNecessary) {
|| format.bitrate <= params.maxVideoBitrate);
boolean satisfiesMinConstraints =
selectedTrackIndices.contains(trackIndex)
&& (format.width == Format.NO_VALUE || format.width >= params.minVideoWidth)
&& (format.height == Format.NO_VALUE || format.height >= params.minVideoHeight)
&& (format.frameRate == Format.NO_VALUE
|| format.frameRate >= params.minVideoFrameRate)
&& (format.bitrate == Format.NO_VALUE
|| format.bitrate >= params.minVideoBitrate);
if (!satisfiesMaxConstraints && !params.exceedVideoConstraintsIfNecessary) {
// Track should not be selected. // Track should not be selected.
continue; continue;
} }
int trackScore = 1; if (selectedTrackScore == null || trackScore.compareTo(selectedTrackScore) > 0) {
boolean isWithinCapabilities = isSupported(trackFormatSupport[trackIndex], false);
if (isWithinCapabilities) {
trackScore += WITHIN_RENDERER_CAPABILITIES_BONUS;
}
if (satisfiesMaxConstraints) {
trackScore += SATISFIES_MAX_VIDEO_CONSTRAINTS_BONUS;
}
if (satisfiesMinConstraints) {
trackScore += SATISFIES_MIN_VIDEO_CONSTRAINTS_BONUS;
}
boolean selectTrack = trackScore > selectedTrackScore;
if (trackScore == selectedTrackScore) {
// The current selection and the track being evaluated have the same score. Apply
// tiebreaking logic to choose between them.
int bitrateComparison = compareFormatValues(format.bitrate, selectedBitrate);
if (params.forceLowestBitrate && bitrateComparison != 0) {
// Use bitrate as a tiebreaker, preferring the lower bitrate.
selectTrack = bitrateComparison < 0;
} else {
// Use pixel count as a tiebreaker, followed by bitrate if pixel counts are equal.
int formatPixelCount = format.getPixelCount();
int comparisonResult = formatPixelCount != selectedPixelCount
? compareFormatValues(formatPixelCount, selectedPixelCount)
: compareFormatValues(format.bitrate, selectedBitrate);
if (!isWithinCapabilities) {
// We're not within capabilities, so pick the lower quality because it's more likely
// to play successfully.
selectTrack = comparisonResult < 0;
} else if (satisfiesMinConstraints && satisfiesMaxConstraints) {
// All constraints are satisfied, so pick the higher quality.
selectTrack = comparisonResult > 0;
} else if (!satisfiesMinConstraints && satisfiesMaxConstraints) {
// Pick the higher quality because it gets us closest to satisfying the violated min
// constraints.
selectTrack = comparisonResult > 0;
} else if (satisfiesMinConstraints) { // !satisfiesMaxConstraints
// Pick the lower quality because it gets us closest to satisfying the violated max
// constraints.
selectTrack = comparisonResult > 0;
} else { // !satisfiesMinConstraints && !satisfiesMaxConstraints
// Arbitrarily pick the lower quality.
selectTrack = comparisonResult < 0;
}
}
}
if (selectTrack) {
selectedGroup = trackGroup; selectedGroup = trackGroup;
selectedTrackIndex = trackIndex; selectedTrackIndex = trackIndex;
selectedTrackScore = trackScore; selectedTrackScore = trackScore;
selectedBitrate = format.bitrate;
selectedPixelCount = format.getPixelCount();
} }
} }
} }
} }
return selectedGroup == null return selectedGroup == null
? null ? null
: new TrackSelection.Definition(selectedGroup, selectedTrackIndex); : new TrackSelection.Definition(selectedGroup, selectedTrackIndex);
...@@ -2146,7 +2098,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -2146,7 +2098,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
throws ExoPlaybackException { throws ExoPlaybackException {
int selectedTrackIndex = C.INDEX_UNSET; int selectedTrackIndex = C.INDEX_UNSET;
int selectedGroupIndex = C.INDEX_UNSET; int selectedGroupIndex = C.INDEX_UNSET;
AudioTrackScore selectedTrackScore = null; @Nullable AudioTrackScore selectedTrackScore = null;
for (int groupIndex = 0; groupIndex < groups.length; groupIndex++) { for (int groupIndex = 0; groupIndex < groups.length; groupIndex++) {
TrackGroup trackGroup = groups.get(groupIndex); TrackGroup trackGroup = groups.get(groupIndex);
@Capabilities int[] trackFormatSupport = formatSupports[groupIndex]; @Capabilities int[] trackFormatSupport = formatSupports[groupIndex];
...@@ -2272,9 +2224,9 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -2272,9 +2224,9 @@ public class DefaultTrackSelector extends MappingTrackSelector {
Parameters params, Parameters params,
@Nullable String selectedAudioLanguage) @Nullable String selectedAudioLanguage)
throws ExoPlaybackException { throws ExoPlaybackException {
TrackGroup selectedGroup = null; @Nullable TrackGroup selectedGroup = null;
int selectedTrackIndex = C.INDEX_UNSET; int selectedTrackIndex = C.INDEX_UNSET;
TextTrackScore selectedTrackScore = null; @Nullable TextTrackScore selectedTrackScore = null;
for (int groupIndex = 0; groupIndex < groups.length; groupIndex++) { for (int groupIndex = 0; groupIndex < groups.length; groupIndex++) {
TrackGroup trackGroup = groups.get(groupIndex); TrackGroup trackGroup = groups.get(groupIndex);
@Capabilities int[] trackFormatSupport = formatSupport[groupIndex]; @Capabilities int[] trackFormatSupport = formatSupport[groupIndex];
...@@ -2319,9 +2271,9 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -2319,9 +2271,9 @@ public class DefaultTrackSelector extends MappingTrackSelector {
protected TrackSelection.Definition selectOtherTrack( protected TrackSelection.Definition selectOtherTrack(
int trackType, TrackGroupArray groups, @Capabilities int[][] formatSupport, Parameters params) int trackType, TrackGroupArray groups, @Capabilities int[][] formatSupport, Parameters params)
throws ExoPlaybackException { throws ExoPlaybackException {
TrackGroup selectedGroup = null; @Nullable TrackGroup selectedGroup = null;
int selectedTrackIndex = 0; int selectedTrackIndex = 0;
int selectedTrackScore = 0; @Nullable OtherTrackScore selectedTrackScore = null;
for (int groupIndex = 0; groupIndex < groups.length; groupIndex++) { for (int groupIndex = 0; groupIndex < groups.length; groupIndex++) {
TrackGroup trackGroup = groups.get(groupIndex); TrackGroup trackGroup = groups.get(groupIndex);
@Capabilities int[] trackFormatSupport = formatSupport[groupIndex]; @Capabilities int[] trackFormatSupport = formatSupport[groupIndex];
...@@ -2329,12 +2281,8 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -2329,12 +2281,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
if (isSupported(trackFormatSupport[trackIndex], if (isSupported(trackFormatSupport[trackIndex],
params.exceedRendererCapabilitiesIfNecessary)) { params.exceedRendererCapabilitiesIfNecessary)) {
Format format = trackGroup.getFormat(trackIndex); Format format = trackGroup.getFormat(trackIndex);
boolean isDefault = (format.selectionFlags & C.SELECTION_FLAG_DEFAULT) != 0; OtherTrackScore trackScore = new OtherTrackScore(format, trackFormatSupport[trackIndex]);
int trackScore = isDefault ? 2 : 1; if (selectedTrackScore == null || trackScore.compareTo(selectedTrackScore) > 0) {
if (isSupported(trackFormatSupport[trackIndex], false)) {
trackScore += WITHIN_RENDERER_CAPABILITIES_BONUS;
}
if (trackScore > selectedTrackScore) {
selectedGroup = trackGroup; selectedGroup = trackGroup;
selectedTrackIndex = trackIndex; selectedTrackIndex = trackIndex;
selectedTrackScore = trackScore; selectedTrackScore = trackScore;
...@@ -2438,21 +2386,6 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -2438,21 +2386,6 @@ public class DefaultTrackSelector extends MappingTrackSelector {
} }
/** /**
* Compares two format values for order. A known value is considered greater than {@link
* Format#NO_VALUE}.
*
* @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 compareFormatValues(int first, int second) {
return first == Format.NO_VALUE
? (second == Format.NO_VALUE ? 0 : -1)
: (second == Format.NO_VALUE ? 1 : (first - second));
}
/**
* Returns true if the {@link FormatSupport} in the given {@link Capabilities} is {@link * Returns true if the {@link FormatSupport} in the given {@link Capabilities} is {@link
* RendererCapabilities#FORMAT_HANDLED} or if {@code allowExceedsCapabilities} is set and the * RendererCapabilities#FORMAT_HANDLED} or if {@code allowExceedsCapabilities} is set and the
* format support is {@link RendererCapabilities#FORMAT_EXCEEDS_CAPABILITIES}. * format support is {@link RendererCapabilities#FORMAT_EXCEEDS_CAPABILITIES}.
...@@ -2590,16 +2523,75 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -2590,16 +2523,75 @@ public class DefaultTrackSelector extends MappingTrackSelector {
} }
} }
/** Represents how well a video track matches the selection {@link Parameters}. */
protected static final class VideoTrackScore implements Comparable<VideoTrackScore> {
/** /**
* Compares two integers in a safe way avoiding potential overflow. * Whether the provided format is within the parameter maximum constraints. If {@code false},
* * the format should not be selected.
* @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) { public final boolean isWithinMaxConstraints;
return first > second ? 1 : (second > first ? -1 : 0);
private final Parameters parameters;
private final boolean isWithinMinConstraints;
private final boolean isWithinRendererCapabilities;
private final int bitrate;
private final int pixelCount;
public VideoTrackScore(
Format format,
Parameters parameters,
@Capabilities int formatSupport,
boolean isSuitableForViewport) {
this.parameters = parameters;
isWithinMaxConstraints =
isSuitableForViewport
&& (format.width == Format.NO_VALUE || format.width <= parameters.maxVideoWidth)
&& (format.height == Format.NO_VALUE || format.height <= parameters.maxVideoHeight)
&& (format.frameRate == Format.NO_VALUE
|| format.frameRate <= parameters.maxVideoFrameRate)
&& (format.bitrate == Format.NO_VALUE
|| format.bitrate <= parameters.maxVideoBitrate);
isWithinMinConstraints =
isSuitableForViewport
&& (format.width == Format.NO_VALUE || format.width >= parameters.minVideoWidth)
&& (format.height == Format.NO_VALUE || format.height >= parameters.minVideoHeight)
&& (format.frameRate == Format.NO_VALUE
|| format.frameRate >= parameters.minVideoFrameRate)
&& (format.bitrate == Format.NO_VALUE
|| format.bitrate >= parameters.minVideoBitrate);
isWithinRendererCapabilities =
isSupported(formatSupport, /* allowExceedsCapabilities= */ false);
bitrate = format.bitrate;
pixelCount = format.getPixelCount();
}
@Override
public int compareTo(VideoTrackScore other) {
// The preferred ordering by video quality depends on the constraints:
// - Not within renderer capabilities: Prefer lower quality because it's more likely to play.
// - Within min and max constraints: Prefer higher quality.
// - Within max constraints only: Prefer higher quality because it gets us closest to
// satisfying the violated min constraints.
// - Within min constraints only: Prefer lower quality because it gets us closest to
// satisfying the violated max constraints.
// - Outside min and max constraints: Arbitrarily prefer lower quality.
Ordering<Integer> qualityOrdering =
isWithinMaxConstraints && isWithinRendererCapabilities
? FORMAT_VALUE_ORDERING
: FORMAT_VALUE_ORDERING.reverse();
return ComparisonChain.start()
.compareFalseFirst(this.isWithinRendererCapabilities, other.isWithinRendererCapabilities)
.compareFalseFirst(this.isWithinMaxConstraints, other.isWithinMaxConstraints)
.compareFalseFirst(this.isWithinMinConstraints, other.isWithinMinConstraints)
.compare(
this.bitrate,
other.bitrate,
parameters.forceLowestBitrate ? FORMAT_VALUE_ORDERING.reverse() : NO_ORDER)
.compare(this.pixelCount, other.pixelCount, qualityOrdering)
.compare(this.bitrate, other.bitrate, qualityOrdering)
.result();
}
} }
/** Represents how well an audio track matches the selection {@link Parameters}. */ /** Represents how well an audio track matches the selection {@link Parameters}. */
...@@ -2625,7 +2617,8 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -2625,7 +2617,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
public AudioTrackScore(Format format, Parameters parameters, @Capabilities int formatSupport) { public AudioTrackScore(Format format, Parameters parameters, @Capabilities int formatSupport) {
this.parameters = parameters; this.parameters = parameters;
this.language = normalizeUndeterminedLanguageToNull(format.language); this.language = normalizeUndeterminedLanguageToNull(format.language);
isWithinRendererCapabilities = isSupported(formatSupport, false); isWithinRendererCapabilities =
isSupported(formatSupport, /* allowExceedsCapabilities= */ false);
preferredLanguageScore = preferredLanguageScore =
getFormatLanguageScore( getFormatLanguageScore(
format, format,
...@@ -2665,44 +2658,34 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -2665,44 +2658,34 @@ public class DefaultTrackSelector extends MappingTrackSelector {
*/ */
@Override @Override
public int compareTo(AudioTrackScore other) { public int compareTo(AudioTrackScore other) {
if (this.isWithinRendererCapabilities != other.isWithinRendererCapabilities) {
return this.isWithinRendererCapabilities ? 1 : -1;
}
if (this.preferredLanguageScore != other.preferredLanguageScore) {
return compareInts(this.preferredLanguageScore, other.preferredLanguageScore);
}
if (this.isWithinConstraints != other.isWithinConstraints) {
return this.isWithinConstraints ? 1 : -1;
}
if (parameters.forceLowestBitrate) {
int bitrateComparison = compareFormatValues(bitrate, other.bitrate);
if (bitrateComparison != 0) {
return bitrateComparison > 0 ? -1 : 1;
}
}
if (this.isDefaultSelectionFlag != other.isDefaultSelectionFlag) {
return this.isDefaultSelectionFlag ? 1 : -1;
}
if (this.localeLanguageMatchIndex != other.localeLanguageMatchIndex) {
return -compareInts(this.localeLanguageMatchIndex, other.localeLanguageMatchIndex);
}
if (this.localeLanguageScore != other.localeLanguageScore) {
return compareInts(this.localeLanguageScore, other.localeLanguageScore);
}
// If the formats are within constraints and renderer capabilities then prefer higher values // If the formats are within constraints and renderer capabilities then prefer higher values
// of channel count, sample rate and bit rate in that order. Otherwise, prefer lower values. // of channel count, sample rate and bit rate in that order. Otherwise, prefer lower values.
int resultSign = isWithinConstraints && isWithinRendererCapabilities ? 1 : -1; Ordering<Integer> qualityOrdering =
if (this.channelCount != other.channelCount) { isWithinConstraints && isWithinRendererCapabilities
return resultSign * compareInts(this.channelCount, other.channelCount); ? FORMAT_VALUE_ORDERING
} : FORMAT_VALUE_ORDERING.reverse();
if (this.sampleRate != other.sampleRate) { return ComparisonChain.start()
return resultSign * compareInts(this.sampleRate, other.sampleRate); .compareFalseFirst(this.isWithinRendererCapabilities, other.isWithinRendererCapabilities)
} .compare(this.preferredLanguageScore, other.preferredLanguageScore)
if (Util.areEqual(this.language, other.language)) { .compareFalseFirst(this.isWithinConstraints, other.isWithinConstraints)
// Only compare bit rates of tracks with the same or unknown language. .compare(
return resultSign * compareInts(this.bitrate, other.bitrate); this.bitrate,
} other.bitrate,
return 0; parameters.forceLowestBitrate ? FORMAT_VALUE_ORDERING.reverse() : NO_ORDER)
.compareFalseFirst(this.isDefaultSelectionFlag, other.isDefaultSelectionFlag)
.compare(
this.localeLanguageMatchIndex,
other.localeLanguageMatchIndex,
Ordering.natural().reverse())
.compare(this.localeLanguageScore, other.localeLanguageScore)
.compare(this.channelCount, other.channelCount, qualityOrdering)
.compare(this.sampleRate, other.sampleRate, qualityOrdering)
.compare(
this.bitrate,
other.bitrate,
// Only compare bit rates of tracks with matching language information.
Util.areEqual(this.language, other.language) ? qualityOrdering : NO_ORDER)
.result();
} }
} }
...@@ -2717,7 +2700,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -2717,7 +2700,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
private final boolean isWithinRendererCapabilities; private final boolean isWithinRendererCapabilities;
private final boolean isDefault; private final boolean isDefault;
private final boolean hasPreferredIsForcedFlag; private final boolean isForced;
private final int preferredLanguageScore; private final int preferredLanguageScore;
private final int preferredRoleFlagsScore; private final int preferredRoleFlagsScore;
private final int selectedAudioLanguageScore; private final int selectedAudioLanguageScore;
...@@ -2733,7 +2716,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -2733,7 +2716,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
int maskedSelectionFlags = int maskedSelectionFlags =
format.selectionFlags & ~parameters.disabledTextTrackSelectionFlags; format.selectionFlags & ~parameters.disabledTextTrackSelectionFlags;
isDefault = (maskedSelectionFlags & C.SELECTION_FLAG_DEFAULT) != 0; isDefault = (maskedSelectionFlags & C.SELECTION_FLAG_DEFAULT) != 0;
boolean isForced = (maskedSelectionFlags & C.SELECTION_FLAG_FORCED) != 0; isForced = (maskedSelectionFlags & C.SELECTION_FLAG_FORCED) != 0;
preferredLanguageScore = preferredLanguageScore =
getFormatLanguageScore( getFormatLanguageScore(
format, parameters.preferredTextLanguage, parameters.selectUndeterminedTextLanguage); format, parameters.preferredTextLanguage, parameters.selectUndeterminedTextLanguage);
...@@ -2741,11 +2724,6 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -2741,11 +2724,6 @@ public class DefaultTrackSelector extends MappingTrackSelector {
Integer.bitCount(format.roleFlags & parameters.preferredTextRoleFlags); Integer.bitCount(format.roleFlags & parameters.preferredTextRoleFlags);
hasCaptionRoleFlags = hasCaptionRoleFlags =
(format.roleFlags & (C.ROLE_FLAG_CAPTION | C.ROLE_FLAG_DESCRIBES_MUSIC_AND_SOUND)) != 0; (format.roleFlags & (C.ROLE_FLAG_CAPTION | C.ROLE_FLAG_DESCRIBES_MUSIC_AND_SOUND)) != 0;
// Prefer non-forced to forced if a preferred text language has been matched. Where both are
// provided the non-forced track will usually contain the forced subtitles as a subset.
// Otherwise, prefer a forced track.
hasPreferredIsForcedFlag =
(preferredLanguageScore > 0 && !isForced) || (preferredLanguageScore == 0 && isForced);
boolean selectedAudioLanguageUndetermined = boolean selectedAudioLanguageUndetermined =
normalizeUndeterminedLanguageToNull(selectedAudioLanguage) == null; normalizeUndeterminedLanguageToNull(selectedAudioLanguage) == null;
selectedAudioLanguageScore = selectedAudioLanguageScore =
...@@ -2766,28 +2744,49 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -2766,28 +2744,49 @@ public class DefaultTrackSelector extends MappingTrackSelector {
*/ */
@Override @Override
public int compareTo(TextTrackScore other) { public int compareTo(TextTrackScore other) {
if (this.isWithinRendererCapabilities != other.isWithinRendererCapabilities) { ComparisonChain chain =
return this.isWithinRendererCapabilities ? 1 : -1; ComparisonChain.start()
} .compareFalseFirst(
if (this.preferredLanguageScore != other.preferredLanguageScore) { this.isWithinRendererCapabilities, other.isWithinRendererCapabilities)
return compareInts(this.preferredLanguageScore, other.preferredLanguageScore); .compare(this.preferredLanguageScore, other.preferredLanguageScore)
.compare(this.preferredRoleFlagsScore, other.preferredRoleFlagsScore)
.compareFalseFirst(this.isDefault, other.isDefault)
.compare(
this.isForced,
other.isForced,
// Prefer non-forced to forced if a preferred text language has been matched.
// Where both are provided the non-forced track will usually contain the forced
// subtitles as a subset. Otherwise, prefer a forced track.
preferredLanguageScore == 0 ? Ordering.natural() : Ordering.natural().reverse())
.compare(this.selectedAudioLanguageScore, other.selectedAudioLanguageScore);
if (preferredRoleFlagsScore == 0) {
chain = chain.compareTrueFirst(this.hasCaptionRoleFlags, other.hasCaptionRoleFlags);
} }
if (this.preferredRoleFlagsScore != other.preferredRoleFlagsScore) { return chain.result();
return compareInts(this.preferredRoleFlagsScore, other.preferredRoleFlagsScore);
} }
if (this.isDefault != other.isDefault) {
return this.isDefault ? 1 : -1;
} }
if (this.hasPreferredIsForcedFlag != other.hasPreferredIsForcedFlag) {
return this.hasPreferredIsForcedFlag ? 1 : -1; /**
} * Represents how well any other track (non video, audio or text) matches the selection {@link
if (this.selectedAudioLanguageScore != other.selectedAudioLanguageScore) { * Parameters}.
return compareInts(this.selectedAudioLanguageScore, other.selectedAudioLanguageScore); */
} protected static final class OtherTrackScore implements Comparable<OtherTrackScore> {
if (preferredRoleFlagsScore == 0 && this.hasCaptionRoleFlags != other.hasCaptionRoleFlags) {
return this.hasCaptionRoleFlags ? -1 : 1; private final boolean isDefault;
private final boolean isWithinRendererCapabilities;
public OtherTrackScore(Format format, @Capabilities int trackFormatSupport) {
isDefault = (format.selectionFlags & C.SELECTION_FLAG_DEFAULT) != 0;
isWithinRendererCapabilities =
isSupported(trackFormatSupport, /* allowExceedsCapabilities= */ false);
} }
return 0;
@Override
public int compareTo(OtherTrackScore other) {
return ComparisonChain.start()
.compareFalseFirst(this.isWithinRendererCapabilities, other.isWithinRendererCapabilities)
.compareFalseFirst(this.isDefault, other.isDefault)
.result();
} }
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment