Don't require the existence of the next period to wait for its stream.

We have a workaround for uneven sample stream durarions in playlists that
assumes a renderer allows playback if it's reading ahead or waiting for
the next stream.

652c2f9c18 changed this logic to no longer require to
wait until the next stream is prepared due to a change in how we advance
media periods in the queue. However, the code falsely still requires the
next stream to exist (even if it's not prepared). This can cause a stuck
buffering state when the difference in the duration of the streams is more
than what we buffer ahead because we never create the next stream in such
a case.

Note: DefaultMediaClock.shouldUseStandaloneClock has roughly the same logic
and also doesn't require the next stream to be present.

Also fix a test that seemed to rely on this stuck buffering case to test
stuck buffering detection. Changed the test to not read the end of stream
to ensure it runs into the desired stuck buffering case.

Issue:#7943
PiperOrigin-RevId: 333050285
This commit is contained in:
tonihei 2020-09-22 12:23:24 +01:00 committed by kim-vde
parent 12e887438b
commit 25e31743d3
3 changed files with 12 additions and 12 deletions

View File

@ -2,6 +2,10 @@
### dev-v2 (not yet released)
* Core library:
* Fix bug where streams with highly uneven durations may get stuck in a
buffering state
([#7943](https://github.com/google/ExoPlayer/issues/7943)).
* Track selection:
* Add option to specify multiple preferred audio or text languages.
* Data sources:

View File

@ -897,10 +897,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
// tracks in the current period have uneven durations and are still being read by another
// renderer. See: https://github.com/google/ExoPlayer/issues/1874.
boolean isReadingAhead = playingPeriodHolder.sampleStreams[i] != renderer.getStream();
boolean isWaitingForNextStream =
!isReadingAhead
&& playingPeriodHolder.getNext() != null
&& renderer.hasReadStreamToEnd();
boolean isWaitingForNextStream = !isReadingAhead && renderer.hasReadStreamToEnd();
boolean allowsPlayback =
isReadingAhead || isWaitingForNextStream || renderer.isReady() || renderer.isEnded();
renderersAllowPlayback = renderersAllowPlayback && allowsPlayback;

View File

@ -7334,6 +7334,8 @@ public final class ExoPlayerTest {
new DefaultLoadControl.Builder()
.setTargetBufferBytes(10 * C.DEFAULT_BUFFER_SEGMENT_SIZE)
.build();
// Return no end of stream signal to prevent playback from ending.
FakeMediaPeriod.TrackDataFactory trackDataWithoutEos = (format, periodId) -> ImmutableList.of();
MediaSource continuouslyAllocatingMediaSource =
new FakeMediaSource(
new FakeTimeline(/* windowCount= */ 1), ExoPlayerTestRunner.VIDEO_FORMAT) {
@ -7348,8 +7350,11 @@ public final class ExoPlayerTest {
@Nullable TransferListener transferListener) {
return new FakeMediaPeriod(
trackGroupArray,
TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US,
mediaSourceEventDispatcher) {
trackDataWithoutEos,
mediaSourceEventDispatcher,
drmSessionManager,
drmEventDispatcher,
/* deferOnPrepared= */ false) {
private final List<Allocation> allocations = new ArrayList<>();
@ -7382,14 +7387,8 @@ public final class ExoPlayerTest {
};
}
};
ActionSchedule actionSchedule =
new ActionSchedule.Builder(TAG)
// Prevent player from ever assuming it finished playing.
.setRepeatMode(Player.REPEAT_MODE_ALL)
.build();
ExoPlayerTestRunner testRunner =
new ExoPlayerTestRunner.Builder(context)
.setActionSchedule(actionSchedule)
.setMediaSources(continuouslyAllocatingMediaSource)
.setLoadControl(loadControl)
.build();