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
  import { CanvasSink, EncodedPacketSink, Input, ALL_FORMATS, VideoSampleSink, ReadableStreamSource, BlobSource, UrlSource, UnsupportedInputFormatError } from 'mediabunny';
2
- import { V as VideoEngineError, a as VideoEngineErrorCode, S as SCRUB, b as asSec, n as nativeResolution, r as resolveDecodeDimensions, c as SourceKind, F as FRAME_TIMELINE, d as FrameTimeline } from './frame-timeline-GINI8Gum.js';
2
+ import { W as WebVideoEngineError, a as WebVideoEngineErrorCode, S as SCRUB, b as asSec, n as nativeResolution, r as resolveDecodeDimensions, c as SourceKind, F as FRAME_TIMELINE, d as FrameTimeline } from './frame-timeline-DreEwsRY.js';
3
3
 
4
4
  /**
5
5
  * An H.264 sync sample that is not an IDR frame cannot open a WebCodecs decode
@@ -372,13 +372,19 @@ class DecodeSession {
372
372
  epoch = 0;
373
373
  outputTimeoutMs;
374
374
  rotation;
375
+ outputWidth;
376
+ outputHeight;
377
+ snapshotFrame;
375
378
  constructor(options) {
376
379
  this.options = options;
377
380
  this.outputTimeoutMs = options.outputTimeoutMs ?? OUTPUT_TIMEOUT_MS;
378
381
  this.rotation = options.rotation;
382
+ this.outputWidth = options.outputWidth;
383
+ this.outputHeight = options.outputHeight;
384
+ this.snapshotFrame = options.snapshotFrame ?? createVideoFrameSnapshotter();
379
385
  const prefixWidth = drivablePrefixWidth(options.config);
380
386
  if (prefixWidth === null) {
381
- throw new VideoEngineError(VideoEngineErrorCode.DecodeUnsupported, `DecodeSession: ${options.config.codec} is not AVCC-framed H.264`);
387
+ throw new WebVideoEngineError(WebVideoEngineErrorCode.DecodeUnsupported, `DecodeSession: ${options.config.codec} is not AVCC-framed H.264`);
382
388
  }
383
389
  this.prefixWidth = prefixWidth;
384
390
  this.keyPacket = new KeyPacketRequirement(prefixWidth);
@@ -402,6 +408,19 @@ class DecodeSession {
402
408
  return queued.timestampS;
403
409
  return this.reachable;
404
410
  }
411
+ async resizeOutput({ width, height }) {
412
+ await this.exclusive(async () => {
413
+ if (this.closed || this.outputWidth === undefined)
414
+ return;
415
+ if (this.outputWidth === width && this.outputHeight === height)
416
+ return;
417
+ this.quiesce();
418
+ this.outputWidth = width;
419
+ this.outputHeight = height;
420
+ this.exhausted = true;
421
+ this.epoch++;
422
+ });
423
+ }
405
424
  /** The frame at or before `targetS`, or null when nothing precedes it. */
406
425
  async frameAt(targetS) {
407
426
  const landed = await this.exclusive(() => this.land(targetS));
@@ -793,11 +812,11 @@ class DecodeSession {
793
812
  // full pipeline of requests with nothing at all may never have been
794
813
  // given a legal place to start, which is the anchor's to answer for.
795
814
  if (this.framesDecodedCount === 0) {
796
- this.anchorError ??= new VideoEngineError(VideoEngineErrorCode.DecoderStalled, `DecodeSession: the decoder acknowledged ${owed} decode requests and produced no frame`);
815
+ this.anchorError ??= new WebVideoEngineError(WebVideoEngineErrorCode.DecoderStalled, `DecodeSession: the decoder acknowledged ${owed} decode requests and produced no frame`);
797
816
  resolve();
798
817
  return;
799
818
  }
800
- reject(new VideoEngineError(VideoEngineErrorCode.BackendCrashed, `DecodeSession: decoder produced no output in ${this.outputTimeoutMs}ms with ${owed} chunks in flight`));
819
+ reject(new WebVideoEngineError(WebVideoEngineErrorCode.BackendCrashed, `DecodeSession: decoder produced no output in ${this.outputTimeoutMs}ms with ${owed} chunks in flight`));
801
820
  }, this.outputTimeoutMs);
