mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 04:38:33 +00:00
perf(player): coalesce _mirrorParentMediaTime writes (#396)
## Summary Coalesce writes to `el.currentTime` inside `_mirrorParentMediaTime` so a single jitter sample no longer triggers a parent-media seek. A drift correction now requires **two consecutive samples** above the threshold (~`MIRROR_DRIFT_THRESHOLD_SECONDS`) before the player writes back. One-shot alignment paths (`promoteToParentProxy`, `_onIframeMediaAdded`) opt out via `force: true` so initial alignment stays immediate. ## Why Step `P1-4` of the player perf proposal. `_mirrorParentMediaTime` is called every animation frame on parent media proxies. Even without true drift, browser internals report tiny jitter on `currentTime` reads — typically below 30 ms but occasionally crossing the threshold for a frame. Writing to `currentTime` triggers a seek, which is expensive *and* invalidates pipeline buffers, which causes the next frame's reading to jitter further. The result was unnecessary seek thrash on otherwise-aligned media. By requiring two consecutive over-threshold samples, transient jitter is filtered out while real drift (a sustained offset) still corrects within ~1 frame of latency. This eliminates the most common cause of dropped frames on the studio thumbnail grid. ## What changed - Each `_parentMedia` entry gains a `driftSamples` counter that increments while the absolute drift is above `MIRROR_DRIFT_THRESHOLD_SECONDS` and resets to 0 on the first sample below. - `_mirrorParentMediaTime(el, opts)` only writes back when `driftSamples >= 2`, except when `opts.force === true`. - `promoteToParentProxy` and `_onIframeMediaAdded` pass `force: true` so the first alignment after registration is still immediate (these are user-visible state transitions, not steady-state telemetry). ## Test plan - [x] 11 new unit/integration tests in `hyperframes-player.test.ts` covering: - Single-sample jitter does not trigger a write. - Two-sample sustained drift does trigger a write. - Trending drift correction (gradually increasing offset) is detected within 2 samples. - `force: true` override bypasses the sample requirement. - Out-of-range proxies (proxies whose source has been removed) do not panic. - Multiple proxies maintain independent counters — drift on one does not affect the other. - `_promoteToParentProxy` alignment is immediate. ## Stack Step `P1-4` of the player perf proposal. Builds on `P1-1` (shared adopted stylesheets) and `P1-2` (scoped media observer). Together these three target the studio multi-player render path — `P0-1*` perf gate scenarios will pick up the wins automatically.
This commit is contained in:
@@ -30,6 +30,27 @@ export type RuntimeState = {
|
||||
bridgeLastPostedAt: number;
|
||||
bridgeLastPostedPlaying: boolean;
|
||||
bridgeLastPostedMuted: boolean;
|
||||
/**
|
||||
* Max interval (ms) between outbound timeline samples on the parent-frame
|
||||
* control bridge. The bridge posts on every changed frame, but also at
|
||||
* least once per this interval so a paused/idle timeline still confirms
|
||||
* its position to any listener.
|
||||
*
|
||||
* **Cross-reference (do not change in isolation)**: the parent-frame
|
||||
* audio-mirror loop in `<hyperframes-player>` waits for
|
||||
* `MIRROR_REQUIRED_CONSECUTIVE_DRIFT_SAMPLES` consecutive over-threshold
|
||||
* samples before issuing a `currentTime` correction. The product of
|
||||
* those two constants is the worst-case A/V re-sync latency:
|
||||
*
|
||||
* worst_case_correction_latency_ms
|
||||
* ≈ MIRROR_REQUIRED_CONSECUTIVE_DRIFT_SAMPLES × bridgeMaxPostIntervalMs
|
||||
*
|
||||
* Today: `2 × 80 ms = 160 ms`, which sits comfortably under the
|
||||
* perceptual A/V re-sync tolerance. If you raise this interval, audit
|
||||
* `MIRROR_REQUIRED_CONSECUTIVE_DRIFT_SAMPLES` in
|
||||
* `packages/player/src/hyperframes-player.ts` — leaving it at `2` will
|
||||
* silently push correction latency past the tolerance budget.
|
||||
*/
|
||||
bridgeMaxPostIntervalMs: number;
|
||||
timelinePollIntervalId: ReturnType<typeof setInterval> | null;
|
||||
controlBridgeHandler: ((event: MessageEvent) => void) | null;
|
||||
|
||||
Reference in New Issue
Block a user