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
This commit is contained in:
christosts 2020-02-26 11:36:53 +00:00 committed by kim-vde
parent 31f0302505
commit f34930ab0d
3 changed files with 40 additions and 54 deletions

View File

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

View File

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

View File

@ -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.
@ -130,8 +145,7 @@ public class DefaultLoadControlTest {
}
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);
}