mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Rollback of d48dc4c159
*** 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 into DefaultLoadControl to keep the workaround, and also apply the
maximum buffer size check in bytes if enabled. ExoPlayerImplInternal will now
throw if a LoadControl lets playback get stuck. This includes the case where
DefaultLoadControl reaches its maximum buffer size and not even a mim...
***
PiperOrigin-RevId: 286071115
This commit is contained in:
parent
36fa9d5a43
commit
3c56b113e4
@ -246,7 +246,7 @@ public class DefaultLoadControl implements LoadControl {
|
||||
private final long backBufferDurationUs;
|
||||
private final boolean retainBackBufferFromKeyframe;
|
||||
|
||||
private int targetBufferBytes;
|
||||
private int targetBufferSize;
|
||||
private boolean isBuffering;
|
||||
private boolean hasVideo;
|
||||
|
||||
@ -334,10 +334,6 @@ public class DefaultLoadControl implements LoadControl {
|
||||
this.bufferForPlaybackUs = C.msToUs(bufferForPlaybackMs);
|
||||
this.bufferForPlaybackAfterRebufferUs = C.msToUs(bufferForPlaybackAfterRebufferMs);
|
||||
this.targetBufferBytesOverwrite = targetBufferBytes;
|
||||
this.targetBufferBytes =
|
||||
targetBufferBytesOverwrite != C.LENGTH_UNSET
|
||||
? targetBufferBytesOverwrite
|
||||
: DEFAULT_MUXED_BUFFER_SIZE;
|
||||
this.prioritizeTimeOverSizeThresholds = prioritizeTimeOverSizeThresholds;
|
||||
this.backBufferDurationUs = C.msToUs(backBufferDurationMs);
|
||||
this.retainBackBufferFromKeyframe = retainBackBufferFromKeyframe;
|
||||
@ -352,11 +348,11 @@ public class DefaultLoadControl implements LoadControl {
|
||||
public void onTracksSelected(Renderer[] renderers, TrackGroupArray trackGroups,
|
||||
TrackSelectionArray trackSelections) {
|
||||
hasVideo = hasVideo(renderers, trackSelections);
|
||||
targetBufferBytes =
|
||||
targetBufferSize =
|
||||
targetBufferBytesOverwrite == C.LENGTH_UNSET
|
||||
? calculateTargetBufferBytes(renderers, trackSelections)
|
||||
? calculateTargetBufferSize(renderers, trackSelections)
|
||||
: targetBufferBytesOverwrite;
|
||||
allocator.setTargetBufferSize(targetBufferBytes);
|
||||
allocator.setTargetBufferSize(targetBufferSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -386,7 +382,7 @@ public class DefaultLoadControl implements LoadControl {
|
||||
|
||||
@Override
|
||||
public boolean shouldContinueLoading(long bufferedDurationUs, float playbackSpeed) {
|
||||
boolean targetBufferSizeReached = allocator.getTotalBytesAllocated() >= targetBufferBytes;
|
||||
boolean targetBufferSizeReached = allocator.getTotalBytesAllocated() >= targetBufferSize;
|
||||
long minBufferUs = hasVideo ? minBufferVideoUs : minBufferAudioUs;
|
||||
if (playbackSpeed > 1) {
|
||||
// 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);
|
||||
minBufferUs = Math.min(mediaDurationMinBufferUs, maxBufferUs);
|
||||
}
|
||||
// Prevent playback from getting stuck if minBufferUs is too small.
|
||||
minBufferUs = Math.max(minBufferUs, 500_000);
|
||||
if (bufferedDurationUs < minBufferUs) {
|
||||
isBuffering = prioritizeTimeOverSizeThresholds || !targetBufferSizeReached;
|
||||
} else if (bufferedDurationUs >= maxBufferUs || targetBufferSizeReached) {
|
||||
@ -413,7 +407,7 @@ public class DefaultLoadControl implements LoadControl {
|
||||
return minBufferDurationUs <= 0
|
||||
|| bufferedDurationUs >= minBufferDurationUs
|
||||
|| (!prioritizeTimeOverSizeThresholds
|
||||
&& allocator.getTotalBytesAllocated() >= targetBufferBytes);
|
||||
&& allocator.getTotalBytesAllocated() >= targetBufferSize);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -424,7 +418,7 @@ public class DefaultLoadControl implements LoadControl {
|
||||
* @param trackSelectionArray The selected tracks.
|
||||
* @return The target buffer size in bytes.
|
||||
*/
|
||||
protected int calculateTargetBufferBytes(
|
||||
protected int calculateTargetBufferSize(
|
||||
Renderer[] renderers, TrackSelectionArray trackSelectionArray) {
|
||||
int targetBufferSize = 0;
|
||||
for (int i = 0; i < renderers.length; i++) {
|
||||
@ -436,10 +430,7 @@ public class DefaultLoadControl implements LoadControl {
|
||||
}
|
||||
|
||||
private void reset(boolean resetAllocator) {
|
||||
targetBufferBytes =
|
||||
targetBufferBytesOverwrite == C.LENGTH_UNSET
|
||||
? DEFAULT_MUXED_BUFFER_SIZE
|
||||
: targetBufferBytesOverwrite;
|
||||
targetBufferSize = 0;
|
||||
isBuffering = false;
|
||||
if (resetAllocator) {
|
||||
allocator.reset();
|
||||
|
@ -830,14 +830,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
for (Renderer renderer : enabledRenderers) {
|
||||
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)
|
||||
@ -1992,6 +1984,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
}
|
||||
long bufferedDurationUs =
|
||||
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;
|
||||
return loadControl.shouldContinueLoading(bufferedDurationUs, playbackSpeed);
|
||||
}
|
||||
|
@ -46,7 +46,6 @@ public class DefaultLoadControlTest {
|
||||
@Test
|
||||
public void testShouldContinueLoading_untilMaxBufferExceeded() {
|
||||
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();
|
||||
@ -57,27 +56,11 @@ public class DefaultLoadControlTest {
|
||||
public void testShouldNotContinueLoadingOnceBufferingStopped_untilBelowMinBuffer() {
|
||||
createDefaultLoadControl();
|
||||
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();
|
||||
}
|
||||
|
||||
@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() {
|
||||
createDefaultLoadControl();
|
||||
@ -98,7 +81,6 @@ public class DefaultLoadControlTest {
|
||||
makeSureTargetBufferBytesReached();
|
||||
|
||||
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(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.
|
||||
assertThat(loadControl.shouldContinueLoading(MIN_BUFFER_US, SPEED)).isFalse();
|
||||
|
||||
// At double playback speed, we continue loading.
|
||||
assertThat(loadControl.shouldContinueLoading(MIN_BUFFER_US, /* playbackSpeed= */ 2f)).isTrue();
|
||||
}
|
||||
|
@ -3393,8 +3393,8 @@ public final class ExoPlayerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadControlNeverWantsToLoad_throwsIllegalStateException() throws Exception {
|
||||
LoadControl neverLoadingLoadControl =
|
||||
public void loadControlNeverWantsToLoadOrPlay_playbackDoesNotGetStuck() throws Exception {
|
||||
LoadControl neverLoadingOrPlayingLoadControl =
|
||||
new DefaultLoadControl() {
|
||||
@Override
|
||||
public boolean shouldContinueLoading(long bufferedDurationUs, float playbackSpeed) {
|
||||
@ -3404,7 +3404,7 @@ public final class ExoPlayerTest {
|
||||
@Override
|
||||
public boolean shouldStartPlayback(
|
||||
long bufferedDurationUs, float playbackSpeed, boolean rebuffering) {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
@ -3418,18 +3418,13 @@ public final class ExoPlayerTest {
|
||||
new TrackGroupArray(new TrackGroup(Builder.VIDEO_FORMAT)),
|
||||
new FakeChunkSource.Factory(dataSetFactory, new FakeDataSource.Factory()));
|
||||
|
||||
try {
|
||||
new ExoPlayerTestRunner.Builder()
|
||||
.setLoadControl(neverLoadingLoadControl)
|
||||
.setMediaSources(chunkedMediaSource)
|
||||
.build(context)
|
||||
.start()
|
||||
.blockUntilEnded(TIMEOUT_MS);
|
||||
fail();
|
||||
} catch (ExoPlaybackException e) {
|
||||
assertThat(e.type).isEqualTo(ExoPlaybackException.TYPE_UNEXPECTED);
|
||||
assertThat(e.getUnexpectedException()).isInstanceOf(IllegalStateException.class);
|
||||
}
|
||||
new ExoPlayerTestRunner.Builder()
|
||||
.setLoadControl(neverLoadingOrPlayingLoadControl)
|
||||
.setMediaSources(chunkedMediaSource)
|
||||
.build(context)
|
||||
.start()
|
||||
// This throws if playback doesn't finish within timeout.
|
||||
.blockUntilEnded(TIMEOUT_MS);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
Loading…
x
Reference in New Issue
Block a user