Commit 2a718c5a by olly Committed by Oliver Woodman

Rollback of https://github.com/google/ExoPlayer/commit/3e41c0a1d28020251a841c86255d677f3d760792

*** Original commit ***

Rollback of https://github.com/google/ExoPlayer/commit/3c56b113e43812f188bdd9750f48897e812697ce

*** Original commit ***

Rollback of https://github.com/google/ExoPlayer/commit/d48dc4c15933dd8354cbcc6260c48565bb850e15

*** Original commit ***

Move getting-stuck-prevention into DefaultLoadControl.

We recently added code that prevents getting stuck if the buffer is low and
the LoadControl refuses to continue loading (https://github.com/google/ExoPlayer/commit/b84bde025258e7307c52eaf6bbe58157d788aa06).

Move this logic...

***

PiperOrigin-RevId: 292457964
parent 2adec8d9
...@@ -246,7 +246,7 @@ public class DefaultLoadControl implements LoadControl { ...@@ -246,7 +246,7 @@ public class DefaultLoadControl implements LoadControl {
private final long backBufferDurationUs; private final long backBufferDurationUs;
private final boolean retainBackBufferFromKeyframe; private final boolean retainBackBufferFromKeyframe;
private int targetBufferBytes; private int targetBufferSize;
private boolean isBuffering; private boolean isBuffering;
private boolean hasVideo; private boolean hasVideo;
...@@ -334,10 +334,6 @@ public class DefaultLoadControl implements LoadControl { ...@@ -334,10 +334,6 @@ public class DefaultLoadControl implements LoadControl {
this.bufferForPlaybackUs = C.msToUs(bufferForPlaybackMs); this.bufferForPlaybackUs = C.msToUs(bufferForPlaybackMs);
this.bufferForPlaybackAfterRebufferUs = C.msToUs(bufferForPlaybackAfterRebufferMs); this.bufferForPlaybackAfterRebufferUs = C.msToUs(bufferForPlaybackAfterRebufferMs);
this.targetBufferBytesOverwrite = targetBufferBytes; this.targetBufferBytesOverwrite = targetBufferBytes;
this.targetBufferBytes =
targetBufferBytesOverwrite != C.LENGTH_UNSET
? targetBufferBytesOverwrite
: DEFAULT_MUXED_BUFFER_SIZE;
this.prioritizeTimeOverSizeThresholds = prioritizeTimeOverSizeThresholds; this.prioritizeTimeOverSizeThresholds = prioritizeTimeOverSizeThresholds;
this.backBufferDurationUs = C.msToUs(backBufferDurationMs); this.backBufferDurationUs = C.msToUs(backBufferDurationMs);
this.retainBackBufferFromKeyframe = retainBackBufferFromKeyframe; this.retainBackBufferFromKeyframe = retainBackBufferFromKeyframe;
...@@ -352,11 +348,11 @@ public class DefaultLoadControl implements LoadControl { ...@@ -352,11 +348,11 @@ public class DefaultLoadControl implements LoadControl {
public void onTracksSelected(Renderer[] renderers, TrackGroupArray trackGroups, public void onTracksSelected(Renderer[] renderers, TrackGroupArray trackGroups,
TrackSelectionArray trackSelections) { TrackSelectionArray trackSelections) {
hasVideo = hasVideo(renderers, trackSelections); hasVideo = hasVideo(renderers, trackSelections);
targetBufferBytes = targetBufferSize =
targetBufferBytesOverwrite == C.LENGTH_UNSET targetBufferBytesOverwrite == C.LENGTH_UNSET
? calculateTargetBufferBytes(renderers, trackSelections) ? calculateTargetBufferSize(renderers, trackSelections)
: targetBufferBytesOverwrite; : targetBufferBytesOverwrite;
allocator.setTargetBufferSize(targetBufferBytes); allocator.setTargetBufferSize(targetBufferSize);
} }
@Override @Override
...@@ -386,7 +382,7 @@ public class DefaultLoadControl implements LoadControl { ...@@ -386,7 +382,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() >= targetBufferBytes; boolean targetBufferSizeReached = allocator.getTotalBytesAllocated() >= targetBufferSize;
long minBufferUs = hasVideo ? minBufferVideoUs : minBufferAudioUs; 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
...@@ -395,8 +391,6 @@ public class DefaultLoadControl implements LoadControl { ...@@ -395,8 +391,6 @@ public class DefaultLoadControl implements LoadControl {
Util.getMediaDurationForPlayoutDuration(minBufferUs, playbackSpeed); Util.getMediaDurationForPlayoutDuration(minBufferUs, playbackSpeed);
minBufferUs = Math.min(mediaDurationMinBufferUs, maxBufferUs); minBufferUs = Math.min(mediaDurationMinBufferUs, maxBufferUs);
} }
// Prevent playback from getting stuck if minBufferUs is too small.
minBufferUs = Math.max(minBufferUs, 500_000);
if (bufferedDurationUs < minBufferUs) { if (bufferedDurationUs < minBufferUs) {
isBuffering = prioritizeTimeOverSizeThresholds || !targetBufferSizeReached; isBuffering = prioritizeTimeOverSizeThresholds || !targetBufferSizeReached;
} else if (bufferedDurationUs >= maxBufferUs || targetBufferSizeReached) { } else if (bufferedDurationUs >= maxBufferUs || targetBufferSizeReached) {
...@@ -413,7 +407,7 @@ public class DefaultLoadControl implements LoadControl { ...@@ -413,7 +407,7 @@ public class DefaultLoadControl implements LoadControl {
return minBufferDurationUs <= 0 return minBufferDurationUs <= 0
|| bufferedDurationUs >= minBufferDurationUs || bufferedDurationUs >= minBufferDurationUs
|| (!prioritizeTimeOverSizeThresholds || (!prioritizeTimeOverSizeThresholds
&& allocator.getTotalBytesAllocated() >= targetBufferBytes); && allocator.getTotalBytesAllocated() >= targetBufferSize);
} }
/** /**
...@@ -424,7 +418,7 @@ public class DefaultLoadControl implements LoadControl { ...@@ -424,7 +418,7 @@ public class DefaultLoadControl implements LoadControl {
* @param trackSelectionArray The selected tracks. * @param trackSelectionArray The selected tracks.
* @return The target buffer size in bytes. * @return The target buffer size in bytes.
*/ */
protected int calculateTargetBufferBytes( protected int calculateTargetBufferSize(
Renderer[] renderers, TrackSelectionArray trackSelectionArray) { Renderer[] renderers, TrackSelectionArray trackSelectionArray) {
int targetBufferSize = 0; int targetBufferSize = 0;
for (int i = 0; i < renderers.length; i++) { for (int i = 0; i < renderers.length; i++) {
...@@ -436,10 +430,7 @@ public class DefaultLoadControl implements LoadControl { ...@@ -436,10 +430,7 @@ public class DefaultLoadControl implements LoadControl {
} }
private void reset(boolean resetAllocator) { private void reset(boolean resetAllocator) {
targetBufferBytes = targetBufferSize = 0;
targetBufferBytesOverwrite == C.LENGTH_UNSET
? DEFAULT_MUXED_BUFFER_SIZE
: targetBufferBytesOverwrite;
isBuffering = false; isBuffering = false;
if (resetAllocator) { if (resetAllocator) {
allocator.reset(); allocator.reset();
......
...@@ -834,14 +834,6 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -834,14 +834,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
for (Renderer renderer : enabledRenderers) { for (Renderer renderer : enabledRenderers) {
renderer.maybeThrowStreamError(); renderer.maybeThrowStreamError();
} }
if (!shouldContinueLoading
&& playbackInfo.totalBufferedDurationUs < 500_000
&& isLoadingPossible()) {
// Throw if the LoadControl prevents loading even if the buffer is empty or almost empty. We
// can't compare against 0 to account for small differences between the renderer position
// and buffered position in the media at the point where playback gets stuck.
throw new IllegalStateException("Playback stuck buffering and not loading");
}
} }
if ((playWhenReady && playbackInfo.playbackState == Player.STATE_READY) if ((playWhenReady && playbackInfo.playbackState == Player.STATE_READY)
...@@ -1883,6 +1875,13 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -1883,6 +1875,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
} }
long bufferedDurationUs = long bufferedDurationUs =
getTotalBufferedDurationUs(queue.getLoadingPeriod().getNextLoadPositionUs()); getTotalBufferedDurationUs(queue.getLoadingPeriod().getNextLoadPositionUs());
if (bufferedDurationUs < 500_000) {
// Prevent loading from getting stuck even if LoadControl.shouldContinueLoading returns false
// when the buffer is empty or almost empty. We can't compare against 0 to account for small
// differences between the renderer position and buffered position in the media at the point
// where playback gets stuck.
return true;
}
float playbackSpeed = mediaClock.getPlaybackParameters().speed; float playbackSpeed = mediaClock.getPlaybackParameters().speed;
return loadControl.shouldContinueLoading(bufferedDurationUs, playbackSpeed); return loadControl.shouldContinueLoading(bufferedDurationUs, playbackSpeed);
} }
......
...@@ -46,7 +46,6 @@ public class DefaultLoadControlTest { ...@@ -46,7 +46,6 @@ public class DefaultLoadControlTest {
@Test @Test
public void testShouldContinueLoading_untilMaxBufferExceeded() { public void testShouldContinueLoading_untilMaxBufferExceeded() {
createDefaultLoadControl(); createDefaultLoadControl();
assertThat(loadControl.shouldContinueLoading(/* bufferedDurationUs= */ 0, SPEED)).isTrue(); assertThat(loadControl.shouldContinueLoading(/* bufferedDurationUs= */ 0, SPEED)).isTrue();
assertThat(loadControl.shouldContinueLoading(MIN_BUFFER_US, 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 - 1, SPEED)).isTrue();
...@@ -57,28 +56,12 @@ public class DefaultLoadControlTest { ...@@ -57,28 +56,12 @@ public class DefaultLoadControlTest {
public void testShouldNotContinueLoadingOnceBufferingStopped_untilBelowMinBuffer() { public void testShouldNotContinueLoadingOnceBufferingStopped_untilBelowMinBuffer() {
createDefaultLoadControl(); 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(MAX_BUFFER_US - 1, SPEED)).isFalse();
assertThat(loadControl.shouldContinueLoading(MIN_BUFFER_US, SPEED)).isFalse(); assertThat(loadControl.shouldContinueLoading(MIN_BUFFER_US, SPEED)).isFalse();
assertThat(loadControl.shouldContinueLoading(MIN_BUFFER_US - 1, SPEED)).isTrue(); assertThat(loadControl.shouldContinueLoading(MIN_BUFFER_US - 1, SPEED)).isTrue();
} }
@Test @Test
public void
testContinueLoadingOnceBufferingStopped_andBufferAlmostEmpty_evenIfMinBufferNotReached() {
builder.setBufferDurationsMs(
/* minBufferMs= */ 0,
/* maxBufferMs= */ (int) C.usToMs(MAX_BUFFER_US),
/* bufferForPlaybackMs= */ 0,
/* bufferForPlaybackAfterRebufferMs= */ 0);
createDefaultLoadControl();
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 testShouldContinueLoadingWithTargetBufferBytesReached_untilMinBufferReached() { public void testShouldContinueLoadingWithTargetBufferBytesReached_untilMinBufferReached() {
createDefaultLoadControl(); createDefaultLoadControl();
makeSureTargetBufferBytesReached(); makeSureTargetBufferBytesReached();
...@@ -98,7 +81,6 @@ public class DefaultLoadControlTest { ...@@ -98,7 +81,6 @@ public class DefaultLoadControlTest {
makeSureTargetBufferBytesReached(); makeSureTargetBufferBytesReached();
assertThat(loadControl.shouldContinueLoading(/* bufferedDurationUs= */ 0, SPEED)).isFalse(); assertThat(loadControl.shouldContinueLoading(/* bufferedDurationUs= */ 0, SPEED)).isFalse();
assertThat(loadControl.shouldContinueLoading(MIN_BUFFER_US - 1, SPEED)).isFalse();
assertThat(loadControl.shouldContinueLoading(MIN_BUFFER_US, SPEED)).isFalse(); assertThat(loadControl.shouldContinueLoading(MIN_BUFFER_US, SPEED)).isFalse();
assertThat(loadControl.shouldContinueLoading(MAX_BUFFER_US, SPEED)).isFalse(); assertThat(loadControl.shouldContinueLoading(MAX_BUFFER_US, SPEED)).isFalse();
} }
...@@ -109,6 +91,7 @@ public class DefaultLoadControlTest { ...@@ -109,6 +91,7 @@ public class DefaultLoadControlTest {
// At normal playback speed, we stop buffering when the buffer reaches the minimum. // At normal playback speed, we stop buffering when the buffer reaches the minimum.
assertThat(loadControl.shouldContinueLoading(MIN_BUFFER_US, SPEED)).isFalse(); assertThat(loadControl.shouldContinueLoading(MIN_BUFFER_US, SPEED)).isFalse();
// At double playback speed, we continue loading. // At double playback speed, we continue loading.
assertThat(loadControl.shouldContinueLoading(MIN_BUFFER_US, /* playbackSpeed= */ 2f)).isTrue(); assertThat(loadControl.shouldContinueLoading(MIN_BUFFER_US, /* playbackSpeed= */ 2f)).isTrue();
} }
......
...@@ -3391,8 +3391,8 @@ public final class ExoPlayerTest { ...@@ -3391,8 +3391,8 @@ public final class ExoPlayerTest {
} }
@Test @Test
public void loadControlNeverWantsToLoad_throwsIllegalStateException() throws Exception { public void loadControlNeverWantsToLoadOrPlay_playbackDoesNotGetStuck() throws Exception {
LoadControl neverLoadingLoadControl = LoadControl neverLoadingOrPlayingLoadControl =
new DefaultLoadControl() { new DefaultLoadControl() {
@Override @Override
public boolean shouldContinueLoading(long bufferedDurationUs, float playbackSpeed) { public boolean shouldContinueLoading(long bufferedDurationUs, float playbackSpeed) {
...@@ -3402,7 +3402,7 @@ public final class ExoPlayerTest { ...@@ -3402,7 +3402,7 @@ public final class ExoPlayerTest {
@Override @Override
public boolean shouldStartPlayback( public boolean shouldStartPlayback(
long bufferedDurationUs, float playbackSpeed, boolean rebuffering) { long bufferedDurationUs, float playbackSpeed, boolean rebuffering) {
return true; return false;
} }
}; };
...@@ -3416,18 +3416,13 @@ public final class ExoPlayerTest { ...@@ -3416,18 +3416,13 @@ public final class ExoPlayerTest {
new TrackGroupArray(new TrackGroup(Builder.VIDEO_FORMAT)), new TrackGroupArray(new TrackGroup(Builder.VIDEO_FORMAT)),
new FakeChunkSource.Factory(dataSetFactory, new FakeDataSource.Factory())); new FakeChunkSource.Factory(dataSetFactory, new FakeDataSource.Factory()));
try { new ExoPlayerTestRunner.Builder()
new ExoPlayerTestRunner.Builder() .setLoadControl(neverLoadingOrPlayingLoadControl)
.setLoadControl(neverLoadingLoadControl) .setMediaSources(chunkedMediaSource)
.setMediaSources(chunkedMediaSource) .build(context)
.build(context) .start()
.start() // This throws if playback doesn't finish within timeout.
.blockUntilEnded(TIMEOUT_MS); .blockUntilEnded(TIMEOUT_MS);
fail();
} catch (ExoPlaybackException e) {
assertThat(e.type).isEqualTo(ExoPlaybackException.TYPE_UNEXPECTED);
assertThat(e.getUnexpectedException()).isInstanceOf(IllegalStateException.class);
}
} }
@Test @Test
......
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