Skip to content

[LL-HLS] Prevent excessive reloads when CAN-BLOCK-RELOAD=YES is not respected #2317

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

mojsdev
Copy link

@mojsdev mojsdev commented Apr 8, 2025

In low-latency HLS playback, some servers may respond with CAN-BLOCK-RELOAD=YES but return the playlist before it has actually been updated. This can lead to repeated calls to loadPlaylistImmediately() until a new playlist version becomes available.

Although this server behavior is not ideal, the client should still avoid unnecessary polling and redundant requests. This pull request introduces a short wait duration before allowing the next load attempt in such cases, reducing excessive reloads.

The existing behavior—where playlist loading blocks until an update is available—is preserved when the server correctly delays its response, as the logic checks playlistSnapshot == oldPlaylist before deferring the next attempt.

@marcbaechinger
Copy link
Contributor

marcbaechinger commented Apr 8, 2025

Many thanks for your PR.

Have you filed a bug against the server that exhibits this behaviour? Can you unveil a bit more about what servers that are so we knwo and we can potentially test against one of these implementations?

If you have a content URI for a LL stream exhibiting this behaviour this would be great as well.

@mojsdev
Copy link
Author

mojsdev commented Apr 8, 2025

Hi @marcbaechinger,

Thank you for your response.

To provide further context, the issue was observed on our internal media server, so I'm unable to share the content URI. I appreciate your understanding.

At our service, we have implemented a process where requests are held at the CDN layer until the media server updates the playlist, at which point the latest playlist is returned. The issue occurred because blocking wasn't happening as expected, but we have since resolved it.

This issue is difficult to detect because normal playback continues even when it is reproduced. I discovered it while analyzing the reported ANR, during which infinite requests were reproduced with the specific stream.

@mojsdev mojsdev force-pushed the feature/prevent-infinite-reloads-for-ll-hls branch from 058db9c to 758fef9 Compare April 9, 2025 06:13
Waits for half the part target duration before retrying when the server responds with
`CAN-BLOCK-RELOAD=YES` but does not actually block until the playlist updates.
Prevents excessive requests in such cases.
@mojsdev mojsdev force-pushed the feature/prevent-infinite-reloads-for-ll-hls branch from 758fef9 to bca1618 Compare April 14, 2025 12:57
@tonihei
Copy link
Collaborator

tonihei commented Apr 17, 2025

The server behavior is obviously not fully spec compliant, but given the quite severe permanent reloading, it's probably a good idea to have this fallback. Thanks for filing the PR for it, I'll start the internal merge process now.

@tonihei tonihei self-assigned this Apr 17, 2025
@@ -853,6 +853,13 @@ private void processLoadedPlaylist(
playlistSnapshot != oldPlaylist
? playlistSnapshot.targetDurationUs
: (playlistSnapshot.targetDurationUs / 2);
} else if (
playlistSnapshot == oldPlaylist && playlistSnapshot.partTargetDurationUs != C.TIME_UNSET
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason you only do this if a part target duration is defined? The same should apply to playlists that canBlockReload but are not low-latency I assume.

Related to that: the fallback should probably be to use playlistSnapshot.targetDurationUs / 2 as in the if case above for when canBlockReload is not supported.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @tonihei,

First of all, thank you for reviewing the PR positively.

The IETF standard RFC 8216 does not mention CAN-BLOCK-RELOAD at all. This attribute is introduced in the context of LL-HLS and described in Section 6.2.5.2 of draft-pantos-hls-rfc8216bis. According to the specification, the use of _HLS_msn and _HLS_part parameters—required for blocking playlist reload behavior—appears to clearly limit this mechanism to LL-HLS.

6.2.5.2.  Blocking Playlist Reload

   A Server MAY offer Blocking Playlist Reloads, which enable immediate
   client discovery of Playlist updates as an alternative to polling.

   A Server advertises support for Blocking Playlist Reload by adding
   the CAN-BLOCK-RELOAD=YES attribute to the EXT-X-SERVER-CONTROL tag.

   A Client requests a Blocking Playlist Reload using an _HLS_msn
   directive with a decimal-integer value M.  When the Playlist URI
   contains an _HLS_msn directive and no _HLS_part directive, the Server
   MUST defer responding to the request until the Playlist contains a
   Media Segment with a Media Sequence Number of M or later or it
   responds with an error.

That said, I understand and agree with your concern, as this PR handles an edge case where the server does not fully comply with the RFC.

As a refinement, I’d suggest the following logic:

} else if (playlistSnapshot == oldPlaylist) {
  // To prevent infinite requests when the server responds with CAN-BLOCK-RELOAD=YES but does
  // not actually block until the playlist updates, wait before retrying.
  durationUntilNextLoadUs = playlistSnapshot.partTargetDurationUs != C.TIME_UNSET
      ? playlistSnapshot.partTargetDurationUs / 2
      : playlistSnapshot.targetDurationUs / 2;
}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you have any thoughts or suggestions on this approach, I’d be happy to revise the code accordingly.

Also, would you prefer that I squash the changes into a single commit and force-push, or keep them as separate commits?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants