Exoplayer: BehindLiveWindowException when playing HLS

Created on 18 Dec 2015  路  37Comments  路  Source: google/ExoPlayer

With latest ExoPlayer library, when playing HLS, sometimes the video hang with BehindLiveWindowException:

E/ExoPlayerImplInternal: Internal track renderer error.
  com.google.android.exoplayer.ExoPlaybackException: com.google.android.exoplayer.BehindLiveWindowException
  at com.google.android.exoplayer.SampleSourceTrackRenderer.maybeThrowError(SampleSourceTrackRenderer.java:154)
  at com.google.android.exoplayer.SampleSourceTrackRenderer.maybeThrowError(SampleSourceTrackRenderer.java:141)
  at com.google.android.exoplayer.ExoPlayerImplInternal.doSomeWork(ExoPlayerImplInternal.java:438)
  at com.google.android.exoplayer.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:213)
  at android.os.Handler.dispatchMessage(Handler.java:98)
  at android.os.Looper.loop(Looper.java:168)
  at android.os.HandlerThread.run(HandlerThread.java:61)
  at com.google.android.exoplayer.util.PriorityHandlerThread.run(PriorityHandlerThread.java:40)
  Caused by: com.google.android.exoplayer.BehindLiveWindowException
  at com.google.android.exoplayer.hls.HlsChunkSource.getChunkOperation(HlsChunkSource.java:290)
  at com.google.android.exoplayer.hls.HlsSampleSource.maybeStartLoading(HlsSampleSource.java:541)
  at com.google.android.exoplayer.hls.HlsSampleSource.continueBuffering(HlsSampleSource.java:253)
  at com.google.android.exoplayer.SampleSourceTrackRenderer.continueBufferingSource(SampleSourceTrackRenderer.java:173)
  at com.google.android.exoplayer.MediaCodecTrackRenderer.doSomeWork(MediaCodecTrackRenderer.java:462)
  at com.google.android.exoplayer.ExoPlayerImplInternal.doSomeWork(ExoPlayerImplInternal.java:431)
  at com.google.android.exoplayer.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:213)聽
  at android.os.Handler.dispatchMessage(Handler.java:98)聽
  at android.os.Looper.loop(Looper.java:168)聽
  at android.os.HandlerThread.run(HandlerThread.java:61)聽
  at com.google.android.exoplayer.util.PriorityHandlerThread.run(PriorityHandlerThread.java:40)聽

But if I just wait for about 3 - 5 seconds (or call preparePlayer() again) then the video starts playing again with no problem. Sometimes there's also SocketTimeoutException of okhttp before this exception, I tried to increase timeout in DefaultHttpDataSource and SocketTimeoutException is gone but this exception persists.

So how to get rid of this? Maybe it's because of slow network connection, is there any way to increase timeout other than in DefaultHttpDataSource?

question

All 37 comments

BehindLiveWindowException can occur when playing a rolling live stream in which segments become unavailable after a certain period of time. For example a rolling live stream might allow you to request segments that correspond to the most recent 2 minutes of the broadcast, but not earlier. In this case we'd say that the window is 2 minutes in length.

BehindLiveWindowException is thrown if the player attempts to request a segment that's no longer available. This is typically caused by:

  • The window being unreasonably small. In this case the fix is to increase the window on the server side.
  • Repeated client buffering causing the client to be playing further and further behind the broadcast until it eventually ends up trying to request segments that are no longer available. In this case the fix might be on the server side if the buffering is due to a server issue. If it's not a server side issue, it's most likely just that the client doesn't have sufficient bandwidth. In this case preparing the player again will allow playback to resume. It should be possible to do this immediately (the 3 - 5 second you mention wait shouldn't be necessary). In previous releases something equivalent happened internally, but we opted to propagate the error and require that applications do this instead. This was for consistency reasons, and also because some applications may wish to do something different. See https://github.com/google/ExoPlayer/issues/765 and the change referenced there for details.

Note that most often the exception is caused by a combination of the two bullet points above. Repeated client buffering will only cause the exception if the accumulated duration of time spent buffering exceeds the window size minus the duration that the client wants to buffer locally (typically ~30 seconds). So the larger the window size, the less likely this is to occur. I would say that you'd want the window to be at least ~2 minutes.

@ojw28 How to workaround?

@crossle Here's my workaround: (HlsChunkSource)

    if (live) {
      if (previousTsChunk == null) {
        chunkMediaSequence = getLiveStartChunkMediaSequence(nextVariantIndex);
      } else {
        chunkMediaSequence = switchingVariantSpliced
            ? previousTsChunk.chunkIndex : previousTsChunk.chunkIndex + 1;
        if (chunkMediaSequence < mediaPlaylist.mediaSequence) {
            if (allowSkipAhead) {
                chunkMediaSequence = getLiveStartChunkMediaSequence(nextVariantIndex);
                liveDiscontinuity = true;
            } else {
                fatalError = new BehindLiveWindowException();
                return;
            }
        }
      }
    } else {
        ....
    }

Where should I use this code? ;(

You should really just fix the source content as per my initial response.

Am facing the same issue at my end. @ojw28 as per you we have to increase the buffer time from default 10 sec to more then 30 sec and it will solve the problem. But in my case i am using Akamai as my CDN and they don't provide any other option to increase it more then 10 sec, though they provide option to decrease this below 10 (which is not useful). Is there any other alternative option to resolve this.

That doesn't sound right. Are you sure the variable you're looking at isn't the chunk size, for which a 10 second limit would make sense.

I guess we are not sync. I'm consuming AKAMAI HLS Playback URL (.m3u8) as stream URL. I havent found that we may request custom config against stream URL.

Also would like to understand the term "window" as I'm referring it as HLS segment(.ts) duration size(default is 10 secs) which can't be changed to 30 secs.

I end up fix this problem with:

  • When BehindLiveWindowException is thrown, just call preparePlayer()
  • When player is buffering for too long (3 seconds in my case), call preparePlayer() too

Window refers to (N-1)*(SegmentDuration), where SegmentDuration is 10s in your case, and N is the number of segments that are listed in each HLS playlist at any given point in time.

I suspect you'll find that your HLS media playlists only contain 3 or 4 segments at any given point in time. Increasing that would help you to avoid this issue.

@ojw28 Hmm .. consider the fact that I cannot ask/demand customization from server (Akamai) in my case. I just have HLS URL to play though any suggestion to handle it in client side (Player) would be help.

I'd be surprised if Akamai don't allow configuration of the window length. How many segments are listed in your manifests, at any given point in time?

@ojw28 I have checked and find that it provides me 3 ts segment at a particular time.

Right, and that's your problem. It should be possible for you to increase that somehow.

@ojw28 Firstly let me know you that i am working with Live stream (not VOD). I am using Akamai playback url given by end client. So it not possible to update anything from Akamai. Is there any kind of solution for client side to resolve this ?

You should request that the provider resolves the issue. Fundamentally you're trying to play a live stream with a live window of insufficient size. It's a content issue. It can only be properly resolved by the content provider.

The best thing that you can do on the client is as @kientux suggested further up this thread, which is to automatically restart the playback each time it fails in this way. This will not be seamless, but it will allow playback to continue once it buffers again.

@ojw28 Ok....but the purposed client side solution is not suitable for me. I have tried the same stream with BrightCove player (Paid Solution) and it works fine but the strange thing is that they are also using ExoPlayer internally.

So get the provider to fix their content. It's simply not a client side problem if content is not provided correctly.

Dear colleagues,
How to calculate window size for the Widevine mpegdash content? I have access to the manifest mpd file but I am not well familiar with the structure in order to detect it?

I found another bug still opened and I think that in case of mpegdash we facing BehindLiveWindowException because of it.
Here is link:
https://github.com/google/ExoPlayer/issues/765

Where is the correct place to catch the BehindLiveWindowException? I realize that the provider should fix the content, but until I can get the addressed, what is the best place to catch this exception and restart the player?

Here is stack trace:

com.google.android.exoplayer.ExoPlaybackException: com.google.android.exoplayer.BehindLiveWindowException
       at com.google.android.exoplayer.SampleSourceTrackRenderer.maybeThrowError(SampleSourceTrackRenderer.java:263)
       at com.google.android.exoplayer.SampleSourceTrackRenderer.maybeThrowError(SampleSourceTrackRenderer.java:145)
       at com.google.android.exoplayer.ExoPlayerImplInternal.doSomeWork(ExoPlayerImplInternal.java:438)
       at com.google.android.exoplayer.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:213)
       at android.os.Handler.dispatchMessage(Handler.java:98)
       at android.os.Looper.loop(Looper.java:135)
       at android.os.HandlerThread.run(HandlerThread.java:61)
       at com.google.android.exoplayer.util.PriorityHandlerThread.run(PriorityHandlerThread.java:40)
