Skip to content

Commit 276d5ff

Browse files
committed
Apply comparator-driven ABR selection without default-specific branching
1 parent 299eef5 commit 276d5ff

3 files changed

Lines changed: 19 additions & 104 deletions

File tree

libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/AdaptiveTrackSelection.java

Lines changed: 11 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,7 @@ public void updateSelectedTrack(
528528
// Make initial selection
529529
if (reason == C.SELECTION_REASON_UNKNOWN) {
530530
reason = C.SELECTION_REASON_INITIAL;
531-
selectedIndex = determineIdealSelectedIndexForEffectiveBitrate(nowMs, effectiveBitrate);
531+
selectedIndex = determineIdealSelectedIndex(nowMs, effectiveBitrate);
532532
return;
533533
}
534534

@@ -540,11 +540,10 @@ public void updateSelectedTrack(
540540
previousSelectedIndex = formatIndexOfPreviousChunk;
541541
previousReason = Iterables.getLast(queue).trackSelectionReason;
542542
}
543-
int newSelectedIndex = determineIdealSelectedIndexForEffectiveBitrate(nowMs, effectiveBitrate);
543+
int newSelectedIndex = determineIdealSelectedIndex(nowMs, effectiveBitrate);
544544
if (newSelectedIndex != previousSelectedIndex
545545
&& !isTrackExcluded(previousSelectedIndex, nowMs)) {
546-
// Revert back to the previous selection if conditions are not suitable for switching. Do not
547-
// defer a switch when the previous format no longer fits into available bandwidth.
546+
// Revert back to the previous selection if conditions are not suitable for switching.
548547
long minDurationForQualityIncreaseUs =
549548
minDurationForQualityIncreaseUs(availableDurationUs, chunkDurationUs);
550549
if (newSelectedIndex < previousSelectedIndex
@@ -553,11 +552,9 @@ public void updateSelectedTrack(
553552
// up. Defer switching up for now.
554553
newSelectedIndex = previousSelectedIndex;
555554
} else if (newSelectedIndex > previousSelectedIndex
556-
&& (isUsingDefaultFormatComparator()
557-
|| isTrackSelectable(previousSelectedIndex, effectiveBitrate))
558555
&& bufferedDurationUs >= maxDurationForQualityDecreaseUs) {
559556
// The selected track is lower priority, but we have sufficient buffer to defer switching
560-
// down while preserving existing behavior for default ordering.
557+
// down for now.
561558
newSelectedIndex = previousSelectedIndex;
562559
}
563560
}
@@ -604,7 +601,8 @@ public int evaluateQueueSize(long playbackPositionUs, List<? extends MediaChunk>
604601
if (playoutBufferedDurationBeforeLastChunkUs < minDurationToRetainAfterDiscardUs) {
605602
return queueSize;
606603
}
607-
int idealSelectedIndex = determineIdealSelectedIndex(nowMs, getLastChunkDurationUs(queue));
604+
int idealSelectedIndex =
605+
determineIdealSelectedIndex(nowMs, getAllocatedBandwidth(getLastChunkDurationUs(queue)));
608606
Format idealFormat = getFormat(idealSelectedIndex);
609607
// If chunks contain video, discard from the first chunk after minDurationToRetainAfterDiscardUs
610608
// whose resolution and bitrate are both lower than the ideal track, and whose width and height
@@ -676,37 +674,21 @@ protected long getMinDurationToRetainAfterDiscardUs() {
676674
*
677675
* @param nowMs The current time in the timebase of {@link Clock#elapsedRealtime()}, or {@link
678676
* Long#MIN_VALUE} to ignore track exclusion.
679-
* @param chunkDurationUs The duration of a media chunk in microseconds, or {@link C#TIME_UNSET}
680-
* if unknown.
677+
* @param effectiveBitrate The bitrate available to this selection.
681678
*/
682-
private int determineIdealSelectedIndex(long nowMs, long chunkDurationUs) {
683-
return determineIdealSelectedIndexForEffectiveBitrate(nowMs, getAllocatedBandwidth(chunkDurationUs));
684-
}
685-
686-
private int determineIdealSelectedIndexForEffectiveBitrate(long nowMs, long effectiveBitrate) {
687-
int lowestBitrateAllowedIndex = C.INDEX_UNSET;
688-
int lowestBitrate = Integer.MAX_VALUE;
679+
private int determineIdealSelectedIndex(long nowMs, long effectiveBitrate) {
680+
int lowestBitrateAllowedIndex = 0;
689681
for (int i = 0; i < length; i++) {
690682
if (nowMs == Long.MIN_VALUE || !isTrackExcluded(i, nowMs)) {
691683
Format format = getFormat(i);
692684
if (canSelectFormat(format, format.bitrate, effectiveBitrate)) {
693685
return i;
694-
}
695-
int formatBitrate = format.bitrate == Format.NO_VALUE ? 0 : format.bitrate;
696-
if (formatBitrate <= lowestBitrate) {
697-
// fallback semantics by selecting the lowest bitrate non-excluded track when no track
698-
// is selectable within available bandwidth.
686+
} else {
699687
lowestBitrateAllowedIndex = i;
700-
lowestBitrate = formatBitrate;
701688
}
702689
}
703690
}
704-
return lowestBitrateAllowedIndex == C.INDEX_UNSET ? 0 : lowestBitrateAllowedIndex;
705-
}
706-
707-
private boolean isTrackSelectable(int index, long effectiveBitrate) {
708-
Format format = getFormat(index);
709-
return canSelectFormat(format, format.bitrate, effectiveBitrate);
691+
return lowestBitrateAllowedIndex;
710692
}
711693

712694
private long minDurationForQualityIncreaseUs(long availableDurationUs, long chunkDurationUs) {

libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/BaseTrackSelection.java

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,6 @@ public abstract class BaseTrackSelection implements ExoTrackSelection {
5757
/** Selected track exclusion timestamps, in selection order. */
5858
private final long[] excludeUntilTimes;
5959

60-
/** Whether selected formats are ordered with the default bitrate comparator. */
61-
private final boolean isUsingDefaultFormatComparator;
62-
6360
// Lazily initialized hashcode.
6461
private int hashCode;
6562

@@ -103,21 +100,13 @@ protected BaseTrackSelection(
103100
for (int i = 0; i < tracks.length; i++) {
104101
formats[i] = group.getFormat(tracks[i]);
105102
}
106-
Comparator<Format> safeFormatComparator = checkNotNull(formatComparator);
107-
boolean isUsingDefaultFormatComparator = safeFormatComparator == DEFAULT_FORMAT_COMPARATOR;
108-
try {
109-
Arrays.sort(formats, safeFormatComparator);
110-
} catch (Throwable throwable) {
111-
Arrays.sort(formats, DEFAULT_FORMAT_COMPARATOR);
112-
isUsingDefaultFormatComparator = true;
113-
}
103+
Arrays.sort(formats, checkNotNull(formatComparator));
114104
// Set the format indices in the same order.
115105
this.tracks = new int[length];
116106
for (int i = 0; i < length; i++) {
117107
this.tracks[i] = group.indexOf(formats[i]);
118108
}
119109
excludeUntilTimes = new long[length];
120-
this.isUsingDefaultFormatComparator = isUsingDefaultFormatComparator;
121110
playWhenReady = false;
122111
}
123112

@@ -239,11 +228,6 @@ protected final boolean getPlayWhenReady() {
239228
return playWhenReady;
240229
}
241230

242-
/** Returns whether formats are ordered with the default bitrate comparator. */
243-
protected final boolean isUsingDefaultFormatComparator() {
244-
return isUsingDefaultFormatComparator;
245-
}
246-
247231
// Object overrides.
248232

249233
@Override

libraries/exoplayer/src/test/java/androidx/media3/exoplayer/trackselection/AdaptiveTrackSelectionTest.java

Lines changed: 7 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ public void initial_updateSelectedTrack_usesFactoryProvidedTrackFormatComparator
248248
}
249249

250250
@Test
251-
public void initial_updateSelectedTrack_withNoSelectableFormat_fallsBackToLowestBitrate() {
251+
public void initial_updateSelectedTrack_withNoSelectableFormat_fallsBackToLowestPriorityFormat() {
252252
Format format1 = videoFormat(/* bitrate= */ 500, /* width= */ 320, /* height= */ 240);
253253
Format format2 = videoFormat(/* bitrate= */ 1000, /* width= */ 640, /* height= */ 480);
254254
Format format3 = videoFormat(/* bitrate= */ 2000, /* width= */ 960, /* height= */ 720);
@@ -259,60 +259,7 @@ public void initial_updateSelectedTrack_withNoSelectableFormat_fallsBackToLowest
259259
prepareAdaptiveTrackSelectionWithFormatComparator(
260260
trackGroup, formatComparatorInOrder(ImmutableList.of(format2, format1, format3)));
261261

262-
assertThat(adaptiveTrackSelection.getSelectedFormat()).isEqualTo(format1);
263-
assertThat(adaptiveTrackSelection.getSelectionReason()).isEqualTo(C.SELECTION_REASON_INITIAL);
264-
}
265-
266-
@Test
267-
public void initial_updateSelectedTrack_withInvalidComparator_fallsBackToDefaultOrdering() {
268-
Format format1 = videoFormat(/* bitrate= */ 500, /* width= */ 320, /* height= */ 240);
269-
Format format2 = videoFormat(/* bitrate= */ 1000, /* width= */ 640, /* height= */ 480);
270-
Format format3 = videoFormat(/* bitrate= */ 2000, /* width= */ 960, /* height= */ 720);
271-
TrackGroup trackGroup = new TrackGroup(format1, format2, format3);
272-
Comparator<Format> throwingComparator =
273-
(firstFormat, secondFormat) -> {
274-
throw new RuntimeException("Comparator failure");
275-
};
276-
277-
when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(1000L);
278-
AdaptiveTrackSelection adaptiveTrackSelection =
279-
prepareAdaptiveTrackSelectionWithFormatComparator(trackGroup, throwingComparator);
280-
281-
assertThat(adaptiveTrackSelection.getSelectedFormat()).isEqualTo(format2);
282-
assertThat(adaptiveTrackSelection.getSelectionReason()).isEqualTo(C.SELECTION_REASON_INITIAL);
283-
}
284-
285-
@Test
286-
public void
287-
updateSelectedTrack_withInvalidComparator_preservesDefaultDownSwitchDeferralWhenBufferedEnough() {
288-
Format format1 = videoFormat(/* bitrate= */ 500, /* width= */ 320, /* height= */ 240);
289-
Format format2 = videoFormat(/* bitrate= */ 1000, /* width= */ 640, /* height= */ 480);
290-
Format format3 = videoFormat(/* bitrate= */ 2000, /* width= */ 960, /* height= */ 720);
291-
TrackGroup trackGroup = new TrackGroup(format1, format2, format3);
292-
Comparator<Format> throwingComparator =
293-
(firstFormat, secondFormat) -> {
294-
throw new RuntimeException("Comparator failure");
295-
};
296-
297-
// The second measurement onward returns 500L, which prompts the track selection to switch down
298-
// if necessary.
299-
when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(1000L, 500L);
300-
AdaptiveTrackSelection adaptiveTrackSelection =
301-
prepareAdaptiveTrackSelectionWithFormatComparatorAndDurations(
302-
trackGroup,
303-
AdaptiveTrackSelection.DEFAULT_MIN_DURATION_FOR_QUALITY_INCREASE_MS,
304-
/* maxDurationForQualityDecreaseMs= */ 25_000,
305-
throwingComparator);
306-
307-
adaptiveTrackSelection.updateSelectedTrack(
308-
/* playbackPositionUs= */ 0,
309-
/* bufferedDurationUs= */ 25_000_000,
310-
/* availableDurationUs= */ C.TIME_UNSET,
311-
/* queue= */ Collections.emptyList(),
312-
createMediaChunkIterators(trackGroup, TEST_CHUNK_DURATION_US));
313-
314-
// Invalid comparators fall back to the default ordering and preserve the default behavior.
315-
assertThat(adaptiveTrackSelection.getSelectedFormat()).isEqualTo(format2);
262+
assertThat(adaptiveTrackSelection.getSelectedFormat()).isEqualTo(format3);
316263
assertThat(adaptiveTrackSelection.getSelectionReason()).isEqualTo(C.SELECTION_REASON_INITIAL);
317264
}
318265

@@ -622,7 +569,7 @@ public void updateSelectedTrack_withCustomFormatOrder_usesCustomOrderForSwitchDi
622569

623570
@Test
624571
public void
625-
updateSelectedTrack_withCustomFormatOrder_switchesDownIfCurrentTrackExceedsBandwidth() {
572+
updateSelectedTrack_withCustomFormatOrder_defersSwitchDownWhenBufferedEnough() {
626573
Format format1 = videoFormat(/* bitrate= */ 500, /* width= */ 320, /* height= */ 240);
627574
Format format2 = videoFormat(/* bitrate= */ 1000, /* width= */ 640, /* height= */ 480);
628575
Format format3 = videoFormat(/* bitrate= */ 2000, /* width= */ 960, /* height= */ 720);
@@ -645,8 +592,10 @@ public void updateSelectedTrack_withCustomFormatOrder_usesCustomOrderForSwitchDi
645592
/* queue= */ Collections.emptyList(),
646593
createMediaChunkIterators(trackGroup, TEST_CHUNK_DURATION_US));
647594

648-
assertThat(adaptiveTrackSelection.getSelectedFormat()).isEqualTo(format1);
649-
assertThat(adaptiveTrackSelection.getSelectionReason()).isEqualTo(C.SELECTION_REASON_ADAPTIVE);
595+
// With sufficient buffer (bufferedDurationUs >= maxDurationForQualityDecreaseUs), the
596+
// down-switch is deferred regardless of which comparator is in use.
597+
assertThat(adaptiveTrackSelection.getSelectedFormat()).isEqualTo(format2);
598+
assertThat(adaptiveTrackSelection.getSelectionReason()).isEqualTo(C.SELECTION_REASON_INITIAL);
650599
}
651600

652601
@Test

0 commit comments

Comments
 (0)