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.
- package/dist/constants/media-renderer.d.ts +3 -0
- package/dist/constants/media-renderer.d.ts.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1722 -1395
- package/dist/index.js.map +1 -1
- package/dist/mask-preparation.worker.js +109 -31
- package/dist/mask-preparation.worker.js.map +1 -1
- package/dist/media/media-frame-clock.d.ts +4 -0
- package/dist/media/media-frame-clock.d.ts.map +1 -0
- package/dist/media/media-source-state.d.ts.map +1 -1
- package/dist/media/media-source.d.ts +6 -0
- package/dist/media/media-source.d.ts.map +1 -1
- package/dist/media/video-engine-media-source.d.ts +17 -7
- package/dist/media/video-engine-media-source.d.ts.map +1 -1
- package/dist/media/video-engine-media-source.js +3 -0
- package/dist/media/video-engine-media-source.js.map +1 -0
- package/dist/playback/media-playback-controller.d.ts +7 -1
- package/dist/playback/media-playback-controller.d.ts.map +1 -1
- package/dist/render-preparation/mask-frame-artifact.d.ts +2 -0
- package/dist/render-preparation/mask-frame-artifact.d.ts.map +1 -1
- package/dist/render-preparation/mask-frame-compositor.d.ts +7 -0
- package/dist/render-preparation/mask-frame-compositor.d.ts.map +1 -1
- package/dist/render-preparation/mask-preparation-worker-protocol.d.ts +1 -0
- package/dist/render-preparation/mask-preparation-worker-protocol.d.ts.map +1 -1
- package/dist/render-preparation/prepared-render-window.d.ts +21 -2
- package/dist/render-preparation/prepared-render-window.d.ts.map +1 -1
- package/dist/render-preparation/prepared-window-timeline.d.ts +1 -0
- package/dist/render-preparation/prepared-window-timeline.d.ts.map +1 -1
- package/dist/renderers/injected-pixi.d.ts +38 -0
- package/dist/renderers/injected-pixi.d.ts.map +1 -0
- package/dist/renderers/media-frame-navigation.d.ts +17 -0
- package/dist/renderers/media-frame-navigation.d.ts.map +1 -0
- package/dist/renderers/media-renderer-core.d.ts +9 -0
- package/dist/renderers/media-renderer-core.d.ts.map +1 -1
- package/dist/renderers/media-renderer-scene.d.ts +17 -5
- package/dist/renderers/media-renderer-scene.d.ts.map +1 -1
- package/dist/renderers/media-renderer-state.d.ts +9 -0
- package/dist/renderers/media-renderer-state.d.ts.map +1 -1
- package/dist/renderers/media-renderer-transport.d.ts +19 -3
- package/dist/renderers/media-renderer-transport.d.ts.map +1 -1
- package/dist/renderers/pixi-focus-layer.d.ts +1 -0
- package/dist/renderers/pixi-focus-layer.d.ts.map +1 -1
- package/dist/renderers/pixi-frame-present.d.ts +1 -1
- package/dist/renderers/pixi-frame-present.d.ts.map +1 -1
- package/dist/renderers/pixi-id-mask-shader.d.ts +4 -33
- package/dist/renderers/pixi-id-mask-shader.d.ts.map +1 -1
- package/dist/renderers/pixi-interaction-presentation-layer.d.ts +0 -1
- package/dist/renderers/pixi-interaction-presentation-layer.d.ts.map +1 -1
- package/dist/renderers/pixi-mask-halo.d.ts +4 -33
- package/dist/renderers/pixi-mask-halo.d.ts.map +1 -1
- package/dist/renderers/pixi-mask-layer.d.ts +28 -29
- package/dist/renderers/pixi-mask-layer.d.ts.map +1 -1
- package/dist/renderers/pixi-media-scene.d.ts.map +1 -1
- package/dist/renderers/pixi-polygon-layer.d.ts +2 -0
- package/dist/renderers/pixi-polygon-layer.d.ts.map +1 -1
- package/dist/renderers/pixi-region-coverage-mask.d.ts +4 -33
- package/dist/renderers/pixi-region-coverage-mask.d.ts.map +1 -1
- package/dist/renderers/pixi-region-layer.d.ts +6 -31
- package/dist/renderers/pixi-region-layer.d.ts.map +1 -1
- package/dist/renderers/pixi-shader-lifecycle.d.ts +14 -0
- package/dist/renderers/pixi-shader-lifecycle.d.ts.map +1 -0
- package/dist/renderers/prepared-annotation-window.d.ts.map +1 -1
- package/dist/renderers/presented-frame-channel.d.ts +34 -1
- package/dist/renderers/presented-frame-channel.d.ts.map +1 -1
- package/dist/sessions/media-session-defaults.d.ts.map +1 -1
- package/dist/sessions/media-session-detections.d.ts.map +1 -1
- package/dist/sessions/media-session-state.d.ts.map +1 -1
- package/dist/sessions/media-session.d.ts.map +1 -1
- package/dist/tracking.worker.js +19 -5
- package/dist/tracking.worker.js.map +1 -1
- package/dist/types/media-frame-clock.d.ts +16 -0
- package/dist/types/media-frame-clock.d.ts.map +1 -0
- package/dist/types/media-frame-navigation.d.ts +28 -0
- package/dist/types/media-frame-navigation.d.ts.map +1 -0
- package/dist/types/media-renderer-display.d.ts +12 -0
- package/dist/types/media-renderer-display.d.ts.map +1 -0
- package/dist/types/media-renderer.d.ts +13 -0
- package/dist/types/media-renderer.d.ts.map +1 -1
- package/dist/types/media-session.d.ts +35 -32
- package/dist/types/media-session.d.ts.map +1 -1
- package/dist/types/render-preparation.d.ts +78 -28
- package/dist/types/render-preparation.d.ts.map +1 -1
- package/dist/video-engine-media-source-DR-x1UfK.js +483 -0
- package/dist/video-engine-media-source-DR-x1UfK.js.map +1 -0
- package/dist/web-video-engine/analysis-session.d.ts +0 -1
- package/dist/web-video-engine/analysis.d.ts +0 -1
- package/dist/web-video-engine/analysis.js +211 -23
- package/dist/web-video-engine/cache-budget.d.ts +0 -1
- package/dist/web-video-engine/canvas-sink-scrub-cursor.d.ts +6 -2
- package/dist/web-video-engine/clock.d.ts +0 -1
- package/dist/web-video-engine/constants.d.ts +9 -1
- package/dist/web-video-engine/create-scrub-cursor.d.ts +6 -3
- package/dist/web-video-engine/decode-resolution.d.ts +0 -1
- package/dist/web-video-engine/decode-scheduler.d.ts +26 -5
- package/dist/web-video-engine/decode-session.d.ts +11 -1
- package/dist/web-video-engine/decode-source.d.ts +11 -1
- package/dist/web-video-engine/diagnostics-store.d.ts +0 -1
- package/dist/web-video-engine/diagnostics.d.ts +19 -19
- package/dist/web-video-engine/embedded-engine-worker.d.ts +0 -1
- package/dist/web-video-engine/engine-core.d.ts +31 -11
- package/dist/web-video-engine/engine.d.ts +3 -4
- package/dist/web-video-engine/engine.js +175 -44
- package/dist/web-video-engine/engine.worker.d.ts +0 -1
- package/dist/web-video-engine/engine.worker.js +5712 -2947
- package/dist/web-video-engine/frame-cache.d.ts +121 -12
- package/dist/web-video-engine/frame-extractor.d.ts +0 -1
- package/dist/web-video-engine/{frame-timeline-GINI8Gum.js → frame-timeline-DreEwsRY.js} +83 -27
- package/dist/web-video-engine/frame-timeline.d.ts +10 -1
- package/dist/web-video-engine/frame-walker.d.ts +0 -1
- package/dist/web-video-engine/index.d.ts +12 -2
- package/dist/web-video-engine/index.d.ts.map +1 -1
- package/dist/web-video-engine/index.js +2 -2
- package/dist/web-video-engine/key-packet.d.ts +0 -1
- package/dist/web-video-engine/keyframe-index.d.ts +0 -1
- package/dist/web-video-engine/mirror-store.d.ts +0 -1
- package/dist/web-video-engine/pausable-deadline.d.ts +11 -0
- package/dist/web-video-engine/renderer.d.ts +0 -1
- package/dist/web-video-engine/rotation.d.ts +0 -1
- package/dist/web-video-engine/scrub-controller.d.ts +8 -5
- package/dist/web-video-engine/scrub-cursor.d.ts +15 -7
- package/dist/web-video-engine/scrub-trajectory.d.ts +0 -1
- package/dist/web-video-engine/source-residency.d.ts +35 -1
- package/dist/web-video-engine/trace-recorder.d.ts +0 -1
- package/dist/web-video-engine/types.d.ts +18 -22
- package/dist/web-video-engine/video-engine.d.ts +39 -17
- package/dist/web-video-engine/webgpu-renderer.d.ts +0 -1
- package/dist/web-video-engine/worker-bridge.d.ts +0 -1
- package/dist/web-video-engine/worker-dispatch.d.ts +0 -1
- package/dist/web-video-engine/worker-protocol.d.ts +38 -15
- package/node_modules/supervision-js-core/dist/detections/buffered-detection-timeline.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/index.d.ts +2 -2
- package/node_modules/supervision-js-core/dist/index.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/index.js +182 -68
- package/node_modules/supervision-js-core/dist/index.js.map +1 -1
- package/node_modules/supervision-js-core/dist/types/detection-timeline.d.ts +20 -3
- package/node_modules/supervision-js-core/dist/types/detection-timeline.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/types/media-rendering.d.ts +28 -26
- package/node_modules/supervision-js-core/dist/types/media-rendering.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/types/session-lifecycle.d.ts +16 -0
- package/node_modules/supervision-js-core/dist/types/session-lifecycle.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/utils/detection-masks.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/utils/id-mask-frame.d.ts +19 -0
- package/node_modules/supervision-js-core/dist/utils/id-mask-frame.d.ts.map +1 -1
- package/package.json +3 -3
- package/dist/media/media-condition-probe.d.ts +0 -51
- package/dist/media/media-condition-probe.d.ts.map +0 -1
- package/dist/media/media-conditions.d.ts +0 -28
- package/dist/media/media-conditions.d.ts.map +0 -1
- package/dist/types/media-conditions.d.ts +0 -149
- package/dist/types/media-conditions.d.ts.map +0 -1
- package/dist/web-video-engine/analysis-session.d.ts.map +0 -1
- package/dist/web-video-engine/analysis.d.ts.map +0 -1
- package/dist/web-video-engine/analysis.js.map +0 -1
- package/dist/web-video-engine/cache-budget.d.ts.map +0 -1
- package/dist/web-video-engine/canvas-sink-scrub-cursor.d.ts.map +0 -1
- package/dist/web-video-engine/clock.d.ts.map +0 -1
- package/dist/web-video-engine/constants.d.ts.map +0 -1
- package/dist/web-video-engine/create-scrub-cursor.d.ts.map +0 -1
- package/dist/web-video-engine/decode-resolution.d.ts.map +0 -1
- package/dist/web-video-engine/decode-scheduler.d.ts.map +0 -1
- package/dist/web-video-engine/decode-session.d.ts.map +0 -1
- package/dist/web-video-engine/decode-source.d.ts.map +0 -1
- package/dist/web-video-engine/diagnostics-store.d.ts.map +0 -1
- package/dist/web-video-engine/diagnostics.d.ts.map +0 -1
- package/dist/web-video-engine/embedded-engine-worker.d.ts.map +0 -1
- package/dist/web-video-engine/engine-core.d.ts.map +0 -1
- package/dist/web-video-engine/engine.d.ts.map +0 -1
- package/dist/web-video-engine/engine.js.map +0 -1
- package/dist/web-video-engine/engine.worker.d.ts.map +0 -1
- package/dist/web-video-engine/engine.worker.js.map +0 -1
- package/dist/web-video-engine/frame-cache.d.ts.map +0 -1
- package/dist/web-video-engine/frame-extractor.d.ts.map +0 -1
- package/dist/web-video-engine/frame-timeline-GINI8Gum.js.map +0 -1
- package/dist/web-video-engine/frame-timeline.d.ts.map +0 -1
- package/dist/web-video-engine/frame-walker.d.ts.map +0 -1
- package/dist/web-video-engine/key-packet.d.ts.map +0 -1
- package/dist/web-video-engine/keyframe-index.d.ts.map +0 -1
- package/dist/web-video-engine/mirror-store.d.ts.map +0 -1
- package/dist/web-video-engine/renderer.d.ts.map +0 -1
- package/dist/web-video-engine/rotation.d.ts.map +0 -1
- package/dist/web-video-engine/scrub-controller.d.ts.map +0 -1
- package/dist/web-video-engine/scrub-cursor.d.ts.map +0 -1
- package/dist/web-video-engine/scrub-trajectory.d.ts.map +0 -1
- package/dist/web-video-engine/source-residency.d.ts.map +0 -1
- package/dist/web-video-engine/trace-recorder.d.ts.map +0 -1
- package/dist/web-video-engine/types.d.ts.map +0 -1
- package/dist/web-video-engine/video-engine.d.ts.map +0 -1
- package/dist/web-video-engine/webgpu-renderer.d.ts.map +0 -1
- package/dist/web-video-engine/worker-bridge.d.ts.map +0 -1
- package/dist/web-video-engine/worker-dispatch.d.ts.map +0 -1
- 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 {
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1112
|
-
: new
|
|
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
|
|
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
|
|
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
|
|
@@ -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
|
|
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
|
|
@@ -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 {
|
|
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:
|
|
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,
|
|
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:
|
|
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
|
|
70
|
+
private cache;
|
|
64
71
|
private keyframeIndex;
|
|
65
|
-
private
|
|
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
|