supervision 0.2.0-next.0 → 0.2.0-next.2

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 (192) 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/index.d.ts +5 -5
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +1722 -1395
  6. package/dist/index.js.map +1 -1
  7. package/dist/mask-preparation.worker.js +109 -31
  8. package/dist/mask-preparation.worker.js.map +1 -1
  9. package/dist/media/media-frame-clock.d.ts +4 -0
  10. package/dist/media/media-frame-clock.d.ts.map +1 -0
  11. package/dist/media/media-source-state.d.ts.map +1 -1
  12. package/dist/media/media-source.d.ts +6 -0
  13. package/dist/media/media-source.d.ts.map +1 -1
  14. package/dist/media/video-engine-media-source.d.ts +17 -7
  15. package/dist/media/video-engine-media-source.d.ts.map +1 -1
  16. package/dist/media/video-engine-media-source.js +3 -0
  17. package/dist/media/video-engine-media-source.js.map +1 -0
  18. package/dist/playback/media-playback-controller.d.ts +7 -1
  19. package/dist/playback/media-playback-controller.d.ts.map +1 -1
  20. package/dist/render-preparation/mask-frame-artifact.d.ts +2 -0
  21. package/dist/render-preparation/mask-frame-artifact.d.ts.map +1 -1
  22. package/dist/render-preparation/mask-frame-compositor.d.ts +7 -0
  23. package/dist/render-preparation/mask-frame-compositor.d.ts.map +1 -1
  24. package/dist/render-preparation/mask-preparation-worker-protocol.d.ts +1 -0
  25. package/dist/render-preparation/mask-preparation-worker-protocol.d.ts.map +1 -1
  26. package/dist/render-preparation/prepared-render-window.d.ts +21 -2
  27. package/dist/render-preparation/prepared-render-window.d.ts.map +1 -1
  28. package/dist/render-preparation/prepared-window-timeline.d.ts +1 -0
  29. package/dist/render-preparation/prepared-window-timeline.d.ts.map +1 -1
  30. package/dist/renderers/injected-pixi.d.ts +38 -0
  31. package/dist/renderers/injected-pixi.d.ts.map +1 -0
  32. package/dist/renderers/media-frame-navigation.d.ts +17 -0
  33. package/dist/renderers/media-frame-navigation.d.ts.map +1 -0
  34. package/dist/renderers/media-renderer-core.d.ts +9 -0
  35. package/dist/renderers/media-renderer-core.d.ts.map +1 -1
  36. package/dist/renderers/media-renderer-scene.d.ts +17 -5
  37. package/dist/renderers/media-renderer-scene.d.ts.map +1 -1
  38. package/dist/renderers/media-renderer-state.d.ts +9 -0
  39. package/dist/renderers/media-renderer-state.d.ts.map +1 -1
  40. package/dist/renderers/media-renderer-transport.d.ts +19 -3
  41. package/dist/renderers/media-renderer-transport.d.ts.map +1 -1
  42. package/dist/renderers/pixi-focus-layer.d.ts +1 -0
  43. package/dist/renderers/pixi-focus-layer.d.ts.map +1 -1
  44. package/dist/renderers/pixi-frame-present.d.ts +1 -1
  45. package/dist/renderers/pixi-frame-present.d.ts.map +1 -1
  46. package/dist/renderers/pixi-id-mask-shader.d.ts +4 -33
  47. package/dist/renderers/pixi-id-mask-shader.d.ts.map +1 -1
  48. package/dist/renderers/pixi-interaction-presentation-layer.d.ts +0 -1
  49. package/dist/renderers/pixi-interaction-presentation-layer.d.ts.map +1 -1
  50. package/dist/renderers/pixi-mask-halo.d.ts +4 -33
  51. package/dist/renderers/pixi-mask-halo.d.ts.map +1 -1
  52. package/dist/renderers/pixi-mask-layer.d.ts +28 -29
  53. package/dist/renderers/pixi-mask-layer.d.ts.map +1 -1
  54. package/dist/renderers/pixi-media-scene.d.ts.map +1 -1
  55. package/dist/renderers/pixi-polygon-layer.d.ts +2 -0
  56. package/dist/renderers/pixi-polygon-layer.d.ts.map +1 -1
  57. package/dist/renderers/pixi-region-coverage-mask.d.ts +4 -33
  58. package/dist/renderers/pixi-region-coverage-mask.d.ts.map +1 -1
  59. package/dist/renderers/pixi-region-layer.d.ts +6 -31
  60. package/dist/renderers/pixi-region-layer.d.ts.map +1 -1
  61. package/dist/renderers/pixi-shader-lifecycle.d.ts +14 -0
  62. package/dist/renderers/pixi-shader-lifecycle.d.ts.map +1 -0
  63. package/dist/renderers/prepared-annotation-window.d.ts.map +1 -1
  64. package/dist/renderers/presented-frame-channel.d.ts +34 -1
  65. package/dist/renderers/presented-frame-channel.d.ts.map +1 -1
  66. package/dist/sessions/media-session-defaults.d.ts.map +1 -1
  67. package/dist/sessions/media-session-detections.d.ts.map +1 -1
  68. package/dist/sessions/media-session-state.d.ts.map +1 -1
  69. package/dist/sessions/media-session.d.ts.map +1 -1
  70. package/dist/tracking.worker.js +19 -5
  71. package/dist/tracking.worker.js.map +1 -1
  72. package/dist/types/media-frame-clock.d.ts +16 -0
  73. package/dist/types/media-frame-clock.d.ts.map +1 -0
  74. package/dist/types/media-frame-navigation.d.ts +28 -0
  75. package/dist/types/media-frame-navigation.d.ts.map +1 -0
  76. package/dist/types/media-renderer-display.d.ts +12 -0
  77. package/dist/types/media-renderer-display.d.ts.map +1 -0
  78. package/dist/types/media-renderer.d.ts +13 -0
  79. package/dist/types/media-renderer.d.ts.map +1 -1
  80. package/dist/types/media-session.d.ts +35 -32
  81. package/dist/types/media-session.d.ts.map +1 -1
  82. package/dist/types/render-preparation.d.ts +78 -28
  83. package/dist/types/render-preparation.d.ts.map +1 -1
  84. package/dist/video-engine-media-source-DR-x1UfK.js +483 -0
  85. package/dist/video-engine-media-source-DR-x1UfK.js.map +1 -0
  86. package/dist/web-video-engine/analysis-session.d.ts +0 -1
  87. package/dist/web-video-engine/analysis.d.ts +0 -1
  88. package/dist/web-video-engine/analysis.js +211 -23
  89. package/dist/web-video-engine/cache-budget.d.ts +0 -1
  90. package/dist/web-video-engine/canvas-sink-scrub-cursor.d.ts +6 -2
  91. package/dist/web-video-engine/clock.d.ts +0 -1
  92. package/dist/web-video-engine/constants.d.ts +9 -1
  93. package/dist/web-video-engine/create-scrub-cursor.d.ts +6 -3
  94. package/dist/web-video-engine/decode-resolution.d.ts +0 -1
  95. package/dist/web-video-engine/decode-scheduler.d.ts +26 -5
  96. package/dist/web-video-engine/decode-session.d.ts +11 -1
  97. package/dist/web-video-engine/decode-source.d.ts +11 -1
  98. package/dist/web-video-engine/diagnostics-store.d.ts +0 -1
  99. package/dist/web-video-engine/diagnostics.d.ts +19 -19
  100. package/dist/web-video-engine/embedded-engine-worker.d.ts +0 -1
  101. package/dist/web-video-engine/engine-core.d.ts +31 -11
  102. package/dist/web-video-engine/engine.d.ts +3 -4
  103. package/dist/web-video-engine/engine.js +175 -44
  104. package/dist/web-video-engine/engine.worker.d.ts +0 -1
  105. package/dist/web-video-engine/engine.worker.js +5712 -2947
  106. package/dist/web-video-engine/frame-cache.d.ts +121 -12
  107. package/dist/web-video-engine/frame-extractor.d.ts +0 -1
  108. package/dist/web-video-engine/{frame-timeline-GINI8Gum.js → frame-timeline-DreEwsRY.js} +83 -27
  109. package/dist/web-video-engine/frame-timeline.d.ts +10 -1
  110. package/dist/web-video-engine/frame-walker.d.ts +0 -1
  111. package/dist/web-video-engine/index.d.ts +12 -2
  112. package/dist/web-video-engine/index.d.ts.map +1 -1
  113. package/dist/web-video-engine/index.js +2 -2
  114. package/dist/web-video-engine/key-packet.d.ts +0 -1
  115. package/dist/web-video-engine/keyframe-index.d.ts +0 -1
  116. package/dist/web-video-engine/mirror-store.d.ts +0 -1
  117. package/dist/web-video-engine/pausable-deadline.d.ts +11 -0
  118. package/dist/web-video-engine/renderer.d.ts +0 -1
  119. package/dist/web-video-engine/rotation.d.ts +0 -1
  120. package/dist/web-video-engine/scrub-controller.d.ts +8 -5
  121. package/dist/web-video-engine/scrub-cursor.d.ts +15 -7
  122. package/dist/web-video-engine/scrub-trajectory.d.ts +0 -1
  123. package/dist/web-video-engine/source-residency.d.ts +35 -1
  124. package/dist/web-video-engine/trace-recorder.d.ts +0 -1
  125. package/dist/web-video-engine/types.d.ts +18 -22
  126. package/dist/web-video-engine/video-engine.d.ts +39 -17
  127. package/dist/web-video-engine/webgpu-renderer.d.ts +0 -1
  128. package/dist/web-video-engine/worker-bridge.d.ts +0 -1
  129. package/dist/web-video-engine/worker-dispatch.d.ts +0 -1
  130. package/dist/web-video-engine/worker-protocol.d.ts +38 -15
  131. package/node_modules/supervision-js-core/dist/detections/buffered-detection-timeline.d.ts.map +1 -1
  132. package/node_modules/supervision-js-core/dist/index.d.ts +2 -2
  133. package/node_modules/supervision-js-core/dist/index.d.ts.map +1 -1
  134. package/node_modules/supervision-js-core/dist/index.js +182 -68
  135. package/node_modules/supervision-js-core/dist/index.js.map +1 -1
  136. package/node_modules/supervision-js-core/dist/types/detection-timeline.d.ts +20 -3
  137. package/node_modules/supervision-js-core/dist/types/detection-timeline.d.ts.map +1 -1
  138. package/node_modules/supervision-js-core/dist/types/media-rendering.d.ts +28 -26
  139. package/node_modules/supervision-js-core/dist/types/media-rendering.d.ts.map +1 -1
  140. package/node_modules/supervision-js-core/dist/types/session-lifecycle.d.ts +16 -0
  141. package/node_modules/supervision-js-core/dist/types/session-lifecycle.d.ts.map +1 -1
  142. package/node_modules/supervision-js-core/dist/utils/detection-masks.d.ts.map +1 -1
  143. package/node_modules/supervision-js-core/dist/utils/id-mask-frame.d.ts +19 -0
  144. package/node_modules/supervision-js-core/dist/utils/id-mask-frame.d.ts.map +1 -1
  145. package/package.json +3 -3
  146. package/dist/media/media-condition-probe.d.ts +0 -51
  147. package/dist/media/media-condition-probe.d.ts.map +0 -1
  148. package/dist/media/media-conditions.d.ts +0 -28
  149. package/dist/media/media-conditions.d.ts.map +0 -1
  150. package/dist/types/media-conditions.d.ts +0 -149
  151. package/dist/types/media-conditions.d.ts.map +0 -1
  152. package/dist/web-video-engine/analysis-session.d.ts.map +0 -1
  153. package/dist/web-video-engine/analysis.d.ts.map +0 -1
  154. package/dist/web-video-engine/analysis.js.map +0 -1
  155. package/dist/web-video-engine/cache-budget.d.ts.map +0 -1
  156. package/dist/web-video-engine/canvas-sink-scrub-cursor.d.ts.map +0 -1
  157. package/dist/web-video-engine/clock.d.ts.map +0 -1
  158. package/dist/web-video-engine/constants.d.ts.map +0 -1
  159. package/dist/web-video-engine/create-scrub-cursor.d.ts.map +0 -1
  160. package/dist/web-video-engine/decode-resolution.d.ts.map +0 -1
  161. package/dist/web-video-engine/decode-scheduler.d.ts.map +0 -1
  162. package/dist/web-video-engine/decode-session.d.ts.map +0 -1
  163. package/dist/web-video-engine/decode-source.d.ts.map +0 -1
  164. package/dist/web-video-engine/diagnostics-store.d.ts.map +0 -1
  165. package/dist/web-video-engine/diagnostics.d.ts.map +0 -1
  166. package/dist/web-video-engine/embedded-engine-worker.d.ts.map +0 -1
  167. package/dist/web-video-engine/engine-core.d.ts.map +0 -1
  168. package/dist/web-video-engine/engine.d.ts.map +0 -1
  169. package/dist/web-video-engine/engine.js.map +0 -1
  170. package/dist/web-video-engine/engine.worker.d.ts.map +0 -1
  171. package/dist/web-video-engine/engine.worker.js.map +0 -1
  172. package/dist/web-video-engine/frame-cache.d.ts.map +0 -1
  173. package/dist/web-video-engine/frame-extractor.d.ts.map +0 -1
  174. package/dist/web-video-engine/frame-timeline-GINI8Gum.js.map +0 -1
  175. package/dist/web-video-engine/frame-timeline.d.ts.map +0 -1
  176. package/dist/web-video-engine/frame-walker.d.ts.map +0 -1
  177. package/dist/web-video-engine/key-packet.d.ts.map +0 -1
  178. package/dist/web-video-engine/keyframe-index.d.ts.map +0 -1
  179. package/dist/web-video-engine/mirror-store.d.ts.map +0 -1
  180. package/dist/web-video-engine/renderer.d.ts.map +0 -1
  181. package/dist/web-video-engine/rotation.d.ts.map +0 -1
  182. package/dist/web-video-engine/scrub-controller.d.ts.map +0 -1
  183. package/dist/web-video-engine/scrub-cursor.d.ts.map +0 -1
  184. package/dist/web-video-engine/scrub-trajectory.d.ts.map +0 -1
  185. package/dist/web-video-engine/source-residency.d.ts.map +0 -1
  186. package/dist/web-video-engine/trace-recorder.d.ts.map +0 -1
  187. package/dist/web-video-engine/types.d.ts.map +0 -1
  188. package/dist/web-video-engine/video-engine.d.ts.map +0 -1
  189. package/dist/web-video-engine/webgpu-renderer.d.ts.map +0 -1
  190. package/dist/web-video-engine/worker-bridge.d.ts.map +0 -1
  191. package/dist/web-video-engine/worker-dispatch.d.ts.map +0 -1
  192. package/dist/web-video-engine/worker-protocol.d.ts.map +0 -1
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Two-tier frame cache, keyed by millisecond timestamp.
2
+ * Two-tier frame cache.
3
3
  *
