Files
v10/internal/design/spf/features/ll-hls-support.md
T

15 KiB
Raw Blame History

status, date, definition
status date definition
draft 2026-05-20 technical

LL-HLS support

Low-Latency HLS extensions on top of the live-stream-support reload-loop foundation: blocking reload for server-gated playlist refresh, partial segments for sub-segment append granularity at the live edge, preload hints for pre-fetching the next part before it's announced, and delta playlists for bandwidth-efficient reloads. Together these mechanisms collapse the engine's distance from the live edge from "≥3× target duration" (regular live) to "≤ a few parts" (low-latency live).

A Media-src feature in the framing from clusters.md § Feature classification axes: LL-HLS-encoded sources play technically via the regular live reload loop (each full segment lands when announced, just with regular-live latency), but "actual support" — engaging the partials / blocking-reload / preload mechanisms the server advertises — requires this feature. Without it, LL-HLS sources play at regular-live latency, not the low-latency the producer optimized the stream for.

Status

  • Composition: not implemented. Hard prerequisite live-stream-support is also not implemented. None of parseMediaPlaylist, the reload loop (which doesn't exist yet), forward-buffer's planner, or createTrackedFetch currently handle LL-HLS shapes.
  • Definition depth: technical — scope and SPF touchpoints articulated against the HLS LL-HLS spec extensions; implementation specifics open. Source material: SPF Epics Working Doc — LL-HLS Support (epic #1) (cluster A, eng size XL, validation M-L, "Largest single gap").
  • Hard prerequisite: live-stream-support. The four LL-HLS mechanisms all build on the reload-loop, sliding- window, live-edge-tracking, and Infinity-duration primitives that feature introduces. LL-HLS is not independently scopable.

Phases of complexity

Capability slices, each one an HLS LL-HLS spec extension. Naive vs Full depth applies within each phase per clusters.md § Feature classification axes; e.g., blocking reload at naive depth without preload hints, full depth combining the two.

Phase What Notes
Blocking reload _HLS_msn=N&_HLS_part=P query params on playlist reload; server holds the response until the target msn/part is available. Replaces target-duration interval polling with response-driven request chaining. Parser must surface #EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES to enable the mechanism Largest shift from regular live: the reload loop's pacing model changes (response-driven, not interval-driven). createTrackedFetch needs long-poll-aware request semantics — a single request may legitimately take ~target-duration to respond
Partial segments #EXT-X-PART:URI=...,DURATION=...,INDEPENDENT=...,GAP=...,BYTERANGE=... tags within the most-recent few segments. Client fetches and appends partials before the full segment is announced via #EXTINF. Parser must surface #EXT-X-PART-INF:PART-TARGET=... (target part duration) and per-part attributes Append still goes through the same SourceBufferActor (same variant, same codec — no changeType()). The forward-buffer planner must extend beyond track.segments[] to include a "partial head" past the last complete segment; reconcile a part eventually being superseded by its containing full segment (same media interval, two playlist entries)
Preload hints #EXT-X-PRELOAD-HINT:TYPE=PART,URI=... (or TYPE=MAP). Client may pre-fetch the indicated URL before it appears in the playlist as a regular EXT-X-PART. Used in combination with blocking reload to pipeline the next fetch behind the in-flight blocking-reload response Pre-fetched response is retained and consumed when the URL appears as a regular EXT-X-PART on the next reload; double-fetch must be avoided. Pre-fetch is only a latency optimization — never gates other phases
Delta playlists _HLS_skip=YES query param on reload; server responds with a playlist containing #EXT-X-SKIP:SKIPPED-SEGMENTS=N denoting N segments removed from the start. Client merges the delta into its retained playlist state. Parser must surface #EXT-X-SERVER-CONTROL:CAN-SKIP-UNTIL=... (the minimum-skippable boundary advertised by the server) Requires the client to retain playlist state across reloads — today's parseMediaPlaylist returns a fresh Track per fetch, so the reload-loop behavior gains a delta-merge step. Bandwidth optimization, not latency: complements but doesn't replace blocking reload + partials

What's in scope vs out of scope

In scope:

  • All four phases above, applied to HLS LL-HLS-encoded sources where #EXT-X-SERVER-CONTROL advertises the capability
  • Parser surface for EXT-X-SERVER-CONTROL, EXT-X-PART-INF, EXT-X-PART, EXT-X-PRELOAD-HINT, EXT-X-SKIP, EXT-X-RENDITION-REPORT
  • Long-poll-aware fetch shape (timeout policy compatible with blocking-reload response holds)
  • Forward-buffer planner extension for partial-head tracking
  • Client-side playlist-state retention across reloads (for delta merge)

Out of scope (sister Media-src candidate features):

  • [live-edge-distance-abr] (candidate, cluster C extension) — using distance-from-live-edge as an ABR signal alongside bandwidth. Consumer of LL-HLS's tighter live-edge tracking but not part of LL-HLS itself.

Out of scope (different architectural layer):

  • Above-engine UI affordances ("seek to live edge," "currently live," live-button states). Consume LL-HLS-tightened live-edge data via the live-stream-support feature's derived live-edge signal; not SPF concerns themselves.

Likely cross-cutting impact

Things this feature probably forces decisions on, not just additions:

  • Reload-loop composition variant — LL-HLS isn't a runtime branch inside a regular-live reload behavior; it's a different reload-loop behavior composed into a low-latency live engine variant. Same shape as live-stream-support.md's setLiveSeekableRange placement decision: a new variant-specific behavior, not a conditional inside an existing variant-agnostic behavior. Per the failure-mode catalog and conventions/behaviors.md § Inverse: behaviors that operate uniformly across tracks, the existing updateMediaSourceDuration is the canonical example of a behavior that stays composition- variant-agnostic; the LL-HLS reload-loop is its inverse — a variant-specific behavior that exists only in the LL-HLS engine.
  • Variant-decision point — open — where does the engine commit to the LL-HLS variant: adapter-level upfront (consumer opts in), or after-first-playlist-parse from EXT-X-SERVER-CONTROL flags? The spec allows a stream to advertise some LL-HLS capabilities and not others (e.g., partials without blocking-reload), so per-mechanism composition is possible. Either way the behaviors are variant- specific; the question is when the composition is finalized.
  • parseMediaPlaylist extensions — parser today returns a Track with segments only. LL-HLS adds: serverControl flags, partInf (part target duration), parts[] within the head segment(s), a current preloadHints[], optional skip metadata. The parser output schema grows; the parsed-track shape that downstream behaviors consume gains LL-HLS-specific fields. Intersects with presentation-modeling.md's open question on parseMediaPlaylist pluggability — LL-HLS-aware parsing is a strong forcing function for explicit parser-extension shape.
  • createTrackedFetch request shape — today the wrapper handles streaming responses chunk-by-chunk with EWMA bandwidth sampling. No changes needed for partial-segment loading (same wrapper, smaller payloads — bandwidth sampling continues working). Required change for blocking reload: the wrapper has no long-poll-aware timeout policy today. A blocking-reload request may legitimately take up to target-duration to respond; needs either a configurable per-request timeout or a request-type discriminant. Per the sampling-baked-into- loading pattern, the sample producer doesn't move.
  • Forward-buffer planner extension — current planner iterates track.segments[] (full segments) and filters by buffered ranges within a fixed look-ahead. LL-HLS extends the candidate set with partials past the last complete segment, plus a reconciliation rule: if part P covers [t0, t1] and the same interval later appears as part of full segment S, both entries map to the same media interval; the planner must not double-load. Low-latency-aware buffer-duration setting is a separate tunable — the default 30s target buffer is incompatible with low-latency-live's "stay within a few parts of live edge" goal.
  • Sliding-window awareness under partials — the segment list mutates more frequently (partials append at the live edge between full-segment announcements). Existing sliding-window-aware back- buffer policy from live-stream-support continues to apply to full segments; partials past the head are transient by definition.
  • liveSeekableRange update frequencysetLiveSeekableRange from live-stream-support advances as the live edge advances. Under LL-HLS, the edge advances at part granularity (sub-second updates, not target-duration interval). Same writer, higher update rate; no new multi-writer concern.
  • Per-type partial coordination — audio playlists can also carry EXT-X-PART. Inherits live-stream-support's open question on per- type reload coordination, with the additional concern that audio and video parts may have different target durations.

Open questions

  • Variant-decision point. Per the cross-cutting note above: adapter-level upfront vs after-first-playlist-parse vs per-mechanism granular composition. Affects how the engine is built and how the capability-detection signal flows. Resolving this likely shapes how the regular-live engine variant is composed too.
  • Pre-fetch retention scope. Preload hints pre-fetched in response to one playlist snapshot may be invalidated by the next snapshot (server changed its mind about the next part URI). Retention policy: abort and discard, or hold pending validation against the next playlist? Spec leaves this to implementations.
  • Long-poll fetch shape vs createTrackedFetch extension. Add a new request-type discriminant to the existing wrapper (with longer timeout), or a sibling fetchBlockingReload wrapper that delegates to the same chunk-streaming infrastructure? Trade-off: one wrapper with conditional timeout vs two wrappers with shared chunk handling.
  • Planner extension shape — extend forward-buffer or sibling. The current getForwardBufferSegments is a pure function over Segment[]. Either it grows a parts?: PartSegment[] parameter (and a reconciliation rule baked in), or a sibling getForwardBufferPartials runs alongside and the loader merges. The reconciliation logic (part superseded by containing full segment) is the load-bearing piece either way.
  • Capability advertisement vs runtime probing. If the server advertises CAN-SKIP-UNTIL but the client has already retained less history than the skip boundary, the client must fall back to a full-playlist reload. Where this fallback decision lives — inside the delta-merge step or as a reload-shape decision upstream — is open.
  • #EXT-X-RENDITION-REPORT consumption. Spec lets the server advertise the LAST-MSN/LAST-PART for other renditions in the playlist; clients switching renditions can resume at the right point without an extra round-trip. Useful for ABR-driven variant switching under LL-HLS but a separate optimization. May be a sub-phase of partial-segments or its own follow-on.
  • GAP=YES handling. #EXT-X-PART may carry GAP=YES indicating the encoder failed to produce the part on time; clients are spec- advised to fetch the next part anyway and let the buffer underrun. How this interacts with endOfStream gating (a "gap" past the live edge isn't a true terminator) and stall recovery (the underrun isn't a network stall) is open.
  • live-stream-support (hard prerequisite) — provides the reload loop, sliding-window tracking, live-edge tracking, Infinity-duration semantics, termination detection, and setLiveSeekableRange placement. LL-HLS is structurally an extension of this feature's reload loop with a different pacing model, finer-grained segment list, and added optimizations.
  • dvr-event-stream-support — sibling extension on the same reload loop with different windowing semantics. Orthogonal to LL-HLS: a DVR stream can also be low- latency (DVR + LL-HLS), in which case both features compose.
  • presentation-modelingparseMediaPlaylist extensions for LL-HLS tags forces the question of parser pluggability raised in that doc's open questions. The two features intersect tightly.
  • buffer-management — forward-buffer planner extension for partials lives in the same media/buffer/ neighborhood; same back-buffer policy applies for full segments below the live edge.
  • video-abr — variant switching under LL-HLS uses the same bandwidthState signal; partial-segment chunked- sampling continues to feed it via createTrackedFetch. EXT-X-RENDITION-REPORT consumption (deferred, see Open questions) is the LL-HLS-aware variant-switch optimization.
  • non-zero-pts-support — live streams including LL-HLS typically have PTS far from zero; the time-mapping primitive is consumed by live (including LL-HLS) for correct currentTime / seekable semantics.
  • mse-mms-pipeline — partial-segment append is same-codec append on the same SourceBufferActor; no changeType(), no buffer recreation. The MSE codec-change check explicitly does not fire for LL-HLS.

See also