google / ExoPlayer

This project is deprecated and stale. The latest ExoPlayer code is available in https://github.com/androidx/media
https://developer.android.com/media/media3/exoplayer
Apache License 2.0
21.7k stars 6.02k forks source link

Playback stops at EXT-X-GAP tag if GAP exceeds buffering time #8959

Open stevemayhew opened 3 years ago

stevemayhew commented 3 years ago

If there is a gap across all renditions that lasts longer than the buffered time limit (50s default) the player stops indefinitely in the buffering state. This is of course because buffering is calculated based on the playlist not the samples yet rendering depends on the samples.

This issue is related to #6087 (in that if the gap is smaller than 50s playback simply skips the duration of the audio gap)

Reproduction Steps

  1. Play a VOD sample with EXT-X-GAP duration longer than 50s in all renditions
  2. Observe playback stalls in buffering forever

I have sent two sample stream URL's with a gap in all renditions starting at 1:18 seconds into the stream to the Exo developers email address.

Further Analysis

Use Cases

Gaps in the stream happen in broadcast streams for a couple of reasons:

  1. PID drops — the source transport stream drops an audio PID, this can happen for alternate language tracks
  2. Outages — drops in the multicast feed, issues with satellites, transcoding broadcast stream. All of these can result in gaps across all renditions. Very possible this can exceed buffered time

ExoPlayer's current behavior (Skipping the media time with an instantaneous jump) is quite reasonable for the Outages use case (logging an analytics event would be nice to allow the UX of showing a whisper banner indicating an outage caused the jump). The use cases for PID drops are adequately covered in issue #6087

Suggested Fix

The SequenceableLoader interface needs to understand one or all of its constituent loaders are experiencing a GAP and continue loading across the gap until the discontinuity (first sample after the GAP) is loaded. Here the decision to jump past the discontinuity or play through (if only one rendition has a GAP) can be intelligently made. This may be specific to HLS as DASH appears to be looking at a different approach see issue #4616

stevemayhew commented 3 years ago

Slight update. The latest dev-v2 has playback stuck detection (stuck in buffering and not loading). Great thing by the way for finding issues where un-reported discontinuity causes hangs.

Here's the steps to reproduce:

  1. Play the sample VOD content I sent to the dev.exoplayer email (I just launch the View with the URL directly with adb shell am start -n com.google.android.exoplayer2.demo/.PlayerActivity -a com.google.android.exoplayer.demo.action.VIEW -d
  2. Select the scrub bar and scrub forward 4 clicks (to 00:56 in the stream)
  3. The gap starts at 1:19 in the stream, the player immediately "buffers" the gap then throws a PlaybackException
06-10 18:15:07.530 28739 28739 E EventLogger: playerFailed [eventTime=32.24, mediaPos=130.14, window=0, period=0, errorCode=ERROR_CODE_UNSPECIFIED
06-10 18:15:07.530 28739 28739 E EventLogger:   com.google.android.exoplayer2.ExoPlaybackException: Unexpected runtime error
06-10 18:15:07.530 28739 28739 E EventLogger:       at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:615)
06-10 18:15:07.530 28739 28739 E EventLogger:       at android.os.Handler.dispatchMessage(Handler.java:102)
06-10 18:15:07.530 28739 28739 E EventLogger:       at android.os.Looper.loop(Looper.java:193)
06-10 18:15:07.530 28739 28739 E EventLogger:       at android.os.HandlerThread.run(HandlerThread.java:65)
06-10 18:15:07.530 28739 28739 E EventLogger:   Caused by: java.lang.IllegalStateException: Playback stuck buffering and not loading
06-10 18:15:07.530 28739 28739 E EventLogger:       at com.google.android.exoplayer2.ExoPlayerImplInternal.doSomeWork(ExoPlayerImplInternal.java:1053)
06-10 18:15:07.530 28739 28739 E EventLogger:       at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:486)
06-10 18:15:07.530 28739 28739 E EventLogger:       ... 3 more

We are currently looking at changing the HLS implementation of SequenceableLoader.getBufferedPositionUs() to not count GAP segments as part of the buffer, this seems the most natural architecturally, however it would force re-buffering when playback jumps past a smaller gap.

@ojw28 Let us know if you are working on a fix for this and we will stop, or if we are moving in the completely wrong direction with our approach to the fix.