802
821
  this.wake = () => {
803
822
  this.wake = null;
@@ -836,20 +855,38 @@ class DecodeSession {
836
855
  frame.close();
837
856
  return;
838
857
  }
839
- this.decoded.push({ frame, ...timing });
858
+ let stable = frame;
859
+ if (this.outputWidth !== undefined && this.outputHeight !== undefined) {
860
+ try {
861
+ stable = this.snapshotFrame(frame, this.outputWidth, this.outputHeight);
862
+ }
863
+ catch (error) {
864
+ this.stall("could not own decoder output pixels", error);
865
+ this.wake?.();
866
+ return;
867
+ }
868
+ finally {
869
+ frame.close();
870
+ }
871
+ }
872
+ this.decoded.push({
873
+ frame: stable,
874
+ ...timing,
875
+ independentPixels: stable !== frame,
876
+ });
840
877
  this.wake?.();
841
878
  }
842
879
  fail(from, error) {
843
880
  if (from !== this.decoder)
844
881
  return;
845
- this.anchorError ??= new VideoEngineError(VideoEngineErrorCode.DecoderStalled, "DecodeSession: the decoder reported an error", error);
882
+ this.anchorError ??= new WebVideoEngineError(WebVideoEngineErrorCode.DecoderStalled, "DecodeSession: the decoder reported an error", error);
846
883
  this.wake?.();
847
884
  }
848
885
  /** Latches the terminal failure and returns it. First writer wins, so the
849
886
  * cause a caller is handed is the one that started the failure rather than
850
887
  * whichever consequence surfaced last. */
851
888
  stall(what, cause) {
852
- return this.latch(new VideoEngineError(VideoEngineErrorCode.DecoderStalled, `DecodeSession: ${what}`, cause));
889
+ return this.latch(new WebVideoEngineError(WebVideoEngineErrorCode.DecoderStalled, `DecodeSession: ${what}`, cause));
853
890
  }
854
891
  latch(error) {
855
892
  this.stalledError ??= error;
@@ -861,14 +898,29 @@ class DecodeSession {
861
898
  this.decoded.length = 0;
862
899
  }
863
900
  }
901
+ function createVideoFrameSnapshotter() {
902
+ return (frame, width, height) => {
903
+ const canvas = new OffscreenCanvas(width, height);
904
+ const context = canvas.getContext("2d", { alpha: false });
905
+ if (!context)
906
+ throw new Error("2D canvas unavailable for frame snapshot");
907
+ context.drawImage(frame, 0, 0, width, height);
908
+ context.getImageData(0, 0, 1, 1);
909
+ return new VideoFrame(canvas, {
910
+ timestamp: frame.timestamp,
911
+ ...(frame.duration === null ? {} : { duration: frame.duration }),
912
+ });
913
+ };
914
+ }
864
915
  /** A decoded frame in the runtime's own sample vocabulary, so a session frame
865
916
  * and a VideoSampleSink frame reach the renderer the same way. */
