OptionalcreateWorker: () => EngineWorkerPortTransfers the display canvas to the worker exactly once. The transfer is permanent (it neuters the element's 2D/GPU context), so a repeat call with the same element, or a null detach, is a no-op; the binding lives until dispose terminates the worker. The viewport box is measured here, where layout lives, and shipped so a viewport-aware decode strategy can size the sink worker-side.
Changes frames-mode display output and default scrub-preview sizing without reopening the video. Resolves with true after replacement delivery, or false when decode and preview sizes are unchanged. The host still acknowledges its actual presentation.
Forward playback speed, in media seconds per wall second. Takes effect at once while playing and on the next play while paused; either way it survives pause, seek, interactive-seek, and replay from the end, because it is the clock's slope and none of those touch it.
Throws synchronously on a rate outside the supported forward range, including any reverse rate. The refusal has to happen here: the command is fire-and-forget, so a worker-side throw would reach no caller.
Fire-and-forget seek: latest-wins, does not await idle. A pointer position is not a frame time and never can be, so it is resolved to the frame covering it before it reaches the store or the worker; the table lives on this thread, so the playhead lands on a frame in the same tick the gesture arrives, with no round trip.
intent steers what the cache prepares next. Ignored when cacheStrategy is "none": that backend has no access modes to switch and no prefetch to aim.
Pause-during-drag entry point. Consumers that handle pointer-driven scrub gestures call this on pointerdown so playback freezes and the decoder is not continuously chasing the cursor; the corresponding endInteractiveSeek call on pointerup resumes play if the engine was playing when the drag started. Matches the way native
Pointerup counterpart to beginInteractiveSeek. Resumes play only if the engine was playing when the drag started; an idle-to-idle drag leaves the engine paused. Resolves once the worker has applied the release.
Awaited seek. Resolves after the cursor settles. Use on pointer-up so downstream logic (export, telemetry, marker placement) sees the settled frame.
Serialized so concurrent callers (rapid key-repeat, mashed buttons, a programmatic loop) never run overlapping steps that race the cursor's one-shot decode iterator and wedge it. Each call chains after the previous, so every invocation advances exactly one frame from the settled position, on any surface.
Opt-in diagnostics broadcast. Starts the worker's BROADCAST_HZ timer and flips on the per-rAF counters; snapshots arrive on the diag plane and land in the DiagnosticsStore. The instrument calls this on mount and stopDiagnostics on unmount, so a closed panel costs the engine nothing.
Arm the worker trace rings. Fire-and-forget; disarmTrace frees them.
The capture rescued from a dispose that landed mid-recording, or null.
Awaitable: assembles the worker trace and returns it for download. Null when nothing was armed.
Registers THE consumer of presented frames, in "frames" presentation mode. A second registration replaces the first, so at most one holder exists. After synchronously drawing the frame and its matching layers, the handler calls acknowledgePresentation(), then close(). A discarded frame is only closed. One left open pins a decoder buffer and stalls the decoder.
There is deliberately no counterpart that reports the frame most recently presented. A frame readable apart from the message that carried it can be read at a moment when a newer one has already been handed out, which is the desync this plane is shaped to make impossible.
Subscribe to diagnostics pushes; fires on every diag broadcast. Separate from the playback subscribe channels so a diagnostics consumer never wakes on playback state and vice versa.
Latest broadcast snapshot, or null before the first push.
Loads the configured source. A stream source is transferred on this call, so a repeat load has nothing left to hand over and rejects.
Settled iff the engine is between operations. Derived from mirror state rather than the cursor (which lives in the worker): Idle/Loading read as settled, otherwise a live seek is the only thing that unsettles it.
Where the transport has settled: a frame of the source, never a request and never a clock reading.
Cache hit-rate, scrub-decode latency, and access mode from the worker, or null on the uncached cursor. A round-trip per call; for diagnostics, not the hot path.
Tears the engine down: drains pending requests so an in-flight load/commit rejects rather than hanging, and terminates the worker. The dispose ack is awaited first so the worker closes its cursor cleanly before the realm is killed.
The engine is inert afterwards. A fire-and-forget command is dropped and an awaitable one rejects with WebVideoEngineErrorCode.Aborted; neither starts another worker.
Curated subset exposed to React via useImperativeHandle. Cached so the handle keeps a stable identity for the lifetime of the engine (consumers may list it in useEffect deps).
Main-thread facade for the worker-hosted web video engine. Owns the worker (spawned lazily on the first command), the mirror store React reads through useSyncExternalStore, and the imperative handle. The decode + render loop runs in the worker (EngineCore); this class never touches a cursor or clock directly.
Three planes cross the worker boundary (see workerProtocol): commands go out, broadcast state comes back as MirrorEvents fed into the store, and the display canvas is transferred once on bindCanvas. Awaitable commands carry a requestId the worker echoes back so each call settles its own promise.
Time has two owners, gated by playback. While playing, the worker emits time; while paused, the main thread owns the position: scrub/commit/step write it optimistically so a late worker paint never yanks it back. Consumers render the playhead by reading getTimeMs/getDurationMs at their own cadence.
Engine outlives a single React render. Recreate the engine only when the source identity changes.