feat(spf): honor a pre-applied anchor when first-resolving a media playlist

parseMediaPlaylist now places a first-resolved window relative to a
`startDate` set on the unresolved shell (each segment at PDT − anchor)
instead of always anchoring at the local base 0. This lets the shared
presentation anchor pre-position not-yet-resolved tracks so they resolve
already on the shared timeline — no separate positioning pass needed.

Inert until a caller sets `startDate` on a shell; a playlist with no PDT
falls back to the local base, and the recomputed track `startDate` reads
back as the anchor.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Christian Pillsbury
2026-06-25 10:00:45 -07:00
co-authored by Claude Opus 4.8
parent 2b42ff48ef
commit 2007a173b3
2 changed files with 84 additions and 4 deletions
@@ -132,6 +132,31 @@ function placeOnPreviousTimeline(
return { segments: placed, startTime: anchor };
}
/**
* Place a first-resolved window on a *pre-applied* anchor — the track's
* `startDate` (wall clock at media-time 0) set on the unresolved shell before any
* segments exist. Each PDT-bearing segment lands at `segment.startDate anchor`,
* so the window resolves already on the shared presentation timeline rather than
* a local-from-zero one (the `startDate` recomputed afterwards then reads back as
* the anchor). Falls back to the local base when no segment carries PDT — there's
* nothing to anchor against.
*/
function placeOnAnchor(segments: Segment[], anchor: number): { segments: Segment[]; startTime: number } {
const localBase = segments[0]?.startTime ?? 0;
const anchorSegment = segments.find((segment) => !isUndefined(segment.startDate));
if (!anchorSegment || isUndefined(anchorSegment.startDate)) {
return { segments, startTime: localBase };
}
const shift = anchorSegment.startDate - anchor - anchorSegment.startTime;
if (shift === 0) {
return { segments, startTime: localBase };
}
const placed = segments.map((segment) => ({ ...segment, startTime: segment.startTime + shift }));
return { segments: placed, startTime: localBase + shift };
}
/**
* Parse an HLS media playlist into a resolved track with segments.
*
@@ -285,12 +310,17 @@ export function parseMediaPlaylist<T extends PartiallyResolvedTrack>(
const complete = endList || playlistType === 'VOD';
const trackDuration = complete ? totalDuration : Number.POSITIVE_INFINITY;
// Position this window on the timeline. First resolve (previous is the
// unresolved shell, no segments) anchors at 0; a live reload (previous is the
// prior resolved snapshot) carries the timeline forward from the overlap.
// Position this window on the timeline. A live reload (previous is the prior
// resolved snapshot) carries the timeline forward from the overlap. First
// resolve places relative to a pre-applied anchor (`startDate` set on the
// shell) when present — so a track resolves already on the shared presentation
// timeline — else anchors at the local base 0.
const presetAnchor = previous.startDate;
const placed = isResolvedTrack(previous)
? placeOnPreviousTimeline(previous, segments, mediaSequence, targetDuration)
: { segments, startTime: 0 };
: isUndefined(presetAnchor)
? { segments, startTime: 0 }
: placeOnAnchor(segments, presetAnchor);
// Wall-clock anchor: `startDate startTime` for the first PDT-bearing
// segment (constant along a linear timeline). Maps this track's origin to
@@ -808,4 +808,54 @@ s0.ts`;
expect((audio.startDate ?? 0) - (video.startDate ?? 0)).toBeCloseTo(2, 3);
});
});
describe('pre-applied anchor (startDate on the unresolved shell)', () => {
const shell: PartiallyResolvedVideoTrack = {
type: 'video',
id: 'video-0',
url: 'https://example.com/video/playlist.m3u8',
bandwidth: 1400000,
codecs: ['avc1.4d401f'],
mimeType: 'video/mp4',
};
const epoch = (iso: string) => Date.parse(iso) / 1000;
const anchor = epoch('2026-01-01T00:00:00.000Z');
const withPdt = `#EXTM3U
#EXT-X-TARGETDURATION:2
#EXT-X-MEDIA-SEQUENCE:5
#EXT-X-PROGRAM-DATE-TIME:2026-01-01T00:00:10.000Z
#EXTINF:2,
s5.m4s
#EXTINF:2,
s6.m4s`;
it('places first-resolve segments by PDT relative to the pre-applied startDate', () => {
const r = parseMediaPlaylist(withPdt, { ...shell, startDate: anchor });
// segment.startTime = segment PDT anchor (10s and 12s past media-time 0).
expect(r.segments.map((s) => s.startTime)).toEqual([10, 12]);
expect(r.startTime).toBe(10);
// The recomputed track startDate reads back as the anchor.
expect(r.startDate).toBe(anchor);
});
it('anchors at the local base 0 when the shell carries no startDate (unchanged)', () => {
const r = parseMediaPlaylist(withPdt, shell);
expect(r.segments.map((s) => s.startTime)).toEqual([0, 2]);
expect(r.startTime).toBe(0);
});
it('falls back to the local base when the shell has a startDate but no segment carries PDT', () => {
const noPdt = `#EXTM3U
#EXT-X-TARGETDURATION:2
#EXT-X-MEDIA-SEQUENCE:5
#EXTINF:2,
s5.m4s
#EXTINF:2,
s6.m4s`;
const r = parseMediaPlaylist(noPdt, { ...shell, startDate: anchor });
expect(r.segments.map((s) => s.startTime)).toEqual([0, 2]);
expect(r.startDate).toBeUndefined();
});
});
});