From f34930ab0decee5b8855bc2b0a22666a28140d2c Mon Sep 17 00:00:00 2001 From: christosts Date: Wed, 26 Feb 2020 11:36:53 +0000 Subject: [PATCH] Simplify DefaultLoadControl DefaultLoadControl applies the same min buffer duration to audio and video. By default, min buffer is set equal to max buffer (50 seconds). PiperOrigin-RevId: 297324489 --- RELEASENOTES.md | 3 + .../exoplayer2/DefaultLoadControl.java | 63 +++++-------------- .../exoplayer2/DefaultLoadControlTest.java | 28 ++++++--- 3 files changed, 40 insertions(+), 54 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 37acdca262..4c4aa230a3 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -3,6 +3,9 @@ ### dev-v2 (not yet released) ### * Core library: + * The `DefaultLoadControl` default minimum buffer is set to 50 seconds, + equal to the default maximum buffer. `DefaultLoadControl` applies the + same behavior for audio and video. * Add API in `AnalyticsListener` to report video frame processing offset. `MediaCodecVideoRenderer` reports the event. * Add fields `videoFrameProcessingOffsetUsSum` and 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 7bfd4c7cbe..9fc72152e3 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,14 +29,12 @@ 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. This value is only applied to playbacks without video. + * times, in milliseconds. */ - public static final int DEFAULT_MIN_BUFFER_MS = 15000; + public static final int DEFAULT_MIN_BUFFER_MS = 50000; /** * 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; @@ -90,8 +88,7 @@ public class DefaultLoadControl implements LoadControl { public static final class Builder { private DefaultAllocator allocator; - private int minBufferAudioMs; - private int minBufferVideoMs; + private int minBufferMs; private int maxBufferMs; private int bufferForPlaybackMs; private int bufferForPlaybackAfterRebufferMs; @@ -103,8 +100,7 @@ public class DefaultLoadControl implements LoadControl { /** Constructs a new instance. */ public Builder() { - minBufferAudioMs = DEFAULT_MIN_BUFFER_MS; - minBufferVideoMs = DEFAULT_MAX_BUFFER_MS; + minBufferMs = DEFAULT_MIN_BUFFER_MS; maxBufferMs = DEFAULT_MAX_BUFFER_MS; bufferForPlaybackMs = DEFAULT_BUFFER_FOR_PLAYBACK_MS; bufferForPlaybackAfterRebufferMs = DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS; @@ -158,8 +154,7 @@ public class DefaultLoadControl implements LoadControl { "minBufferMs", "bufferForPlaybackAfterRebufferMs"); assertGreaterOrEqual(maxBufferMs, minBufferMs, "maxBufferMs", "minBufferMs"); - this.minBufferAudioMs = minBufferMs; - this.minBufferVideoMs = minBufferMs; + this.minBufferMs = minBufferMs; this.maxBufferMs = maxBufferMs; this.bufferForPlaybackMs = bufferForPlaybackMs; this.bufferForPlaybackAfterRebufferMs = bufferForPlaybackAfterRebufferMs; @@ -222,8 +217,7 @@ public class DefaultLoadControl implements LoadControl { } return new DefaultLoadControl( allocator, - minBufferAudioMs, - minBufferVideoMs, + minBufferMs, maxBufferMs, bufferForPlaybackMs, bufferForPlaybackAfterRebufferMs, @@ -236,8 +230,7 @@ public class DefaultLoadControl implements LoadControl { private final DefaultAllocator allocator; - private final long minBufferAudioUs; - private final long minBufferVideoUs; + private final long minBufferUs; private final long maxBufferUs; private final long bufferForPlaybackUs; private final long bufferForPlaybackAfterRebufferUs; @@ -248,7 +241,6 @@ public class DefaultLoadControl implements LoadControl { private int targetBufferBytes; private boolean isBuffering; - private boolean hasVideo; /** Constructs a new instance, using the {@code DEFAULT_*} constants defined in this class. */ @SuppressWarnings("deprecation") @@ -261,8 +253,7 @@ public class DefaultLoadControl implements LoadControl { public DefaultLoadControl(DefaultAllocator allocator) { this( allocator, - /* minBufferAudioMs= */ DEFAULT_MIN_BUFFER_MS, - /* minBufferVideoMs= */ DEFAULT_MAX_BUFFER_MS, + DEFAULT_MIN_BUFFER_MS, DEFAULT_MAX_BUFFER_MS, DEFAULT_BUFFER_FOR_PLAYBACK_MS, DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS, @@ -284,8 +275,7 @@ public class DefaultLoadControl implements LoadControl { boolean prioritizeTimeOverSizeThresholds) { this( allocator, - /* minBufferAudioMs= */ minBufferMs, - /* minBufferVideoMs= */ minBufferMs, + minBufferMs, maxBufferMs, bufferForPlaybackMs, bufferForPlaybackAfterRebufferMs, @@ -297,8 +287,7 @@ public class DefaultLoadControl implements LoadControl { protected DefaultLoadControl( DefaultAllocator allocator, - int minBufferAudioMs, - int minBufferVideoMs, + int minBufferMs, int maxBufferMs, int bufferForPlaybackMs, int bufferForPlaybackAfterRebufferMs, @@ -309,27 +298,17 @@ public class DefaultLoadControl implements LoadControl { assertGreaterOrEqual(bufferForPlaybackMs, 0, "bufferForPlaybackMs", "0"); assertGreaterOrEqual( bufferForPlaybackAfterRebufferMs, 0, "bufferForPlaybackAfterRebufferMs", "0"); + assertGreaterOrEqual(minBufferMs, bufferForPlaybackMs, "minBufferMs", "bufferForPlaybackMs"); assertGreaterOrEqual( - minBufferAudioMs, bufferForPlaybackMs, "minBufferAudioMs", "bufferForPlaybackMs"); - assertGreaterOrEqual( - minBufferVideoMs, bufferForPlaybackMs, "minBufferVideoMs", "bufferForPlaybackMs"); - assertGreaterOrEqual( - minBufferAudioMs, + minBufferMs, bufferForPlaybackAfterRebufferMs, - "minBufferAudioMs", + "minBufferMs", "bufferForPlaybackAfterRebufferMs"); - assertGreaterOrEqual( - minBufferVideoMs, - bufferForPlaybackAfterRebufferMs, - "minBufferVideoMs", - "bufferForPlaybackAfterRebufferMs"); - assertGreaterOrEqual(maxBufferMs, minBufferAudioMs, "maxBufferMs", "minBufferAudioMs"); - assertGreaterOrEqual(maxBufferMs, minBufferVideoMs, "maxBufferMs", "minBufferVideoMs"); + assertGreaterOrEqual(maxBufferMs, minBufferMs, "maxBufferMs", "minBufferMs"); assertGreaterOrEqual(backBufferDurationMs, 0, "backBufferDurationMs", "0"); this.allocator = allocator; - this.minBufferAudioUs = C.msToUs(minBufferAudioMs); - this.minBufferVideoUs = C.msToUs(minBufferVideoMs); + this.minBufferUs = C.msToUs(minBufferMs); this.maxBufferUs = C.msToUs(maxBufferMs); this.bufferForPlaybackUs = C.msToUs(bufferForPlaybackMs); this.bufferForPlaybackAfterRebufferUs = C.msToUs(bufferForPlaybackAfterRebufferMs); @@ -351,7 +330,6 @@ public class DefaultLoadControl implements LoadControl { @Override public void onTracksSelected(Renderer[] renderers, TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { - hasVideo = hasVideo(renderers, trackSelections); targetBufferBytes = targetBufferBytesOverwrite == C.LENGTH_UNSET ? calculateTargetBufferBytes(renderers, trackSelections) @@ -387,7 +365,7 @@ public class DefaultLoadControl implements LoadControl { @Override public boolean shouldContinueLoading(long bufferedDurationUs, float playbackSpeed) { boolean targetBufferSizeReached = allocator.getTotalBytesAllocated() >= targetBufferBytes; - long minBufferUs = hasVideo ? minBufferVideoUs : minBufferAudioUs; + long minBufferUs = this.minBufferUs; 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. @@ -467,15 +445,6 @@ public class DefaultLoadControl implements LoadControl { } } - 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); } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/DefaultLoadControlTest.java b/library/core/src/test/java/com/google/android/exoplayer2/DefaultLoadControlTest.java index d53638ce9a..221950f5af 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/DefaultLoadControlTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/DefaultLoadControlTest.java @@ -29,8 +29,8 @@ import org.junit.runner.RunWith; public class DefaultLoadControlTest { private static final float SPEED = 1f; - private static final long MIN_BUFFER_US = C.msToUs(DefaultLoadControl.DEFAULT_MIN_BUFFER_MS); private static final long MAX_BUFFER_US = C.msToUs(DefaultLoadControl.DEFAULT_MAX_BUFFER_MS); + private static final long MIN_BUFFER_US = MAX_BUFFER_US / 2; private static final int TARGET_BUFFER_BYTES = C.DEFAULT_BUFFER_SEGMENT_SIZE * 2; private Builder builder; @@ -48,16 +48,20 @@ public class DefaultLoadControlTest { createDefaultLoadControl(); assertThat(loadControl.shouldContinueLoading(/* bufferedDurationUs= */ 0, SPEED)).isTrue(); - assertThat(loadControl.shouldContinueLoading(MIN_BUFFER_US, SPEED)).isTrue(); assertThat(loadControl.shouldContinueLoading(MAX_BUFFER_US - 1, SPEED)).isTrue(); assertThat(loadControl.shouldContinueLoading(MAX_BUFFER_US, SPEED)).isFalse(); } @Test public void shouldNotContinueLoadingOnceBufferingStopped_untilBelowMinBuffer() { + builder.setBufferDurationsMs( + /* minBufferMs= */ (int) C.usToMs(MIN_BUFFER_US), + /* maxBufferMs= */ (int) C.usToMs(MAX_BUFFER_US), + /* bufferForPlaybackMs= */ 0, + /* bufferForPlaybackAfterRebufferMs= */ 0); createDefaultLoadControl(); - assertThat(loadControl.shouldContinueLoading(MAX_BUFFER_US, SPEED)).isFalse(); + assertThat(loadControl.shouldContinueLoading(MAX_BUFFER_US, SPEED)).isFalse(); assertThat(loadControl.shouldContinueLoading(MAX_BUFFER_US - 1, SPEED)).isFalse(); assertThat(loadControl.shouldContinueLoading(MIN_BUFFER_US, SPEED)).isFalse(); assertThat(loadControl.shouldContinueLoading(MIN_BUFFER_US - 1, SPEED)).isTrue(); @@ -72,14 +76,19 @@ public class DefaultLoadControlTest { /* bufferForPlaybackMs= */ 0, /* bufferForPlaybackAfterRebufferMs= */ 0); createDefaultLoadControl(); - assertThat(loadControl.shouldContinueLoading(MAX_BUFFER_US, SPEED)).isFalse(); + assertThat(loadControl.shouldContinueLoading(MAX_BUFFER_US, SPEED)).isFalse(); assertThat(loadControl.shouldContinueLoading(5 * C.MICROS_PER_SECOND, SPEED)).isFalse(); assertThat(loadControl.shouldContinueLoading(500L, SPEED)).isTrue(); } @Test public void shouldContinueLoadingWithTargetBufferBytesReached_untilMinBufferReached() { + builder.setBufferDurationsMs( + /* minBufferMs= */ (int) C.usToMs(MIN_BUFFER_US), + /* maxBufferMs= */ (int) C.usToMs(MAX_BUFFER_US), + /* bufferForPlaybackMs= */ 0, + /* bufferForPlaybackAfterRebufferMs= */ 0); createDefaultLoadControl(); makeSureTargetBufferBytesReached(); @@ -93,6 +102,7 @@ public class DefaultLoadControlTest { public void shouldNeverContinueLoading_ifMaxBufferReachedAndNotPrioritizeTimeOverSize() { builder.setPrioritizeTimeOverSizeThresholds(false); createDefaultLoadControl(); + // Put loadControl in buffering state. assertThat(loadControl.shouldContinueLoading(/* bufferedDurationUs= */ 0, SPEED)).isTrue(); makeSureTargetBufferBytesReached(); @@ -105,6 +115,11 @@ public class DefaultLoadControlTest { @Test public void shouldContinueLoadingWithMinBufferReached_inFastPlayback() { + builder.setBufferDurationsMs( + /* minBufferMs= */ (int) C.usToMs(MIN_BUFFER_US), + /* maxBufferMs= */ (int) C.usToMs(MAX_BUFFER_US), + /* bufferForPlaybackMs= */ 0, + /* bufferForPlaybackAfterRebufferMs= */ 0); createDefaultLoadControl(); // At normal playback speed, we stop buffering when the buffer reaches the minimum. @@ -124,14 +139,13 @@ public class DefaultLoadControlTest { @Test public void startsPlayback_whenMinBufferSizeReached() { createDefaultLoadControl(); - + assertThat(loadControl.shouldStartPlayback(MIN_BUFFER_US, SPEED, /* rebuffering= */ false)) .isTrue(); } private void createDefaultLoadControl() { - builder.setAllocator(allocator); - builder.setTargetBufferBytes(TARGET_BUFFER_BYTES); + builder.setAllocator(allocator).setTargetBufferBytes(TARGET_BUFFER_BYTES); loadControl = builder.createDefaultLoadControl(); loadControl.onTracksSelected(new Renderer[0], null, null); }