Caused by com.google.android.exoplayer.BehindLiveWindowException
       at com.google.android.exoplayer.hls.HlsChunkSource.getChunkOperation(HlsChunkSource.java:401)
       at com.google.android.exoplayer.hls.HlsSampleSource.maybeStartLoading(HlsSampleSource.java:697)
       at com.google.android.exoplayer.hls.HlsSampleSource.continueBuffering(HlsSampleSource.java:264)
       at com.google.android.exoplayer.SampleSourceTrackRenderer.doSomeWork(SampleSourceTrackRenderer.java:127)
       at com.google.android.exoplayer.ExoPlayerImplInternal.doSomeWork(ExoPlayerImplInternal.java:431)
       at com.google.android.exoplayer.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:213)
       at android.os.Handler.dispatchMessage(Handler.java:98)
       at android.os.Looper.loop(Looper.java:135)
       at android.os.HandlerThread.run(HandlerThread.java:61)
       at com.google.android.exoplayer.util.PriorityHandlerThread.run(PriorityHandlerThread.java:40)

The correct place is implementation of the
ExoPlayer.Listener
and it's method
void onPlayerError(ExoPlaybackException error);
In my case, I add it's implementation via
mExoPlayer.addListener(***)
Then, when you will get onPlayerError you can process _Exception_.

1513
File:HlsRendererBuilder add line chunkSource.setAdaptiveMode(HlsChunkSource.ADAPTIVE_MODE_NONE);
to method: onSingleManifest
Work for me!

@huykn , in this case you loose ability to dynamically select variant base on network conditions and you have to control from your client side which variant select in order to provide best user experience. In other words, if the network quality is low you have to detect it by yourself and switch stream into lower variant, otherwise buffering will be more and more frequent and end user experience of your application will be unpredicted. Keep this in mind.

Specifically for HLS, it seems this issue can be caused by non-aligned segment numbers across the different variants, in addition to the other causes mentioned above. Non-aligned segment numbers are permitted by the HLS spec, and therefore failing to correctly handle this case is an ExoPlayer issue. It's kind of tracked by #699, but it would be good to have a bug explicitly tracking this resulting in BehindLiveWindowException.

@huykn - Are you able to provide test content that reproduces this issue? If so, please file a new issue on this tracker providing all of the information requested in the issue template, and we'll use that to track the problem.

Hello,

I'm also getting this exception on one of my HLS playbacks.
After applying the patch from @kientux the problem resolved.

Any chance for a temporary built-in solution until the root cause for this problem will be found?

Thanks

@krok55 The root cause was found. As you see in the conversation above, this is problem from content provider, not client. So ExoPlayer might never try to handle this exception.

Note - There are fixes in 1.5.10 and 2.x.x releases of ExoPlayer that help a second root cause of BehindLiveWindowException in HLS playbacks.

