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
This commit is contained in:
tonihei 2019-04-23 11:00:55 +01:00 committed by Oliver Woodman
parent d89f3eeb29
commit 9725132e3c
2 changed files with 62 additions and 17 deletions

View File

@ -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 ###

View File

@ -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 {
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 {
/** 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 {
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 {
*/ */
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 {
} }
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 {
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 {
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 {
/** @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 {
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 {
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 {
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 {
@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 {
@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 {
} }
} }
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);
} }