Make usage of live minDurationForQualityIncrease more consistent

We have two ways to choose the minDurationForQualityIncreaseMs value in
AdaptiveTrackSelection: use the configured value for non-live or when
enough buffered data is available, or use a fraction of the available
duration to allow switching when playing close to the live edge.

The decision point when to use which value isn't quite consistent because
we compare against availableDurationUs before making the adjustments. This
means there is range of values where no up-switching is possible despite
perfect buffering. Fix this by choosing the minimum of both values.

Issue: google/ExoPlayer#9784

#minor-release

PiperOrigin-RevId: 428474332
This commit is contained in:
tonihei 2022-02-14 12:39:14 +00:00 committed by Ian Baker
parent 6197a712ff
commit 6048ca2faa
2 changed files with 15 additions and 8 deletions

View File

@ -16,6 +16,7 @@
package androidx.media3.exoplayer.trackselection; package androidx.media3.exoplayer.trackselection;
import static java.lang.Math.max; import static java.lang.Math.max;
import static java.lang.Math.min;
import androidx.annotation.CallSuper; import androidx.annotation.CallSuper;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -605,10 +606,8 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
} }
private long minDurationForQualityIncreaseUs(long availableDurationUs, long chunkDurationUs) { private long minDurationForQualityIncreaseUs(long availableDurationUs, long chunkDurationUs) {
boolean isAvailableDurationTooShort = if (availableDurationUs == C.TIME_UNSET) {
availableDurationUs != C.TIME_UNSET // We are not in a live stream. Use the configured value.
&& availableDurationUs <= minDurationForQualityIncreaseUs;
if (!isAvailableDurationTooShort) {
return minDurationForQualityIncreaseUs; return minDurationForQualityIncreaseUs;
} }
if (chunkDurationUs != C.TIME_UNSET) { if (chunkDurationUs != C.TIME_UNSET) {
@ -619,7 +618,9 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
// actually achievable. // actually achievable.
availableDurationUs -= chunkDurationUs; availableDurationUs -= chunkDurationUs;
} }
return (long) (availableDurationUs * bufferedFractionToLiveEdgeForQualityIncrease); long adjustedMinDurationForQualityIncreaseUs =
(long) (availableDurationUs * bufferedFractionToLiveEdgeForQualityIncrease);
return min(adjustedMinDurationForQualityIncreaseUs, minDurationForQualityIncreaseUs);
} }
/** /**

View File

@ -175,7 +175,9 @@ public final class AdaptiveTrackSelectionTest {
when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(1000L, 2000L); when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(1000L, 2000L);
AdaptiveTrackSelection adaptiveTrackSelection = AdaptiveTrackSelection adaptiveTrackSelection =
prepareAdaptiveTrackSelectionWithBufferedFractionToLiveEdgeForQualiyIncrease( prepareAdaptiveTrackSelectionWithBufferedFractionToLiveEdgeForQualiyIncrease(
trackGroup, /* bufferedFractionToLiveEdgeForQualityIncrease= */ 0.75f); trackGroup,
/* bufferedFractionToLiveEdgeForQualityIncrease= */ 0.75f,
/* minDurationForQualityIncreaseMs= */ 5000);
// Not buffered close to live edge yet. // Not buffered close to live edge yet.
adaptiveTrackSelection.updateSelectedTrack( adaptiveTrackSelection.updateSelectedTrack(
@ -188,6 +190,8 @@ public final class AdaptiveTrackSelectionTest {
assertThat(adaptiveTrackSelection.getSelectedFormat()).isEqualTo(format2); assertThat(adaptiveTrackSelection.getSelectedFormat()).isEqualTo(format2);
// Buffered all possible chunks (except for newly added chunk of 2 seconds). // Buffered all possible chunks (except for newly added chunk of 2 seconds).
// Intentionally choose a situation where availableDurationUs > minDurationForQualityIncreaseMs
// to ensure the live calculation is used regardless.
adaptiveTrackSelection.updateSelectedTrack( adaptiveTrackSelection.updateSelectedTrack(
/* playbackPositionUs= */ 0, /* playbackPositionUs= */ 0,
/* bufferedDurationUs= */ 3_600_000, /* bufferedDurationUs= */ 3_600_000,
@ -768,14 +772,16 @@ public final class AdaptiveTrackSelectionTest {
private AdaptiveTrackSelection private AdaptiveTrackSelection
prepareAdaptiveTrackSelectionWithBufferedFractionToLiveEdgeForQualiyIncrease( prepareAdaptiveTrackSelectionWithBufferedFractionToLiveEdgeForQualiyIncrease(
TrackGroup trackGroup, float bufferedFractionToLiveEdgeForQualityIncrease) { TrackGroup trackGroup,
float bufferedFractionToLiveEdgeForQualityIncrease,
long minDurationForQualityIncreaseMs) {
return prepareTrackSelection( return prepareTrackSelection(
new AdaptiveTrackSelection( new AdaptiveTrackSelection(
trackGroup, trackGroup,
selectedAllTracksInGroup(trackGroup), selectedAllTracksInGroup(trackGroup),
TrackSelection.TYPE_UNSET, TrackSelection.TYPE_UNSET,
mockBandwidthMeter, mockBandwidthMeter,
AdaptiveTrackSelection.DEFAULT_MIN_DURATION_FOR_QUALITY_INCREASE_MS, minDurationForQualityIncreaseMs,
AdaptiveTrackSelection.DEFAULT_MAX_DURATION_FOR_QUALITY_DECREASE_MS, AdaptiveTrackSelection.DEFAULT_MAX_DURATION_FOR_QUALITY_DECREASE_MS,
AdaptiveTrackSelection.DEFAULT_MIN_DURATION_TO_RETAIN_AFTER_DISCARD_MS, AdaptiveTrackSelection.DEFAULT_MIN_DURATION_TO_RETAIN_AFTER_DISCARD_MS,
AdaptiveTrackSelection.DEFAULT_MAX_WIDTH_TO_DISCARD, AdaptiveTrackSelection.DEFAULT_MAX_WIDTH_TO_DISCARD,