mirror of
https://github.com/zoriya/v10.git
synced 2026-08-14 18:04:49 +00:00
refactor(spf): make updateMediaSourceDuration the sole duration writer
syncLiveSeekableRange dropped its defensive mediaSource.duration = Infinity write — it duplicated updateMediaSourceDuration (the canonical owner). Per the W3C MSE spec, setLiveSeekableRange requires only readyState === 'open', not a set duration, so this behavior never needed it. Verified live (Mux LL-HLS): with the write removed, duration is Infinity and the initial seek lands at the holdback from the first frame — updateMediaSourceDuration's async write resolves fast at startup (buffers idle), ahead of seekToLiveEdge's seek. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
7ccac9168e
commit
3859da3dbd
@@ -11,10 +11,11 @@
|
||||
* so the range is declared before that behavior seeks the playhead into it (a
|
||||
* seek outside `seekable` is clamped).
|
||||
*
|
||||
* Out of scope (deliberate, tracked as follow-ups): `clearLiveSeekableRange()`
|
||||
* on the live→ended transition; and dropping the defensive `duration` write
|
||||
* below once duration ownership/ordering with `updateMediaSourceDuration` (the
|
||||
* canonical, but asynchronous, duration writer) is resolved.
|
||||
* Duration is owned solely by `updateMediaSourceDuration`; this behavior only
|
||||
* declares the seekable range (`setLiveSeekableRange` requires only
|
||||
* `readyState === 'open'` per the W3C MSE spec, not a set `duration`). Out of
|
||||
* scope (tracked follow-up): `clearLiveSeekableRange()` on the live→ended
|
||||
* transition.
|
||||
*/
|
||||
import type { Behavior } from '../../../core/composition/create-composition';
|
||||
import { effect } from '../../../core/signals/effect';
|
||||
@@ -49,15 +50,11 @@ function syncLiveSeekableRangeSetup({
|
||||
if (!mediaSource || mediaSource.readyState !== 'open' || !liveWindow) return;
|
||||
|
||||
try {
|
||||
// A live seekable range needs a set duration. `updateMediaSourceDuration`
|
||||
// is the canonical duration owner, but writes asynchronously — so guard
|
||||
// here so this doesn't race ahead of it. (Follow-up: resolve ownership.)
|
||||
if (Number.isNaN(mediaSource.duration)) mediaSource.duration = Number.POSITIVE_INFINITY;
|
||||
// Re-declared as the window slides so seekable tracks the live window
|
||||
// (the full DVR range remains seekable; seek-to-live-edge starts near the edge).
|
||||
mediaSource.setLiveSeekableRange(liveWindow.start, liveWindow.end);
|
||||
} catch {
|
||||
// readyState raced closed, or duration set rejected — retried on the next window change.
|
||||
// readyState raced closed — retried on the next window change.
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -55,13 +55,12 @@ function run(opts: { presentation?: MaybeResolvedPresentation; trackId?: string;
|
||||
}
|
||||
|
||||
describe('syncLiveSeekableRange', () => {
|
||||
it('declares the full live window as seekable and sets Infinity duration', () => {
|
||||
it('declares the full live window as seekable', () => {
|
||||
const ms = fakeMediaSource();
|
||||
const cleanup = run({ presentation: makePresentation(), trackId: 'v-1', mediaSource: ms });
|
||||
|
||||
// [first.startTime, last.startTime + last.duration] = [100, 110].
|
||||
expect(ms.setLiveSeekableRange).toHaveBeenCalledWith(100, 110);
|
||||
expect(ms.duration).toBe(Number.POSITIVE_INFINITY);
|
||||
|
||||
cleanup();
|
||||
});
|
||||
@@ -96,13 +95,13 @@ describe('syncLiveSeekableRange', () => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
it('does not overwrite an already-set (non-NaN) duration', () => {
|
||||
it('leaves duration alone — owned by updateMediaSourceDuration', () => {
|
||||
const ms = fakeMediaSource();
|
||||
ms.duration = 500; // e.g. updateMediaSourceDuration already wrote it
|
||||
const cleanup = run({ presentation: makePresentation(), trackId: 'v-1', mediaSource: ms });
|
||||
|
||||
expect(ms.duration).toBe(500);
|
||||
// Declares the range without touching duration (still NaN from the fake).
|
||||
expect(ms.setLiveSeekableRange).toHaveBeenCalledWith(100, 110);
|
||||
expect(ms.duration).toBeNaN();
|
||||
|
||||
cleanup();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user