From 005336315cd2b79a69ad4f4788dcff80f9c294c2 Mon Sep 17 00:00:00 2001 From: jaeholee104 Date: Fri, 10 Sep 2021 04:57:52 +0900 Subject: [PATCH] Add the options for the maximum resolution values for which the selector may choose to discard when switching up to a higher quality --- .../AdaptiveTrackSelection.java | 46 +++++++++-- .../AdaptiveTrackSelectionTest.java | 80 +++++++++++++++++++ 2 files changed, 121 insertions(+), 5 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelection.java b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelection.java index 95df027ac2..080c0de00b 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelection.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelection.java @@ -53,6 +53,8 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { private final int minDurationForQualityIncreaseMs; private final int maxDurationForQualityDecreaseMs; private final int minDurationToRetainAfterDiscardMs; + private final int maxWidthToDiscard; + private final int maxHeightToDiscard; private final float bandwidthFraction; private final float bufferedFractionToLiveEdgeForQualityIncrease; private final Clock clock; @@ -63,6 +65,8 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { DEFAULT_MIN_DURATION_FOR_QUALITY_INCREASE_MS, DEFAULT_MAX_DURATION_FOR_QUALITY_DECREASE_MS, DEFAULT_MIN_DURATION_TO_RETAIN_AFTER_DISCARD_MS, + DEFAULT_MAX_WIDTH_TO_DISCARD, + DEFAULT_MAX_HEIGHT_TO_DISCARD, DEFAULT_BANDWIDTH_FRACTION, DEFAULT_BUFFERED_FRACTION_TO_LIVE_EDGE_FOR_QUALITY_INCREASE, Clock.DEFAULT); @@ -80,6 +84,10 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { * be discarded to speed up the switch. This is the minimum duration of media that must be * retained at the lower quality. It must be at least {@code * minDurationForQualityIncreaseMs}. + * @param maxWidthToDiscard This is the maximum width for which the selector may choose to + * discard when switching up to a higher quality. + * @param maxHeightToDiscard This is the maximum height for which the selector may choose to + * discard when switching up to a higher quality. * @param bandwidthFraction The fraction of the available bandwidth that the selection should * consider available for use. Setting to a value less than 1 is recommended to account for * inaccuracies in the bandwidth estimator. @@ -88,11 +96,15 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { int minDurationForQualityIncreaseMs, int maxDurationForQualityDecreaseMs, int minDurationToRetainAfterDiscardMs, + int maxWidthToDiscard, + int maxHeightToDiscard, float bandwidthFraction) { this( minDurationForQualityIncreaseMs, maxDurationForQualityDecreaseMs, minDurationToRetainAfterDiscardMs, + maxWidthToDiscard, + maxHeightToDiscard, bandwidthFraction, DEFAULT_BUFFERED_FRACTION_TO_LIVE_EDGE_FOR_QUALITY_INCREASE, Clock.DEFAULT); @@ -110,6 +122,10 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { * be discarded to speed up the switch. This is the minimum duration of media that must be * retained at the lower quality. It must be at least {@code * minDurationForQualityIncreaseMs}. + * @param maxWidthToDiscard This is the maximum width for which the selector may choose to + * discard when switching up to a higher quality. + * @param maxHeightToDiscard This is the maximum height for which the selector may choose to + * discard when switching up to a higher quality. * @param bandwidthFraction The fraction of the available bandwidth that the selection should * consider available for use. Setting to a value less than 1 is recommended to account for * inaccuracies in the bandwidth estimator. @@ -125,12 +141,16 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { int minDurationForQualityIncreaseMs, int maxDurationForQualityDecreaseMs, int minDurationToRetainAfterDiscardMs, + int maxWidthToDiscard, + int maxHeightToDiscard, float bandwidthFraction, float bufferedFractionToLiveEdgeForQualityIncrease, Clock clock) { this.minDurationForQualityIncreaseMs = minDurationForQualityIncreaseMs; this.maxDurationForQualityDecreaseMs = maxDurationForQualityDecreaseMs; this.minDurationToRetainAfterDiscardMs = minDurationToRetainAfterDiscardMs; + this.maxWidthToDiscard = maxWidthToDiscard; + this.maxHeightToDiscard = maxHeightToDiscard; this.bandwidthFraction = bandwidthFraction; this.bufferedFractionToLiveEdgeForQualityIncrease = bufferedFractionToLiveEdgeForQualityIncrease; @@ -192,6 +212,8 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { minDurationForQualityIncreaseMs, maxDurationForQualityDecreaseMs, minDurationToRetainAfterDiscardMs, + maxWidthToDiscard, + maxHeightToDiscard, bandwidthFraction, bufferedFractionToLiveEdgeForQualityIncrease, adaptationCheckpoints, @@ -202,6 +224,8 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { public static final int DEFAULT_MIN_DURATION_FOR_QUALITY_INCREASE_MS = 10_000; public static final int DEFAULT_MAX_DURATION_FOR_QUALITY_DECREASE_MS = 25_000; public static final int DEFAULT_MIN_DURATION_TO_RETAIN_AFTER_DISCARD_MS = 25_000; + public static final int DEFAULT_MAX_WIDTH_TO_DISCARD = 1279; + public static final int DEFAULT_MAX_HEIGHT_TO_DISCARD = 719; public static final float DEFAULT_BANDWIDTH_FRACTION = 0.7f; public static final float DEFAULT_BUFFERED_FRACTION_TO_LIVE_EDGE_FOR_QUALITY_INCREASE = 0.75f; @@ -211,6 +235,8 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { private final long minDurationForQualityIncreaseUs; private final long maxDurationForQualityDecreaseUs; private final long minDurationToRetainAfterDiscardUs; + private final int maxWidthToDiscard; + private final int maxHeightToDiscard; private final float bandwidthFraction; private final float bufferedFractionToLiveEdgeForQualityIncrease; private final ImmutableList adaptationCheckpoints; @@ -237,6 +263,8 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { DEFAULT_MIN_DURATION_FOR_QUALITY_INCREASE_MS, DEFAULT_MAX_DURATION_FOR_QUALITY_DECREASE_MS, DEFAULT_MIN_DURATION_TO_RETAIN_AFTER_DISCARD_MS, + DEFAULT_MAX_WIDTH_TO_DISCARD, + DEFAULT_MAX_HEIGHT_TO_DISCARD, DEFAULT_BANDWIDTH_FRACTION, DEFAULT_BUFFERED_FRACTION_TO_LIVE_EDGE_FOR_QUALITY_INCREASE, /* adaptationCheckpoints= */ ImmutableList.of(), @@ -257,6 +285,10 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { * quality, the selection may indicate that media already buffered at the lower quality can be * discarded to speed up the switch. This is the minimum duration of media that must be * retained at the lower quality. It must be at least {@code minDurationForQualityIncreaseMs}. + * @param maxWidthToDiscard This is the maximum width for which the selector may choose to + * discard when switching up to a higher quality. + * @param maxHeightToDiscard This is the maximum height for which the selector may choose to + * discard when switching up to a higher quality. * @param bandwidthFraction The fraction of the available bandwidth that the selection should * consider available for use. Setting to a value less than 1 is recommended to account for * inaccuracies in the bandwidth estimator. @@ -278,6 +310,8 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { long minDurationForQualityIncreaseMs, long maxDurationForQualityDecreaseMs, long minDurationToRetainAfterDiscardMs, + int maxWidthToDiscard, + int maxHeightToDiscard, float bandwidthFraction, float bufferedFractionToLiveEdgeForQualityIncrease, List adaptationCheckpoints, @@ -294,6 +328,8 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { this.minDurationForQualityIncreaseUs = minDurationForQualityIncreaseMs * 1000L; this.maxDurationForQualityDecreaseUs = maxDurationForQualityDecreaseMs * 1000L; this.minDurationToRetainAfterDiscardUs = minDurationToRetainAfterDiscardMs * 1000L; + this.maxWidthToDiscard = maxWidthToDiscard; + this.maxHeightToDiscard = maxHeightToDiscard; this.bandwidthFraction = bandwidthFraction; this.bufferedFractionToLiveEdgeForQualityIncrease = bufferedFractionToLiveEdgeForQualityIncrease; @@ -410,9 +446,9 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { } int idealSelectedIndex = determineIdealSelectedIndex(nowMs, getLastChunkDurationUs(queue)); Format idealFormat = getFormat(idealSelectedIndex); - // If the chunks contain video, discard from the first SD chunk beyond - // minDurationToRetainAfterDiscardUs whose resolution and bitrate are both lower than the ideal - // track. + // If the chunks contain video, discard from the first chunk less than or equal to + // maxWidthToDiscard and maxHeightToDiscard beyond minDurationToRetainAfterDiscardUs + // whose resolution and bitrate are both lower than the ideal track. for (int i = 0; i < queueSize; i++) { MediaChunk chunk = queue.get(i); Format format = chunk.trackFormat; @@ -422,9 +458,9 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { if (playoutDurationBeforeThisChunkUs >= minDurationToRetainAfterDiscardUs && format.bitrate < idealFormat.bitrate && format.height != Format.NO_VALUE - && format.height < 720 + && format.height <= maxHeightToDiscard && format.width != Format.NO_VALUE - && format.width < 1280 + && format.width <= maxWidthToDiscard && format.height < idealFormat.height) { return i; } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelectionTest.java b/library/core/src/test/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelectionTest.java index 021694eb23..5f231c91cd 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelectionTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelectionTest.java @@ -321,6 +321,52 @@ public final class AdaptiveTrackSelectionTest { assertThat(newSize).isEqualTo(2); } + @Test + public void evaluateQueueSizeDiscardChunksLessThanOrEqualToMaximumResolution() { + Format format1 = videoFormat(/* bitrate= */ 500, /* width= */ 320, /* height= */ 240); + Format format2 = videoFormat(/* bitrate= */ 1000, /* width= */ 640, /* height= */ 480); + Format format3 = videoFormat(/* bitrate= */ 2000, /* width= */ 960, /* height= */ 720); + TrackGroup trackGroup = new TrackGroup(format1, format2, format3); + + FakeMediaChunk chunk1 = + new FakeMediaChunk(format2, /* startTimeUs= */ 0, /* endTimeUs= */ 10_000_000); + FakeMediaChunk chunk2 = + new FakeMediaChunk(format2, /* startTimeUs= */ 10_000_000, /* endTimeUs= */ 20_000_000); + FakeMediaChunk chunk3 = + new FakeMediaChunk(format2, /* startTimeUs= */ 20_000_000, /* endTimeUs= */ 30_000_000); + FakeMediaChunk chunk4 = + new FakeMediaChunk(format2, /* startTimeUs= */ 30_000_000, /* endTimeUs= */ 40_000_000); + FakeMediaChunk chunk5 = + new FakeMediaChunk(format1, /* startTimeUs= */ 40_000_000, /* endTimeUs= */ 50_000_000); + + List queue = new ArrayList<>(); + queue.add(chunk1); + queue.add(chunk2); + queue.add(chunk3); + queue.add(chunk4); + queue.add(chunk5); + + when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(500L); + AdaptiveTrackSelection adaptiveTrackSelection = + prepareAdaptiveTrackSelectionWithMaxResolutionToDiscard( + trackGroup, + /* maxWidthToDiscard= */320, + /* maxHeightToDiscard= */240 + ); + + int initialQueueSize = adaptiveTrackSelection.evaluateQueueSize(0, queue); + assertThat(initialQueueSize).isEqualTo(5); + + fakeClock.advanceTime(2000); + when(mockBandwidthMeter.getBitrateEstimate()).thenReturn(2000L); + + // When bandwidth estimation is updated and time has advanced enough, we can discard chunks at + // the end of the queue now. + // In this case, only chunks less than or equal to width = 320 and height = 240 are discarded. + int newSize = adaptiveTrackSelection.evaluateQueueSize(0, queue); + assertThat(newSize).isEqualTo(4); + } + @Test public void updateSelectedTrack_usesFormatOfLastChunkInTheQueueForSelection() { Format format1 = videoFormat(/* bitrate= */ 500, /* width= */ 320, /* height= */ 240); @@ -331,6 +377,8 @@ public final class AdaptiveTrackSelectionTest { /* minDurationForQualityIncreaseMs= */ 10_000, /* maxDurationForQualityDecreaseMs= */ 10_000, /* minDurationToRetainAfterDiscardMs= */ 25_000, + /* maxWidthToDiscard= */ 1279, + /* maxHeightToDiscard= */ 719, /* bandwidthFraction= */ 1f) .createAdaptiveTrackSelection( trackGroup, @@ -616,6 +664,8 @@ public final class AdaptiveTrackSelectionTest { AdaptiveTrackSelection.DEFAULT_MIN_DURATION_FOR_QUALITY_INCREASE_MS, AdaptiveTrackSelection.DEFAULT_MAX_DURATION_FOR_QUALITY_DECREASE_MS, AdaptiveTrackSelection.DEFAULT_MIN_DURATION_TO_RETAIN_AFTER_DISCARD_MS, + AdaptiveTrackSelection.DEFAULT_MAX_WIDTH_TO_DISCARD, + AdaptiveTrackSelection.DEFAULT_MAX_HEIGHT_TO_DISCARD, bandwidthFraction, AdaptiveTrackSelection.DEFAULT_BUFFERED_FRACTION_TO_LIVE_EDGE_FOR_QUALITY_INCREASE, /* adaptationCheckpoints= */ ImmutableList.of(), @@ -633,6 +683,8 @@ public final class AdaptiveTrackSelectionTest { minDurationForQualityIncreaseMs, AdaptiveTrackSelection.DEFAULT_MAX_DURATION_FOR_QUALITY_DECREASE_MS, AdaptiveTrackSelection.DEFAULT_MIN_DURATION_TO_RETAIN_AFTER_DISCARD_MS, + AdaptiveTrackSelection.DEFAULT_MAX_WIDTH_TO_DISCARD, + AdaptiveTrackSelection.DEFAULT_MAX_HEIGHT_TO_DISCARD, /* bandwidthFraction= */ 1.0f, AdaptiveTrackSelection.DEFAULT_BUFFERED_FRACTION_TO_LIVE_EDGE_FOR_QUALITY_INCREASE, /* adaptationCheckpoints= */ ImmutableList.of(), @@ -650,6 +702,8 @@ public final class AdaptiveTrackSelectionTest { AdaptiveTrackSelection.DEFAULT_MIN_DURATION_FOR_QUALITY_INCREASE_MS, maxDurationForQualityDecreaseMs, AdaptiveTrackSelection.DEFAULT_MIN_DURATION_TO_RETAIN_AFTER_DISCARD_MS, + AdaptiveTrackSelection.DEFAULT_MAX_WIDTH_TO_DISCARD, + AdaptiveTrackSelection.DEFAULT_MAX_HEIGHT_TO_DISCARD, /* bandwidthFraction= */ 1.0f, AdaptiveTrackSelection.DEFAULT_BUFFERED_FRACTION_TO_LIVE_EDGE_FOR_QUALITY_INCREASE, /* adaptationCheckpoints= */ ImmutableList.of(), @@ -668,6 +722,30 @@ public final class AdaptiveTrackSelectionTest { AdaptiveTrackSelection.DEFAULT_MIN_DURATION_FOR_QUALITY_INCREASE_MS, AdaptiveTrackSelection.DEFAULT_MAX_DURATION_FOR_QUALITY_DECREASE_MS, durationToRetainAfterDiscardMs, + AdaptiveTrackSelection.DEFAULT_MAX_WIDTH_TO_DISCARD, + AdaptiveTrackSelection.DEFAULT_MAX_HEIGHT_TO_DISCARD, + /* bandwidthFraction= */ 1.0f, + AdaptiveTrackSelection.DEFAULT_BUFFERED_FRACTION_TO_LIVE_EDGE_FOR_QUALITY_INCREASE, + /* adaptationCheckpoints= */ ImmutableList.of(), + fakeClock)); + } + + private AdaptiveTrackSelection + prepareAdaptiveTrackSelectionWithMaxResolutionToDiscard( + TrackGroup trackGroup, + int maxWidthToDiscard, + int maxHeightToDiscard) { + return prepareTrackSelection( + new AdaptiveTrackSelection( + trackGroup, + selectedAllTracksInGroup(trackGroup), + TrackSelection.TYPE_UNSET, + mockBandwidthMeter, + AdaptiveTrackSelection.DEFAULT_MIN_DURATION_FOR_QUALITY_INCREASE_MS, + AdaptiveTrackSelection.DEFAULT_MAX_DURATION_FOR_QUALITY_DECREASE_MS, + AdaptiveTrackSelection.DEFAULT_MIN_DURATION_TO_RETAIN_AFTER_DISCARD_MS, + maxWidthToDiscard, + maxHeightToDiscard, /* bandwidthFraction= */ 1.0f, AdaptiveTrackSelection.DEFAULT_BUFFERED_FRACTION_TO_LIVE_EDGE_FOR_QUALITY_INCREASE, /* adaptationCheckpoints= */ ImmutableList.of(), @@ -685,6 +763,8 @@ public final class AdaptiveTrackSelectionTest { AdaptiveTrackSelection.DEFAULT_MIN_DURATION_FOR_QUALITY_INCREASE_MS, AdaptiveTrackSelection.DEFAULT_MAX_DURATION_FOR_QUALITY_DECREASE_MS, AdaptiveTrackSelection.DEFAULT_MIN_DURATION_TO_RETAIN_AFTER_DISCARD_MS, + AdaptiveTrackSelection.DEFAULT_MAX_WIDTH_TO_DISCARD, + AdaptiveTrackSelection.DEFAULT_MAX_HEIGHT_TO_DISCARD, /* bandwidthFraction= */ 1.0f, AdaptiveTrackSelection.DEFAULT_BUFFERED_FRACTION_TO_LIVE_EDGE_FOR_QUALITY_INCREASE, adaptationCheckpoints,