4
4
  * The preview tier holds many downscaled frames: a long, coarse history that
5
5
  * answers a scrub instantly while the crisp frame decodes. The exact tier holds
@@ -10,11 +10,18 @@
10
10
  * served it. Only an exact hit is full-resolution; a preview hit still owes the
11
11
  * caller a crisp decode.
12
12
  *
13
+ * The exact tier is keyed by the timeline's identity for the frame, so two
14
+ * source frames can never share a slot at any frame rate. The preview tier is
15
+ * keyed by rounded millisecond, which merges frames spaced under a millisecond;
16
+ * it is the tier whose answer is already declared approximate, and it never
17
+ * claims to be the frame at the target.
18
+ *
13
19
  * Both tiers store OffscreenCanvas blits, never VideoFrames: a raw-frame cache
14
20
  * pins decoder output, stalling the decoder and growing VRAM without bound. A
15
21
  * frame fed as a live VideoSample is drawn into the blit and never retained, so
16
22
  * the no-retention rule holds on the zero-copy path too.
17
23
  */
24
+ import type { FrameId } from "./frame-timeline";
18
25
  import type { VideoSampleLike } from "./scrub-cursor";
19
26
  /**
20
27
  * What the cache can blit into a tier: an already-decoded canvas/image, or a
@@ -45,11 +52,12 @@ export interface FrameCacheStats {
45
52
  readonly exactTimestampsMs: number[];
46
53
  readonly previewTimestampsMs: number[];
47
54
  /** Configured source frame interval: the visual width of each cached mark on
48
- * the timeline. Keys are per-millisecond, so this is a display width and
49
- * not the grid entries land on. */
55
+ * the timeline. Nothing is keyed by it, so this is a display width and not
56
+ * the grid entries land on. */
50
57
  readonly bucketMs: number;
51
- /** Frames dropped to LRU pressure per tier, and puts that landed in an
52
- * occupied bucket (re-decodes collapsed onto one slot). */
58
+ /** Frames dropped to LRU pressure per tier, and puts that overwrote a live
59
+ * key: a frame decoded again on the exact tier, and on the preview tier
60
+ * either that or two frames sharing one rounded millisecond. */
53
61
  readonly exactEvictions: number;
54
62
  readonly previewEvictions: number;
55
63
  readonly bucketCollapses: number;
@@ -72,10 +80,7 @@ export interface FrameCacheOptions {
72
80
  /** Coarse-tier slot count. */
73
81
  readonly previewCapacity: number;
74
82
  /** Source frame interval in ms, reported to diagnostics as the timeline mark
75
- * width. It does not key the tiers: a grid this wide rounds two adjacent
76
- * frames onto one slot wherever the rounded interval exceeds the true one
77
- * (24, 60fps and their pulldown rates), so each frame is keyed by its own
78
- * millisecond instead. */
83
+ * width. It keys nothing. */
79
84
  readonly bucketMs: number;
80
85
  /** Floor on exact-tier slots, applied even when the byte budget would yield
81
86
  * fewer. Lets a caller that owns the prefetch-window width guarantee a full
@@ -84,6 +89,7 @@ export interface FrameCacheOptions {
84
89
  readonly minExactSlots?: number;
85
90
  }
86
91
  export declare class FrameCache {
92
+ private readonly options;
87
93
  private readonly exact;
88
94
  private readonly preview;
89
95
  private exactHits;
@@ -92,10 +98,24 @@ export declare class FrameCache {
92
98
  private readonly bucketMs;
93
99
  private readonly exactBudgetBytes;
94
100
  constructor(options: FrameCacheOptions);
95
- /** Store a crisp full-resolution frame for the exact (scrub) tier. */
96
- putExact(timestampMs: number, src: CacheBlitSource, srcWidth: number, srcHeight: number): void;
101
+ resized(width: number, height: number): FrameCache;
102
+ /**
103
+ * Store a crisp full-resolution frame for the exact (scrub) tier under
104
+ * `frame`, the timeline's name for it. `timestampMs` is the decoded
105
+ * timestamp, which is what lookups match on and what a hit reports back.
106
+ */
107
+ putExact(frame: FrameId, timestampMs: number, src: CacheBlitSource, srcWidth: number, srcHeight: number): void;
97
108
  /** Store a downscaled frame for the coarse preview tier. */
98
109
  putPreview(timestampMs: number, src: CacheBlitSource, srcWidth: number, srcHeight: number): void;
110
+ /**
111
+ * Best cached answer for a named source frame. The exact tier is looked up
112
+ * only by `frame` identity; time is used solely for the approximate preview
113
+ * fallback. This keeps a long variable-rate frame from borrowing a crisp
114
+ * neighbour merely because both timestamps fit a millisecond tolerance.
115
+ */
116
+ getForFrame(frame: FrameId, timestampMs: number, previewTolMs: number): CachedFrame | null;
117
+ /** Non-accounting form of {@link getForFrame}. */
118
+ peekForFrame(frame: FrameId, timestampMs: number, previewTolMs: number): CachedFrame | null;
99
119
  /**
100
120
  * Best cached frame for `timestampMs`, consulting the exact tier then the
101
121
  * preview tier: a crisp hit within `exactTolMs`, else a coarse hit within
@@ -123,7 +143,96 @@ export declare class FrameCache {
123
143
  * from being the LRU victim of its own neighbor sweep. No-op on a miss.
124
144
  */
125
145
  bumpExact(timestampMs: number): void;
146
+ /** Promotes one exact frame by identity. */
147
+ bumpExactFrame(frame: FrameId): void;
126
148
  clear(): void;
127
149
  get stats(): FrameCacheStats;
128
150
  }
129
- //# sourceMappingURL=frame-cache.d.ts.map
151
+ interface TierHit {
152
+ readonly canvas: OffscreenCanvas;
153
+ readonly timestampMs: number;
154
+ }
155
+ /**
156
+ * One cache tier: a fixed-capacity, MRU-ordered set of OffscreenCanvas copies
157
+ * under keys its owner assigns. A put onto a live key overwrites that slot in
158
+ * place, so an owner that lets two frames share a key loses one of them
159
+ * silently; overflow past capacity evicts the least-recently-used entry
160
+ * instead. Lookups match on the stored frame's true timestamp and never on the
161
+ * key, so a cache-served frame carries the same timestamp a fresh decode would.
162
+ */
163
+ export declare class TierStore {
164
+ readonly capacity: number;
165
+ readonly width: number;
166
+ readonly height: number;
167
+ /** Width of the grid the owner's keys round onto, or 0 when a key names a
168
+ * frame outright. Two entries closer together than this are one frame
169
+ * whose two decodes rounded onto either side of a key boundary. */
170
+ private readonly keyGridMs;
171
+ /** MRU-ordered keys. Index 0 is the LRU, the last index is the MRU. */
172
+ private readonly keys;
173
+ private readonly entries;
174
+ /** Canvases released by eviction, held for the next put to fill. */
175
+ private readonly spare;
176
+ private evictionCount;
177
+ private bucketCollapseCount;
178
+ /** Smallest gap between two stored frames, which is the source frame interval
179
+ * once neighbours are resident. Learned from the timestamps the tier is
180
+ * handed, so it is what the source really did rather than a declared rate.
181
+ * Null until two frames have landed. */
182
+ private observedIntervalMs;
183
+ constructor(capacity: number, width: number, height: number,
184
+ /** Width of the grid the owner's keys round onto, or 0 when a key names a
185
+ * frame outright. Two entries closer together than this are one frame
186
+ * whose two decodes rounded onto either side of a key boundary. */
187
+ keyGridMs: number);
188
+ /** Frames dropped to the LRU policy; a cache-pressure signal for diagnostics. */
189
+ get evictions(): number;
190
+ /** Puts that landed on a key already live, collapsing both onto one slot. */
191
+ get bucketCollapses(): number;
192
+ put(key: number, timestampMs: number, src: CacheBlitSource, srcWidth: number, srcHeight: number): void;
193
+ /**
194
+ * Nearest resident frame to `timestampMs` within `tolMs`, or null.
195
+ *
196
+ * `atOrBefore` restricts it to frames at or before the target, which is the
197
+ * contract a decode answers a seek with. Without it the tier can answer with
198
+ * the frame AFTER the target, one no decode would ever return, so the same
199
+ * pointer position paints a different frame depending on whether the cache or
200
+ * the decoder served it, and the picture steps forward and back as the two
201
+ * alternate.
202
+ */
203
+ get(timestampMs: number, tolMs: number, atOrBefore?: boolean): TierHit | null;
204
+ /** Retrieves exactly one owner-assigned key and promotes it to MRU. */
205
+ getByKey(key: number): TierHit | null;
206
+ /** Promotes the entry nearest `timestampMs` to most-recently-used. Scans
207
+ * rather than keys off the timestamp: callers pass a gesture position, which
208
+ * is not a frame timestamp, so a keyed lookup would miss the very frame on
209
+ * screen. No accounting; it is not a lookup. */
210
+ touch(timestampMs: number): void;
211
+ /** Promotes an owner-assigned key without a timestamp scan. */
212
+ touchKey(key: number): void;
213
+ clear(): void;
214
+ get size(): number;
215
+ /** True timestamps (ms) of resident frames, for diagnostics. Bounded by capacity. */
216
+ timestampsSnapshot(): number[];
217
+ private bump;
218
+ /**
219
+ * Best candidate for `timestampMs`, or null when nothing qualifies.
220
+ *
221
+ * Under `atOrBefore` a stored frame stands for the span from its own
222
+ * timestamp up to the next source frame; a target past that span belongs to
223
+ * the next frame, which is what a decode for it returns. Answering it from
224
+ * this tier would paint a frame no decode for that time ever produces, so a
225
+ * candidate outside its own frame's span is not eligible however generous the
226
+ * caller's tolerance is. Until the tier has learned a frame gap it has no
227
+ * spacing to bound with and the caller's tolerance stands alone.
228
+ */
229
+ private nearest;
230
+ /** Narrows the learned frame interval to the smallest gap seen so far. A gap
231
+ * at or below the key grid is one frame that rounded onto two keys rather
232
+ * than two frames, so it is no interval at all; what survives is a gap
233
+ * between two frames, and never zero. It only ever tightens, so a tier that
234
+ * has watched an unrepresentative stretch of a variable-rate source bounds
235
+ * lookups too tightly (a decode) rather than too loosely (the wrong frame). */
236
+ private observeSpacing;
237
+ }
238
+ export {};
@@ -25,4 +25,3 @@ export declare class FrameExtractor {
25
25
  /** Disposes the underlying analysis session. */
26
26
  close(): Promise<void>;
27
27
  }
28
- //# sourceMappingURL=frame-extractor.d.ts.map
@@ -157,6 +157,7 @@ const DIAGNOSTICS = {
157
157
  TRACE_EVENT_CAP: 2000,
158
158
  TRACE_SNAPSHOT_CAP: 600,
159
159
  SCRUB_LATENCY_RING: 64,
160
+ KEYFRAME_TIMESTAMPS_CAP: 512,
160
161
  };
161
162
  /**
162
163
  * What an armed capture actually keeps. The snapshot ring is fed at the fixed
@@ -205,8 +206,9 @@ const TRACE_RING_BOUNDS = {
205
206
  * reply before the facade gives up on it.
206
207
  */
207
208
  const DECODE_HANG_TIMEOUT_MS = 30_000;
209
+ const WORKER_COMMAND_TIMEOUT_MS = DECODE_HANG_TIMEOUT_MS + 15_000;
208
210
  const HANG_RECOVERY = {
209
- WORKER_COMMAND_TIMEOUT_MS: DECODE_HANG_TIMEOUT_MS + 15_000,
211
+ WORKER_COMMAND_TIMEOUT_MS,
210
212
  };
211
213
 
212
214
  const asSec = (n) => n;
@@ -234,49 +236,48 @@ var PlaybackStatus;
234
236
  PlaybackStatus["Ended"] = "ENDED";
235
237
  PlaybackStatus["Errored"] = "ERRORED";
236
238
  })(PlaybackStatus || (PlaybackStatus = {}));
237
- var VideoEngineErrorCode;
238
- (function (VideoEngineErrorCode) {
239
- VideoEngineErrorCode["DecodeUnsupported"] = "DECODE_UNSUPPORTED";
240
- VideoEngineErrorCode["SourceUnreadable"] = "SOURCE_UNREADABLE";
239
+ var WebVideoEngineErrorCode;
240
+ (function (WebVideoEngineErrorCode) {
241
+ WebVideoEngineErrorCode["DecodeUnsupported"] = "DECODE_UNSUPPORTED";
242
+ WebVideoEngineErrorCode["SourceUnreadable"] = "SOURCE_UNREADABLE";
241
243
  /**
242
244
  * The demuxer refused the file outright: its container is not one this build
243
245
  * reads, so no track was ever listed and no decoder was ever asked.
244
246
  */
245
- VideoEngineErrorCode["ContainerUnreadable"] = "CONTAINER_UNREADABLE";
247
+ WebVideoEngineErrorCode["ContainerUnreadable"] = "CONTAINER_UNREADABLE";
246
248
  /**
247
249
  * The container opened and the demuxer parsed no track at all out of it. The
248
250
  * file's streams are in formats it does not carry, so their video cannot be
249
251
  * reached even though it is there.
250
252
  */
251
- VideoEngineErrorCode["VideoTrackUnreadable"] = "VIDEO_TRACK_UNREADABLE";
253
+ WebVideoEngineErrorCode["VideoTrackUnreadable"] = "VIDEO_TRACK_UNREADABLE";
252
254
  /**
253
255
  * The container opened, its tracks listed, and none of them is video. This
254
256
  * is the only case where the file itself is what lacks video.
255
257
  */
256
- VideoEngineErrorCode["NoVideoTrack"] = "NO_VIDEO_TRACK";
257
- VideoEngineErrorCode["BackendCrashed"] = "BACKEND_CRASHED";
258
- VideoEngineErrorCode["Aborted"] = "ABORTED";
258
+ WebVideoEngineErrorCode["NoVideoTrack"] = "NO_VIDEO_TRACK";
259
+ WebVideoEngineErrorCode["BackendCrashed"] = "BACKEND_CRASHED";
260
+ WebVideoEngineErrorCode["Aborted"] = "ABORTED";
259
261
  /** A canvas was offered to an engine loaded in "frames" presentation mode,
260
262
  * where the host owns the only canvas. */
261
- VideoEngineErrorCode["PresentationMismatch"] = "PRESENTATION_MISMATCH";
263
+ WebVideoEngineErrorCode["PresentationMismatch"] = "PRESENTATION_MISMATCH";
262
264
  /**
263
265
  * The decoder cannot decode this source at all: it refused to configure, it
264
266
  * errored, or it acknowledged decode requests and never produced a frame.
265
- * Distinct from BackendCrashed, which a rebuild can recover; this one
266
- * survives every rebuild, so the runtime stops rebuilding and says so. The
267
- * usual cause is outside the page: another tab holding every hardware
268
- * decoder session the machine has.
267
+ * Distinct from BackendCrashed; this one survives every rebuild, so the
268
+ * runtime stops rebuilding and says so. The usual cause is outside the page:
269
+ * another tab holding every hardware decoder session the machine has.
269
270
  */
270
- VideoEngineErrorCode["DecoderStalled"] = "DECODER_STALLED";
271
+ WebVideoEngineErrorCode["DecoderStalled"] = "DECODER_STALLED";
271
272
  /** A playback rate outside the forward range the engine supports. */
272
- VideoEngineErrorCode["RateUnsupported"] = "RATE_UNSUPPORTED";
273
- })(VideoEngineErrorCode || (VideoEngineErrorCode = {}));
273
+ WebVideoEngineErrorCode["RateUnsupported"] = "RATE_UNSUPPORTED";
274
+ })(WebVideoEngineErrorCode || (WebVideoEngineErrorCode = {}));
274
275
  /**
275
- * Thrown by createScrubCursor / VideoEngine.load when decode is unsupported
276
- * or the source is unreadable. Branch on `error.code` (VideoEngineErrorCode)
276
+ * Thrown by createScrubCursor / WebVideoEngine.load when decode is unsupported
277
+ * or the source is unreadable. Branch on `error.code` (WebVideoEngineErrorCode)
277
278
  * to differentiate decode failures from network failures.
278
279
  */
279
- class VideoEngineError extends Error {
280
+ class WebVideoEngineError extends Error {
280
281
  code;
281
282
  cause;
282
283
  constructor(code, message, cause) {
@@ -288,7 +289,7 @@ class VideoEngineError extends Error {
288
289
  /** Shared by the facade and the core so a host gets the same refusal wherever
289
290
  * its canvas is caught. */
290
291
  function canvasBindingRefused() {
291
- return new VideoEngineError(VideoEngineErrorCode.PresentationMismatch, 'presentation "frames" leaves the canvas to the host: this engine paints nothing and hands out VideoFrames instead');
292
+ return new WebVideoEngineError(WebVideoEngineErrorCode.PresentationMismatch, 'presentation "frames" leaves the canvas to the host: this engine paints nothing and hands out VideoFrames instead');
292
293
  }
293
294
  /**
294
295
  * Validates a requested playback rate and returns it. The facade and the core
@@ -304,7 +305,7 @@ function resolvePlaybackRate(rate) {
304
305
  if (!Number.isFinite(rate) ||
305
306
  rate < PLAYBACK_RATE.MIN ||
306
307
  rate > PLAYBACK_RATE.MAX) {
307
- throw new VideoEngineError(VideoEngineErrorCode.RateUnsupported, `playback rate ${rate} is outside the supported forward range ${PLAYBACK_RATE.MIN}-${PLAYBACK_RATE.MAX}; reverse playback is not supported`);
308
+ throw new WebVideoEngineError(WebVideoEngineErrorCode.RateUnsupported, `playback rate ${rate} is outside the supported forward range ${PLAYBACK_RATE.MIN}-${PLAYBACK_RATE.MAX}; reverse playback is not supported`);
308
309
  }
309
310
  return rate;
310
311
  }
@@ -435,15 +436,55 @@ function oneEntryPerInstant(data) {
435
436
  if (at === ticks.length)
436
437
  return data;
437
438
  const distinct = new Float64Array(ticks.length);
439
+ const distinctSource = data.sourceTicks
440
+ ? new Float64Array(ticks.length)
441
+ : undefined;
438
442
  distinct.set(ticks.subarray(0, at));
443
+ if (distinctSource)
444
+ distinctSource.set(data.sourceTicks.subarray(0, at));
439
445
  let size = at;
440
446
  for (let i = at + 1; i < ticks.length; i += 1) {
441
447
  if (ticks[i] === distinct[size - 1])
442
448
  continue;
443
449
  distinct[size] = ticks[i];
450
+ if (distinctSource)
451
+ distinctSource[size] = data.sourceTicks[i];
444
452
  size += 1;
445
453
  }
446
- return { ...data, ticks: distinct.slice(0, size) };
454
+ return {
455
+ ...data,
456
+ ticks: distinct.slice(0, size),
457
+ ...(distinctSource ? { sourceTicks: distinctSource.slice(0, size) } : {}),
458
+ };
459
+ }
460
+ function normalizePresentationOrigin(data) {
461
+ if (data.sourceTicks !== undefined || !(data.ticks[0] < 0))
462
+ return data;
463
+ let firstVisible = 0;
464
+ while (firstVisible < data.ticks.length) {
465
+ const end = firstVisible + 1 < data.ticks.length
466
+ ? data.ticks[firstVisible + 1]
467
+ : data.ticks[firstVisible] + data.lastDurationTicks;
468
+ if (end > 0)
469
+ break;
470
+ firstVisible += 1;
471
+ }
472
+ if (firstVisible === data.ticks.length) {
473
+ throw new RangeError("FrameTimeline: a track containing only pre-roll has no presentation timeline");
474
+ }
475
+ const sourceTicks = data.ticks.slice(firstVisible);
476
+ const ticks = sourceTicks.slice();
477
+ if (ticks[0] < 0)
478
+ ticks[0] = 0;
479
+ const lastDurationTicks = ticks.length === 1 && sourceTicks[0] < 0
480
+ ? data.lastDurationTicks + sourceTicks[0]
481
+ : data.lastDurationTicks;
482
+ return {
483
+ ...data,
484
+ lastDurationTicks,
485
+ sourceTicks,
486
+ ticks,
487
+ };
447
488
  }
448
489
  /**
449
490
  * Every real frame of one track, in presentation order, by its container tick
@@ -469,7 +510,7 @@ class FrameTimeline {
469
510
  if (!(data.tickRate > 0)) {
470
511
  throw new RangeError(`FrameTimeline: tick rate ${data.tickRate} is not positive`);
471
512
  }
472
- return new FrameTimeline(oneEntryPerInstant(data));
513
+ return new FrameTimeline(oneEntryPerInstant(normalizePresentationOrigin(data)));
473
514
  }
474
515
  /** A synthetic constant-rate table. Tests and fakes only. The default tick
475
516
  * rate is a whole multiple of `fps`, so no test table needs rounding. */
@@ -493,6 +534,22 @@ class FrameTimeline {
493
534
  timeAt(index) {
494
535
  return asSec(this.ticksAt(index) / this.data.tickRate);
495
536
  }
537
+ /** Timestamp passed to the decoder for the named presentation frame. */
538
+ sourceTimeAt(index) {
539
+ const at = this.clampIndex(index);
540
+ return asSec((this.data.sourceTicks?.[at] ?? this.ticksArray[at]) / this.data.tickRate);
541
+ }
542
+ /** Maps a public presentation time onto the container's source clock. */
543
+ toSourceTime(timeS) {
544
+ if (this.data.sourceTicks && timeS <= this.timeAt(0)) {
545
+ return this.sourceTimeAt(0);
546
+ }
547
+ return asSec(timeS);
548
+ }
549
+ /** Maps a decoded/container timestamp onto the public presentation clock. */
550
+ fromSourceTime(timeS) {
551
+ return asSec(this.data.sourceTicks ? Math.max(0, timeS) : timeS);
552
+ }
496
553
  idAt(index) {
497
554
  const at = this.clampIndex(index);
498
555
  return { index: at, ticks: this.ticksArray[at] };
@@ -556,5 +613,4 @@ class FrameTimeline {
556
613
  }
557
614
  }
558
615
 
559
- export { DIAGNOSTICS as D, FRAME_TIMELINE as F, HANG_RECOVERY as H, PlaybackStatus as P, SCRUB as S, TRACE_RING_BOUNDS as T, VideoEngineError as V, VideoEngineErrorCode as a, asSec as b, SourceKind as c, FrameTimeline as d, asPaintSeq as e, canvasBindingRefused as f, resolvePlaybackRate as g, PLAYBACK_RATE as h, asFps as i, cappedResolution as j, displayBoxResolution as k, nativeResolution as n, resolveDecodeDimensions as r, viewportResolution as v };
560
- //# sourceMappingURL=frame-timeline-GINI8Gum.js.map
616
+ export { DIAGNOSTICS as D, FRAME_TIMELINE as F, HANG_RECOVERY as H, PlaybackStatus as P, SCRUB as S, TRACE_RING_BOUNDS as T, WebVideoEngineError as W, WebVideoEngineErrorCode as a, asSec as b, SourceKind as c, FrameTimeline as d, asPaintSeq as e, canvasBindingRefused as f, resolvePlaybackRate as g, PLAYBACK_RATE as h, asFps as i, cappedResolution as j, displayBoxResolution as k, nativeResolution as n, resolveDecodeDimensions as r, viewportResolution as v };
@@ -22,6 +22,10 @@ export interface FrameTimelineData {
22
22
  readonly tickRate: TickRate;
23
23
  readonly ticks: Float64Array;
24
24
  readonly lastDurationTicks: number;
25
+ /** Original source timestamps when presentation pre-roll was removed. The
26
+ * public `ticks` stay zero-based while decode requests retain the addresses
27
+ * needed to reach a frame straddling presentation time zero. */
28
+ readonly sourceTicks?: Float64Array;
25
29
  }
26
30
  /**
27
31
  * Every real frame of one track, in presentation order, by its container tick
@@ -46,6 +50,12 @@ export declare class FrameTimeline {
46
50
  toData(): FrameTimelineData;
47
51
  ticksAt(index: number): number;
48
52
  timeAt(index: number): Sec;
53
+ /** Timestamp passed to the decoder for the named presentation frame. */
54
+ sourceTimeAt(index: number): Sec;
55
+ /** Maps a public presentation time onto the container's source clock. */
56
+ toSourceTime(timeS: number): Sec;
57
+ /** Maps a decoded/container timestamp onto the public presentation clock. */
58
+ fromSourceTime(timeS: number): Sec;
49
59
  idAt(index: number): FrameId;
50
60
  landingAt(index: number): FrameLanding;
51
61
  /** Ticks the frame at `index` ends at, which is where the next one starts. */
@@ -68,4 +78,3 @@ export declare class FrameTimeline {
68
78
  indexOfDecoded(timeS: number): number;
69
79
  private clampIndex;
70
80
  }
71
- //# sourceMappingURL=frame-timeline.d.ts.map
@@ -71,4 +71,3 @@ export declare class FrameWalker {
71
71
  walkFrames(range?: FrameWalkRange): AsyncGenerator<WalkedFrame, void, unknown>;
72
72
  close(): Promise<void>;
73
73
  }
74
- //# sourceMappingURL=frame-walker.d.ts.map
@@ -1,3 +1,13 @@
1
- export * from "#web-video-engine";
2
- export { createVideoEngineMediaRendererSource, openVideoEngineMediaSource, type VideoEngineMediaSource, type VideoEngineMediaSourceOptions, } from "supervision";
1
+ /**
2
+ * The web video engine, and the two adapters that hand what it decodes to a
3
+ * media renderer.
4
+ *
5
+ * The engine's own barrel is wider than this, because its modules export for
6
+ * one another. A name reaches an application only by being listed here, so
7
+ * listing one is a decision to support it. Worker message shapes, mediabunny
8
+ * pass-through configuration, and the React imperative handle stay inside.
9
+ */
10
+ export { createWebVideoEngineMediaRendererSource, openWebVideoEngineMediaSource, type WebVideoEngineMediaSource, type WebVideoEngineMediaSourceOptions, } from "#media/video-engine-media-source";
11
+ export { DIAGNOSTICS, FrameTimeline, PLAYBACK_RATE, PlaybackStatus, SourceKind, TRACE_RING_BOUNDS, WebVideoEngine, WebVideoEngineError, WebVideoEngineErrorCode, cappedResolution, displayBoxResolution, nativeResolution, viewportResolution, } from "#web-video-engine";
12
+ export type { BlobVideoSource, DecodeResolutionStrategy, DiagnosticsSnapshot, DisplayBoxResolutionOptions, EngineChannel, EngineReadySnapshot, Fps, FrameId, FrameLanding, FrameQuality, FrameTimelineData, PaintSeq, PlaybackState, PresentationMode, PresentedFrame, PresentedFrameHandler, Rotation, Sec, SeekIntent, SeekTarget, SourceResidencyConfig, SourceResidencyDiagnostics, StreamVideoSource, TickRate, UrlVideoSource, VideoMetadata, VideoSource, ViewportResolutionOptions, Warning, WarningSeverity, WebVideoEngineOptions, } from "#web-video-engine";
3
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/web-video-engine/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,OAAO,EACL,oCAAoC,EACpC,0BAA0B,EAC1B,KAAK,sBAAsB,EAC3B,KAAK,6BAA6B,GACnC,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/web-video-engine/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EACL,uCAAuC,EACvC,6BAA6B,EAC7B,KAAK,yBAAyB,EAC9B,KAAK,gCAAgC,GACtC,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACL,WAAW,EACX,aAAa,EACb,aAAa,EACb,cAAc,EACd,UAAU,EACV,iBAAiB,EACjB,cAAc,EACd,mBAAmB,EACnB,uBAAuB,EACvB,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,eAAe,EACf,wBAAwB,EACxB,mBAAmB,EACnB,2BAA2B,EAC3B,aAAa,EACb,mBAAmB,EACnB,GAAG,EACH,OAAO,EACP,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,QAAQ,EACR,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,qBAAqB,EACrB,QAAQ,EACR,GAAG,EACH,UAAU,EACV,UAAU,EACV,qBAAqB,EACrB,0BAA0B,EAC1B,iBAAiB,EACjB,QAAQ,EACR,cAAc,EACd,aAAa,EACb,WAAW,EACX,yBAAyB,EACzB,OAAO,EACP,eAAe,EACf,qBAAqB,GACtB,MAAM,mBAAmB,CAAC"}
@@ -1,3 +1,3 @@
1
- export * from './engine.js';
2
- export { createVideoEngineMediaRendererSource, openVideoEngineMediaSource } from '../index.js';
1
+ export { createWebVideoEngineMediaRendererSource, openWebVideoEngineMediaSource } from '../media/video-engine-media-source.js';
2
+ export { DIAGNOSTICS, FrameTimeline, PLAYBACK_RATE, PlaybackStatus, SourceKind, TRACE_RING_BOUNDS, WebVideoEngine, WebVideoEngineError, WebVideoEngineErrorCode, cappedResolution, displayBoxResolution, nativeResolution, viewportResolution } from './engine.js';
3
3
  //# sourceMappingURL=index.js.map
@@ -62,4 +62,3 @@ export declare class KeyPacketRequirement {
62
62
  */
63
63
  satisfy(packetBytes: Uint8Array): Uint8Array;
64
64
  }
65
- //# sourceMappingURL=key-packet.d.ts.map
@@ -88,4 +88,3 @@ export declare class KeyframeIndex {
88
88
  private record;
89
89
  }
90
90
  export {};
91
- //# sourceMappingURL=keyframe-index.d.ts.map
@@ -53,4 +53,3 @@ export declare class MirrorStore {
53
53
  subscribe(channel: EngineChannel, listener: () => void): () => void;
54
54
  private emit;
55
55
  }
56
- //# sourceMappingURL=mirror-store.d.ts.map
@@ -0,0 +1,11 @@
1
+ /**
2
+ * A timeout whose budget advances only while its owner is visible. The owner
3
+ * still decides which work is eligible: this helper never pauses the work
4
+ * itself, nor any unrelated watchdog.
5
+ */
6
+ export interface PausableDeadline {
7
+ cancel(): void;
8
+ pause(): void;
9
+ resume(): void;
10
+ }
11
+ export declare function createPausableDeadline(timeoutMs: number, onExpire: () => void): PausableDeadline;
@@ -91,4 +91,3 @@ export interface RendererOptions {
91
91
  * 2D renderer instead, so a caller always gets a working renderer.
92
92
  */
93
93
  export declare function createRenderer(canvas: OffscreenCanvas, options?: RendererOptions): Promise<Renderer>;
94
- //# sourceMappingURL=renderer.d.ts.map
@@ -32,4 +32,3 @@ export declare function drawRotated(context: CanvasRenderingContext2D | Offscree
32
32
  * stretch-to-fill absorbs the aspect change for free.
33
33
  */
34
34
  export declare function uvRotationMatrix(rotation: Rotation): readonly [number, number, number, number];
35
- //# sourceMappingURL=rotation.d.ts.map
@@ -349,6 +349,8 @@ export declare class ScrubController {
349
349
  * walk. Detaches the iterator and drops the read-ahead so paused ticks
350
350
  * never paint a stale play frame. */
351
351
  endPlay(): void;
352
+ holdOutput(): void;
353
+ releaseOutput(): void;
352
354
  /**
353
355
  * Cache lookup helper used by the engine on scrub. Asks the cursor for a
354
356
  * cached frame near the target and, if it is far enough from what the canvas
@@ -356,12 +358,14 @@ export declare class ScrubController {
356
358
  * calls cursor.seekTo to land the full-res frame; the cursor's emit listener
357
359
  * stashes it for the next tick.
358
360
  *
359
- * The skip-near-current check rejects hits whose timestamp falls within
360
- * cacheSkipNearMs of the currently-displayed frame. This is the knob
361
- * consumers tune to control single-frame-step UX: a higher value forces a
362
- * fresh decode on tighter movements, a value of 0 always serves the cache.
361
+ * Nearby approximate hits respect cacheSkipNearMs. An exact hit for the
362
+ * requested frame can advance immediately, without waiting for a decode.
363
363
  */
364
364
  tryPaintFromCache(timestampMs: number): boolean;
365
+ /** A new commit needs its own presentation even when its exact pixels were
366
+ * already shown by the preceding scrub. Approximate neighbors cannot answer it. */
367
+ presentExactFromCache(timestampMs: number): boolean;
368
+ private paintCached;
365
369
  private start;
366
370
  private runTick;
367
371
  private paint;
@@ -373,4 +377,3 @@ export declare class ScrubController {
373
377
  getRealtimeStats(): RealtimeStats;
374
378
  private stop;
375
379
  }
376
- //# sourceMappingURL=scrub-controller.d.ts.map
@@ -1,4 +1,5 @@
1
1
  import type { CreateScrubCursorOptions } from "./create-scrub-cursor";
2
+ import type { DecodeDimensions } from "./decode-resolution";
2
3
  import type { FrameCacheStats } from "./frame-cache";
3
4
  import type { FrameId, FrameTimeline } from "./frame-timeline";
4
5
  import type { GopStats } from "./keyframe-index";
@@ -27,8 +28,9 @@ export interface ScrubFrameBase {
27
28
  }
28
29
  /**
29
30
  * The structural slice of mediabunny's VideoSample the runtime touches: enough
30
- * to draw it into a 2D canvas, hand its pixels to WebGPU zero-copy, and release
31
- * it. Kept as a slice rather than the concrete class so the runtime never
31
+ * to draw it into a 2D canvas, hand its pixels to WebGPU without an intermediate
32
+ * transfer copy where the selected path supports that, and release it. Kept as
33
+ * a slice rather than the concrete class so the runtime never
32
34
  * imports mediabunny and stays fake-injectable under test.
33
35
  *
34
36
  * close() is the caller's obligation and is made idempotent at the cursor
@@ -44,6 +46,9 @@ export interface VideoSampleLike {
44
46
  * whoever takes one owes it the turn named by `rotation` below.
45
47
  */
46
48
  toVideoFrame(): VideoFrame;
49
+ /** The sample owns storage independent of a decoder output pool, so a host
50
+ * transfer may rewrap it without copying its pixels again. */
51
+ readonly independentPixels?: true;
47
52
  /** The turn the pixels still need, already applied by draw() and dropped by
48
53
  * toVideoFrame(). */
49
54
  readonly rotation: Rotation;
@@ -58,9 +63,11 @@ export interface VideoSampleLike {
58
63
  *
59
64
  * A canvas frame carries a canvas and no close obligation: it is a cache blit
60
65
  * or a CanvasSink decode. WebGPU uploads it with copyExternalImageToTexture,
61
- * which accepts neither an SVG image nor a video element. A sample frame carries a live VideoSample
62
- * the zero-copy path imports straight into WebGPU; whoever stashes it owns its
63
- * close, so only fresh decodes on the sample source produce one.
66
+ * which accepts neither an SVG image nor a video element. A sample frame carries
67
+ * a live VideoSample that an eligible VideoSampleSink route can import directly
68
+ * into WebGPU. A DecodeSession route may first materialize independently owned
69
+ * pixels. Whoever stashes the sample owns its close, so only fresh decodes on
70
+ * the sample source produce one.
64
71
  */
65
72
  export interface CanvasScrubFrame extends ScrubFrameBase {
66
73
  readonly kind: "canvas";
@@ -270,13 +277,15 @@ export interface ScrubTrackInfo {
270
277
  * frame as a keyframe result.
271
278
  * - next is VFR-correct and a no-op while paused (no iterator attached),
272
279
  * so it never advances media time across a paused canvas. The 1/fps step
273
- * approximation lives in VideoEngine.step, not here.
280
+ * approximation lives in WebVideoEngine.step, not here.
274
281
  * - isIdle is true only when no seek is draining and no pull is in flight.
275
282
  * - subscribe replays the most recent frame to a new listener so the seed
276
283
  * frame from open() is never dropped.
277
284
  * - close is final; reusing a closed cursor is a programming error.
278
285
  */
279
286
  export interface ScrubCursor {
287
+ /** Drains old output and changes frame/cache size without reopening decode. */
288
+ resizeOutput?(dimensions: DecodeDimensions): Promise<boolean>;
280
289
  readonly state: ScrubCursorState;
281
290
  readonly track: ScrubTrackInfo;
282
291
  readonly isIdle: boolean;
@@ -339,4 +348,3 @@ export interface ScrubCursor {
339
348
  close(): Promise<void>;
340
349
  }
341
350
  export type ScrubCursorFactory = (options: CreateScrubCursorOptions) => Promise<ScrubCursor>;
342
- //# sourceMappingURL=scrub-cursor.d.ts.map
@@ -27,4 +27,3 @@ export declare class ScrubTrajectory {
27
27
  private indexFromNewest;
28
28
  private indexFromOldest;
29
29
  }
30
- //# sourceMappingURL=scrub-trajectory.d.ts.map
@@ -53,5 +53,39 @@ export interface SourceResidency {
53
53
  * the network for less than half a mebibyte, so every read it makes banks
54
54
  * at least one block. */
55
55
  export declare const READ_BLOCK_BYTES: number;
56
+ /** How long a delivered chunk may sit unclaimed before the walk stops deferring
57
+ * to the read that produced it. It measures the gap between a chunk and the
58
+ * consumer's next pull, not the wait for the server, so a slow link reads as
59
+ * a live read while a reader that walked away frees the link. */
60
+ export declare const FOREGROUND_STALL_MS = 250;
61
+ /**
62
+ * Bytes held in one buffer per contiguous run, so a lookup is a single scan and
63
+ * a served range is a subarray with nothing copied. Insert is where copying
64
+ * happens: a run keeps spare capacity so an append that extends it writes into
65
+ * that room, and only a backward or overlapping insert re-materializes the run.
66
+ */
67
+ export declare class ByteStore {
68
+ #private;
69
+ get residentBytes(): number;
70
+ /** Bytes retained by backing arrays, including append headroom. */
71
+ get allocatedBytes(): number;
72
+ ranges(): ResidentRange[];
73
+ /** The held run from `offset` to the end of its run, or null. */
74
+ runAt(offset: number): Uint8Array | null;
75
+ /** Where the gap containing `offset` ends, or null when nothing is held past it. */
76
+ nextHeldStart(offset: number): number | null;
77
+ insert(start: number, bytes: Uint8Array): void;
78
+ /**
79
+ * Brings held bytes under the budget: whole runs go first, furthest from
80
+ * `focus`, and a lone run that has outgrown the budget is narrowed to a
81
+ * `budgetBytes` window at `focus`.
82
+ */
83
+ evictTo(budgetBytes: number, focus: number): void;
84
+ clear(): void;
85
+ }
86
+ /**
87
+ * Outstanding claims on the link, for this module's own tests: they drive every
88
+ * way a read can end and assert the count comes back to zero.
89
+ */
90
+ export declare function outstandingForegroundHolds(residency: SourceResidency): number;
56
91
  export declare function createSourceResidency(options: SourceResidencyOptions): SourceResidency;
57
- //# sourceMappingURL=source-residency.d.ts.map