mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
Two decision docs for the live-HLS timeline work: - mse-timestamp-offset — native-PTS default for live; timestampOffset reserved for relocation (the manifest→buffer / encoded layer). - live-timeline-anchoring — align demuxed tracks and recover turnover via PDT, not sequence number (the manifest layer). Resolve open questions [4] (sync anchor) and turnover startTime recovery in live-presentation-modeling, pointing at the decisions. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
95 lines
4.6 KiB
Markdown
95 lines
4.6 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). How that anchor drives a
|
||
shared presentation timeline — the cross-track adjuster / per-track
|
||
`presentationTimeOffset` derivation, and whether the model holds a single
|
||
rolling anchor vs. per-segment measured times — 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
|
||
|
||
- [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.
|