Mirror of the v/a extraction for the text side. The text step vocabulary
(TextFrame/TextLoadStep/TextStepDeps/TextMessagePipelines/TextLoadTask/
TextTrackSegmentResolver + resolveCues/dispatchCues) and the cue-sink message
DTOs (AddCuesMessage/CueSegmentMeta) move out of the text loader / text-tracks
actor into primitives/. All DOM-free (generic over Cue), so they land in
primitives/ root.
The dispatch step reaches its sink through a structural CueSink seam instead of
the concrete TextTracksActor, so the pipeline names no actor; the loader keeps
only scheduling. actors/ (root) and engines/hls gain primitives references.
Net effect: relocation-pipelines now imports only core/media/primitives — zero
actors/behaviors. It's ready to move to primitives; only the DOM VTTCue in its
text step keeps it in behaviors/dom for now (slice 3).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bundled as one WIP checkpoint — the changes interleave in relocation-pipelines.ts
and media/types, so they can't be split per-file. To be curated before the PR.
- Match box-time by track_id: the default relocation pipeline now uses
findMediaTrack + readBaseMediaDecodeTime (init tkhd track_id → segment tfhd
track_id) instead of the first-traf presumptive readers, so muxed ISO-BMFF
(e.g. a clcp caption traf) reads the media track's origin rather than relying
on box order. Adds MediaContainerData.trackId + a caption-first-muxing test.
- Rename relocation-steps → relocation-pipelines; reframe it as the
config-supplied, loader-facing half of establishStartMediaTime (the relocation
analog of track-switching's constraint/rule chain).
- Extract the loader step vocabulary + base steps and the SourceBuffer message
DTOs out of the actors into primitives/, behind a structural AppendSink seam so
the pipeline names no concrete actor; the loader actors keep only scheduling.
- Decouple the vocabulary from the DOM: add media/types SegmentData and make
media/dom AppendData an alias, so the whole v/a load pipeline lives DOM-free in
primitives/ root.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Spike/implementation working notes and pre-PR checklist. Internal AI-agent
context; drop or compact before merge if not wanted in the PR.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Design doc for the 3-coordinate model (startTime / startDate / startMediaTime)
and the timestampOffset relocation approach the reactor + steps implement.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Assets whose A/V encode starts at a non-zero native PTS (Mux instant clips,
Apple bipbop @10s) are relocated onto a 0-based presentation timeline via
`SourceBuffer.timestampOffset`, so buffer / model / `currentTime` / `seekable`
stay 0-based and the adapter is untouched.
- `establishStartMediaTime` reactor establishes each track's media-timeline
origin once per source (the DOM-free half), running an injected
`deriveStartMediaTime` seam over discovered container data.
- Default `deriveSharedMinStartMediaTime`: relocate every track by the `min`
across selected A/V origins — preserves real A/V skew and keeps every DTS >= 0.
Origins below NEAR_ZERO_ORIGIN_THRESHOLD (1s) are left native (ordinary
~0-PTS VOD isn't perturbed).
- `relocation-steps` message pipelines discover the origin (mdhd/tfdt head-peek)
and stamp `timestampOffset`; text cues rebase by X-TIMESTAMP-MAP − origin.
- Wired into both the default and audio-only HLS engines.
See internal/design/spf/presentation-timeline-model.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Make a complete VOD reach native `ended` (and loop) reliably even when audio
and video tracks end a few ms apart:
1. `end-of-stream` reactor gains LAST_SEGMENT_REACHED_SLACK (0.5s) on the
"playhead reached the last segment" gate — a tiny final segment plus Chrome's
audio-clock freeze otherwise deadlocks endOfStream().
2. New `recover-end-stall` behavior: on `waiting`, if the MediaSource is
'ended' and the playhead is within endStallNudgeWindow (0.2s) of the
reachable buffered end (getMinBufferedEnd over the SourceBuffers), nudge
currentTime = duration to force native ended. Event-driven; inert for
live / clean-ending streams.
ADR: internal/decisions/end-of-stream-av-skew-recovery.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Parse the WebVTT X-TIMESTAMP-MAP header (MPEGTS/LOCAL) and surface it as
text-segment metadata, so text-cue relocation can align cue times to the
relocated media timeline.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Minimal fMP4 box parser (media/mp4/box.ts) plus decode-time origin extraction
(timestamp-origin.ts) reading mdhd timescale + tfdt baseMediaDecodeTime, and a
head-peek primitive to read the leading bytes of a segment stream. Foundation
for relocating non-zero-PTS sources onto a 0-based presentation timeline.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>