mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Rollback of 3e41c0a1d2
*** Original commit *** Rollback of3c56b113e4*** Original commit *** Rollback ofd48dc4c159*** 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 (b84bde0252). Move this logic... *** PiperOrigin-RevId: 292457964
This commit is contained in:
parent
2adec8d984
commit
2a718c5aa6
4 changed files with 26 additions and 58 deletions
|
|
@ -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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
|
|
||||||
@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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
* @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 {
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
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;
|
||||||
}
|
}
|
||||||
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 {
|
||||||
@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,27 +56,11 @@ 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
|
|
||||||
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
|
@Test
|
||||||
public void testShouldContinueLoadingWithTargetBufferBytesReached_untilMinBufferReached() {
|
public void testShouldContinueLoadingWithTargetBufferBytesReached_untilMinBufferReached() {
|
||||||
createDefaultLoadControl();
|
createDefaultLoadControl();
|
||||||
|
|
@ -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 {
|
||||||
|
|
||||||
// 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 {
|
||||||
}
|
}
|
||||||
|
|
||||||
@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 {
|
||||||
@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 {
|
||||||
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(neverLoadingLoadControl)
|
.setLoadControl(neverLoadingOrPlayingLoadControl)
|
||||||
.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
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue