supervision 0.1.7 → 0.2.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. package/dist/constants/media-renderer.d.ts +3 -0
  2. package/dist/constants/media-renderer.d.ts.map +1 -1
  3. package/dist/detections/chunked-detection-frame-source.d.ts.map +1 -1
  4. package/dist/index.d.ts +8 -4
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +9281 -5038
  7. package/dist/index.js.map +1 -1
  8. package/dist/mask-preparation.worker.js +409 -185
  9. package/dist/mask-preparation.worker.js.map +1 -1
  10. package/dist/media/display-pixel-ratio.d.ts +15 -0
  11. package/dist/media/display-pixel-ratio.d.ts.map +1 -0
  12. package/dist/media/engine-import-failure.d.ts +16 -0
  13. package/dist/media/engine-import-failure.d.ts.map +1 -0
  14. package/dist/media/media-errors.d.ts +6 -0
  15. package/dist/media/media-errors.d.ts.map +1 -1
  16. package/dist/media/media-normalization.d.ts.map +1 -1
  17. package/dist/media/media-probe.d.ts.map +1 -1
  18. package/dist/media/media-source-state.d.ts.map +1 -1
  19. package/dist/media/media-source.d.ts +17 -0
  20. package/dist/media/media-source.d.ts.map +1 -1
  21. package/dist/media/mediabunny-media-source.d.ts.map +1 -1
  22. package/dist/media/video-engine-media-source.d.ts +54 -0
  23. package/dist/media/video-engine-media-source.d.ts.map +1 -0
  24. package/dist/media/video-engine-media-source.js +3 -0
  25. package/dist/media/video-engine-media-source.js.map +1 -0
  26. package/dist/playback/media-playback-controller.d.ts +7 -1
  27. package/dist/playback/media-playback-controller.d.ts.map +1 -1
  28. package/dist/render-preparation/mask-frame-artifact.d.ts +48 -12
  29. package/dist/render-preparation/mask-frame-artifact.d.ts.map +1 -1
  30. package/dist/render-preparation/mask-frame-compositor.d.ts +12 -5
  31. package/dist/render-preparation/mask-frame-compositor.d.ts.map +1 -1
  32. package/dist/render-preparation/mask-frame-preparer.d.ts.map +1 -1
  33. package/dist/render-preparation/mask-preparation-worker-count.d.ts +4 -4
  34. package/dist/render-preparation/mask-preparation-worker-protocol.d.ts +13 -3
  35. package/dist/render-preparation/mask-preparation-worker-protocol.d.ts.map +1 -1
  36. package/dist/render-preparation/prepared-render-window.d.ts +44 -2
  37. package/dist/render-preparation/prepared-render-window.d.ts.map +1 -1
  38. package/dist/render-preparation/prepared-window-timeline.d.ts +2 -6
  39. package/dist/render-preparation/prepared-window-timeline.d.ts.map +1 -1
  40. package/dist/renderers/injected-pixi.d.ts +38 -0
  41. package/dist/renderers/injected-pixi.d.ts.map +1 -0
  42. package/dist/renderers/mask-palette.d.ts +14 -0
  43. package/dist/renderers/mask-palette.d.ts.map +1 -0
  44. package/dist/renderers/mask-vertex.d.ts +14 -0
  45. package/dist/renderers/mask-vertex.d.ts.map +1 -0
  46. package/dist/renderers/media-renderer-core.d.ts +9 -0
  47. package/dist/renderers/media-renderer-core.d.ts.map +1 -1
  48. package/dist/renderers/media-renderer-scene.d.ts +50 -2
  49. package/dist/renderers/media-renderer-scene.d.ts.map +1 -1
  50. package/dist/renderers/media-renderer-state.d.ts +20 -1
  51. package/dist/renderers/media-renderer-state.d.ts.map +1 -1
  52. package/dist/renderers/media-renderer-transport.d.ts +72 -0
  53. package/dist/renderers/media-renderer-transport.d.ts.map +1 -0
  54. package/dist/renderers/pixi-box-layer.d.ts +5 -0
  55. package/dist/renderers/pixi-box-layer.d.ts.map +1 -1
  56. package/dist/renderers/pixi-focus-layer.d.ts +19 -2
  57. package/dist/renderers/pixi-focus-layer.d.ts.map +1 -1
  58. package/dist/renderers/pixi-frame-present.d.ts +77 -0
  59. package/dist/renderers/pixi-frame-present.d.ts.map +1 -0
  60. package/dist/renderers/pixi-id-mask-shader.d.ts +6 -25
  61. package/dist/renderers/pixi-id-mask-shader.d.ts.map +1 -1
  62. package/dist/renderers/pixi-interaction-layer.d.ts.map +1 -1
  63. package/dist/renderers/pixi-interaction-presentation-layer.d.ts +12 -2
  64. package/dist/renderers/pixi-interaction-presentation-layer.d.ts.map +1 -1
  65. package/dist/renderers/pixi-mask-halo.d.ts +18 -23
  66. package/dist/renderers/pixi-mask-halo.d.ts.map +1 -1
  67. package/dist/renderers/pixi-mask-layer.d.ts +94 -32
  68. package/dist/renderers/pixi-mask-layer.d.ts.map +1 -1
  69. package/dist/renderers/pixi-media-scene.d.ts +67 -0
  70. package/dist/renderers/pixi-media-scene.d.ts.map +1 -1
  71. package/dist/renderers/pixi-polygon-layer.d.ts +9 -2
  72. package/dist/renderers/pixi-polygon-layer.d.ts.map +1 -1
  73. package/dist/renderers/pixi-region-coverage-mask.d.ts +62 -0
  74. package/dist/renderers/pixi-region-coverage-mask.d.ts.map +1 -0
  75. package/dist/renderers/pixi-region-effect.d.ts +42 -0
  76. package/dist/renderers/pixi-region-effect.d.ts.map +1 -0
  77. package/dist/renderers/pixi-region-layer.d.ts +43 -2
  78. package/dist/renderers/pixi-region-layer.d.ts.map +1 -1
  79. package/dist/renderers/pixi-shader-lifecycle.d.ts +14 -0
  80. package/dist/renderers/pixi-shader-lifecycle.d.ts.map +1 -0
  81. package/dist/renderers/pixi-vector-layer.d.ts.map +1 -1
  82. package/dist/renderers/prepared-annotation-window.d.ts +47 -0
  83. package/dist/renderers/prepared-annotation-window.d.ts.map +1 -0
  84. package/dist/renderers/presented-frame-channel.d.ts +138 -0
  85. package/dist/renderers/presented-frame-channel.d.ts.map +1 -0
  86. package/dist/renderers/scene-render-scheduler.d.ts +20 -0
  87. package/dist/renderers/scene-render-scheduler.d.ts.map +1 -0
  88. package/dist/sessions/media-session-defaults.d.ts +10 -6
  89. package/dist/sessions/media-session-defaults.d.ts.map +1 -1
  90. package/dist/sessions/media-session-media.d.ts.map +1 -1
  91. package/dist/sessions/media-session-state.d.ts.map +1 -1
  92. package/dist/sessions/media-session.d.ts.map +1 -1
  93. package/dist/tracking.worker.js +73 -4
  94. package/dist/tracking.worker.js.map +1 -1
  95. package/dist/types/media-normalization.d.ts +34 -0
  96. package/dist/types/media-normalization.d.ts.map +1 -1
  97. package/dist/types/media-renderer.d.ts +31 -1
  98. package/dist/types/media-renderer.d.ts.map +1 -1
  99. package/dist/types/media-session.d.ts +89 -15
  100. package/dist/types/media-session.d.ts.map +1 -1
  101. package/dist/types/render-preparation.d.ts +149 -5
  102. package/dist/types/render-preparation.d.ts.map +1 -1
  103. package/dist/video-engine-media-source-CUXnOJOV.js +450 -0
  104. package/dist/video-engine-media-source-CUXnOJOV.js.map +1 -0
  105. package/dist/web-video-engine/analysis-session.d.ts +75 -0
  106. package/dist/web-video-engine/analysis.d.ts +6 -0
  107. package/dist/web-video-engine/analysis.js +1850 -0
  108. package/dist/web-video-engine/cache-budget.d.ts +18 -0
  109. package/dist/web-video-engine/canvas-sink-scrub-cursor.d.ts +89 -0
  110. package/dist/web-video-engine/clock.d.ts +82 -0
  111. package/dist/web-video-engine/constants.d.ts +261 -0
  112. package/dist/web-video-engine/create-scrub-cursor.d.ts +65 -0
  113. package/dist/web-video-engine/decode-resolution.d.ts +114 -0
  114. package/dist/web-video-engine/decode-scheduler.d.ts +371 -0
  115. package/dist/web-video-engine/decode-session.d.ts +283 -0
  116. package/dist/web-video-engine/decode-source.d.ts +258 -0
  117. package/dist/web-video-engine/diagnostics-store.d.ts +17 -0
  118. package/dist/web-video-engine/diagnostics.d.ts +316 -0
  119. package/dist/web-video-engine/embedded-engine-worker.d.ts +1 -0
  120. package/dist/web-video-engine/engine-core.d.ts +269 -0
  121. package/dist/web-video-engine/engine.d.ts +25 -0
  122. package/dist/web-video-engine/engine.js +912 -0
  123. package/dist/web-video-engine/engine.worker.d.ts +1 -0
  124. package/dist/web-video-engine/engine.worker.js +33624 -0
  125. package/dist/web-video-engine/frame-cache.d.ts +236 -0
  126. package/dist/web-video-engine/frame-extractor.d.ts +27 -0
  127. package/dist/web-video-engine/frame-timeline-DreEwsRY.js +616 -0
  128. package/dist/web-video-engine/frame-timeline.d.ts +80 -0
  129. package/dist/web-video-engine/frame-walker.d.ts +73 -0
  130. package/dist/web-video-engine/index.d.ts +13 -0
  131. package/dist/web-video-engine/index.d.ts.map +1 -0
  132. package/dist/web-video-engine/index.js +3 -0
  133. package/dist/web-video-engine/index.js.map +1 -0
  134. package/dist/web-video-engine/key-packet.d.ts +64 -0
  135. package/dist/web-video-engine/keyframe-index.d.ts +90 -0
  136. package/dist/web-video-engine/mirror-store.d.ts +55 -0
  137. package/dist/web-video-engine/renderer.d.ts +93 -0
  138. package/dist/web-video-engine/rotation.d.ts +34 -0
  139. package/dist/web-video-engine/scrub-controller.d.ts +375 -0
  140. package/dist/web-video-engine/scrub-cursor.d.ts +347 -0
  141. package/dist/web-video-engine/scrub-trajectory.d.ts +29 -0
  142. package/dist/web-video-engine/source-residency.d.ts +91 -0
  143. package/dist/web-video-engine/trace-recorder.d.ts +163 -0
  144. package/dist/web-video-engine/types.d.ts +200 -0
  145. package/dist/web-video-engine/video-engine.d.ts +437 -0
  146. package/dist/web-video-engine/webgpu-renderer.d.ts +61 -0
  147. package/dist/web-video-engine/worker-bridge.d.ts +11 -0
  148. package/dist/web-video-engine/worker-dispatch.d.ts +16 -0
  149. package/dist/web-video-engine/worker-protocol.d.ts +282 -0
  150. package/node_modules/supervision-js-core/dist/detections/buffered-detection-timeline.d.ts.map +1 -1
  151. package/node_modules/supervision-js-core/dist/detections/composite-detection-frame-source.d.ts.map +1 -1
  152. package/node_modules/supervision-js-core/dist/index.d.ts +5 -4
  153. package/node_modules/supervision-js-core/dist/index.d.ts.map +1 -1
  154. package/node_modules/supervision-js-core/dist/index.js +1167 -280
  155. package/node_modules/supervision-js-core/dist/index.js.map +1 -1
  156. package/node_modules/supervision-js-core/dist/styles/default-annotation-presentation.d.ts.map +1 -1
  157. package/node_modules/supervision-js-core/dist/styles/interaction-style.d.ts +4 -36
  158. package/node_modules/supervision-js-core/dist/styles/interaction-style.d.ts.map +1 -1
  159. package/node_modules/supervision-js-core/dist/styles/polyline-style.d.ts +5 -0
  160. package/node_modules/supervision-js-core/dist/styles/polyline-style.d.ts.map +1 -1
  161. package/node_modules/supervision-js-core/dist/types/annotation-renderer.d.ts +92 -7
  162. package/node_modules/supervision-js-core/dist/types/annotation-renderer.d.ts.map +1 -1
  163. package/node_modules/supervision-js-core/dist/types/detection-timeline.d.ts +191 -17
  164. package/node_modules/supervision-js-core/dist/types/detection-timeline.d.ts.map +1 -1
  165. package/node_modules/supervision-js-core/dist/types/media-rendering.d.ts +83 -5
  166. package/node_modules/supervision-js-core/dist/types/media-rendering.d.ts.map +1 -1
  167. package/node_modules/supervision-js-core/dist/types/polyline-style.d.ts +2 -0
  168. package/node_modules/supervision-js-core/dist/types/polyline-style.d.ts.map +1 -1
  169. package/node_modules/supervision-js-core/dist/types/session-lifecycle.d.ts +32 -0
  170. package/node_modules/supervision-js-core/dist/types/session-lifecycle.d.ts.map +1 -1
  171. package/node_modules/supervision-js-core/dist/utils/detection-conversions.d.ts.map +1 -1
  172. package/node_modules/supervision-js-core/dist/utils/detection-frames.d.ts +5 -0
  173. package/node_modules/supervision-js-core/dist/utils/detection-frames.d.ts.map +1 -1
  174. package/node_modules/supervision-js-core/dist/utils/detection-masks.d.ts +9 -0
  175. package/node_modules/supervision-js-core/dist/utils/detection-masks.d.ts.map +1 -1
  176. package/node_modules/supervision-js-core/dist/utils/detection-ranges.d.ts +18 -0
  177. package/node_modules/supervision-js-core/dist/utils/detection-ranges.d.ts.map +1 -0
  178. package/node_modules/supervision-js-core/dist/utils/id-mask-frame.d.ts +34 -2
  179. package/node_modules/supervision-js-core/dist/utils/id-mask-frame.d.ts.map +1 -1
  180. package/node_modules/supervision-js-core/dist/utils/wait-bound.d.ts +7 -0
  181. package/node_modules/supervision-js-core/dist/utils/wait-bound.d.ts.map +1 -0
  182. package/package.json +24 -3
