How should live latency be measured and maintained with AVPlayer (HLS / LL-HLS)?

We keep live playback at a consistent distance from the live edge using small playback rate adjustments, with a target range based on recommendedTimeOffsetFromLive. Since the live edge is not exposed by AVPlayer, we currently fall back to seekableTimeRanges.end as our best approximation.

  1. What should be treated as the live edge, and how should the current live latency be measured?
  2. Is rate adjustment the appropriate way to hold a target latency? While playing above 1.0x, the playhead can reach the seekable end, at which point AVPlayerItemDidPlayToEndTime fires and halts the live stream. How can we guard against ?
  3. Does any of this differ between regular HLS and LL-HLS?

A clear statement of the intended contract here would resolve a lot of uncertainty.

Thanks in advance.

1.What should be treated as the live edge, and how should the current live latency be measured?

seekableTimeRanges.end is a good working approximation of the live edge — it represents the furthest point in the stream the player can seek to

Live latency at any moment is therefore (Note:  Live latency is playhead distance from Live Edge)

##  let latency = playerItem.seekableTimeRanges.last
   .map { CMTimeRangeGetEnd($0.timeRangeValue) }
   .map { $0 - playerItem.currentTime() }

2. Is rate > 1.0 the right mechanism? What happens when the playhead reaches the end?

Yes — playing above 1.0x to reduce live latency is a supported pattern.

The concern about AVPlayerItemDidPlayToEndTimeNotification firing does not apply here. For live streams (duration == kCMTimeIndefinite) this notification is only posted if forwardPlaybackEndTime is explicitly set on the item. Reaching seekableTimeRanges.end during live playback at rate > 1.0 does not trigger it.

from iOS/macOS 26.4 onwards, when the playhead reaches the live edge at rate > 1.0, the player automatically steps the rate back to 1.0, no guard needed.

3. Differences between regular HLS and LL-HLS

The fundamental model is the same for both — seekableTimeRanges, configuredTimeOffsetFromLive, recommendedTimeOffsetFromLive, and the rate > 1.0 pattern all apply.

The differences are in scale: seekableTimeRanges.end granularity Regular HLS : Advances by full segment. LL-HLS : Advances by partial segment

For LL-HLS, seekableTimeRanges.end moves forward continuously at part traget intervals, so the rate > 1.0 catch-up converges smoothly. For regular HLS, the boundary advances in segment-sized steps, so the playhead catches up in larger increments.

Summary :- the intended contract

  1. Live latency = seekableTimeRanges.end − currentTime(). This is slightly conservative relative to the true live edge but is the closest approximation.
  2. Target latency :- set via configuredTimeOffsetFromLive. Follow recommendedTimeOffsetFromLive for network-adaptive targets.
  3. Catch-up :- playing at rate > 1.0 is supported. From (iOS/macOS 26.4+) the player automatically steps back to 1.0 when the playhead reaches the live edge,. No additional guard is needed.
  4. AVPlayerItemDidPlayToEndTimeNotification does not fire during normal live playback. Only fires if forwardPlaybackEndTime s explicitly set.
  5. LL-HLS vs regular HLS :- same API surface, different numeric scales. Always use recommendedTimeOffsetFromLive rather than a hardcoded offset.

when it comes to live streaming latency, it's all about how quickly you can get the video from the source to the viewer's screen. Think of it like a river – the shorter and faster the flow, the lower the latency!For HLS (HTTP Live Streaming) and especially LL-HLS (Low-Latency HLS), there are a few key things to consider:Chunk Size: This is like the size of the water droplets in our river analogy. Smaller chunks mean faster delivery, reducing latency. LL-HLS specifically

How should live latency be measured and maintained with AVPlayer (HLS / LL-HLS)?
 
 
Q