@taole/giftstage 0.1.33 → 0.1.35

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.
@@ -26,14 +26,16 @@ export interface TextureOpts {
26
26
  sourcePremultiplied?: boolean;
27
27
  }
28
28
  export type ShaderType = 'svga-batch-premul'
29
- /** WebGPU / WebGL2: SVGA premultiplied atlas sprites batched as instances using `FrameJobData` payloads. */
29
+ /** WebGPU / WebGL2: SVGA premultiplied atlas sprites batched as packed instances. */
30
30
  | 'svga-instanced-premul' | 'vap'
31
31
  /** Main-context VAP mixed resource draw: resource texture multiplied by raw-video mask. */
32
32
  | 'vap-mix-overlay'
33
33
  /** WebGPU / WebGL2: batched VAP sharing one texture; see `instanceCount` / `instanceData` (32 floats/instance). */
34
34
  | 'vap-instanced' | 'alpha-video'
35
35
  /** WebGPU: batched draws sharing one texture (e.g. atlas); see `instanceCount` / `instanceData`. */
36
- | 'alpha-video-instanced' | 'rgba-textured' | 'clip-mask';
36
+ | 'alpha-video-instanced' | 'rgba-textured' | 'clip-mask'
37
+ /** WebGPU / WebGL2: one local-space clip mesh transformed by per-instance affine data. */
38
+ | 'clip-mask-instanced';
37
39
  export type StencilMode = 'none' | 'write' | 'test' | 'clear';
