mirror of
https://github.com/androidx/media.git
synced 2025-05-16 20:19:57 +08:00
Discard backbuffer before playback gets stuck.
If the back buffer is using too much memory, there is a risk playback could get stuck because LoadControl refuses to load further data. This eventually results in a stuck-buffering playback error. We can detect this case, clear the back buffer and then ask the LoadControl again to avoid failing playback in such a case. PiperOrigin-RevId: 472679797
This commit is contained in:
parent
9a9baa54a5
commit
125646e4c9
@ -167,6 +167,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
* to load it.
|
||||
*/
|
||||
private static final long PLAYBACK_STUCK_AFTER_MS = 4000;
|
||||
/**
|
||||
* Threshold under which a buffered duration is assumed to be empty. We cannot use zero to account
|
||||
* for buffers currently hold but not played by the renderer.
|
||||
*/
|
||||
private static final long PLAYBACK_BUFFER_EMPTY_THRESHOLD_US = 500_000;
|
||||
|
||||
private final Renderer[] renderers;
|
||||
private final Set<Renderer> renderersToReset;
|
||||
@ -1050,7 +1055,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
}
|
||||
}
|
||||
if (!playbackInfo.isLoading
|
||||
&& playbackInfo.totalBufferedDurationUs < 500_000
|
||||
&& playbackInfo.totalBufferedDurationUs < PLAYBACK_BUFFER_EMPTY_THRESHOLD_US
|
||||
&& isLoadingPossible()) {
|
||||
// The renderers are not ready, there is more media available to load, and the LoadControl
|
||||
// is refusing to load it (indicated by !playbackInfo.isLoading). This could be because the
|
||||
@ -2306,8 +2311,23 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
? loadingPeriodHolder.toPeriodTime(rendererPositionUs)
|
||||
: loadingPeriodHolder.toPeriodTime(rendererPositionUs)
|
||||
- loadingPeriodHolder.info.startPositionUs;
|
||||
return loadControl.shouldContinueLoading(
|
||||
playbackPositionUs, bufferedDurationUs, mediaClock.getPlaybackParameters().speed);
|
||||
boolean shouldContinueLoading =
|
||||
loadControl.shouldContinueLoading(
|
||||
playbackPositionUs, bufferedDurationUs, mediaClock.getPlaybackParameters().speed);
|
||||
if (!shouldContinueLoading
|
||||
&& bufferedDurationUs < PLAYBACK_BUFFER_EMPTY_THRESHOLD_US
|
||||
&& (backBufferDurationUs > 0 || retainBackBufferFromKeyframe)) {
|
||||
// LoadControl doesn't want to continue loading despite no buffered data. Clear back buffer
|
||||
// and try again in case it's blocked on memory usage of the back buffer.
|
||||
queue
|
||||
.getPlayingPeriod()
|
||||
.mediaPeriod
|
||||
.discardBuffer(playbackInfo.positionUs, /* toKeyframe= */ false);
|
||||
shouldContinueLoading =
|
||||
loadControl.shouldContinueLoading(
|
||||
playbackPositionUs, bufferedDurationUs, mediaClock.getPlaybackParameters().speed);
|
||||
}
|
||||
return shouldContinueLoading;
|
||||
}
|
||||
|
||||
private boolean isLoadingPossible() {
|
||||
|
@ -12085,6 +12085,26 @@ public final class ExoPlayerTest {
|
||||
verify(listener, atLeast(2)).onDeviceVolumeChanged(anyInt(), anyBoolean());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadControlBackBuffer_withInsufficientMemoryLimits_stillContinuesPlayback()
|
||||
throws Exception {
|
||||
DefaultLoadControl loadControl =
|
||||
new DefaultLoadControl.Builder()
|
||||
.setTargetBufferBytes(500_000)
|
||||
.setBackBuffer(
|
||||
/* backBufferDurationMs= */ 1_000_000, /* retainBackBufferFromKeyframe= */ true)
|
||||
.build();
|
||||
|
||||
ExoPlayer player = new TestExoPlayerBuilder(context).setLoadControl(loadControl).build();
|
||||
player.setMediaItem(
|
||||
MediaItem.fromUri("asset:///media/mp4/sample_with_increasing_timestamps_360p.mp4"));
|
||||
player.prepare();
|
||||
player.play();
|
||||
runUntilPlaybackState(player, Player.STATE_ENDED);
|
||||
|
||||
// Assert that playing works without getting stuck due to the memory used by the back buffer.
|
||||
}
|
||||
|
||||
// Internal methods.
|
||||
|
||||
private static ActionSchedule.Builder addSurfaceSwitch(ActionSchedule.Builder builder) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user