mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
Capture the decision to hold one rolling (media-time ↔ PDT) anchor — learned from the first A/V track to obtain SourceBuffer ground truth and applied to all selected tracks (incl. text) by per-segment PDT — replacing today's per-track buffer pins, which are redundant under the no-skew assumption and can't anchor text (no SourceBuffer). New decision doc live-presentation-anchor.md; resolves the deferred single-rolling-anchor question in live-timeline-anchoring and promotes [4] in live-presentation-modeling to presentation-level. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
100 lines
4.9 KiB
Markdown
100 lines
4.9 KiB
Markdown
---
|
||
status: decided
|
||
date: 2026-06-15
|
||
---
|
||
|
||
# Live timeline anchoring via PROGRAM-DATE-TIME
|
||
|
||
## Decision
|
||
|
||
Anchor the live timeline on `EXT-X-PROGRAM-DATE-TIME` (PDT), not on media
|
||
sequence number. PDT gives each segment an absolute wall-clock time (epoch
|
||
seconds) used for two things:
|
||
|
||
1. **Cross-track alignment** — demuxed audio and video are aligned by equal
|
||
PDT (same real instant), not by equal sequence number or per-track relative
|
||
`startTime`.
|
||
2. **Turnover recovery** — on a full live-window slide with no overlap, the
|
||
absolute `startTime` is recovered from PDT rather than estimated from
|
||
`targetDuration × offset`.
|
||
|
||
Scope of this decision: the **anchor source** is PDT. The parser *surfaces*
|
||
`Segment.programDateTime` (landed; see Verification). Whether the model holds a
|
||
single rolling anchor vs. per-segment measured times is **fixed in
|
||
[live-presentation-anchor](./live-presentation-anchor.md)** — a single
|
||
presentation-level `(media-time ↔ PDT)` anchor, learned from the first A/V
|
||
track to buffer and applied to all tracks (incl. text) by PDT. The remaining
|
||
per-track `presentationTimeOffset` derivation for the skew case is downstream
|
||
and not fixed here.
|
||
|
||
This resolves open questions **[4] sync anchor** and **turnover `startTime`
|
||
recovery** in
|
||
[live-presentation-modeling](../design/spf/live-presentation-modeling.md).
|
||
|
||
## Context
|
||
|
||
For demuxed live HLS, each track is parsed independently and its segment
|
||
`startTime`s accumulate from 0, so the same real instant lands at different
|
||
per-track `startTime`s (measured: a demuxed Mux CMAF stream put `segment-82` at
|
||
`startTime` 2 in video but 0 in audio — a 2 s A/V skew). Sequence number
|
||
identifies and orders segments but does not place them in time. The model needs
|
||
a shared, absolute anchor that survives variable durations and window turnover.
|
||
|
||
## Alternatives Considered
|
||
|
||
- **Sequence × duration.** Reconstruct a segment's time as
|
||
`(seq − seq₀) × duration`. Rejected — it assumes uniform duration, which
|
||
fails three ways: (a) audio and video segment durations differ (AAC frames
|
||
are 1024 samples → a "2 s" audio segment is 2.005 s / 2.048 s, never the
|
||
video's exact duration), so the tracks drift apart; (b) manifests round
|
||
`EXTINF` (both Mux streams advertised integer durations over non-integer
|
||
media), compounding error; (c) once a segment rolls out of the window its
|
||
actual `EXTINF` is gone, so absolute time can't be reconstructed by summing.
|
||
PDT gives each in-window segment its own absolute time, immune to all three.
|
||
|
||
- **CMAF-HAM `presentationTimeOffset`.** Borrow the model shape from
|
||
common-media-library. Rejected as a *source* — HAM declares the field but
|
||
never populates it for HLS, its HLS mapper accumulates each track from 0
|
||
(same skew as ours), and it drops PDT entirely. We adopt the *name*
|
||
`presentationTimeOffset` for the per-track offset, not HAM's DASH-centric
|
||
(and unimplemented) derivation.
|
||
|
||
## Rationale
|
||
|
||
PDT is the mechanism RFC 8216 itself describes for correlating positions across
|
||
renditions (§4.3.2.6 associates a segment's first sample with absolute time).
|
||
It is optional in RFC 8216 but **required by Apple's HLS Authoring
|
||
Specification**, so it is reliably present on conformant content — Mux emits it
|
||
on every segment of both the TS and CMAF/LL-HLS profiles we captured. It is the
|
||
only anchor that is duration-variance-robust, survives window turnover, and is
|
||
already shared across tracks.
|
||
|
||
**Boundary.** PDT aligns the *manifest* timelines across tracks. It does not by
|
||
itself align the *buffer* (native-PTS) coordinate — that rides on CMAF's
|
||
common encoded presentation timeline plus the per-track offset learned from
|
||
`buffered`/`tfdt` (see [mse-timestamp-offset](./mse-timestamp-offset.md)). Two
|
||
distinct steps: PDT → shared manifest timeline (alignment); manifest → buffer
|
||
(the learned offset). PDT does the first only.
|
||
|
||
## Verification
|
||
|
||
`Segment.programDateTime` (epoch seconds) and PDT capture landed in the parser
|
||
(`parse-media-playlist.ts`), covered by `parse-media-playlist.test.ts`:
|
||
synthetic cases (capture, EXTINF forward-interpolation, re-anchor on an
|
||
explicit tag, variable-duration interpolation, absent-PDT → undefined) and real
|
||
Mux fixtures — the demuxed CMAF case asserts video/audio `segment-82` share PDT
|
||
while per-track `startTime` disagrees (2 vs 0). PDT-anchored placement and the
|
||
cross-track adjuster are not yet implemented.
|
||
|
||
## See also
|
||
|
||
- [live-presentation-anchor](./live-presentation-anchor.md) — fixes how this
|
||
anchor drives a single shared presentation timeline (the deferred
|
||
single-rolling-anchor question).
|
||
- [mse-timestamp-offset](./mse-timestamp-offset.md) — the buffer-layer half;
|
||
the manifest→buffer offset this decision's anchor feeds into.
|
||
- [live-presentation-modeling](../design/spf/live-presentation-modeling.md) —
|
||
open questions [4] and turnover recovery, resolved here.
|
||
- [non-zero-pts-support](../design/spf/features/non-zero-pts-support.md) —
|
||
consumes the offset-corrected timeline.
|