From 9463c31cded5cd6523769c4deab04cc4204eeb3c Mon Sep 17 00:00:00 2001 From: tonihei Date: Tue, 23 Apr 2019 11:00:55 +0100 Subject: [PATCH] Update default min duration for playbacks with video to match max duration. Experiments show this is beneficial for rebuffers with only minor impact on battery usage. Configurations which explicitly set a minimum buffer duration are unaffected. Issue:#2083 PiperOrigin-RevId: 244823642 --- .../exoplayer2/DefaultLoadControl.java | 77 +++++++++++++++---- 1 file changed, 60 insertions(+), 17 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/DefaultLoadControl.java b/library/core/src/main/java/com/google/android/exoplayer2/DefaultLoadControl.java index 83cb5b723c1..972f651a41a 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/DefaultLoadControl.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/DefaultLoadControl.java @@ -29,12 +29,14 @@ public class DefaultLoadControl implements LoadControl { /** * The default minimum duration of media that the player will attempt to ensure is buffered at all - * times, in milliseconds. + * times, in milliseconds. This value is only applied to playbacks without video. */ public static final int DEFAULT_MIN_BUFFER_MS = 15000; /** * The default maximum duration of media that the player will attempt to buffer, in milliseconds. + * For playbacks with video, this is also the default minimum duration of media that the player + * will attempt to ensure is buffered. */ public static final int DEFAULT_MAX_BUFFER_MS = 50000; @@ -69,7 +71,8 @@ public class DefaultLoadControl implements LoadControl { public static final class Builder { private DefaultAllocator allocator; - private int minBufferMs; + private int minBufferAudioMs; + private int minBufferVideoMs; private int maxBufferMs; private int bufferForPlaybackMs; private int bufferForPlaybackAfterRebufferMs; @@ -81,7 +84,8 @@ public static final class Builder { /** Constructs a new instance. */ public Builder() { - minBufferMs = DEFAULT_MIN_BUFFER_MS; + minBufferAudioMs = DEFAULT_MIN_BUFFER_MS; + minBufferVideoMs = DEFAULT_MAX_BUFFER_MS; maxBufferMs = DEFAULT_MAX_BUFFER_MS; bufferForPlaybackMs = DEFAULT_BUFFER_FOR_PLAYBACK_MS; bufferForPlaybackAfterRebufferMs = DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS; @@ -125,7 +129,18 @@ public Builder setBufferDurationsMs( int bufferForPlaybackMs, int bufferForPlaybackAfterRebufferMs) { Assertions.checkState(!createDefaultLoadControlCalled); - this.minBufferMs = minBufferMs; + assertGreaterOrEqual(bufferForPlaybackMs, 0, "bufferForPlaybackMs", "0"); + assertGreaterOrEqual( + bufferForPlaybackAfterRebufferMs, 0, "bufferForPlaybackAfterRebufferMs", "0"); + assertGreaterOrEqual(minBufferMs, bufferForPlaybackMs, "minBufferMs", "bufferForPlaybackMs"); + assertGreaterOrEqual( + minBufferMs, + bufferForPlaybackAfterRebufferMs, + "minBufferMs", + "bufferForPlaybackAfterRebufferMs"); + assertGreaterOrEqual(maxBufferMs, minBufferMs, "maxBufferMs", "minBufferMs"); + this.minBufferAudioMs = minBufferMs; + this.minBufferVideoMs = minBufferMs; this.maxBufferMs = maxBufferMs; this.bufferForPlaybackMs = bufferForPlaybackMs; this.bufferForPlaybackAfterRebufferMs = bufferForPlaybackAfterRebufferMs; @@ -173,6 +188,7 @@ public Builder setPrioritizeTimeOverSizeThresholds(boolean prioritizeTimeOverSiz */ public Builder setBackBuffer(int backBufferDurationMs, boolean retainBackBufferFromKeyframe) { Assertions.checkState(!createDefaultLoadControlCalled); + assertGreaterOrEqual(backBufferDurationMs, 0, "backBufferDurationMs", "0"); this.backBufferDurationMs = backBufferDurationMs; this.retainBackBufferFromKeyframe = retainBackBufferFromKeyframe; return this; @@ -187,7 +203,8 @@ public DefaultLoadControl createDefaultLoadControl() { } return new DefaultLoadControl( allocator, - minBufferMs, + minBufferAudioMs, + minBufferVideoMs, maxBufferMs, bufferForPlaybackMs, bufferForPlaybackAfterRebufferMs, @@ -200,7 +217,8 @@ public DefaultLoadControl createDefaultLoadControl() { private final DefaultAllocator allocator; - private final long minBufferUs; + private final long minBufferAudioUs; + private final long minBufferVideoUs; private final long maxBufferUs; private final long bufferForPlaybackUs; private final long bufferForPlaybackAfterRebufferUs; @@ -211,6 +229,7 @@ public DefaultLoadControl createDefaultLoadControl() { private int targetBufferSize; private boolean isBuffering; + private boolean hasVideo; /** Constructs a new instance, using the {@code DEFAULT_*} constants defined in this class. */ @SuppressWarnings("deprecation") @@ -220,16 +239,18 @@ public DefaultLoadControl() { /** @deprecated Use {@link Builder} instead. */ @Deprecated - @SuppressWarnings("deprecation") public DefaultLoadControl(DefaultAllocator allocator) { this( allocator, - DEFAULT_MIN_BUFFER_MS, + /* minBufferAudioMs= */ DEFAULT_MIN_BUFFER_MS, + /* minBufferVideoMs= */ DEFAULT_MAX_BUFFER_MS, DEFAULT_MAX_BUFFER_MS, DEFAULT_BUFFER_FOR_PLAYBACK_MS, DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS, DEFAULT_TARGET_BUFFER_BYTES, - DEFAULT_PRIORITIZE_TIME_OVER_SIZE_THRESHOLDS); + DEFAULT_PRIORITIZE_TIME_OVER_SIZE_THRESHOLDS, + DEFAULT_BACK_BUFFER_DURATION_MS, + DEFAULT_RETAIN_BACK_BUFFER_FROM_KEYFRAME); } /** @deprecated Use {@link Builder} instead. */ @@ -244,7 +265,8 @@ public DefaultLoadControl( boolean prioritizeTimeOverSizeThresholds) { this( allocator, - minBufferMs, + /* minBufferAudioMs= */ minBufferMs, + /* minBufferVideoMs= */ minBufferMs, maxBufferMs, bufferForPlaybackMs, bufferForPlaybackAfterRebufferMs, @@ -256,7 +278,8 @@ public DefaultLoadControl( protected DefaultLoadControl( DefaultAllocator allocator, - int minBufferMs, + int minBufferAudioMs, + int minBufferVideoMs, int maxBufferMs, int bufferForPlaybackMs, int bufferForPlaybackAfterRebufferMs, @@ -267,17 +290,27 @@ protected DefaultLoadControl( assertGreaterOrEqual(bufferForPlaybackMs, 0, "bufferForPlaybackMs", "0"); assertGreaterOrEqual( bufferForPlaybackAfterRebufferMs, 0, "bufferForPlaybackAfterRebufferMs", "0"); - assertGreaterOrEqual(minBufferMs, bufferForPlaybackMs, "minBufferMs", "bufferForPlaybackMs"); assertGreaterOrEqual( - minBufferMs, + minBufferAudioMs, bufferForPlaybackMs, "minBufferAudioMs", "bufferForPlaybackMs"); + assertGreaterOrEqual( + minBufferVideoMs, bufferForPlaybackMs, "minBufferVideoMs", "bufferForPlaybackMs"); + assertGreaterOrEqual( + minBufferAudioMs, bufferForPlaybackAfterRebufferMs, - "minBufferMs", + "minBufferAudioMs", "bufferForPlaybackAfterRebufferMs"); - assertGreaterOrEqual(maxBufferMs, minBufferMs, "maxBufferMs", "minBufferMs"); + assertGreaterOrEqual( + minBufferVideoMs, + bufferForPlaybackAfterRebufferMs, + "minBufferVideoMs", + "bufferForPlaybackAfterRebufferMs"); + assertGreaterOrEqual(maxBufferMs, minBufferAudioMs, "maxBufferMs", "minBufferAudioMs"); + assertGreaterOrEqual(maxBufferMs, minBufferVideoMs, "maxBufferMs", "minBufferVideoMs"); assertGreaterOrEqual(backBufferDurationMs, 0, "backBufferDurationMs", "0"); this.allocator = allocator; - this.minBufferUs = C.msToUs(minBufferMs); + this.minBufferAudioUs = C.msToUs(minBufferAudioMs); + this.minBufferVideoUs = C.msToUs(minBufferVideoMs); this.maxBufferUs = C.msToUs(maxBufferMs); this.bufferForPlaybackUs = C.msToUs(bufferForPlaybackMs); this.bufferForPlaybackAfterRebufferUs = C.msToUs(bufferForPlaybackAfterRebufferMs); @@ -295,6 +328,7 @@ public void onPrepared() { @Override public void onTracksSelected(Renderer[] renderers, TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { + hasVideo = hasVideo(renderers, trackSelections); targetBufferSize = targetBufferBytesOverwrite == C.LENGTH_UNSET ? calculateTargetBufferSize(renderers, trackSelections) @@ -330,7 +364,7 @@ public boolean retainBackBufferFromKeyframe() { @Override public boolean shouldContinueLoading(long bufferedDurationUs, float playbackSpeed) { boolean targetBufferSizeReached = allocator.getTotalBytesAllocated() >= targetBufferSize; - long minBufferUs = this.minBufferUs; + long minBufferUs = hasVideo ? minBufferVideoUs : minBufferAudioUs; if (playbackSpeed > 1) { // The playback speed is faster than real time, so scale up the minimum required media // duration to keep enough media buffered for a playout duration of minBufferUs. @@ -384,6 +418,15 @@ private void reset(boolean resetAllocator) { } } + private static boolean hasVideo(Renderer[] renderers, TrackSelectionArray trackSelectionArray) { + for (int i = 0; i < renderers.length; i++) { + if (renderers[i].getTrackType() == C.TRACK_TYPE_VIDEO && trackSelectionArray.get(i) != null) { + return true; + } + } + return false; + } + private static void assertGreaterOrEqual(int value1, int value2, String name1, String name2) { Assertions.checkArgument(value1 >= value2, name1 + " cannot be less than " + name2); }