866
- function videoFrameSample({ frame, timestampS, durationS }, rotation) {
917
+ function videoFrameSample({ frame, timestampS, durationS, independentPixels }, rotation) {
867
918
  const quarterTurn = rotation % 180 !== 0;
868
919
  return {
869
920
  timestamp: timestampS,
870
921
  duration: durationS,
871
922
  rotation,
923
+ ...(independentPixels ? { independentPixels: true } : {}),
872
924
  toVideoFrame: () => frame.clone(),
873
925
  draw: (ctx, dx, dy, dWidth, dHeight) => {
874
926
  drawRotated(ctx, frame, rotation, dx, dy, dWidth ?? (quarterTurn ? frame.displayHeight : frame.displayWidth), dHeight ?? (quarterTurn ? frame.displayWidth : frame.displayHeight));
@@ -894,6 +946,7 @@ function idempotentSample(sample) {
894
946
  let closed = false;
895
947
  return {
896
948
  toVideoFrame: () => sample.toVideoFrame(),
949
+ independentPixels: sample.independentPixels,
897
950
  rotation: sample.rotation,
898
951
  draw: (ctx, dx, dy, dWidth, dHeight) => sample.draw(ctx, dx, dy, dWidth, dHeight),
899
952
  close: () => {
@@ -911,6 +964,115 @@ function idempotentSample(sample) {
911
964
  };
912
965
  }
913
966
 
967
+ function sampleAtPresentationTime(sample, timestamp) {
968
+ const owned = idempotentSample(sample);
969
+ return {
970
+ toVideoFrame: () => owned.toVideoFrame(),
971
+ independentPixels: owned.independentPixels,
972
+ rotation: owned.rotation,
973
+ draw: (ctx, dx, dy, dWidth, dHeight) => owned.draw(ctx, dx, dy, dWidth, dHeight),
974
+ close: () => owned.close(),
975
+ timestamp,
976
+ duration: owned.duration,
977
+ };
978
+ }
979
+ function presentationCanvasSource(source, timeline) {
980
+ const wrap = (frame) => frame
981
+ ? {
982
+ canvas: frame.canvas,
983
+ timestamp: timeline.fromSourceTime(frame.timestamp),
984
+ }
985
+ : null;
986
+ return {
987
+ async getCanvas(timestampS) {
988
+ return wrap(await source.getCanvas(timeline.toSourceTime(timestampS)));
989
+ },
990
+ async *canvases(startS) {
991
+ for await (const frame of source.canvases(timeline.toSourceTime(startS))) {
992
+ yield wrap(frame);
993
+ }
994
+ },
995
+ async *canvasesAtTimestamps(timestamps) {
996
+ const sourceTimestamps = Array.from(timestamps, (timestamp) => timeline.toSourceTime(timestamp));
997
+ for await (const frame of source.canvasesAtTimestamps(sourceTimestamps)) {
998
+ yield wrap(frame);
999
+ }
1000
+ },
1001
+ };
1002
+ }
1003
+ function presentationSampleSource(source, timeline) {
1004
+ const wrap = (sample) => sample
1005
+ ? sampleAtPresentationTime(sample, timeline.fromSourceTime(sample.timestamp))
1006
+ : null;
1007
+ return {
1008
+ async getSample(timestampS) {
1009
+ return wrap(await source.getSample(timeline.toSourceTime(timestampS)));
1010
+ },
1011
+ async *samples(startS) {
1012
+ for await (const sample of source.samples(timeline.toSourceTime(startS))) {
1013
+ yield wrap(sample);
1014
+ }
1015
+ },
1016
+ async *samplesAtTimestamps(timestamps) {
1017
+ const sourceTimestamps = Array.from(timestamps, (timestamp) => timeline.toSourceTime(timestamp));
1018
+ for await (const sample of source.samplesAtTimestamps(sourceTimestamps)) {
1019
+ yield wrap(sample);
1020
+ }
1021
+ },
1022
+ };
1023
+ }
1024
+ function presentationSessionSource(source, timeline) {
1025
+ const wrap = (sample) => sample
1026
+ ? sampleAtPresentationTime(sample, timeline.fromSourceTime(sample.timestamp))
1027
+ : null;
1028
+ return {
1029
+ ...(source.resizeOutput
1030
+ ? { resizeOutput: source.resizeOutput.bind(source) }
1031
+ : {}),
1032
+ async frameAt(targetS) {
1033
+ return wrap(await source.frameAt(timeline.toSourceTime(targetS)));
1034
+ },
1035
+ async *framesFrom(startS) {
1036
+ for await (const sample of source.framesFrom(timeline.toSourceTime(startS))) {
1037
+ yield wrap(sample);
1038
+ }
1039
+ },
1040
+ async *framesCovering(startS, endS) {
1041
+ for await (const sample of source.framesCovering(timeline.toSourceTime(startS), timeline.toSourceTime(endS))) {
1042
+ yield wrap(sample);
1043
+ }
1044
+ },
1045
+ get reachableFromS() {
1046
+ return source.reachableFromS === -Infinity
1047
+ ? -Infinity
1048
+ : timeline.fromSourceTime(source.reachableFromS);
1049
+ },
1050
+ get framesDecoded() {
1051
+ return source.framesDecoded;
1052
+ },
1053
+ };
1054
+ }
1055
+ function presentationKeyframeProbe(source, timeline) {
1056
+ const sourcePackets = new WeakMap();
1057
+ const wrap = (packet) => {
1058
+ if (!packet)
1059
+ return null;
1060
+ const mapped = {
1061
+ timestamp: timeline.fromSourceTime(packet.timestamp),
1062
+ };
1063
+ sourcePackets.set(mapped, packet);
1064
+ return mapped;
1065
+ };
1066
+ return {
1067
+ async getKeyPacket(timestamp, options) {
1068
+ return wrap(await source.getKeyPacket(timeline.toSourceTime(timestamp), options));
1069
+ },
1070
+ async getNextKeyPacket(packet, options) {
1071
+ const sourcePacket = sourcePackets.get(packet) ?? packet;
1072
+ return wrap(await source.getNextKeyPacket(sourcePacket, options));
1073
+ },
1074
+ };
1075
+ }
914
1076
  /**
915
1077
  * Opens a source on the CanvasSink path, the one every decodable source
916
1078
  * supports. Track facts are resolved here too, so the consumer receives a fully
@@ -962,14 +1124,15 @@ function canvasHandle(opened, poolSize) {
962
1124
  // canvas the frame is drawn into; it does NOT reduce decode cost. The codec
963
1125
  // decodes the full coded frame regardless, and CanvasSink resizes after, so
964
1126
  // the win is paint work and cached-blit memory, not decode throughput.
965
- const sink = new CanvasSink(opened.videoTrack, {
1127
+ const source = new CanvasSink(opened.videoTrack, {
966
1128
  poolSize,
967
1129
  width: opened.track.decodeWidth,
968
1130
  });
1131
+ const { timeline } = opened.track;
969
1132
  return {
970
1133
  track: opened.track,
971
- sink,
972
- keyframeProbe: new EncodedPacketSink(opened.videoTrack),
1134
+ sink: presentationCanvasSource(source, timeline),
1135
+ keyframeProbe: presentationKeyframeProbe(new EncodedPacketSink(opened.videoTrack), timeline),
973
1136
  dispose: async () => {
974
1137
  opened.input.dispose();
975
1138
  },
@@ -977,11 +1140,12 @@ function canvasHandle(opened, poolSize) {
977
1140
  }
978
1141
  /** The zero-copy sample path: a VideoSampleSink in place of the CanvasSink. It
979
1142
  * takes no width, so frames arrive at the source's own dimensions. */
980
- function sampleHandle(opened) {
1143
+ function sampleHandle(opened, optimizeForLatency = false) {
1144
+ const { timeline } = opened.track;
981
1145
  return {
982
1146
  track: opened.track,
983
- sampleSink: new VideoSampleSink(opened.videoTrack),
984
- keyframeProbe: new EncodedPacketSink(opened.videoTrack),
1147
+ sampleSink: presentationSampleSource(new VideoSampleSink(opened.videoTrack, optimizeForLatency ? { optimizeForLatency: true } : undefined), timeline),
1148
+ keyframeProbe: presentationKeyframeProbe(new EncodedPacketSink(opened.videoTrack), timeline),
985
1149
  dispose: async () => {
986
1150
  opened.input.dispose();
987
1151
  },
@@ -997,16 +1161,24 @@ function sampleHandle(opened) {
997
1161
  */
998
1162
  function sessionHandle(opened, config) {
999
1163
  const packets = new EncodedPacketSink(opened.videoTrack);
1164
+ const ownDecoderOutputPixels = detectDecodeSessionPixelOwnership();
1000
1165
  const session = new DecodeSession({
1001
1166
  packets,
1002
1167
  config,
1003
1168
  createDecoder: webCodecsDecoder,
1004
1169
  rotation: opened.track.rotation,
1170
+ ...(ownDecoderOutputPixels
1171
+ ? {
1172
+ outputHeight: opened.track.decodeHeight,
1173
+ outputWidth: opened.track.decodeWidth,
1174
+ }
1175
+ : {}),
1005
1176
  });
1177
+ const { timeline } = opened.track;
1006
1178
  return {
1007
1179
  track: opened.track,
1008
- session,
1009
- keyframeProbe: packets,
1180
+ session: presentationSessionSource(session, timeline),
1181
+ keyframeProbe: presentationKeyframeProbe(packets, timeline),
1010
1182
  dispose: async () => {
1011
1183
  session.close();
1012
1184
  opened.input.dispose();
@@ -1051,7 +1223,7 @@ async function openInput(options) {
1051
1223
  const videoTrack = await resolveVideoTrack(input);
1052
1224
  if (!(await videoTrack.canDecode())) {
1053
1225
  const decoderConfig = await videoTrack.getDecoderConfig();
1054
- throw new VideoEngineError(VideoEngineErrorCode.DecodeUnsupported, `openInput: browser cannot decode this video track's codec ${decoderConfig?.codec ?? "(unknown)"}`);
1226
+ throw new WebVideoEngineError(WebVideoEngineErrorCode.DecodeUnsupported, `openInput: browser cannot decode this video track's codec ${decoderConfig?.codec ?? "(unknown)"}`);
1055
1227
  }
1056
1228
  const displayWidth = videoTrack.displayWidth;
1057
1229
  const displayHeight = videoTrack.displayHeight;
@@ -1100,7 +1272,7 @@ async function resolveVideoTrack(input) {
1100
1272
  }
1101
1273
  catch (error) {
1102
1274
  if (error instanceof UnsupportedInputFormatError) {
1103
- throw new VideoEngineError(VideoEngineErrorCode.ContainerUnreadable, "openInput: the demuxer does not read this file's container", error);
1275
+ throw new WebVideoEngineError(WebVideoEngineErrorCode.ContainerUnreadable, "openInput: the demuxer does not read this file's container", error);
1104
1276
  }
1105
1277
  throw error;
1106
1278
  }
@@ -1108,8 +1280,8 @@ async function resolveVideoTrack(input) {
1108
1280
  return videoTrack;
1109
1281
  const tracks = await input.getTracks();
1110
1282
  throw tracks.length === 0
1111
- ? new VideoEngineError(VideoEngineErrorCode.VideoTrackUnreadable, "openInput: the container opened and the demuxer parsed no track out of it")
1112
- : new VideoEngineError(VideoEngineErrorCode.NoVideoTrack, "openInput: the container's tracks read and none of them carries video");
1283
+ ? new WebVideoEngineError(WebVideoEngineErrorCode.VideoTrackUnreadable, "openInput: the container opened and the demuxer parsed no track out of it")
1284
+ : new WebVideoEngineError(WebVideoEngineErrorCode.NoVideoTrack, "openInput: the container's tracks read and none of them carries video");
1113
1285
  }
1114
1286
  /**
1115
1287
  * Walks the track's packets metadata-only and records each one's timestamp in
@@ -1134,7 +1306,7 @@ async function readFrameTimeline(videoTrack) {
1134
1306
  })) {
1135
1307
  const at = Math.round(packet.timestamp * tickRate);
1136
1308
  if (ticks.length >= FRAME_TIMELINE.MAX_FRAMES) {
1137
- throw new VideoEngineError(VideoEngineErrorCode.DecodeUnsupported, `openInput: source video track carries more than ${FRAME_TIMELINE.MAX_FRAMES} frames`);
1309
+ throw new WebVideoEngineError(WebVideoEngineErrorCode.DecodeUnsupported, `openInput: source video track carries more than ${FRAME_TIMELINE.MAX_FRAMES} frames`);
1138
1310
  }
1139
1311
  ticks.push(at);
1140
1312
  if (at >= lastTicks) {
@@ -1143,7 +1315,7 @@ async function readFrameTimeline(videoTrack) {
1143
1315
  }
1144
1316
  }
1145
1317
  if (ticks.length === 0) {
1146
- throw new VideoEngineError(VideoEngineErrorCode.DecodeUnsupported, "openInput: source video track has no frames");
1318
+ throw new WebVideoEngineError(WebVideoEngineErrorCode.DecodeUnsupported, "openInput: source video track has no frames");
1147
1319
  }
1148
1320
  ticks.sort((a, b) => a - b);
1149
1321
  return FrameTimeline.from({
@@ -1152,6 +1324,23 @@ async function readFrameTimeline(videoTrack) {
1152
1324
  ticks: Float64Array.from(ticks),
1153
1325
  });
1154
1326
  }
1327
+ function shouldOwnDecodeSessionPixels(context) {
1328
+ if (!context.offscreenCanvasAvailable)
1329
+ return false;
1330
+ const clientPlatform = context.clientPlatform?.trim();
1331
+ if (clientPlatform)
1332
+ return clientPlatform.toLowerCase() === "android";
1333
+ return /\bAndroid\b/i.test(context.userAgent ?? "");
1334
+ }
1335
+ function detectDecodeSessionPixelOwnership() {
1336
+ const navigator = globalThis.navigator;
1337
+ return shouldOwnDecodeSessionPixels({
1338
+ clientPlatform: navigator?.userAgentData?.platform,
1339
+ offscreenCanvasAvailable: typeof globalThis.OffscreenCanvas ===
1340
+ "function",
1341
+ userAgent: navigator?.userAgent,
1342
+ });
1343
+ }
1155
1344
  /**
1156
1345
  * The gate. The session reaches a non-IDR anchor by declaring it a recovery
1157
1346
  * point, which only holds for AVCC-framed H.264 carrying its parameter sets, so
@@ -1675,4 +1864,3 @@ class FrameWalker {
1675
1864
  }
1676
1865
 
1677
1866
  export { AnalysisSession, FrameExtractor, FrameWalker, walkFrames };
1678
- //# sourceMappingURL=analysis.js.map
@@ -16,4 +16,3 @@ export interface CacheBudgets {
16
16
  * small 16:9 one.
17
17
  */
18
18
  export declare function resolveCacheBudgets(decodeWidth: number, decodeHeight: number, previewWidth: number): CacheBudgets;
19
- //# sourceMappingURL=cache-budget.d.ts.map
@@ -1,5 +1,6 @@
1
1
  import { type AnySourceHandle } from "./decode-source";
2
2
  import type { FrameId } from "./frame-timeline";
3
+ import type { DecodeDimensions } from "./decode-resolution";
3
4
  import { type ScrubCursor, ScrubCursorState, type ScrubFrame, type ScrubFrameListener, type ScrubTrackInfo } from "./scrub-cursor";
4
5
  import { type Sec } from "./types";
5
6
  /**
@@ -23,7 +24,9 @@ import { type Sec } from "./types";
23
24
  */
24
25
  export declare class CanvasSinkScrubCursor implements ScrubCursor {
25
26
  private readonly provider;
26
- private readonly trackInfo;
27
+ private trackInfo;
28
+ private outputGeneration;
29
+ private readonly outputReads;
27
30
  private readonly disposeSource;
28
31
  /**
29
32
  * Forward-playback iterator. Lives only between attachPlay and detachPlay so
@@ -50,6 +53,8 @@ export declare class CanvasSinkScrubCursor implements ScrubCursor {
50
53
  get state(): ScrubCursorState;
51
54
  get track(): ScrubTrackInfo;
52
55
  get isIdle(): boolean;
56
+ resizeOutput({ width, height }: DecodeDimensions): Promise<boolean>;
57
+ private trackOutputRead;
53
58
  open(): Promise<void>;
54
59
  /** No cache to read; the scheduler is the caching backend. */
55
60
  peekCached(): ScrubFrame | null;
@@ -87,4 +92,3 @@ export declare class CanvasSinkScrubCursor implements ScrubCursor {
87
92
  private flushIdleResolvers;
88
93
  private flushSettleResolvers;
89
94
  }
90
- //# sourceMappingURL=canvas-sink-scrub-cursor.d.ts.map
@@ -80,4 +80,3 @@ export declare class AudioContextMediaClock implements MediaClock {
80
80
  pause(): void;
81
81
  seek(t: number): void;
82
82
  }
83
- //# sourceMappingURL=clock.d.ts.map
@@ -231,6 +231,7 @@ export declare const DIAGNOSTICS: {
231
231
  readonly TRACE_EVENT_CAP: 2000;
232
232
  readonly TRACE_SNAPSHOT_CAP: 600;
233
233
  readonly SCRUB_LATENCY_RING: 64;
234
+ readonly KEYFRAME_TIMESTAMPS_CAP: 512;
234
235
  };
235
236
  /**
236
237
  * What an armed capture actually keeps. The snapshot ring is fed at the fixed
@@ -248,6 +249,13 @@ export declare const HANG_RECOVERY: {
248
249
  readonly DECODE_HANG_TIMEOUT_MS: 30000;
249
250
  readonly SEED_HANG_TIMEOUT_MS: 8000;
250
251
  readonly SEED_DECODE_ATTEMPTS: 3;
252
+ /**
253
+ * A movement whose decode settled without a matching crisp paint is ended
254
+ * here, while the worker can still return its specific failure through the
255
+ * command channel. The remaining margin belongs to message delivery and
256
+ * main-thread scheduling; without it the facade replaces the useful verdict
257
+ * with its generic worker-command timeout.
258
+ */
259
+ readonly PRESENTATION_LATCH_TIMEOUT_MS: number;
251
260
  readonly WORKER_COMMAND_TIMEOUT_MS: number;
252
261
  };
253
- //# sourceMappingURL=constants.d.ts.map
@@ -1,7 +1,8 @@
1
1
  import type { DecodeResolutionStrategy } from "./decode-resolution";
2
2
  import type { ScrubCursorFactory } from "./scrub-cursor";
3
3
  import type { SourceResidency } from "./source-residency";
4
- import type { UrlSourceReadConfig, VideoEngineError, VideoSource } from "./types";
4
+ import type { PresentationMode } from "./types";
5
+ import type { UrlSourceReadConfig, WebVideoEngineError, VideoSource } from "./types";
5
6
  /** On-screen canvas measurements the decode strategy reasons over. */
6
7
  export interface ScrubViewport {
7
8
  /** CSS-pixel width of the canvas box, or null when unmeasured. */
@@ -20,6 +21,9 @@ export interface ScrubCacheConfig {
20
21
  }
21
22
  export interface CreateScrubCursorOptions {
22
23
  source: VideoSource;
24
+ /** How decoded pixels leave the worker. Host-frame presentation keeps samples
25
+ * available until the selected output can be copied for transfer. */
26
+ presentation?: PresentationMode;
23
27
  /**
24
28
  * Decides the resolution frames are held and painted at: the display canvas
25
29
  * backing store and every cache blit. Combined with the viewport and the
@@ -42,7 +46,7 @@ export interface CreateScrubCursorOptions {
42
46
  * all. A failure after open() cannot surface as a rejected promise, since by
43
47
  * then nobody is awaiting one; this is how it reaches the transport.
44
48
  */
45
- onDecodeFailure?: (error: VideoEngineError) => void;
49
+ onDecodeFailure?: (error: WebVideoEngineError) => void;
46
50
  /** Serves the demuxer's byte reads from what this process already holds. */
47
51
  sourceResidency?: SourceResidency;
48
52
  /** Read tuning for a URL source, passed through to mediabunny. */
@@ -59,4 +63,3 @@ export interface CreateScrubCursorOptions {
59
63
  * both backends share the same opened source.
60
64
  */
61
65
  export declare const createScrubCursor: ScrubCursorFactory;
62
- //# sourceMappingURL=create-scrub-cursor.d.ts.map
@@ -112,4 +112,3 @@ export declare function displayBoxResolution(options: DisplayBoxResolutionOption
112
112
  * native rather than producing a broken surface.
113
113
  */
114
114
  export declare function resolveDecodeDimensions(strategy: DecodeResolutionStrategy, ctx: DecodeResolutionContext): DecodeDimensions;
115
- //# sourceMappingURL=decode-resolution.d.ts.map
@@ -1,8 +1,9 @@
1
+ import type { DecodeDimensions } from "./decode-resolution";
1
2
  import { type AnySourceHandle } from "./decode-source";
2
3
  import { FrameCache } from "./frame-cache";
3
4
  import type { FrameId } from "./frame-timeline";
4
5
  import { type ScrubCursor, ScrubCursorState, type ScrubFrame, type ScrubFrameListener, type ScrubTrackInfo, type SchedulerStats, type SeekIntent } from "./scrub-cursor";
5
- import { type Sec, VideoEngineError } from "./types";
6
+ import { type Sec, WebVideoEngineError } from "./types";
6
7
  export interface DecodeSchedulerOptions {
7
8
  readonly source: AnySourceHandle;
8
9
  readonly cache: FrameCache;
@@ -12,6 +13,8 @@ export interface DecodeSchedulerOptions {
12
13
  readonly previewToleranceMs?: number;
13
14
  /** Millisecond time source for latency stats; defaults to performance.now. */
14
15
  readonly now?: () => number;
16
+ /** Delay before scrub-neighbor prefetch; overridable for deterministic tests. */
17
+ readonly scrubPrefetchQuietMs?: number;
15
18
  /**
16
19
  * Opens a fresh source handle to replace a wedged decoder, or null when the
17
20
  * source cannot be re-opened (a one-shot stream). mediabunny decodes take no
@@ -39,8 +42,12 @@ export interface DecodeSchedulerOptions {
39
42
  * accepts every command and answers none of them, so without this the
40
43
  * transport reads as playing over a canvas that will never change again.
41
44
  */
42
- readonly onDecodeFailure?: (error: VideoEngineError) => void;
45
+ readonly onDecodeFailure?: (error: WebVideoEngineError) => void;
43
46
  }
47
+ /** Bounds the clone payload while preserving the first and last discovered
48
+ * anchors and sampling the interior evenly. GOP statistics still use the full
49
+ * index; only the diagnostic lane is thinned. */
50
+ export declare function boundedKeyframeTimestamps(keyframesS: readonly number[]): number[];
44
51
  /**
45
52
  * Caching scrub cursor. Wraps the same decode primitives as CanvasSinkScrubCursor
46
53
  * but routes every random-access seek through a two-tier FrameCache and prefetches
@@ -60,12 +67,16 @@ export interface DecodeSchedulerOptions {
60
67
  */
61
68
  export declare class DecodeScheduler implements ScrubCursor {
62
69
  private provider;
63
- private readonly cache;
70
+ private cache;
64
71
  private keyframeIndex;
65
- private readonly trackInfo;
72
+ private trackInfo;
73
+ private outputGeneration;
74
+ private resizingOutput;
75
+ private readonly outputReads;
66
76
  private readonly exactTolMs;
67
77
  private readonly previewTolMs;
68
78
  private readonly now;
79
+ private readonly scrubPrefetchQuietMs;
69
80
  /** Re-opens the source after a hung decode, or null when it cannot be re-opened. */
70
81
  private readonly reopen;
71
82
  private readonly decodeHangTimeoutMs;
@@ -111,6 +122,8 @@ export declare class DecodeScheduler implements ScrubCursor {
111
122
  /** Outstanding background prefetch chain, or null once it unwinds. Awaited
112
123
  * only by close(), so teardown cannot outrun a sweep still holding a decoder. */
113
124
  private prefetchTask;
125
+ /** True only while a sweep is decoding, not while scrub prefetch is parked. */
126
+ private prefetchDecoding;
114
127
  /** Bumped when a running prefetch is cancelled; that prefetch bails when it
115
128
  * sees the token move, which is how cancellation propagates. */
116
129
  private prefetchGen;
@@ -143,6 +156,7 @@ export declare class DecodeScheduler implements ScrubCursor {
143
156
  get track(): ScrubTrackInfo;
144
157
  get playReadAhead(): number;
145
158
  get isIdle(): boolean;
159
+ resizeOutput({ width, height }: DecodeDimensions): Promise<boolean>;
146
160
  open(): Promise<void>;
147
161
  /**
148
162
  * The first frame, on the seed ceiling and its own attempt budget.
@@ -260,6 +274,7 @@ export declare class DecodeScheduler implements ScrubCursor {
260
274
  * decoder having a bad moment from one that will never answer again.
261
275
  */
262
276
  private withDecodeWatchdog;
277
+ private trackOutputRead;
263
278
  /**
264
279
  * The same watchdog over a container metadata read. Deliberately unbooked:
265
280
  * the keyframe probe never touches the decoder, so its answer is no evidence
@@ -342,6 +357,13 @@ export declare class DecodeScheduler implements ScrubCursor {
342
357
  private perSideWindowFrames;
343
358
  private frameIntervalS;
344
359
  private currentPositionS;
360
+ /** The timeline's name for a decoded frame, which is what the exact cache
361
+ * tier stores it under. Two decodes of one frame land on one identity
362
+ * however their microsecond timestamps differ, and two frames never share
363
+ * one however close together the source puts them. */
364
+ private frameIdOf;
365
+ /** The timeline identity of the frame covering a presentation time. */
366
+ private frameAtTime;
345
367
  private emitDecoded;
346
368
  /** Puts a decoded frame in both tiers without painting it, and releases it.
347
369
  * For a frame worth keeping whose moment to be seen has passed. */
@@ -353,4 +375,3 @@ export declare class DecodeScheduler implements ScrubCursor {
353
375
  private flushIdleResolvers;
354
376
  private flushSettleResolvers;
355
377
  }
356
- //# sourceMappingURL=decode-scheduler.d.ts.map
@@ -1,3 +1,4 @@
1
+ import type { DecodeDimensions } from "./decode-resolution";
1
2
  import { type Rotation } from "./rotation";
2
3
  import type { VideoSampleLike } from "./scrub-cursor";
3
4
  /**
@@ -33,6 +34,11 @@ export interface DecodeSessionOptions {
33
34
  readonly packets: PacketSource;
34
35
  readonly config: VideoDecoderConfig;
35
36
  readonly createDecoder: (init: VideoDecoderInit) => VideoDecoderLike;
37
+ /** Dimensions used to take ownership of decoder output pixels when set. */
38
+ readonly outputWidth?: number;
39
+ readonly outputHeight?: number;
40
+ /** Test seam for taking ownership of decoder output pixels. */
41
+ readonly snapshotFrame?: (frame: VideoFrame, width: number, height: number) => VideoFrame;
36
42
  /**
37
43
  * The track's quarter turn, which the session's frames carry and its draw
38
44
  * applies. A raw decoded picture holds the stored pixels and nothing about
@@ -59,6 +65,7 @@ export declare function sessionDrivable(config: VideoDecoderConfig): boolean;
59
65
  * wraps it stays fake-injectable under test.
60
66
  */
61
67
  export interface SessionFrameSource {
68
+ resizeOutput?(dimensions: DecodeDimensions): Promise<void>;
62
69
  frameAt(targetS: number): Promise<VideoSampleLike | null>;
63
70
  framesFrom(startS: number): AsyncGenerator<VideoSampleLike, void, unknown>;
64
71
  framesCovering(startS: number, endS: number): AsyncGenerator<VideoSampleLike, void, unknown>;
@@ -144,6 +151,9 @@ export declare class DecodeSession implements SessionFrameSource {
144
151
  private epoch;
145
152
  private readonly outputTimeoutMs;
146
153
  private readonly rotation;
154
+ private outputWidth;
155
+ private outputHeight;
156
+ private readonly snapshotFrame;
147
157
  constructor(options: DecodeSessionOptions);
148
158
  /** Times the session has configured the decoder onto an anchor. */
149
159
  get anchorCount(): number;
@@ -155,6 +165,7 @@ export declare class DecodeSession implements SessionFrameSource {
155
165
  * when there is one, since those are still servable, else the last position
156
166
  * handed out. */
157
167
  get reachableFromS(): number;
168
+ resizeOutput({ width, height }: DecodeDimensions): Promise<void>;
158
169
  /** The frame at or before `targetS`, or null when nothing precedes it. */
159
170
  frameAt(targetS: number): Promise<VideoSampleLike | null>;
160
171
  /**
@@ -273,4 +284,3 @@ export declare class DecodeSession implements SessionFrameSource {
273
284
  private latch;
274
285
  private discardDecoded;
275
286
  }
276
- //# sourceMappingURL=decode-session.d.ts.map