Note that Apple's documentation on using HTTP live streaming has 3 segments in it https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/StreamingMediaGuide/UsingHTTPLiveStreaming/UsingHTTPLiveStreaming.html

I hardly think this is a content problem

  • Is this still actually an issue in 1.5.10? As per my comment above, we pushed some fixes as part of that release.
  • I wouldn't class a non-productionized sample command as evidence of best practice. Note that they're actually overriding the default value (=5) of their own tool with a smaller value (=3) in that sample command, without giving any justification. I can't see any good reason for doing this, where-as it _will_ definitely make failures/re-buffering/content-jumps more likely across all platforms by restricting the window length to be unnecessarily short.

I'm still seeing this in 1.5.10 across a variety of HLS Live streams.

I am also facing the same issue with Exoplayer 2.x.x and 1.5.x versions while playing the Live HLS Video. Need resolution of it on client end, guys?

I just experienced this with ExoPlayer 1.5.11 with a live hls stream. Stack trace is the same as aforementioned:

                                                                                     com.google.android.exoplayer.ExoPlaybackException: com.google.android.exoplayer.BehindLiveWindowException
                                                                                         at com.google.android.exoplayer.SampleSourceTrackRenderer.maybeThrowError(SampleSourceTrackRenderer.java:262)
                                                                                         at com.google.android.exoplayer.SampleSourceTrackRenderer.maybeThrowError(SampleSourceTrackRenderer.java:144)
                                                                                         at com.google.android.exoplayer.ExoPlayerImplInternal.doSomeWork(ExoPlayerImplInternal.java:439)
                                                                                         at com.google.android.exoplayer.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:211)
                                                                                         at android.os.Handler.dispatchMessage(Handler.java:98)
                                                                                         at android.os.Looper.loop(Looper.java:136)
                                                                                         at android.os.HandlerThread.run(HandlerThread.java:61)
                                                                                         at com.google.android.exoplayer.util.PriorityHandlerThread.run(PriorityHandlerThread.java:40)
                                                                                      Caused by: com.google.android.exoplayer.BehindLiveWindowException
                                                                                         at com.google.android.exoplayer.hls.HlsChunkSource.getChunkOperation(HlsChunkSource.java:414)
                                                                                         at com.google.android.exoplayer.hls.HlsSampleSource.maybeStartLoading(HlsSampleSource.java:711)
                                                                                         at com.google.android.exoplayer.hls.HlsSampleSource.continueBuffering(HlsSampleSource.java:262)
                                                                                         at com.google.android.exoplayer.SampleSourceTrackRenderer.doSomeWork(SampleSourceTrackRenderer.java:126)
                                                                                         at com.google.android.exoplayer.ExoPlayerImplInternal.doSomeWork(ExoPlayerImplInternal.java:432)
                                                                                         at com.google.android.exoplayer.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:211)聽
                                                                                         at android.os.Handler.dispatchMessage(Handler.java:98)聽
                                                                                         at android.os.Looper.loop(Looper.java:136)聽
                                                                                         at android.os.HandlerThread.run(HandlerThread.java:61)聽
                                                                                         at com.google.android.exoplayer.util.PriorityHandlerThread.run(PriorityHandlerThread.java:40)聽

I'm unable to provide a sample stream as they are authenticated, but please let me know if there's any additional information i can provide.

@theBradfo Just wait for some couple of days, new fixed release is about to come for this issue.

@ishantsagar, @ojw28 can you provide any information on this forthcoming release and potentially some detail on how it would fix this issue?

@ishantsagar - Please don't cross post on multiple issues (i.e. this one and https://github.com/google/ExoPlayer/issues/1782), it adds unnecessary noise to the issue tracker. Please continue this discussion on the open issue, which is https://github.com/google/ExoPlayer/issues/1782.

Was this page helpful?
0 / 5 - 0 ratings