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:
parent
d89f3eeb29
commit
9725132e3c
@ -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 {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 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);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user