diff --git a/internal/design/spf/features/ll-hls-support.md b/internal/design/spf/features/ll-hls-support.md index 885402b4..a82c40e0 100644 --- a/internal/design/spf/features/ll-hls-support.md +++ b/internal/design/spf/features/ll-hls-support.md @@ -94,6 +94,15 @@ Things this feature probably forces decisions on, not just additions: 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. +- **Reload change-detection vs. partial segments** — the regular-live + cadence (`mediaPlaylistReloadDelay`) classifies a reload "changed vs. + unchanged" — full target vs. ½× per §6.3.4 — from the **full-segment** + window signature (`mediaSequence` + segment count), so a parts-only LL-HLS + update reads as *unchanged* and would poll at ½ target. The LL-HLS reload- + loop must either fold part progression into change-detection (interval- + polling variant) or replace interval pacing with response-driven blocking + reload, which sidesteps the question. (Regular-live cadence is verified + §6.3.4-correct in `reload-cadence.test.ts`; this is the LL-HLS-only gap.) - **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 diff --git a/internal/design/spf/live-presentation-modeling.md b/internal/design/spf/live-presentation-modeling.md index 57752ba2..77cd7279 100644 --- a/internal/design/spf/live-presentation-modeling.md +++ b/internal/design/spf/live-presentation-modeling.md @@ -177,8 +177,20 @@ here. target; ½× on an unchanged playlist). This is the category the decomposition most insists on isolating: it is a **delivery / scheduling** concern, *not* part of the time-*t* content snapshot, even though HLS -happens to deliver both in the same playlist text. Its model placement -(per-track vs presentation-level) is an [open question](#open-questions). +happens to deliver both in the same playlist text. + +**Realized** (`feat/spf-hls-live`) as a pure §6.3.4 cadence function +`mediaPlaylistReloadDelay(current, previous)` (`media/hls/reload-policy.ts`) — full +target on a changed window, ½× on an unchanged one, `null` once complete — consumed +by `delayedReschedule` + a `RecurringRunner` (`core/tasks/`) that owns the loop but +knows nothing about time. Fetch scheduling landed **per-track**: each `resolveXTrack` +owns its runner (see the [placement open question](#open-questions)). A failed reload +is **not** retried by the policy — the rejection propagates and ends that track's +recurrence; transient-fetch resilience (retry/backoff) is a fetch-layer concern +deferred to [network-resilience](./features/network-resilience.md), kept out of the +cadence. "Changed" is detected from the full-segment window (`mediaSequence` + segment +count); a parts-only LL-HLS update is invisible to it, which +[ll-hls-support](./features/ll-hls-support.md) must extend. ### [4] Sync anchor — `PROGRAM-DATE-TIME` @@ -228,7 +240,8 @@ behavior are the implementation efforts that consume this. model argues for a faithful per-fetch representation. This is the seam where this doc meets the parser refactor. - **`Presentation`** — add `streamType` ([2a]). Completeness ([2b]) rides the - existing `duration` field. Refetch policy ([3]) placement TBD. + existing `duration` field. Refetch policy ([3]) realized per-track — each + `resolveXTrack` owns a `RecurringRunner` driven by an engine-injected reschedule. --- @@ -314,16 +327,17 @@ prematurely fix. ## Open questions -- **[3] Refetch-policy placement — per-track vs presentation-level.** In +- **[3] Refetch-policy placement — DECIDED (fetch scheduling per-track).** In HLS each media playlist (= each `Track`) carries its own `TARGETDURATION`; in DASH, `minimumUpdatePeriod` is MPD-level (presentation). Per-track bakes an HLS assumption; presentation-level - fights HLS's per-playlist reality. Likely answer: a presentation-level - concept fed by per-track source data — but decide deliberately. Ties - to live-stream-support's per-type reload-coordination question, and to - the observation that a not-yet-ended track *must* keep being refetched - to stay current, so timeline reconciliation across tracks can't be - fully independent even if fetch scheduling is. + fights HLS's per-playlist reality. Landed (`feat/spf-hls-live`) as the + anticipated split: **fetch scheduling is per-track** — each `resolveXTrack` + owns a `RecurringRunner` paced by its own `TARGETDURATION` — while + cross-track **timeline reconciliation** stays separate (the parser carries + the timeline forward per fetch; `anchorLiveTracks` aligns renditions by PDT). + So fetch scheduling is independent without the timeline reconciliation being + forced independent, as this question anticipated. - **[4] How captured PDT feeds the A/V-sync anchor — DECIDED (anchor source).** Resolved in [live-timeline-anchoring](../../decisions/live-timeline-anchoring.md): align demuxed audio/video by equal PDT (same real instant), not by