38
40
  export interface DrawCommand {
39
41
  shader: ShaderType;
@@ -44,13 +46,15 @@ export interface DrawCommand {
44
46
  uniforms: Record<string, Float32Array | number>;
45
47
  stencil?: StencilMode;
46
48
  /**
47
- * `svga-instanced-premul`: the first `instanceCount * 13` floats are consumed
48
- * (layout + transform + alpha + uv rect).
49
+ * `svga-instanced-premul`: the first `instanceCount * 13` words are consumed
50
+ * (pre-scaled transform, alpha, UV rect, and two packed framebuffer clip pairs).
49
51
  *
50
52
  * `alpha-video-instanced`: the first `instanceCount * 12` floats are consumed
51
53
  * (display rect + atlasRemap + opacity/source-alpha-mode vec4).
52
54
  * `vap-instanced`: the first `instanceCount * 32` floats are consumed
53
55
  * (mat4 + atlasRemap + rgb/alpha rects + opacity/source-alpha-mode vec4).
56
+ * `clip-mask-instanced`: the first `instanceCount * 6` floats are consumed
57
+ * (a, b, c, d, tx, ty affine transform).
54
58
  * Renderers may pass a larger reused scratch array to avoid allocating a subarray view per draw.
55
59
  */
56
60
  instanceCount?: number;
@@ -80,6 +84,15 @@ export interface GPUBackend {
80
84
  clearStencil?(): void;
81
85
  setScissor(x: number, y: number, w: number, h: number): void;
82
86
  resetScissor(): void;
87
+ /** Internal performance-harness diagnostics; absent on backends without native buffer uploads. */
88
+ getPerformanceSnapshot?(): {
89
+ bufferWrites: number;
90
+ bufferWriteBytes: number;
91
+ storageBufferWrites: number;
92
+ storageBufferWriteBytes: number;
93
+ };
94
+ /** Resets the internal performance-harness diagnostics for a new sampling window. */
95
+ resetPerformanceSnapshot?(): void;
83
96
  destroy(): void;
84
97
  }
85
98
  export interface SVGAParams {
@@ -148,6 +161,8 @@ export interface SVGASpriteTable {
148
161
  shapesTable: SVGAShape[][];
149
162
  shapesIndex: Int32Array;
150
163
  hasAnyShapes: boolean;
164
+ /** Internal parser-derived fast-path flag; undefined only for legacy/incomplete data. */
165
+ allFramesInsideViewBox?: boolean;
151
166
  }
152
167
  export interface SVGAAudio {
153
168
  audioKey: string;
@@ -271,6 +286,47 @@ export interface ResolvedSVGASlotTexture {
271
286
  logicalWidth: number;
272
287
  logicalHeight: number;
273
288
  }
289
+ /**
290
+ * SVGABatchRenderer-internal: one packed instance row for a `stopped` slot's static batch
291
+ * cache (P1-10). `row` mirrors the exact `FRAME_DATA_FLOATS` layout `packFrameData` produces.
292
+ * `isOverride` mirrors whether the sprite used a `svgaSlots` texture override (drawn immediately,
293
+ * breaking any running atlas batch) vs. a normal atlas sprite (batched with same-texture sprites).
294
+ */
295
+ export interface SVGAStaticBatchCacheEntry {
296
+ row: Float32Array;
297
+ handle: GPUTextureHandle;
298
+ isOverride: boolean;
299
+ }
300
+ /**
301
+ * SVGABatchRenderer-internal: cached packed-instance-row list for a `stopped` SVGA slot, plus
302
+ * the exact snapshot of every input it was built from. Any mismatch against the slot's current
303
+ * values invalidates the cache (see `SVGABatchRenderer`'s match check) — favors over-invalidating
304
+ * to guarantee correctness over maximizing hit rate.
305
+ */
306
+ export interface SVGAStaticBatchCache {
307
+ entries: SVGAStaticBatchCacheEntry[];
308
+ /** `VideoEntity` reference the cache was built against (covers spriteTable/atlasRects identity). */
309
+ entity: unknown;
310
+ /** `entity.atlasTextureHandles` (if any) or `entity.atlasTextureHandle`, whichever is in use. */
311
+ atlasHandlesDep: unknown;
312
+ /**
313
+ * `slot.svgaSlotTextures` map reference. `svgaSlots` overrides are immutable post-load, but
314
+ * tracked defensively so a future change to that invariant can't silently go stale here.
315
+ */
316
+ svgaSlotTextures: unknown;
317
+ currentFrame: number;
318
+ opacity: number;
319
+ x: number;
320
+ y: number;
321
+ width: number;
322
+ height: number;
323
+ scale: number;
324
+ rotation: number;
325
+ sx: number;
326
+ sy: number;
327
+ offsetX: number;
328
+ offsetY: number;
329
+ }
274
330
  export interface ResolvedVAPResource {
275
331
  source: CanvasImageSource;
276
332
  width: number;
@@ -566,6 +622,11 @@ export interface GiftSlot {
566
622
  accumulatedTime: number;
567
623
  lastTickTime: number;
568
624
  loopCount: number;
625
+ /**
626
+ * SVGABatchRenderer internal: most recent frame whose complete set of referenced clip meshes
627
+ * is ready. A later frame with a mesh miss renders this frame until the missing meshes land.
628
+ */
629
+ svgaClipReadyFrame?: number | null;
569
630
  videoEntity: VideoEntity | null;
570
631
  loadAbortController?: AbortController | null;
571
632
  /** HTMLVideoElement finite-playback watchdog; cleared on normal completion or resource release. */
@@ -613,6 +674,32 @@ export interface GiftSlot {
613
674
  map: unknown;
614
675
  size: number;
615
676
  };
677
+ /**
678
+ * RenderManager internal: cached coincident-visual-duplicate key + the exact field snapshot it
679
+ * was built from. Rebuilt only when one of those fields actually changes, avoiding a template
680
+ * string array + `join('|')` allocation on every dirty frame for every visible slot.
681
+ */
682
+ coincidentVisualKeyCache?: {
683
+ key: string;
684
+ resourceKey: string;
685
+ frameKey: string;
686
+ svgaSlotKey: string;
687
+ objectFit: string;
688
+ x: number;
689
+ y: number;
690
+ width: number;
691
+ height: number;
692
+ scale: number;
693
+ opacity: number;
694
+ rotation: number | undefined;
695
+ originX: string | number;
696
+ originY: string | number;
697
+ };
698
+ /**
699
+ * SVGABatchRenderer internal: cached packed instance rows for a `stopped` slot with no
700
+ * shape/clip sprites this frame (P1-10). Rebuilt whenever any tracked input changes.
701
+ */
702
+ svgaStaticBatchCache?: SVGAStaticBatchCache | null;
616
703
  onFrame?: (frame: number, gift: GiftHandle) => void;
617
704
  onComplete?: (gift: GiftHandle) => void;
618
705
  }
@@ -654,6 +741,8 @@ export interface VAPFrameObj {
654
741
  mt?: number;
655
742
  frame: [number, number, number, number];
656
743
  mFrame: [number, number, number, number];
744
+ /** `String(srcId)` precomputed once by `buildVAPFrameMap` to avoid per-draw re-stringification. */
745
+ srcKey?: string;
657
746
  }
658
747
  export interface FlyAnimation {
659
748
  giftId: string;
@@ -20,10 +20,11 @@ export declare function resolveGiftTransformOrigin(slot: GiftSlot, out?: GiftTra
20
20
  export declare function fillGiftTransformMatrix(slot: GiftSlot, x: number, y: number, width: number, height: number, out: Float32Array): Float32Array;
21
21
  /** Apply the gift's parent rotation to an existing local-to-stage affine transform. */
22
22
  export declare function rotateGiftAffine(slot: GiftSlot, transform: Affine2D, out?: Affine2D): Affine2D;
23
- /** AABB of a rotated stage-space rectangle, suitable for the backend scissor. */
24
- export declare function getRotatedRectAABB(slot: GiftSlot, x: number, y: number, width: number, height: number): {
23
+ export interface RectAABB {
25
24
  x: number;
26
25
  y: number;
27
26
  w: number;
28
27
  h: number;
29
- };
28
+ }
29
+ /** AABB of a rotated stage-space rectangle, suitable for the backend scissor. */
30
+ export declare function getRotatedRectAABB(slot: GiftSlot, x: number, y: number, width: number, height: number, out?: RectAABB): RectAABB;
@@ -10,6 +10,19 @@ export declare class AsyncLimiter {
10
10
  run<T>(task: () => Promise<T>): Promise<T>;
11
11
  private drain;
12
12
  }
13
+ /**
14
+ * Structural estimate; avoids JSON.stringify of the whole sprites tree (main-thread spike).
15
+ *
16
+ * When the worker-binary path is used, `imageBlob` is the *entire* deserialized SGPB buffer
17
+ * (see `deserializeSVGAWorkerPayload`), not just the image bytes — `spriteTable.frameData`/
18
+ * `clipPathIndex`/`shapesIndex` are typed-array *views* sharing that same underlying
19
+ * `ArrayBuffer` (see `readSpriteTable` in `svga-sprite-table-binary.ts`). Adding their
20
+ * `byteLength` again on top of `imageBlob.byteLength` would double-count them, systematically
21
+ * inflating the estimate and causing the memory LRU to evict/re-parse too eagerly. Only add
22
+ * them when they do *not* share `imageBlob`'s buffer (the legacy `images` record path, where
23
+ * the sprite table is a separate allocation).
24
+ */
25
+ export declare function estimateSVGAWorkerPayloadBytes(payload: SVGAWorkerMoviePayload): number;
13
26
  export declare class ResourceCacheManager {
14
27
  private readonly limiter;
15
28
  private readonly pending;
@@ -32,6 +32,57 @@ export declare function decodeH264ToFrames(codec: string, description: ArrayBuff
32
32
  frames: VideoFrame[];
33
33
  timesSec: Float64Array;
34
34
  }>;
35
+ export interface IncrementalH264DecoderCallbacks {
36
+ onFrame: (index: number, frame: VideoFrame) => void;
37
+ onDecoded?: () => void;
38
+ onDropped?: () => void;
39
+ onEos?: () => void;
40
+ onError?: (error: unknown) => void;
41
+ }
42
+ /**
43
+ * Playhead-driven incremental H.264 decode: feeds chunks in decode order only up to
44
+ * `needIdx + decodeAheadFrames` (via {@link ensure}), so memory stays bounded by how far ahead
45
+ * of playback the caller asks for instead of decoding (and retaining) the entire clip upfront.
46
+ * Originally the Worker's demux/decode pump (see `webcodecs-mp4.worker.ts`); lifted here so the
47
+ * main-thread WebCodecs fallback (no Worker available, or `useWorker: false`) can reuse the exact
48
+ * same backpressure/rewind semantics instead of decoding the whole file into memory at once —
49
+ * see P1-10 optimization notes / `WebCodecsMp4Playback.createOnMainThread`.
50
+ */
51
+ export declare class IncrementalH264Decoder {
52
+ private readonly decodeOrder;
53
+ private readonly presTimes;
54
+ private readonly timescale;
55
+ private readonly codec;
56
+ private readonly description;
57
+ private readonly duration;
58
+ private readonly callbacks;
59
+ private decoder;
60
+ private nextChunk;
61
+ private chunksFed;
62
+ private targetNeedIdx;
63
+ private lastPostedMax;
64
+ private minPosted;
65
+ private playbackNeedFloor;
66
+ /** Remaining output budget from the latest `ensure`; decremented only when a frame is posted. */
67
+ private freeSlots;
68
+ private eosClosed;
69
+ private pumpScheduled;
70
+ private pumping;
71
+ private destroyed;
72
+ private readonly onDecoderDequeue;
73
+ constructor(decodeOrder: Sample[], presTimes: Float64Array, timescale: number, codec: string, description: ArrayBuffer | undefined, duration: number, callbacks: IncrementalH264DecoderCallbacks, initialNeedIdx: number);
74
+ /** Raise the decode target and/or update main-thread ring headroom; resumes the pump if idle. */
75
+ ensure(needIdx: number, freeSlots: number): void;
76
+ /** Restart decode from the nearest keyframe at/before t=0 (backward seek / loop wrap). */
77
+ rewind(): void;
78
+ destroy(): void;
79
+ private schedulePump;
80
+ private doRewind;
81
+ private openDecoder;
82
+ private feedOneChunk;
83
+ private finalizeEos;
84
+ private runPump;
85
+ }
35
86
  export declare function frameIndexForTime(timesSec: Float64Array, clipDurationSec: number, t: number): number;
36
87
  export declare function computeClipDurationSeconds(metaDurationSec: number, timesSec: Float64Array, frames: VideoFrame[]): number;
37
88
  /** Clip duration from demux metadata + presentation timeline only (no decoded frames). */
@@ -1,3 +1,12 @@
1
+ export interface WebCodecsPerformanceSnapshot {
2
+ decodedFrames: number;
3
+ postedFrames: number;
4
+ closedFrames: number;
5
+ residentPeak: number;
6
+ workerMessages: number;
7
+ }
8
+ export declare function resetWebCodecsPerformanceSnapshot(): void;
9
+ export declare function getWebCodecsPerformanceSnapshot(): WebCodecsPerformanceSnapshot;
1
10
  /**
2
11
  * Presentation-index → VideoFrame with bounded size.
3
12
  * Eviction follows the playhead (see {@link setPlaybackHint}) so we keep a window around the
@@ -11,6 +20,7 @@ export declare class VideoFrameRing {
11
20
  private playbackHint;
12
21
  constructor(maxSize: number);
13
22
  setPlaybackHint(idx: number): void;
23
+ private evictConsumedFrames;
14
24
  minKey(): number | undefined;
15
25
  set(idx: number, frame: VideoFrame): void;
16
26
  private evictDownToCapacity;
@@ -48,6 +58,8 @@ export declare class WebCodecsMp4Playback {
48
58
  private readonly timesSec;
49
59
  private readonly worker;
50
60
  private readonly workerMsgId;
61
+ /** Main-thread (no-Worker) counterpart of `worker`: same incremental pump, called in-process. */
62
+ private readonly mainThreadDecoder;
51
63
  private readonly streaming;
52
64
  private readonly ring;
53
65
  private readonly frames;
@@ -64,7 +76,19 @@ export declare class WebCodecsMp4Playback {
64
76
  */
65
77
  static create(source: string | ArrayBuffer, signal?: AbortSignal, opts?: WebCodecsMp4CreateOptions): Promise<WebCodecsMp4Playback>;
66
78
  private static createViaWorker;
79
+ /**
80
+ * Same incremental/backpressured decode as the Worker path (see `IncrementalH264Decoder`),
81
+ * called in-process instead of via `postMessage`. Bounds resident `VideoFrame`s to
82
+ * `maxDecodedFrames` via a `VideoFrameRing` instead of decoding the whole clip into a
83
+ * `VideoFrame[]` that stays fully resident for the playback's lifetime (P1-11) — the previous
84
+ * behavior made the main-thread fallback (used when Workers are unavailable or `useWorker:
85
+ * false`, typically the lowest-end devices) the worst case for decode-time memory peaks.
86
+ */
67
87
  private static createOnMainThread;
88
+ /** Send a `rewind` through whichever transport is active (Worker `postMessage` or in-process). */
89
+ private sendRewind;
90
+ /** Send an `ensure` through whichever transport is active (Worker `postMessage` or in-process). */
91
+ private sendEnsure;
68
92
  ensureDecodedThrough(tSec: number): void;
69
93
  getFrameForTime(tSec: number): VideoFrame | null;
70
94
  destroy(): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taole/giftstage",
3
- "version": "0.1.33",
3
+ "version": "0.1.35",
4
4
  "description": "High-performance WebGPU/WebGL gift animation player unifying SVGA, VAP, AlphaVideo, and images",
5
5
  "type": "module",
6
6
  "main": "dist/gift-stage.cjs.js",
@@ -1,369 +0,0 @@
1
- let y = null;
2
- function v() {
3
- return y || (y = import("./mp4box.all-Bcqr6y1c.js").catch((e) => {
4
- throw y = null, e;
5
- })), y;
6
- }
7
- const C = 800;
8
- function S(e) {
9
- return e.startsWith("avc1") || e.startsWith("avc3");
10
- }
11
- function I(e) {
12
- const t = new Uint8Array(e);
13
- if (t.byteLength < 8) return e;
14
- const n = t[4], r = t[5], o = t[6], i = t[7];
15
- return n === 97 && r === 118 && o === 99 && i === 67 ? e.slice(8) : e;
16
- }
17
- async function F(e) {
18
- const t = e.avcC;
19
- if (!t) return;
20
- const { DataStream: n } = await v(), r = new n();
21
- t.write(r);
22
- const o = r.byteLength;
23
- if (o <= 0) return;
24
- const i = r.buffer.slice(0, o);
25
- return I(i);
26
- }
27
- async function P(e) {
28
- const { createFile: t } = await v();
29
- return new Promise((n, r) => {
30
- const o = t(), i = e.slice(0);
31
- i.fileStart = 0, o.onError = (d) => {
32
- r(new Error(`mp4box: ${d}`));
33
- }, o.onReady = (d) => {
34
- var a, c, l, f, h, m, g;
35
- const s = (a = d.videoTracks) == null ? void 0 : a[0];
36
- if (!s) {
37
- r(new Error("mp4box: no video track"));
38
- return;
39
- }
40
- if (!S(s.codec)) {
41
- r(new Error(`mp4box: unsupported video codec ${s.codec}`));
42
- return;
43
- }
44
- if (s.nb_samples > C) {
45
- r(new Error(`mp4box: too many samples (${s.nb_samples})`));
46
- return;
47
- }
48
- const p = [];
49
- o.onSamples = (q, B, k) => {
50
- p.push(...k);
51
- }, o.setExtractionOptions(s.id, null, {
52
- nbSamples: Math.max(s.nb_samples, 1)
53
- }), o.start();
54
- const b = s.timescale > 0 ? s.duration / s.timescale : d.duration / (d.timescale || 1);
55
- if (p.length === 0) {
56
- r(new Error("mp4box: extraction produced no samples"));
57
- return;
58
- }
59
- n({
60
- codec: s.codec,
61
- timescale: s.timescale,
62
- durationSec: Number.isFinite(b) ? b : 0,
63
- videoWidth: (f = (l = (c = s.video) == null ? void 0 : c.width) != null ? l : s.track_width) != null ? f : 0,
64
- videoHeight: (g = (m = (h = s.video) == null ? void 0 : h.height) != null ? m : s.track_height) != null ? g : 0,
65
- samples: p
66
- });
67
- }, o.appendBuffer(i), o.flush();
68
- });
69
- }
70
- const x = 25e3;
71
- async function D(e, t, n) {
72
- const r = performance.now();
73
- let o = 0;
74
- for (; e.decodeQueueSize > 0; )
75
- if (await new Promise((i) => queueMicrotask(i)), o++, o % 120 === 0 && await new Promise((i) => setTimeout(i, 0)), performance.now() - r > n) {
76
- console.warn("[GiftStage:WC] decodeQueue drain gave up", {
77
- label: t,
78
- decodeQueueSize: e.decodeQueueSize,
79
- spins: o
80
- });
81
- break;
82
- }
83
- }
84
- async function E(e, t) {
85
- console.info("[GiftStage:WC] VideoDecoder.flush…", t);
86
- try {
87
- await Promise.race([
88
- e.flush(),
89
- new Promise((n, r) => {
90
- setTimeout(
91
- () => r(
92
- new Error(
93
- `VideoDecoder.flush() timed out after ${x}ms (${JSON.stringify(t)})`
94
- )
95
- ),
96
- x
97
- );
98
- })
99
- ]);
100
- } catch (n) {
101
- try {
102
- e.close();
103
- } catch (r) {
104
- }
105
- throw n;
106
- }
107
- }
108
- function _(e, t, n) {
109
- if (e.length === 0) return 0;
110
- const r = e.length;
111
- if (n <= e[0]) return 0;
112
- const o = e[r - 1];
113
- if (n >= t || n >= o) return r - 1;
114
- let i = 0, d = r - 1;
115
- for (; i + 1 < d; ) {
116
- const a = i + d >>> 1;
117
- e[a] <= n ? i = a : d = a;
118
- }
119
- return i;
120
- }
121
- function N(e, t) {
122
- if (t.length === 0) return 1e-6;
123
- const n = t.length, r = n - 1, o = t[r], i = n >= 2 ? t[r] - t[r - 1] : 1 / 30, d = o + i;
124
- let a = e > 0 ? e : d;
125
- if (!(a > 0) && n >= 2) {
126
- const c = (t[n - 1] - t[0]) / (n - 1);
127
- a = t[n - 1] + (c > 0 ? c : 1 / 30);
128
- }
129
- return a > 0 || (a = Math.max(d, 1 / 30)), a;
130
- }
131
- function T(e, t) {
132
- var n, r;
133
- if (e.length === 0) return new Float64Array(0);
134
- const o = e.map((d, a) => a);
135
- o.sort((d, a) => {
136
- var c, l, f, h;
137
- const m = e[d], g = e[a], s = (l = (c = m.cts) != null ? c : m.dts) != null ? l : 0, p = (h = (f = g.cts) != null ? f : g.dts) != null ? h : 0;
138
- return s !== p ? s - p : d - a;
139
- });
140
- const i = new Float64Array(o.length);
141
- for (let d = 0; d < o.length; d++) {
142
- const a = e[o[d]];
143
- i[d] = ((r = (n = a.cts) != null ? n : a.dts) != null ? r : 0) / t;
144
- }
145
- return i;
146
- }
147
- function O(e) {
148
- return [...e].sort((t, n) => {
149
- var r, o, i, d;
150
- const a = ((o = (r = t.dts) != null ? r : t.cts) != null ? o : 0) - ((d = (i = n.dts) != null ? i : n.cts) != null ? d : 0);
151
- return a !== 0 ? a : 0;
152
- });
153
- }
154
- function W(e, t) {
155
- if (e.length === 0) return 0;
156
- const n = t / 1e6;
157
- let r = 0, o = 1 / 0;
158
- for (let i = 0; i < e.length; i++) {
159
- const d = Math.abs(e[i] - n);
160
- d < o && (o = d, r = i);
161
- }
162
- return r;
163
- }
164
- function A(e) {
165
- for (let t = 0; t < e.length; t++)
166
- if (e[t].is_sync) return t;
167
- return 0;
168
- }
169
- async function V(e, t) {
170
- var n;
171
- const r = {
172
- codec: e,
173
- hardwareAcceleration: "prefer-software",
174
- ...t && t.byteLength > 0 ? { description: t } : {}
175
- };
176
- let o = await VideoDecoder.isConfigSupported(r), i = r;
177
- if (!o.supported) {
178
- const d = {
179
- codec: e,
180
- ...t && t.byteLength > 0 ? { description: t } : {}
181
- };
182
- if (o = await VideoDecoder.isConfigSupported(d), !o.supported)
183
- throw new Error(`WebCodecs: unsupported config ${e}`);
184
- i = d;
185
- }
186
- return (n = o.config) != null ? n : i;
187
- }
188
- let u = null;
189
- self.onmessage = (e) => {
190
- const t = e.data;
191
- if (t) {
192
- if (t.type === "init") {
193
- H(t);
194
- return;
195
- }
196
- if (!(!u || t.id !== u.id)) {
197
- if (t.type === "ensure") {
198
- u.targetNeedIdx = Math.max(u.targetNeedIdx, t.needIdx), w(u);
199
- return;
200
- }
201
- if (t.type === "rewind") {
202
- Q(u);
203
- return;
204
- }
205
- t.type === "destroy" && M(u);
206
- }
207
- }
208
- };
209
- function M(e) {
210
- if (e.destroyed = !0, e.decoder && e.decoder.state !== "closed")
211
- try {
212
- e.decoder.close();
213
- } catch (t) {
214
- }
215
- e.decoder = null, u = null;
216
- }
217
- function w(e) {
218
- e.pumpScheduled || e.destroyed || (e.pumpScheduled = !0, queueMicrotask(() => {
219
- e.pumpScheduled = !1, U(e);
220
- }));
221
- }
222
- async function H(e) {
223
- u && M(u);
224
- const t = e.id;
225
- try {
226
- const n = await P(e.buffer);
227
- if (n.samples.length === 0) {
228
- self.postMessage({ type: "error", id: t, message: "mp4box: no samples" });
229
- return;
230
- }
231
- const r = O(n.samples), o = T(n.samples, n.timescale), i = N(n.durationSec, o), d = n.videoWidth || 0, a = n.videoHeight || 0, c = new Float64Array(o.length);
232
- c.set(o), self.postMessage(
233
- {
234
- type: "meta",
235
- id: t,
236
- duration: i,
237
- videoWidth: d,
238
- videoHeight: a,
239
- frameCount: o.length,
240
- timesSec: c
241
- },
242
- { transfer: [c.buffer] }
243
- );
244
- const l = _(o, i, 0), f = Math.min(
245
- o.length - 1,
246
- Math.max(0, l + Math.max(0, e.decodeAheadFrames))
247
- );
248
- u = {
249
- id: t,
250
- destroyed: !1,
251
- decodeOrder: r,
252
- presTimes: o,
253
- timescale: n.timescale,
254
- codec: n.codec,
255
- description: n.samples[0] ? await F(
256
- n.samples[0].description
257
- ) : void 0,
258
- duration: i,
259
- videoWidth: d,
260
- videoHeight: a,
261
- decoder: null,
262
- nextChunk: 0,
263
- chunksFed: 0,
264
- targetNeedIdx: f,
265
- lastPostedMax: -1,
266
- minPosted: Number.POSITIVE_INFINITY,
267
- playbackNeedFloor: l,
268
- eosClosed: !1,
269
- pumpScheduled: !1,
270
- pumping: !1
271
- }, w(u);
272
- } catch (n) {
273
- self.postMessage({
274
- type: "error",
275
- id: t,
276
- message: n instanceof Error ? n.message : String(n)
277
- });
278
- }
279
- }
280
- async function L(e) {
281
- if (e.decoder && e.decoder.state !== "closed") return;
282
- const t = await V(e.codec, e.description), n = new VideoDecoder({
283
- output: (r) => {
284
- if (e.destroyed) {
285
- try {
286
- r.close();
287
- } catch (i) {
288
- }
289
- return;
290
- }
291
- const o = W(e.presTimes, r.timestamp);
292
- e.lastPostedMax = Math.max(e.lastPostedMax, o), e.minPosted = Math.min(e.minPosted, o), self.postMessage({ type: "frame", id: e.id, index: o, frame: r }, { transfer: [r] });
293
- },
294
- error: (r) => {
295
- console.error("[GiftStage:WC] worker VideoDecoder error", r);
296
- }
297
- });
298
- n.configure(t), e.decoder = n;
299
- }
300
- async function Q(e) {
301
- if (e.destroyed) return;
302
- if (e.decoder && e.decoder.state !== "closed")
303
- try {
304
- e.decoder.close();
305
- } catch (n) {
306
- }
307
- e.decoder = null, e.nextChunk = A(e.decodeOrder), e.chunksFed = 0, e.lastPostedMax = -1, e.minPosted = Number.POSITIVE_INFINITY, e.eosClosed = !1;
308
- const t = _(e.presTimes, e.duration, 0);
309
- e.playbackNeedFloor = t, e.targetNeedIdx = Math.max(
310
- e.targetNeedIdx,
311
- Math.min(e.presTimes.length - 1, Math.max(0, t))
312
- ), w(e);
313
- }
314
- function $(e) {
315
- var t, n;
316
- const r = e.decoder, o = e.nextChunk;
317
- if (o >= e.decodeOrder.length) return;
318
- const i = e.decodeOrder[o];
319
- e.nextChunk = o + 1;
320
- const d = i.data;
321
- if (!d || d.byteLength === 0) return;
322
- const a = (n = (t = i.cts) != null ? t : i.dts) != null ? n : 0, c = Math.round(a / e.timescale * 1e6), l = i.duration && e.timescale > 0 ? Math.round(i.duration / e.timescale * 1e6) : void 0, f = new Uint8Array(d.byteLength);
323
- f.set(d);
324
- const h = new EncodedVideoChunk({
325
- type: i.is_sync ? "key" : "delta",
326
- timestamp: c,
327
- duration: l,
328
- data: f
329
- });
330
- r.decode(h), e.chunksFed++;
331
- }
332
- async function z(e) {
333
- if (!e.decoder || e.eosClosed) return;
334
- const t = e.decoder;
335
- await D(t, "before flush", 1e4);
336
- const n = t.decodeQueueSize;
337
- await E(t, {
338
- chunksDecoded: e.chunksFed,
339
- outputsBeforeFlush: e.lastPostedMax + 1,
340
- queueBeforeFlush: n
341
- });
342
- try {
343
- t.close();
344
- } catch (r) {
345
- }
346
- e.decoder = null, e.eosClosed = !0, self.postMessage({ type: "eos", id: e.id });
347
- }
348
- async function U(e) {
349
- if (!e.destroyed && !e.pumping) {
350
- e.pumping = !0;
351
- try {
352
- for (; !e.destroyed; ) {
353
- if (e.eosClosed)
354
- return;
355
- const t = e.nextChunk < e.decodeOrder.length, n = Number.isFinite(e.minPosted) && e.minPosted <= e.playbackNeedFloor, r = e.lastPostedMax < e.targetNeedIdx || !n;
356
- if (!t && !e.eosClosed) {
357
- await z(e);
358
- continue;
359
- }
360
- if (!r && t || !r && !t)
361
- return;
362
- await L(e), $(e), e.chunksFed % 4 === 0 && await new Promise((o) => setTimeout(o, 0));
363
- }
364
- } finally {
365
- e.pumping = !1;
366
- }
367
- }
368
- }
369
- //# sourceMappingURL=webcodecs-mp4.worker-BvV8Fiyc.js.map