Files
v10/packages/spf/src/media/tests/buffered-anchor.test.ts
T
Christian PillsburyandClaude Opus 4.8 b32783fbd7 fix(spf): pin the live track timeline to the buffer so streams end cleanly
The model timeline (an averageDuration×sequence estimate) could drift from
the SourceBuffer's native-PTS timeline across reloads. At ENDLIST the drifted
model placed the final segments behind the playhead, so the loader skipped
them and end-of-stream's isLastSegmentAppended never satisfied — playback
stalled in `waiting`, duration stuck at Infinity, `ended` never fired.

Pin the model to ground truth: once a segment is buffered, re-origin the track
onto where it actually landed (anchorTrackToBufferedSegment), correlated via
bufferedAnchorFor over the buffer actor's DOM-free snapshot. Pin once per track
(the offset is constant under no-discontinuity); the parser's now-PDT-exact
carry-forward maintains it. The sequence estimate stays the pre-buffer
bootstrap. anchorLiveTracks stays DOM-free — the engine injects the resolver
from the buffer actors; no timestampOffset (preserves A/V sync). The same pin
serves non-zero-PTS VOD.

Smoke-tested on a live Mux LL-HLS stream: durationchange → finite duration →
plays out → `ended`, no stall.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 09:59:45 -07:00

34 lines
1.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, expect, it } from 'vitest';
import { bufferedAnchorFor } from '../buffered-anchor';
describe('bufferedAnchorFor', () => {
it('pairs the leading-edge segment with its actual native-PTS start', () => {
// Model says the latest segment sits at 110 (+10s long); the buffer's real
// leading edge is 550 → it actually starts at 540 (a +430 native-PTS offset).
const anchor = bufferedAnchorFor(
[
{ id: 's1', startTime: 100, duration: 10 },
{ id: 's2', startTime: 110, duration: 10 },
],
[{ start: 530, end: 550 }]
);
expect(anchor).toEqual({ segmentId: 's2', actualStart: 540 });
});
it('uses the max end across discontiguous ranges', () => {
const anchor = bufferedAnchorFor(
[{ id: 's2', startTime: 110, duration: 10 }],
[
{ start: 530, end: 545 },
{ start: 548, end: 552 },
]
);
expect(anchor?.actualStart).toBe(542); // 552 10
});
it('returns undefined before anything is buffered', () => {
expect(bufferedAnchorFor([{ id: 's1', startTime: 0, duration: 10 }], [])).toBeUndefined();
expect(bufferedAnchorFor([], [{ start: 0, end: 10 }])).toBeUndefined();
});
});