Commit 25e31743 by tonihei Committed by kim-vde

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.

https://github.com/google/ExoPlayer/commit/652c2f9c188bf9d9d6e323ff5333e5026454a082 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
parent 12e88743
......@@ -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:
......
......@@ -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;
......
......@@ -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();
......
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