@@ -0,0 +1,200 @@
1
+ import type { FrameTimelineData } from "./frame-timeline";
2
+ /**
3
+ * Seconds, as a branded float. Promoted out of `number` so a duration
4
+ * cannot accidentally be passed where a frame index is expected.
5
+ */
6
+ export type Sec = number & {
7
+ readonly __sec: unique symbol;
8
+ };
9
+ /** Frames per second. Branded for the same reason as Sec. */
10
+ export type Fps = number & {
11
+ readonly __fps: unique symbol;
12
+ };
13
+ /**
14
+ * Paints since the source loaded, monotonic within it and reset on source swap.
15
+ * It counts paints, not source frames: two paints of one media position can
16
+ * take two sequence numbers. Source-frame identity is carried separately by
17
+ * the presented frame's frameId and media timestamp.
18
+ */
19
+ export type PaintSeq = number & {
20
+ readonly __paintSeq: unique symbol;
21
+ };
22
+ export declare const asSec: (n: number) => Sec;
23
+ export declare const asFps: (n: number) => Fps;
24
+ export declare const asPaintSeq: (n: number) => PaintSeq;
25
+ export declare enum SourceKind {
26
+ Url = "url",
27
+ Blob = "blob",
28
+ Stream = "stream"
29
+ }
30
+ export interface UrlVideoSource {
31
+ kind: SourceKind.Url;
32
+ url: string;
33
+ /** The engine fetches rather than building an element, so these reach the
34
+ * request as the CORS fetch they stand for. Undeclared leaves the request at
35
+ * the demuxer's own same-origin default. */
36
+ crossOrigin?: "anonymous" | "use-credentials";
37
+ }
38
+ /**
39
+ * Mediabunny's own read tuning for a URL source, passed straight through to its
40
+ * `UrlSource`. Every field absent leaves mediabunny's defaults in place.
41
+ */
42
+ export interface UrlSourceReadConfig {
43
+ /** Range requests the demuxer may have in flight at once. */
44
+ readonly parallelism?: number;
45
+ /** Ceiling on the bytes mediabunny's own read cache holds. */
46
+ readonly maxCacheSize?: number;
47
+ }
48
+ export interface BlobVideoSource {
49
+ kind: SourceKind.Blob;
50
+ blob: Blob;
51
+ }
52
+ export interface StreamVideoSource {
53
+ kind: SourceKind.Stream;
54
+ /** The load transfers this stream to the worker, which detaches this side's
55
+ * reference: afterwards the worker holds the only readable end. */
56
+ stream: ReadableStream<Uint8Array>;
57
+ /** Declared container type. Nothing reads it: the demuxer sniffs the bytes,
58
+ * and no metadata surface carries it back out. */
59
+ mimeType: string;
60
+ }
61
+ /**
62
+ * Source descriptor. Discriminated so each backend can route without runtime
63
+ * sniffing and so the future mediabunny v2 StreamSource slots in as a new
64
+ * variant rather than a refactor.
65
+ */
66
+ export type VideoSource = UrlVideoSource | BlobVideoSource | StreamVideoSource;
67
+ /**
68
+ * Which decode machinery a source was opened through: mediabunny's CanvasSink,
69
+ * mediabunny's VideoSampleSink, or the runtime's own long-lived DecodeSession.
70
+ * Resolved per source at open time from what the track and the realm support.
71
+ */
72
+ export type DecodePath = "canvas" | "sample" | "session";
73
+ /**
74
+ * Who owns the pixels. "canvas" is the engine: it holds a display canvas and
75
+ * paints every frame that earns the screen. "frames" is the host: the engine
76
+ * holds no canvas, paints nothing, and hands each of those frames out as a
77
+ * VideoFrame instead, so an external compositor can own the only canvas.
78
+ */
79
+ export type PresentationMode = "canvas" | "frames";
80
+ /**
81
+ * Coarse-grained engine status. Updates rarely (load, play, pause, end,
82
+ * error). Sits on its own emit channel so subscribers do not wake up on
83
+ * 60Hz time ticks.
84
+ */
85
+ export declare enum PlaybackStatus {
86
+ Idle = "IDLE",
87
+ Loading = "LOADING",
88
+ Ready = "READY",
89
+ Playing = "PLAYING",
90
+ Paused = "PAUSED",
91
+ Seeking = "SEEKING",
92
+ Ended = "ENDED",
93
+ Errored = "ERRORED"
94
+ }
95
+ export declare enum WebVideoEngineErrorCode {
96
+ DecodeUnsupported = "DECODE_UNSUPPORTED",
97
+ SourceUnreadable = "SOURCE_UNREADABLE",
98
+ /**
99
+ * The demuxer refused the file outright: its container is not one this build
100
+ * reads, so no track was ever listed and no decoder was ever asked.
101
+ */
102
+ ContainerUnreadable = "CONTAINER_UNREADABLE",
103
+ /**
104
+ * The container opened and the demuxer parsed no track at all out of it. The
105
+ * file's streams are in formats it does not carry, so their video cannot be
106
+ * reached even though it is there.
107
+ */
108
+ VideoTrackUnreadable = "VIDEO_TRACK_UNREADABLE",
109
+ /**
110
+ * The container opened, its tracks listed, and none of them is video. This
111
+ * is the only case where the file itself is what lacks video.
112
+ */
113
+ NoVideoTrack = "NO_VIDEO_TRACK",
114
+ BackendCrashed = "BACKEND_CRASHED",
115
+ Aborted = "ABORTED",
116
+ /** A canvas was offered to an engine loaded in "frames" presentation mode,
117
+ * where the host owns the only canvas. */
118
+ PresentationMismatch = "PRESENTATION_MISMATCH",
119
+ /**
120
+ * The decoder cannot decode this source at all: it refused to configure, it
121
+ * errored, or it acknowledged decode requests and never produced a frame.
122
+ * Distinct from BackendCrashed; this one survives every rebuild, so the
123
+ * runtime stops rebuilding and says so. The usual cause is outside the page:
124
+ * another tab holding every hardware decoder session the machine has.
125
+ */
126
+ DecoderStalled = "DECODER_STALLED",
127
+ /** A playback rate outside the forward range the engine supports. */
128
+ RateUnsupported = "RATE_UNSUPPORTED"
129
+ }
130
+ /**
131
+ * Thrown by createScrubCursor / WebVideoEngine.load when decode is unsupported
132
+ * or the source is unreadable. Branch on `error.code` (WebVideoEngineErrorCode)
133
+ * to differentiate decode failures from network failures.
134
+ */
135
+ export declare class WebVideoEngineError extends Error {
136
+ readonly code: WebVideoEngineErrorCode;
137
+ readonly cause?: unknown;
138
+ constructor(code: WebVideoEngineErrorCode, message: string, cause?: unknown);
139
+ }
140
+ /** Shared by the facade and the core so a host gets the same refusal wherever
141
+ * its canvas is caught. */
142
+ export declare function canvasBindingRefused(): WebVideoEngineError;
143
+ /**
144
+ * Validates a requested playback rate and returns it. The facade and the core
145
+ * both call it, so a rate is refused at whichever boundary it arrives at and
146
+ * with the same message: the facade posts fire-and-forget, so a refusal raised
147
+ * only in the worker would reach nobody.
148
+ *
149
+ * Reverse is rejected, not clamped: backwards playback is a different decode
150
+ * problem, and a -1 quietly serviced as +0.25 plays the wrong direction while
151
+ * reporting success.
152
+ */
153
+ export declare function resolvePlaybackRate(rate: number): number;
154
+ export interface PlaybackState {
155
+ status: PlaybackStatus;
156
+ error: WebVideoEngineError | null;
157
+ }
158
+ export interface VideoMetadata {
159
+ durationMs: number;
160
+ nativeFps: Fps | null;
161
+ naturalWidth: number;
162
+ naturalHeight: number;
163
+ /** Media time of the first sample, ms. Non-zero on trimmed/offset sources,
164
+ * where an annotation timeline that assumes 0 drifts by exactly this. */
165
+ firstTimestampMs: number;
166
+ codec: string | null;
167
+ canDecode: boolean;
168
+ }
169
+ /**
170
+ * Snapshot delivered through the `onReady` lifecycle callback exactly once
171
+ * per loaded source after metadata resolves.
172
+ */
173
+ export interface EngineReadySnapshot extends VideoMetadata {
174
+ /**
175
+ * Every real frame of the source, by its container tick timestamp. A host
176
+ * holding this can name any position the engine publishes, and can resolve
177
+ * one of its own pointer positions to a frame without asking.
178
+ */
179
+ readonly timeline: FrameTimelineData;
180
+ /**
181
+ * The kind of reader the demuxer was opened over, recorded once the open
182
+ * succeeded. A host knows what it asked for; only the engine knows what it
183
+ * got, and the three kinds differ in what they can do afterwards — a stream
184
+ * cannot be rewound or reopened, and only a URL is fetched.
185
+ */
186
+ readonly byteSource?: SourceKind;
187
+ }
188
+ /**
189
+ * Channels the engine store emits on. Pick the channel matching the slice
190
+ * cadence so consumers do not wake up on unrelated mutations.
191
+ *
192
+ * - time: emits per paint (raw ms; bucketing lives in hook selectors).
193
+ * - frame: emits per settled cursor frame (frame-index changes).
194
+ * - state: coarse status transitions (load, play, pause, end, error).
195
+ * - duration: once per loaded source.
196
+ * - seeking: cursor scrub-in-flight transitions (opt-in indicator).
197
+ * - rate: playback-rate changes. Rare, and on its own channel so a rate
198
+ * readout does not wake on status and vice versa.
199
+ */
200
+ export type EngineChannel = "time" | "frame" | "state" | "duration" | "seeking" | "rate";
@@ -0,0 +1,437 @@
1
+ import type { DecodeResolutionStrategy } from "./decode-resolution";
2
+ import type { DiagnosticsSnapshot, EngineDiagnostics } from "./diagnostics";
3
+ import { type FrameId, type FrameLanding } from "./frame-timeline";
4
+ import type { SeekIntent } from "./scrub-cursor";
5
+ import type { EngineTrace } from "./trace-recorder";
6
+ import { type EngineChannel, type EngineReadySnapshot, type PaintSeq, type PlaybackState, PlaybackStatus, type PresentationMode, type UrlSourceReadConfig, type VideoSource } from "./types";
7
+ import { type EngineCommand, type EngineEvent, type PresentedFrame, type SourceResidencyConfig } from "./worker-protocol";
8
+ /**
9
+ * Where a caller wants the playhead: a frame of the source, or a position in
10
+ * milliseconds. A pointer only ever has the second kind; anything the engine
11
+ * published carries the first, and handing that back needs no conversion.
12
+ */
13
+ export type SeekTarget = number | FrameId;
14
+ export interface WebVideoEngineOptions {
15
+ source: VideoSource;
16
+ /**
17
+ * Creates the worker that hosts decode and presentation. Omit it to use the
18
+ * embedded Blob worker. A host whose Content Security Policy disallows
19
+ * `blob:` workers can return a Worker created from the separately exported
20
+ * `supervision/web-video-engine/worker` URL instead.
21
+ */
22
+ workerFactory?: () => Worker;
23
+ /**
24
+ * Who owns the pixels, fixed for the life of the engine. Default "canvas":
25
+ * the engine holds the display canvas bindCanvas transfers to it and paints
26
+ * every frame that earns the screen. Under "frames" it holds no canvas,
27
+ * paints nothing, and hands those frames to onPresentedFrame instead, for a
28
+ * host compositor to draw; bindCanvas then throws.
29
+ */
30
+ presentation?: PresentationMode;
31
+ /**
32
+ * Cache strategy for instant scrub feedback. Default "tiered" keeps a
33
+ * downscaled preview history plus a RAM-bounded full-resolution tier;
34
+ * "none" disables caching (the cursor decodes every seek from scratch).
35
+ */
36
+ cacheStrategy?: "tiered" | "none";
37
+ /** Preview-tier capacity (frames). Ignored when cacheStrategy is "none". */
38
+ previewCapacity?: number;
39
+ /** Preview-tier entry width in CSS pixels. Ignored when cacheStrategy is "none". */
40
+ previewWidth?: number;
41
+ /**
42
+ * Cache lookups whose nearest hit lies within this many milliseconds of
43
+ * what the canvas already shows are rejected, forcing a full-res decode.
44
+ * See constants.FRAME_CACHE.SKIP_NEAR_MS for the default and tuning notes.
45
+ */
46
+ cacheSkipNearMs?: number;
47
+ /**
48
+ * Decides the resolution preview frames decode to. Defaults to native, so a
49
+ * consumer that says nothing keeps full source resolution and pays for it in
50
+ * paint work and frame-cache slots. Governs the live preview only; never the
51
+ * timestamps a consumer extracts.
52
+ *
53
+ * Which strategy fits follows from `presentation`. Under "canvas" the engine
54
+ * measures the box it was handed, so viewportResolution() reads it. Under
55
+ * "frames" nothing binds a canvas and there is no box to read, so
56
+ * viewportResolution() resolves to native there and displayBoxResolution()
57
+ * is how that consumer states the size it composites into.
58
+ */
59
+ decodeStrategy?: DecodeResolutionStrategy;
60
+ /**
61
+ * Pin the 2D renderer instead of WebGPU. WebGPU is the default; leaving this
62
+ * unset prefers it and falls back to the 2D renderer only when WebGPU is
63
+ * unavailable, so unset is not a guarantee of WebGPU. Both renderers paint
64
+ * the same already-decoded frames on the same cadence (the render loop draws
65
+ * a frame only when a new one is decoded). The 2D path is one canvas blit
66
+ * per frame, not a re-decode and not a per-tick CPU repaint; the two differ
67
+ * only in where a frame is composited, the GPU versus a 2D context.
68
+ */
69
+ prefer2d?: boolean;
70
+ /**
71
+ * Hold the source's bytes in this process and serve the demuxer's reads from
72
+ * them, so a position read once is read locally ever after. Off by default:
73
+ * it spends memory the host has to be willing to spend, and prefetching
74
+ * spends the viewer's link on bytes they may never watch.
75
+ *
76
+ * Only a `SourceKind.Url` source can use this. A Blob source is already local
77
+ * and a Stream source is consumed once, so neither has anything to hold.
78
+ */
79
+ sourceResidency?: SourceResidencyConfig;
80
+ /**
81
+ * Read tuning handed to mediabunny for a `SourceKind.Url` source: how many
82
+ * range requests it may run at once, and how many bytes its own reader keeps.
83
+ * Nothing here is read for a Blob or Stream source.
84
+ */
85
+ urlSource?: UrlSourceReadConfig;
86
+ }
87
+ /**
88
+ * The slice of a Worker this facade depends on. A real Worker is structurally
89
+ * assignable, so production passes one through createEngineWorker; tests
90
+ * pass a fake port that hosts an EngineCore in-process.
91
+ */
92
+ export interface EngineWorkerPort {
93
+ postMessage(message: EngineCommand, transfer: Transferable[]): void;
94
+ addEventListener(type: "message", listener: (event: MessageEvent<EngineEvent>) => void): void;
95
+ terminate(): void;
96
+ }
97
+ /**
98
+ * Main-thread facade for the worker-hosted web video engine. Owns the worker
99
+ * (spawned lazily on the first command), the mirror store React reads through
100
+ * useSyncExternalStore, and the imperative handle. The decode + render loop runs
101
+ * in the worker (EngineCore); this class never touches a cursor or clock directly.
102
+ *
103
+ * Three planes cross the worker boundary (see workerProtocol): commands go out,
104
+ * broadcast state comes back as MirrorEvents fed into the store, and the display
105
+ * canvas is transferred once on bindCanvas. Awaitable commands carry a requestId
106
+ * the worker echoes back so each call settles its own promise.
107
+ *
108
+ * Time has two owners, gated by playback. While playing, the worker emits time;
109
+ * while paused, the main thread owns the position: scrub/commit/step write it
110
+ * optimistically so a late worker paint never yanks it back. Consumers render the
111
+ * playhead by reading getTimeMs/getDurationMs at their own cadence.
112
+ *
113
+ * Engine outlives a single React render. Recreate the engine only when the
114
+ * source identity changes.
115
+ */
116
+ export declare class WebVideoEngine {
117
+ private readonly options;
118
+ private readonly store;
119
+ private readonly diagnosticsStore;
120
+ private readonly createWorker;
121
+ private port;
122
+ private disposed;
123
+ private lastTrace;
124
+ private traceArmed;
125
+ private readonly pending;
126
+ private nextRequestId;
127
+ private metadata;
128
+ /** The loaded source's frame table, held here so a gesture is resolved to a
129
+ * frame in the tick it arrives, with no worker round trip. */
130
+ private timeline;
131
+ private transferredCanvas;
132
+ private presentedFrameHandler;
133
+ /** Deliveries counted toward the one frame-ownership check, frozen past it,
134
+ * and the frame that check reads. Weak, so watching costs the frame no
135
+ * lifetime: a host that closes and drops it leaves nothing to find, which is
136
+ * itself the answer. */
137
+ private presentedDeliveries;
138
+ private watchedFrame;
139
+ private cachedHandle;
140
+ /** WebGPU support is a main-thread fact the worker cannot cheaply probe, so
141
+ * the facade fills it onto each diagnostics snapshot for the warning rules. */
142
+ private readonly webgpuAvailable;
143
+ constructor(options: WebVideoEngineOptions, createWorker?: () => EngineWorkerPort);
144
+ /**
145
+ * Loads the configured source. A stream source is transferred on this call,
146
+ * so a repeat load has nothing left to hand over and rejects.
147
+ */
148
+ load(): Promise<EngineReadySnapshot>;
149
+ /**
150
+ * Transfers the display canvas to the worker exactly once. The transfer is
151
+ * permanent (it neuters the element's 2D/GPU context), so a repeat call with
152
+ * the same element, or a null detach, is a no-op; the binding lives until
153
+ * dispose terminates the worker. The viewport box is measured here, where
154
+ * layout lives, and shipped so a viewport-aware decode strategy can size the
155
+ * sink worker-side.
156
+ */
157
+ bindCanvas: (el: HTMLCanvasElement | null) => void;
158
+ play: () => Promise<void>;
159
+ pause: () => void;
160
+ togglePlayback: () => void;
161
+ /**
162
+ * Forward playback speed, in media seconds per wall second. Takes effect at
163
+ * once while playing and on the next play while paused; either way it
164
+ * survives pause, seek, interactive-seek, and replay from the end, because
165
+ * it is the clock's slope and none of those touch it.
166
+ *
167
+ * Throws synchronously on a rate outside the supported forward range,
168
+ * including any reverse rate. The refusal has to happen here: the command is
169
+ * fire-and-forget, so a worker-side throw would reach no caller.
170
+ */
171
+ setPlaybackRate: (rate: number) => void;
172
+ getPlaybackRate(): number;
173
+ /**
174
+ * Fire-and-forget seek: latest-wins, does not await idle. A pointer position
175
+ * is not a frame time and never can be, so it is resolved to the frame
176
+ * covering it before it reaches the store or the worker; the table lives on
177
+ * this thread, so the playhead lands on a frame in the same tick the gesture
178
+ * arrives, with no round trip.
179
+ *
180
+ * intent steers what the cache prepares next. Ignored when cacheStrategy is
181
+ * "none": that backend has no access modes to switch and no prefetch to aim.
182
+ */
183
+ scrub: (target: SeekTarget, intent?: SeekIntent) => void;
184
+ /**
185
+ * Pause-during-drag entry point. Consumers that handle pointer-driven
186
+ * scrub gestures call this on pointerdown so playback freezes and the
187
+ * decoder is not continuously chasing the cursor; the corresponding
188
+ * endInteractiveSeek call on pointerup resumes play if the engine was
189
+ * playing when the drag started. Matches the way native <video> pauses
190
+ * during the scrub-bar drag and resumes on release.
191
+ */
192
+ beginInteractiveSeek: () => void;
193
+ /**
194
+ * Pointerup counterpart to beginInteractiveSeek. Resumes play only if the
195
+ * engine was playing when the drag started; an idle-to-idle drag leaves the
196
+ * engine paused. Resolves once the worker has applied the release.
197
+ */
198
+ endInteractiveSeek: () => Promise<void>;
199
+ /**
200
+ * Awaited seek. Resolves after the cursor settles. Use on pointer-up so
201
+ * downstream logic (export, telemetry, marker placement) sees the
202
+ * settled frame.
203
+ */
204
+ commit: (target: SeekTarget) => Promise<void>;
205
+ seekToKey: (target: SeekTarget) => Promise<void>;
206
+ /**
207
+ * Moves one frame along the source in presentation order. Which frame that
208
+ * is depends on where the walk actually settled, which only the worker
209
+ * knows, so the landing rides back on the ack and is written here, keeping
210
+ * the paused playhead in step with the freshly painted frame.
211
+ */
212
+ private stepChain;
213
+ /**
214
+ * Serialized so concurrent callers (rapid key-repeat, mashed buttons, a
215
+ * programmatic loop) never run overlapping steps that race the cursor's
216
+ * one-shot decode iterator and wedge it. Each call chains after the previous,
217
+ * so every invocation advances exactly one frame from the settled position,
218
+ * on any surface.
219
+ */
220
+ step: (direction: 1 | -1) => Promise<void>;
221
+ private runStep;
222
+ /**
223
+ * Settled iff the engine is between operations. Derived from mirror state
224
+ * rather than the cursor (which lives in the worker): Idle/Loading read as
225
+ * settled, otherwise a live seek is the only thing that unsettles it.
226
+ */
227
+ isIdle(): boolean;
228
+ getSeeking(): boolean;
229
+ /** Where the transport has settled: a frame of the source, never a request
230
+ * and never a clock reading. */
231
+ getPlayhead(): FrameLanding;
232
+ getTimeMs(): number;
233
+ getDurationMs(): number;
234
+ getStatus(): PlaybackStatus;
235
+ getPaintSeq(): PaintSeq;
236
+ getMetadata(): EngineReadySnapshot | null;
237
+ /**
238
+ * Cache hit-rate, scrub-decode latency, and access mode from the worker, or
239
+ * null on the uncached cursor. A round-trip per call; for diagnostics, not
240
+ * the hot path.
241
+ */
242
+ getStats(): Promise<EngineDiagnostics | null>;
243
+ /**
244
+ * Opt-in diagnostics broadcast. Starts the worker's BROADCAST_HZ timer and
245
+ * flips on the per-rAF counters; snapshots arrive on the diag plane and land
246
+ * in the DiagnosticsStore. The instrument calls this on mount and
247
+ * stopDiagnostics on unmount, so a closed panel costs the engine nothing.
248
+ */
249
+ startDiagnostics: (hz?: number) => void;
250
+ stopDiagnostics: () => void;
251
+ /** Arm the worker trace rings. Fire-and-forget; disarmTrace frees them. */
252
+ armTrace: (windowMs: number) => void;
253
+ disarmTrace: () => void;
254
+ /** The capture rescued from a dispose that landed mid-recording, or null. */
255
+ getLastTrace: () => EngineTrace | null;
256
+ /** Awaitable: assembles the worker trace and returns it for download. Null
257
+ * when nothing was armed. */
258
+ exportTrace: () => Promise<EngineTrace | null>;
259
+ /**
260
+ * Registers THE consumer of presented frames, in "frames" presentation mode.
261
+ * A second registration replaces the first, so at most one holder exists.
262
+ * After synchronously drawing the frame and its matching layers, the handler
263
+ * calls acknowledgePresentation(), then close(). A discarded frame is only
264
+ * closed. One left open pins a decoder buffer and stalls the decoder.
265
+ *
266
+ * There is deliberately no counterpart that reports the frame most recently
267
+ * presented. A frame readable apart from the message that carried it can be
268
+ * read at a moment when a newer one has already been handed out, which is
269
+ * the desync this plane is shaped to make impossible.
270
+ */
271
+ onPresentedFrame: (handler: PresentedFrameHandler) => void;
272
+ /** Subscribe to diagnostics pushes; fires on every diag broadcast. Separate
273
+ * from the playback subscribe channels so a diagnostics consumer never wakes
274
+ * on playback state and vice versa. */
275
+ subscribeDiagnostics: (listener: () => void) => (() => void);
276
+ /** Latest broadcast snapshot, or null before the first push. */
277
+ getLatestDiagnostics: () => DiagnosticsSnapshot | null;
278
+ subscribe(channel: EngineChannel, listener: () => void): () => void;
279
+ /**
280
+ * Tears the engine down: drains pending requests so an in-flight load/commit
281
+ * rejects rather than hanging, and terminates the worker. The dispose ack is
282
+ * awaited first so the worker closes its cursor cleanly before the realm is
283
+ * killed.
284
+ *
285
+ * The engine is inert afterwards. A fire-and-forget command is dropped and an
286
+ * awaitable one rejects with {@link WebVideoEngineErrorCode.Aborted}; neither
287
+ * starts another worker.
288
+ */
289
+ dispose(): Promise<void>;
290
+ /**
291
+ * Curated subset exposed to React via useImperativeHandle. Cached so the
292
+ * handle keeps a stable identity for the lifetime of the engine (consumers
293
+ * may list it in useEffect deps).
294
+ */
295
+ toHandle(): WebVideoEngineHandle;
296
+ private readonly isIdleBound;
297
+ private readonly getPlayheadBound;
298
+ private readonly getTimeMsBound;
299
+ private readonly getDurationMsBound;
300
+ private readonly getPaintSeqBound;
301
+ private readonly getMetadataBound;
302
+ private readonly getStatsBound;
303
+ private readonly getPlaybackStateBound;
304
+ private readonly getSeekingBound;
305
+ private readonly getPlaybackRateBound;
306
+ private readonly subscribeBound;
307
+ private readonly onWorkerMessage;
308
+ /**
309
+ * Hands one frame to the registered consumer, which owns it from here. With
310
+ * nobody registered it is closed at once rather than dropped on the floor:
311
+ * the worker has already let go of it, so an unclaimed frame is a leak that
312
+ * pins a decoder buffer.
313
+ */
314
+ private deliverPresentedFrame;
315
+ /**
316
+ * Says out loud that this host does not close the frames it is handed.
317
+ * Nothing else in the runtime can: the frame left the worker on the transfer
318
+ * list, so no engine-side counter ever sees it again, and the decoder it
319
+ * starves reports the damage as a hung decode instead.
320
+ *
321
+ * One frame is watched and read once, so the check costs a host that closes a
322
+ * single allocation for the whole session and every later delivery one
323
+ * predicted-false branch.
324
+ */
325
+ private checkFrameOwnership;
326
+ private settle;
327
+ /**
328
+ * Spawns the worker on first use and wires the state plane into the mirror
329
+ * store. Deferring the spawn keeps construction side-effect-free, so a
330
+ * WebVideoEngine built during render (e.g. a useState initializer) never leaks
331
+ * a worker when React discards a duplicate.
332
+ */
333
+ private ensurePort;
334
+ private request;
335
+ private post;
336
+ private rejectAllPending;
337
+ /**
338
+ * The frame a target names.
339
+ *
340
+ * A frame names itself. A millisecond position is narrowed in seconds and
341
+ * then settled against `timeAt(i) * 1000`, the expression getTimeMs
342
+ * publishes: dividing a published millisecond back into seconds re-rounds
343
+ * it, and on tick rates like NTSC's 24000 the quotient can land just under
344
+ * the frame's own second, one frame early.
345
+ */
346
+ private snap;
347
+ private writePlayheadAt;
348
+ private writePlayhead;
349
+ private requireTimeline;
350
+ private measureViewport;
351
+ }
352
+ /** Receives one presented frame and owns it. After drawing its pixels and
353
+ * matching layers, call acknowledgePresentation(), then close(). If it is
354
+ * discarded before display, only close it. */
355
+ export type PresentedFrameHandler = (presented: PresentedFrame) => void;
356
+ /**
357
+ * Imperative handle exposed to React via useImperativeHandle. Stable identity
358
+ * for the lifetime of one source, so consumers may list it in effect deps.
359
+ *
360
+ * Curated subset on purpose: coarse status + error are reachable via the
361
+ * PlaybackStateContext or useVideoEngineState hook, NOT through the handle, so a
362
+ * consumer that latches onto the handle does not also depend on the shape of
363
+ * getPlaybackState.
364
+ */
365
+ export interface WebVideoEngineHandle {
366
+ play(): Promise<void>;
367
+ pause(): void;
368
+ togglePlayback(): void;
369
+ /**
370
+ * Forward playback speed in media seconds per wall second, within the range
371
+ * PLAYBACK_RATE bounds. Throws synchronously outside it, reverse included.
372
+ * Subscribe on the "rate" channel to follow it.
373
+ */
374
+ setPlaybackRate(rate: number): void;
375
+ getPlaybackRate(): number;
376
+ scrub(target: SeekTarget, intent?: SeekIntent): void;
377
+ commit(target: SeekTarget): Promise<void>;
378
+ seekToKey(target: SeekTarget): Promise<void>;
379
+ step(direction: 1 | -1): Promise<void>;
380
+ /**
381
+ * Pointerdown of a drag-based scrub. Pauses if the engine was playing
382
+ * and remembers to resume on endInteractiveSeek. Idempotent inside a
383
+ * drag (re-entries while the resume flag is armed are no-ops, so the
384
+ * original "was playing" state survives).
385
+ */
386
+ beginInteractiveSeek(): void;
387
+ /** Pointerup of a drag-based scrub. Resumes play iff begin paused us. */
388
+ endInteractiveSeek(): Promise<void>;
389
+ isIdle(): boolean;
390
+ /** Where the transport has settled, as a frame of the source. */
391
+ getPlayhead(): FrameLanding;
392
+ /** The same position on the whole-millisecond plane, for a host that still
393
+ * speaks it. Handing this value back to scrub, commit or seekToKey lands on
394
+ * the frame it came from, on every source. */
395
+ getTimeMs(): number;
396
+ getDurationMs(): number;
397
+ getPaintSeq(): PaintSeq;
398
+ getMetadata(): EngineReadySnapshot | null;
399
+ /** Worker-side runtime stats (cache hit-rate, scrub latency, access mode)
400
+ * for diagnostics; null on the uncached cursor. A round-trip per call. */
401
+ getStats(): Promise<EngineDiagnostics | null>;
402
+ getPlaybackState(): PlaybackState;
403
+ /**
404
+ * True while the cursor is mid-scrub. Cache-hit scrubs do not flip this
405
+ * because the cache paint is synchronous; only real cursor decode walks
406
+ * trip it. Subscribed via the "seeking" channel.
407
+ */
408
+ getSeeking(): boolean;
409
+ bindCanvas(el: HTMLCanvasElement | null): void;
410
+ /**
411
+ * Registers the single consumer of presented frames ("frames" presentation
412
+ * mode). Registering again replaces the previous consumer. The handler owns
413
+ * each frame; a displayed frame is acknowledged before it is closed, while a
414
+ * discarded frame is only closed.
415
+ */
416
+ onPresentedFrame(handler: PresentedFrameHandler): void;
417
+ subscribe(channel: EngineChannel, listener: () => void): () => void;
418
+ /**
419
+ * Opt-in diagnostics broadcast control. The instrument starts on mount and
420
+ * stops on unmount (and on visibilitychange), so the worker pays nothing when
421
+ * no panel listens. Snapshots arrive via subscribeDiagnostics; the playback
422
+ * channels are untouched.
423
+ */
424
+ startDiagnostics(hz?: number): void;
425
+ stopDiagnostics(): void;
426
+ /** Arm/disarm the worker trace rings (fire-and-forget). */
427
+ armTrace(windowMs: number): void;
428
+ disarmTrace(): void;
429
+ /** Assemble and return the worker trace for download; null when not armed. */
430
+ exportTrace(): Promise<EngineTrace | null>;
431
+ /** The capture rescued from a dispose that landed mid-recording, or null. */
432
+ getLastTrace(): EngineTrace | null;
433
+ /** Subscribe to diagnostics pushes. Separate from the playback channels. */
434
+ subscribeDiagnostics(listener: () => void): () => void;
435
+ /** Latest broadcast snapshot, or null before the first push. */
436
+ getLatestDiagnostics(): DiagnosticsSnapshot | null;
437
+ }