@taole/giftstage 0.1.34 → 0.1.36

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.
@@ -0,0 +1,398 @@
1
+ let w = null;
2
+ function x() {
3
+ return w || (w = import("./mp4box.all-Bcqr6y1c.js").catch((o) => {
4
+ throw w = null, o;
5
+ })), w;
6
+ }
7
+ var k = Object.defineProperty, D = (o, e, t) => e in o ? k(o, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : o[e] = t, u = (o, e, t) => D(o, typeof e != "symbol" ? e + "" : e, t);
8
+ const P = 800;
9
+ function M(o) {
10
+ return o.startsWith("avc1") || o.startsWith("avc3");
11
+ }
12
+ function F(o) {
13
+ const e = new Uint8Array(o);
14
+ if (e.byteLength < 8) return o;
15
+ const t = e[4], r = e[5], s = e[6], i = e[7];
16
+ return t === 97 && r === 118 && s === 99 && i === 67 ? o.slice(8) : o;
17
+ }
18
+ async function C(o) {
19
+ const e = o.avcC;
20
+ if (!e) return;
21
+ const { DataStream: t } = await x(), r = new t();
22
+ e.write(r);
23
+ const s = r.byteLength;
24
+ if (s <= 0) return;
25
+ const i = r.buffer.slice(0, s);
26
+ return F(i);
27
+ }
28
+ async function E(o) {
29
+ const { createFile: e } = await x();
30
+ return new Promise((t, r) => {
31
+ const s = e(), i = o.slice(0);
32
+ i.fileStart = 0, s.onError = (n) => {
33
+ r(new Error(`mp4box: ${n}`));
34
+ }, s.onReady = (n) => {
35
+ var d, c, l, h, f, y, m;
36
+ const a = (d = n.videoTracks) == null ? void 0 : d[0];
37
+ if (!a) {
38
+ r(new Error("mp4box: no video track"));
39
+ return;
40
+ }
41
+ if (!M(a.codec)) {
42
+ r(new Error(`mp4box: unsupported video codec ${a.codec}`));
43
+ return;
44
+ }
45
+ if (a.nb_samples > P) {
46
+ r(new Error(`mp4box: too many samples (${a.nb_samples})`));
47
+ return;
48
+ }
49
+ const g = [];
50
+ s.onSamples = (z, Q, S) => {
51
+ g.push(...S);
52
+ }, s.setExtractionOptions(a.id, null, {
53
+ nbSamples: Math.max(a.nb_samples, 1)
54
+ }), s.start();
55
+ const v = a.timescale > 0 ? a.duration / a.timescale : n.duration / (n.timescale || 1);
56
+ if (g.length === 0) {
57
+ r(new Error("mp4box: extraction produced no samples"));
58
+ return;
59
+ }
60
+ t({
61
+ codec: a.codec,
62
+ timescale: a.timescale,
63
+ durationSec: Number.isFinite(v) ? v : 0,
64
+ videoWidth: (h = (l = (c = a.video) == null ? void 0 : c.width) != null ? l : a.track_width) != null ? h : 0,
65
+ videoHeight: (m = (y = (f = a.video) == null ? void 0 : f.height) != null ? y : a.track_height) != null ? m : 0,
66
+ samples: g
67
+ });
68
+ }, s.appendBuffer(i), s.flush();
69
+ });
70
+ }
71
+ const _ = 25e3;
72
+ async function I(o, e, t) {
73
+ const r = performance.now();
74
+ let s = 0;
75
+ for (; o.decodeQueueSize > 0; )
76
+ if (await new Promise((i) => queueMicrotask(i)), s++, s % 120 === 0 && await new Promise((i) => setTimeout(i, 0)), performance.now() - r > t) {
77
+ console.warn("[GiftStage:WC] decodeQueue drain gave up", {
78
+ label: e,
79
+ decodeQueueSize: o.decodeQueueSize,
80
+ spins: s
81
+ });
82
+ break;
83
+ }
84
+ }
85
+ async function N(o, e) {
86
+ console.info("[GiftStage:WC] VideoDecoder.flush…", e);
87
+ try {
88
+ await Promise.race([
89
+ o.flush(),
90
+ new Promise((t, r) => {
91
+ setTimeout(
92
+ () => r(
93
+ new Error(
94
+ `VideoDecoder.flush() timed out after ${_}ms (${JSON.stringify(e)})`
95
+ )
96
+ ),
97
+ _
98
+ );
99
+ })
100
+ ]);
101
+ } catch (t) {
102
+ try {
103
+ o.close();
104
+ } catch (r) {
105
+ }
106
+ throw t;
107
+ }
108
+ }
109
+ class T {
110
+ constructor(e, t, r, s, i, n, d, c) {
111
+ this.decodeOrder = e, this.presTimes = t, this.timescale = r, this.codec = s, this.description = i, this.duration = n, this.callbacks = d, u(this, "decoder", null), u(this, "nextChunk", 0), u(this, "chunksFed", 0), u(this, "targetNeedIdx"), u(this, "lastPostedMax", -1), u(this, "minPosted", Number.POSITIVE_INFINITY), u(this, "playbackNeedFloor"), u(this, "freeSlots", 0), u(this, "eosClosed", !1), u(this, "pumpScheduled", !1), u(this, "pumping", !1), u(this, "destroyed", !1), u(this, "onDecoderDequeue", () => this.schedulePump()), this.targetNeedIdx = c, this.playbackNeedFloor = b(t, n, 0), this.freeSlots = Math.max(1, c - this.playbackNeedFloor + 1), this.schedulePump();
112
+ }
113
+ /** Raise the decode target and/or update main-thread ring headroom; resumes the pump if idle. */
114
+ ensure(e, t) {
115
+ this.destroyed || (this.targetNeedIdx = Math.max(this.targetNeedIdx, e), this.freeSlots = Math.max(0, t), this.schedulePump());
116
+ }
117
+ /** Restart decode from the nearest keyframe at/before t=0 (backward seek / loop wrap). */
118
+ rewind() {
119
+ this.destroyed || this.doRewind();
120
+ }
121
+ destroy() {
122
+ var e, t;
123
+ if (this.destroyed = !0, this.decoder && this.decoder.state !== "closed") {
124
+ (t = (e = this.decoder).removeEventListener) == null || t.call(e, "dequeue", this.onDecoderDequeue);
125
+ try {
126
+ this.decoder.close();
127
+ } catch (r) {
128
+ }
129
+ }
130
+ this.decoder = null;
131
+ }
132
+ schedulePump() {
133
+ this.pumpScheduled || this.destroyed || (this.pumpScheduled = !0, queueMicrotask(() => {
134
+ this.pumpScheduled = !1, this.runPump();
135
+ }));
136
+ }
137
+ async doRewind() {
138
+ var e, t;
139
+ if (this.decoder && this.decoder.state !== "closed") {
140
+ (t = (e = this.decoder).removeEventListener) == null || t.call(e, "dequeue", this.onDecoderDequeue);
141
+ try {
142
+ this.decoder.close();
143
+ } catch (s) {
144
+ }
145
+ }
146
+ this.decoder = null, this.nextChunk = V(this.decodeOrder), this.chunksFed = 0, this.lastPostedMax = -1, this.minPosted = Number.POSITIVE_INFINITY, this.eosClosed = !1;
147
+ const r = b(this.presTimes, this.duration, 0);
148
+ this.playbackNeedFloor = r, this.targetNeedIdx = Math.max(
149
+ this.targetNeedIdx,
150
+ Math.min(this.presTimes.length - 1, Math.max(0, r))
151
+ ), this.freeSlots = 1, this.schedulePump();
152
+ }
153
+ async openDecoder() {
154
+ var e;
155
+ if (this.decoder && this.decoder.state !== "closed") return;
156
+ const t = await A(this.codec, this.description), r = new VideoDecoder({
157
+ output: (s) => {
158
+ var i, n, d, c;
159
+ if ((n = (i = this.callbacks).onDecoded) == null || n.call(i), this.destroyed) {
160
+ try {
161
+ s.close();
162
+ } catch (f) {
163
+ }
164
+ return;
165
+ }
166
+ const l = W(this.presTimes, s.timestamp), h = Number.isFinite(this.minPosted) && this.minPosted <= this.playbackNeedFloor;
167
+ if (this.freeSlots <= 0 && h) {
168
+ try {
169
+ s.close();
170
+ } catch (f) {
171
+ }
172
+ (c = (d = this.callbacks).onDropped) == null || c.call(d), this.schedulePump();
173
+ return;
174
+ }
175
+ this.lastPostedMax = Math.max(this.lastPostedMax, l), this.minPosted = Math.min(this.minPosted, l), this.freeSlots > 0 && this.freeSlots--, this.callbacks.onFrame(l, s), this.schedulePump();
176
+ },
177
+ error: (s) => {
178
+ this.callbacks.onError ? this.callbacks.onError(s) : console.error("[GiftStage:WC] IncrementalH264Decoder VideoDecoder error", s);
179
+ }
180
+ });
181
+ r.configure(t), (e = r.addEventListener) == null || e.call(r, "dequeue", this.onDecoderDequeue), this.decoder = r;
182
+ }
183
+ feedOneChunk() {
184
+ var e, t;
185
+ const r = this.decoder, s = this.nextChunk;
186
+ if (s >= this.decodeOrder.length) return;
187
+ const i = this.decodeOrder[s];
188
+ this.nextChunk = s + 1;
189
+ const n = i.data;
190
+ if (!n || n.byteLength === 0) return;
191
+ const d = (t = (e = i.cts) != null ? e : i.dts) != null ? t : 0, c = Math.round(d / this.timescale * 1e6), l = i.duration && this.timescale > 0 ? Math.round(i.duration / this.timescale * 1e6) : void 0, h = new Uint8Array(n.byteLength);
192
+ h.set(n);
193
+ const f = new EncodedVideoChunk({
194
+ type: i.is_sync ? "key" : "delta",
195
+ timestamp: c,
196
+ duration: l,
197
+ data: h
198
+ });
199
+ r.decode(f), this.chunksFed++;
200
+ }
201
+ async finalizeEos() {
202
+ var e, t, r;
203
+ if (!this.decoder || this.eosClosed) return;
204
+ const s = this.decoder;
205
+ await I(s, "before flush", 1e4);
206
+ const i = s.decodeQueueSize;
207
+ await N(s, {
208
+ chunksDecoded: this.chunksFed,
209
+ outputsBeforeFlush: this.lastPostedMax + 1,
210
+ queueBeforeFlush: i
211
+ });
212
+ try {
213
+ (e = s.removeEventListener) == null || e.call(s, "dequeue", this.onDecoderDequeue), s.close();
214
+ } catch (n) {
215
+ }
216
+ this.decoder = null, this.eosClosed = !0, (r = (t = this.callbacks).onEos) == null || r.call(t);
217
+ }
218
+ async runPump() {
219
+ if (!(this.destroyed || this.pumping)) {
220
+ this.pumping = !0;
221
+ try {
222
+ for (; !this.destroyed; ) {
223
+ if (this.eosClosed) return;
224
+ const e = this.nextChunk < this.decodeOrder.length, t = Number.isFinite(this.minPosted) && this.minPosted <= this.playbackNeedFloor;
225
+ if (!e && !this.eosClosed) {
226
+ await this.finalizeEos();
227
+ continue;
228
+ }
229
+ if (this.lastPostedMax >= this.targetNeedIdx && t || this.freeSlots <= 0 && t) return;
230
+ await this.openDecoder();
231
+ const r = this.freeSlots > 0 ? this.freeSlots : 1, s = Math.max(1, Math.min(2, r));
232
+ if (this.decoder.decodeQueueSize >= s) return;
233
+ this.feedOneChunk(), this.chunksFed % 2 === 0 && await new Promise((i) => setTimeout(i, 0));
234
+ }
235
+ } finally {
236
+ this.pumping = !1;
237
+ }
238
+ }
239
+ }
240
+ }
241
+ function b(o, e, t) {
242
+ if (o.length === 0) return 0;
243
+ const r = o.length;
244
+ if (t <= o[0]) return 0;
245
+ const s = o[r - 1];
246
+ if (t >= e || t >= s) return r - 1;
247
+ let i = 0, n = r - 1;
248
+ for (; i + 1 < n; ) {
249
+ const d = i + n >>> 1;
250
+ o[d] <= t ? i = d : n = d;
251
+ }
252
+ return i;
253
+ }
254
+ function O(o, e) {
255
+ if (e.length === 0) return 1e-6;
256
+ const t = e.length, r = t - 1, s = e[r], i = t >= 2 ? e[r] - e[r - 1] : 1 / 30, n = s + i;
257
+ let d = o > 0 ? o : n;
258
+ if (!(d > 0) && t >= 2) {
259
+ const c = (e[t - 1] - e[0]) / (t - 1);
260
+ d = e[t - 1] + (c > 0 ? c : 1 / 30);
261
+ }
262
+ return d > 0 || (d = Math.max(n, 1 / 30)), d;
263
+ }
264
+ function q(o, e) {
265
+ var t, r;
266
+ if (o.length === 0) return new Float64Array(0);
267
+ const s = o.map((n, d) => d);
268
+ s.sort((n, d) => {
269
+ var c, l, h, f;
270
+ const y = o[n], m = o[d], a = (l = (c = y.cts) != null ? c : y.dts) != null ? l : 0, g = (f = (h = m.cts) != null ? h : m.dts) != null ? f : 0;
271
+ return a !== g ? a - g : n - d;
272
+ });
273
+ const i = new Float64Array(s.length);
274
+ for (let n = 0; n < s.length; n++) {
275
+ const d = o[s[n]];
276
+ i[n] = ((r = (t = d.cts) != null ? t : d.dts) != null ? r : 0) / e;
277
+ }
278
+ return i;
279
+ }
280
+ function L(o) {
281
+ return [...o].sort((e, t) => {
282
+ var r, s, i, n;
283
+ const d = ((s = (r = e.dts) != null ? r : e.cts) != null ? s : 0) - ((n = (i = t.dts) != null ? i : t.cts) != null ? n : 0);
284
+ return d !== 0 ? d : 0;
285
+ });
286
+ }
287
+ function W(o, e) {
288
+ if (o.length === 0) return 0;
289
+ const t = e / 1e6;
290
+ let r = 0, s = 1 / 0;
291
+ for (let i = 0; i < o.length; i++) {
292
+ const n = Math.abs(o[i] - t);
293
+ n < s && (s = n, r = i);
294
+ }
295
+ return r;
296
+ }
297
+ function V(o) {
298
+ for (let e = 0; e < o.length; e++)
299
+ if (o[e].is_sync) return e;
300
+ return 0;
301
+ }
302
+ async function A(o, e) {
303
+ var t;
304
+ const r = {
305
+ codec: o,
306
+ hardwareAcceleration: "prefer-software",
307
+ ...e && e.byteLength > 0 ? { description: e } : {}
308
+ };
309
+ let s = await VideoDecoder.isConfigSupported(r), i = r;
310
+ if (!s.supported) {
311
+ const n = {
312
+ codec: o,
313
+ ...e && e.byteLength > 0 ? { description: e } : {}
314
+ };
315
+ if (s = await VideoDecoder.isConfigSupported(n), !s.supported)
316
+ throw new Error(`WebCodecs: unsupported config ${o}`);
317
+ i = n;
318
+ }
319
+ return (t = s.config) != null ? t : i;
320
+ }
321
+ let p = null;
322
+ self.onmessage = (o) => {
323
+ const e = o.data;
324
+ if (e) {
325
+ if (e.type === "init") {
326
+ H(e);
327
+ return;
328
+ }
329
+ if (!(!p || e.id !== p.id)) {
330
+ if (e.type === "ensure") {
331
+ p.decoder.ensure(e.needIdx, e.freeSlots);
332
+ return;
333
+ }
334
+ if (e.type === "rewind") {
335
+ p.decoder.rewind();
336
+ return;
337
+ }
338
+ e.type === "destroy" && (p.decoder.destroy(), p = null);
339
+ }
340
+ }
341
+ };
342
+ async function H(o) {
343
+ p && (p.decoder.destroy(), p = null);
344
+ const e = o.id;
345
+ try {
346
+ const t = await E(o.buffer);
347
+ if (t.samples.length === 0) {
348
+ self.postMessage({ type: "error", id: e, message: "mp4box: no samples" });
349
+ return;
350
+ }
351
+ const r = L(t.samples), s = q(t.samples, t.timescale), i = O(t.durationSec, s), n = t.videoWidth || 0, d = t.videoHeight || 0, c = new Float64Array(s.length);
352
+ c.set(s), self.postMessage(
353
+ {
354
+ type: "meta",
355
+ id: e,
356
+ duration: i,
357
+ videoWidth: n,
358
+ videoHeight: d,
359
+ frameCount: s.length,
360
+ timesSec: c
361
+ },
362
+ { transfer: [c.buffer] }
363
+ );
364
+ const l = t.samples[0] ? await C(
365
+ t.samples[0].description
366
+ ) : void 0, h = b(s, i, 0), f = Math.min(
367
+ s.length - 1,
368
+ Math.max(0, h + Math.max(0, o.decodeAheadFrames))
369
+ ), y = new T(
370
+ r,
371
+ s,
372
+ t.timescale,
373
+ t.codec,
374
+ l,
375
+ i,
376
+ {
377
+ onFrame: (m, a) => {
378
+ self.postMessage({ type: "frame", id: e, index: m, frame: a }, { transfer: [a] });
379
+ },
380
+ onEos: () => {
381
+ self.postMessage({ type: "eos", id: e });
382
+ },
383
+ onError: (m) => {
384
+ console.error("[GiftStage:WC] worker VideoDecoder error", m);
385
+ }
386
+ },
387
+ f
388
+ );
389
+ p = { id: e, decoder: y };
390
+ } catch (t) {
391
+ self.postMessage({
392
+ type: "error",
393
+ id: e,
394
+ message: t instanceof Error ? t.message : String(t)
395
+ });
396
+ }
397
+ }
398
+ //# sourceMappingURL=webcodecs-mp4.worker-DeS4ARzq.js.map
@@ -13,6 +13,11 @@ export declare class GiftStage {
13
13
  private static readonly DEFAULT_RESOURCE_CACHE_PERSISTENT_BYTES;
14
14
  private readonly sharedSVGAEntities;
15
15
  private readonly pendingSharedSVGAEntities;
16
+ /** One full clip-prewarm task per entity, shared by every slot using that entity. */
17
+ private readonly svgaFullClipPrewarmPromises;
18
+ /** Deferred persistent-cache writer captured while a URL-backed entity is being assembled. */
19
+ private readonly svgaPersistentCacheWriters;
20
+ private readonly svgaPersistentCacheWritePromises;
16
21
  private readonly sharedVideoSources;
17
22
  private readonly sharedHtmlVideos;
18
23
  private readonly sharedVideoTextures;
@@ -79,13 +84,11 @@ export declare class GiftStage {
79
84
  */
80
85
  private decodeSVGAAudioInBackground;
81
86
  /**
82
- * Builds an SVGA's clip meshes after its first frame has already been painted (see
83
- * `primeSVGAFirstFrame`). Detached from the `finishLoadSVGA` promise chain; the render loop's
84
- * own on-demand fallback (`SVGABatchRenderer.getClipMeshForSprite`, see P1-9) already covers
85
- * correctness for any sprite drawn before this finishes, so this is purely a prefetch that lets
86
- * later frames skip the render loop's slower one-path-at-a-time queueing.
87
+ * Starts or reuses the entity-wide clip prewarm. It is deliberately independent of any one
88
+ * slot's abort signal: shared entities can outlive the slot that first requested the work.
87
89
  */
88
- private prewarmSVGAClipMeshesInBackground;
90
+ private ensureSVGAFullClipPrewarm;
91
+ private queueSVGAPersistentCacheWrite;
89
92
  private svgaAtlasMaxSize;
90
93
  private buildSVGAAtlasIfNeeded;
91
94
  private svgaAtlasMaxPages;
@@ -95,6 +95,13 @@ export declare class RenderManager {
95
95
  checkActive?: () => void;
96
96
  signal?: AbortSignal;
97
97
  }): Promise<number>;
98
+ prewarmSVGAClipMeshesForFrame(entity: NonNullable<GiftSlot['videoEntity']>, frame: number, options?: {
99
+ frameBudgetMs?: number;
100
+ workerTimeSliceMs?: number;
101
+ useWorker?: boolean;
102
+ checkActive?: () => void;
103
+ signal?: AbortSignal;
104
+ }): Promise<number>;
98
105
  hydrateSVGAClipMeshes(meshes: SVGAClipMeshMap | null | undefined): void;
99
106
  snapshotSVGAClipMeshes(entity: NonNullable<GiftSlot['videoEntity']>): SVGAClipMeshMap;
100
107
  private renderLoop;