Commit 9725132e by tonihei Committed by Oliver Woodman

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
parent d89f3eeb
...@@ -120,6 +120,8 @@ ...@@ -120,6 +120,8 @@
order when in shuffle mode. order when in shuffle mode.
* Allow handling of custom commands via `registerCustomCommandReceiver`. * Allow handling of custom commands via `registerCustomCommandReceiver`.
* Add ability to include an extras `Bundle` when reporting a custom error. * Add ability to include an extras `Bundle` when reporting a custom error.
* LoadControl: Set minimum buffer for playbacks with video equal to maximum
buffer ([#2083](https://github.com/google/ExoPlayer/issues/2083)).
### 2.9.6 ### ### 2.9.6 ###
......
...@@ -29,12 +29,14 @@ public class DefaultLoadControl implements LoadControl { ...@@ -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 * 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; public static final int DEFAULT_MIN_BUFFER_MS = 15000;
/** /**
* The default maximum duration of media that the player will attempt to buffer, in milliseconds. * 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; public static final int DEFAULT_MAX_BUFFER_MS = 50000;
...@@ -69,7 +71,8 @@ public class DefaultLoadControl implements LoadControl { ...@@ -69,7 +71,8 @@ public class DefaultLoadControl implements LoadControl {
public static final class Builder { public static final class Builder {
private DefaultAllocator allocator; private DefaultAllocator allocator;
private int minBufferMs; private int minBufferAudioMs;
private int minBufferVideoMs;
private int maxBufferMs; private int maxBufferMs;
private int bufferForPlaybackMs; private int bufferForPlaybackMs;
private int bufferForPlaybackAfterRebufferMs; private int bufferForPlaybackAfterRebufferMs;
...@@ -81,7 +84,8 @@ public class DefaultLoadControl implements LoadControl { ...@@ -81,7 +84,8 @@ public class DefaultLoadControl implements LoadControl {
/** Constructs a new instance. */ /** Constructs a new instance. */
public Builder() { public Builder() {
minBufferMs = DEFAULT_MIN_BUFFER_MS; minBufferAudioMs = DEFAULT_MIN_BUFFER_MS;
minBufferVideoMs = DEFAULT_MAX_BUFFER_MS;
maxBufferMs = DEFAULT_MAX_BUFFER_MS; maxBufferMs = DEFAULT_MAX_BUFFER_MS;
bufferForPlaybackMs = DEFAULT_BUFFER_FOR_PLAYBACK_MS; bufferForPlaybackMs = DEFAULT_BUFFER_FOR_PLAYBACK_MS;
bufferForPlaybackAfterRebufferMs = DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS; bufferForPlaybackAfterRebufferMs = DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS;
...@@ -125,7 +129,18 @@ public class DefaultLoadControl implements LoadControl { ...@@ -125,7 +129,18 @@ public class DefaultLoadControl implements LoadControl {
int bufferForPlaybackMs, int bufferForPlaybackMs,
int bufferForPlaybackAfterRebufferMs) { int bufferForPlaybackAfterRebufferMs) {
Assertions.checkState(!createDefaultLoadControlCalled); 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.maxBufferMs = maxBufferMs;
this.bufferForPlaybackMs = bufferForPlaybackMs; this.bufferForPlaybackMs = bufferForPlaybackMs;
this.bufferForPlaybackAfterRebufferMs = bufferForPlaybackAfterRebufferMs; this.bufferForPlaybackAfterRebufferMs = bufferForPlaybackAfterRebufferMs;
...@@ -173,6 +188,7 @@ public class DefaultLoadControl implements LoadControl { ...@@ -173,6 +188,7 @@ public class DefaultLoadControl implements LoadControl {
*/ */
public Builder setBackBuffer(int backBufferDurationMs, boolean retainBackBufferFromKeyframe) { public Builder setBackBuffer(int backBufferDurationMs, boolean retainBackBufferFromKeyframe) {
Assertions.checkState(!createDefaultLoadControlCalled); Assertions.checkState(!createDefaultLoadControlCalled);
assertGreaterOrEqual(backBufferDurationMs, 0, "backBufferDurationMs", "0");
this.backBufferDurationMs = backBufferDurationMs; this.backBufferDurationMs = backBufferDurationMs;
this.retainBackBufferFromKeyframe = retainBackBufferFromKeyframe; this.retainBackBufferFromKeyframe = retainBackBufferFromKeyframe;
return this; return this;
...@@ -187,7 +203,8 @@ public class DefaultLoadControl implements LoadControl { ...@@ -187,7 +203,8 @@ public class DefaultLoadControl implements LoadControl {
} }
return new DefaultLoadControl( return new DefaultLoadControl(
allocator, allocator,
minBufferMs, minBufferAudioMs,
minBufferVideoMs,
maxBufferMs, maxBufferMs,
bufferForPlaybackMs, bufferForPlaybackMs,
bufferForPlaybackAfterRebufferMs, bufferForPlaybackAfterRebufferMs,
...@@ -200,7 +217,8 @@ public class DefaultLoadControl implements LoadControl { ...@@ -200,7 +217,8 @@ public class DefaultLoadControl implements LoadControl {
private final DefaultAllocator allocator; private final DefaultAllocator allocator;
private final long minBufferUs; private final long minBufferAudioUs;
private final long minBufferVideoUs;
private final long maxBufferUs; private final long maxBufferUs;
private final long bufferForPlaybackUs; private final long bufferForPlaybackUs;
private final long bufferForPlaybackAfterRebufferUs; private final long bufferForPlaybackAfterRebufferUs;
...@@ -211,6 +229,7 @@ public class DefaultLoadControl implements LoadControl { ...@@ -211,6 +229,7 @@ public class DefaultLoadControl implements LoadControl {
private int targetBufferSize; private int targetBufferSize;
private boolean isBuffering; private boolean isBuffering;
private boolean hasVideo;
/** Constructs a new instance, using the {@code DEFAULT_*} constants defined in this class. */ /** Constructs a new instance, using the {@code DEFAULT_*} constants defined in this class. */
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
...@@ -220,16 +239,18 @@ public class DefaultLoadControl implements LoadControl { ...@@ -220,16 +239,18 @@ public class DefaultLoadControl implements LoadControl {
/** @deprecated Use {@link Builder} instead. */ /** @deprecated Use {@link Builder} instead. */
@Deprecated @Deprecated
@SuppressWarnings("deprecation")
public DefaultLoadControl(DefaultAllocator allocator) { public DefaultLoadControl(DefaultAllocator allocator) {
this( this(
allocator, allocator,
DEFAULT_MIN_BUFFER_MS, /* minBufferAudioMs= */ DEFAULT_MIN_BUFFER_MS,
/* minBufferVideoMs= */ DEFAULT_MAX_BUFFER_MS,
DEFAULT_MAX_BUFFER_MS, DEFAULT_MAX_BUFFER_MS,
DEFAULT_BUFFER_FOR_PLAYBACK_MS, DEFAULT_BUFFER_FOR_PLAYBACK_MS,
DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS, DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS,
DEFAULT_TARGET_BUFFER_BYTES, 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. */ /** @deprecated Use {@link Builder} instead. */
...@@ -244,7 +265,8 @@ public class DefaultLoadControl implements LoadControl { ...@@ -244,7 +265,8 @@ public class DefaultLoadControl implements LoadControl {
boolean prioritizeTimeOverSizeThresholds) { boolean prioritizeTimeOverSizeThresholds) {
this( this(
allocator, allocator,
minBufferMs, /* minBufferAudioMs= */ minBufferMs,
/* minBufferVideoMs= */ minBufferMs,
maxBufferMs, maxBufferMs,
bufferForPlaybackMs, bufferForPlaybackMs,
bufferForPlaybackAfterRebufferMs, bufferForPlaybackAfterRebufferMs,
...@@ -256,7 +278,8 @@ public class DefaultLoadControl implements LoadControl { ...@@ -256,7 +278,8 @@ public class DefaultLoadControl implements LoadControl {
protected DefaultLoadControl( protected DefaultLoadControl(
DefaultAllocator allocator, DefaultAllocator allocator,
int minBufferMs, int minBufferAudioMs,
int minBufferVideoMs,
int maxBufferMs, int maxBufferMs,
int bufferForPlaybackMs, int bufferForPlaybackMs,
int bufferForPlaybackAfterRebufferMs, int bufferForPlaybackAfterRebufferMs,
...@@ -267,17 +290,27 @@ public class DefaultLoadControl implements LoadControl { ...@@ -267,17 +290,27 @@ public class DefaultLoadControl implements LoadControl {
assertGreaterOrEqual(bufferForPlaybackMs, 0, "bufferForPlaybackMs", "0"); assertGreaterOrEqual(bufferForPlaybackMs, 0, "bufferForPlaybackMs", "0");
assertGreaterOrEqual( assertGreaterOrEqual(
bufferForPlaybackAfterRebufferMs, 0, "bufferForPlaybackAfterRebufferMs", "0"); bufferForPlaybackAfterRebufferMs, 0, "bufferForPlaybackAfterRebufferMs", "0");
assertGreaterOrEqual(minBufferMs, bufferForPlaybackMs, "minBufferMs", "bufferForPlaybackMs");
assertGreaterOrEqual( assertGreaterOrEqual(
minBufferMs, minBufferAudioMs, bufferForPlaybackMs, "minBufferAudioMs", "bufferForPlaybackMs");
assertGreaterOrEqual(
minBufferVideoMs, bufferForPlaybackMs, "minBufferVideoMs", "bufferForPlaybackMs");
assertGreaterOrEqual(
minBufferAudioMs,
bufferForPlaybackAfterRebufferMs, bufferForPlaybackAfterRebufferMs,
"minBufferMs", "minBufferAudioMs",
"bufferForPlaybackAfterRebufferMs"); "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"); assertGreaterOrEqual(backBufferDurationMs, 0, "backBufferDurationMs", "0");
this.allocator = allocator; this.allocator = allocator;
this.minBufferUs = C.msToUs(minBufferMs); this.minBufferAudioUs = C.msToUs(minBufferAudioMs);
this.minBufferVideoUs = C.msToUs(minBufferVideoMs);
this.maxBufferUs = C.msToUs(maxBufferMs); this.maxBufferUs = C.msToUs(maxBufferMs);
this.bufferForPlaybackUs = C.msToUs(bufferForPlaybackMs); this.bufferForPlaybackUs = C.msToUs(bufferForPlaybackMs);
this.bufferForPlaybackAfterRebufferUs = C.msToUs(bufferForPlaybackAfterRebufferMs); this.bufferForPlaybackAfterRebufferUs = C.msToUs(bufferForPlaybackAfterRebufferMs);
...@@ -295,6 +328,7 @@ public class DefaultLoadControl implements LoadControl { ...@@ -295,6 +328,7 @@ public class DefaultLoadControl implements LoadControl {
@Override @Override
public void onTracksSelected(Renderer[] renderers, TrackGroupArray trackGroups, public void onTracksSelected(Renderer[] renderers, TrackGroupArray trackGroups,
TrackSelectionArray trackSelections) { TrackSelectionArray trackSelections) {
hasVideo = hasVideo(renderers, trackSelections);
targetBufferSize = targetBufferSize =
targetBufferBytesOverwrite == C.LENGTH_UNSET targetBufferBytesOverwrite == C.LENGTH_UNSET
? calculateTargetBufferSize(renderers, trackSelections) ? calculateTargetBufferSize(renderers, trackSelections)
...@@ -330,7 +364,7 @@ public class DefaultLoadControl implements LoadControl { ...@@ -330,7 +364,7 @@ public class DefaultLoadControl implements LoadControl {
@Override @Override
public boolean shouldContinueLoading(long bufferedDurationUs, float playbackSpeed) { public boolean shouldContinueLoading(long bufferedDurationUs, float playbackSpeed) {
boolean targetBufferSizeReached = allocator.getTotalBytesAllocated() >= targetBufferSize; boolean targetBufferSizeReached = allocator.getTotalBytesAllocated() >= targetBufferSize;
long minBufferUs = this.minBufferUs; long minBufferUs = hasVideo ? minBufferVideoUs : minBufferAudioUs;
if (playbackSpeed > 1) { if (playbackSpeed > 1) {
// The playback speed is faster than real time, so scale up the minimum required media // 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. // duration to keep enough media buffered for a playout duration of minBufferUs.
...@@ -384,6 +418,15 @@ public class DefaultLoadControl implements LoadControl { ...@@ -384,6 +418,15 @@ 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) { private static void assertGreaterOrEqual(int value1, int value2, String name1, String name2) {
Assertions.checkArgument(value1 >= value2, name1 + " cannot be less than " + name2); Assertions.checkArgument(value1 >= value2, name1 + " cannot be less than " + name2);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment