@ikonai/sdk 1.0.79 → 1.1.0

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 (55) hide show
  1. package/app-messaging.d.ts +1 -1
  2. package/assets/audio-capture-worker-D-0X2Dy8.js +1 -0
  3. package/assets/audio-playback-worker-DivBZPfS.js +1 -0
  4. package/assets/libopus-CnBCihDi.js +1 -0
  5. package/assets/protocol-worker-BH93CVck.js +1 -0
  6. package/assets/src-BKYD07QQ.js +1 -0
  7. package/assets/src-DOUWsCWT.js +1 -0
  8. package/assets/video-capture-worker-BQGOFbZt.js +1 -0
  9. package/assets/video-playback-worker-tC5nUVnw.js +1 -0
  10. package/assets/viseme-worker-Cd-JqTVM.js +1 -0
  11. package/capabilities-BtJblfdG.js +108 -0
  12. package/channel/channel-manager.d.ts +1 -1
  13. package/channel/channel.d.ts +1 -1
  14. package/client/endpoint-selector.d.ts +1 -1
  15. package/client/ikon-client-config.d.ts +13 -11
  16. package/client/ikon-client.d.ts +98 -4
  17. package/connection/authenticator.d.ts +3 -3
  18. package/connection/urls.d.ts +7 -18
  19. package/errors/index.d.ts +22 -13
  20. package/functions/browser-functions.d.ts +1 -1
  21. package/functions/function-registry.d.ts +1 -1
  22. package/functions/types.d.ts +2 -2
  23. package/ikon-media-zqob_QeE.js +3160 -0
  24. package/index.d.ts +8 -8
  25. package/index.js +8119 -12502
  26. package/media/ikon-audio-capture.d.ts +15 -3
  27. package/media/ikon-audio-playback.d.ts +37 -0
  28. package/media/ikon-video-playback.d.ts +24 -0
  29. package/media/media-session-helper.d.ts +8 -0
  30. package/media/video-constants.d.ts +11 -1
  31. package/media/viseme-tracker.d.ts +10 -1
  32. package/package.json +1 -1
  33. package/storage.d.ts +13 -0
  34. package/transport/transport.d.ts +1 -1
  35. package/transport/web-socket-transport.d.ts +1 -1
  36. package/transport/web-transport-transport.d.ts +1 -1
  37. package/utils/inspect-mode.d.ts +4 -0
  38. package/utils/log-sender.d.ts +1 -1
  39. package/utils/platform.d.ts +4 -0
  40. package/utils/query-params.d.ts +8 -4
  41. package/vite.config.d.ts +2 -0
  42. package/webrtc/webrtc-signaling.d.ts +11 -2
  43. package/worker/audio-playback-worker.d.ts +14 -1
  44. package/worker/video-capture-worker.d.ts +2 -2
  45. package/worker/video-playback-worker.d.ts +14 -2
  46. package/worker/viseme-worker.d.ts +10 -2
  47. package/assets/audio-capture-worker-Brmt2Z7a.js +0 -1279
  48. package/assets/audio-playback-worker-DDtsnLoq.js +0 -1473
  49. package/assets/index-Ca9sTcGt.js +0 -119
  50. package/assets/index-D_vz6N3U.js +0 -267
  51. package/assets/libopus-ChbDZJKA.js +0 -139
  52. package/assets/protocol-worker-BoZcMF0x.js +0 -1674
  53. package/assets/video-capture-worker-DxgvFTcw.js +0 -1126
  54. package/assets/video-playback-worker-Ca4XVGqt.js +0 -1005
  55. package/assets/viseme-worker-DVWM4pRh.js +0 -831
@@ -0,0 +1,3160 @@
1
+ import { a as e, f as t, i as n, l as r, n as i, o as a, r as o, t as s, u as c, v as l } from "./capabilities-BtJblfdG.js";
2
+ //#region \0rolldown/runtime.js
3
+ var u = Object.defineProperty, d = (e, t) => {
4
+ let n = {};
5
+ for (var r in e) u(n, r, {
6
+ get: e[r],
7
+ enumerable: !0
8
+ });
9
+ return t || u(n, Symbol.toStringTag, { value: "Module" }), n;
10
+ }, f = {
11
+ None: 0,
12
+ SendBackToSender: 1,
13
+ Delayable: 2,
14
+ SendToUser: 4,
15
+ Compressed: 8,
16
+ Unreliable: 16
17
+ };
18
+ function p(e) {
19
+ return e;
20
+ }
21
+ function m(e) {
22
+ if (e.length < 8) throw Error("Protocol message too short");
23
+ return (e[4] | e[5] << 8 | e[6] << 16 | e[7] << 24) >>> 0;
24
+ }
25
+ function h(e) {
26
+ return m(e) & 4294901760;
27
+ }
28
+ function g(e) {
29
+ let t = R(e), n = new DataView(t.buffer, t.byteOffset, t.byteLength);
30
+ if (t.length < 27) throw Error("Protocol payload too short");
31
+ let r = n.getUint32(0, !0), i = n.getUint32(4, !0), a = n.getUint32(8, !0), o = n.getUint32(12, !0), s = n.getUint32(16, !0), c = n.getUint32(20, !0), l = n.getUint8(24), u = n.getUint8(25), d = n.getUint8(26);
32
+ if (27 + c * 4 > t.length) throw Error("Protocol header exceeds payload length");
33
+ let f = [], p = 27;
34
+ for (let e = 0; e < c; e++) f.push(n.getUint32(p, !0)), p += 4;
35
+ return {
36
+ length: r,
37
+ opcode: i,
38
+ senderId: a,
39
+ trackId: o,
40
+ sequenceId: s,
41
+ targetIds: f,
42
+ payloadVersion: l,
43
+ payloadType: u,
44
+ flags: d
45
+ };
46
+ }
47
+ function ee(e, t, n) {
48
+ let r = R(e), i = g(r);
49
+ if (t !== void 0 && i.opcode !== t) throw Error(`Unexpected opcode ${i.opcode}`);
50
+ if (i.payloadType !== 8) throw Error(`Unexpected payload type ${i.payloadType}`);
51
+ let a = 27 + i.targetIds.length * 4;
52
+ return r.subarray(a, i.length);
53
+ }
54
+ async function te(e, t, n) {
55
+ let r = R(e), i = g(r);
56
+ if (t !== void 0 && i.opcode !== t) throw Error(`Unexpected opcode ${i.opcode}`);
57
+ if (i.payloadType !== 8) throw Error(`Unexpected payload type ${i.payloadType}`);
58
+ let a = 27 + i.targetIds.length * 4, o = r.subarray(a, i.length);
59
+ return (i.flags & f.Compressed) !== 0 && (o = await ne(o)), o;
60
+ }
61
+ function _(e, t, n, r, i) {
62
+ let a = i?.trackId ?? 0, o = i?.sequenceId ?? 0, s = i?.flags ?? 0, c = i?.targetIds ?? [], l = i?.payloadType ?? 8, u = 27 + c.length * 4, d = u + t.length, f = new Uint8Array(d), m = new DataView(f.buffer);
63
+ m.setUint32(0, d, !0), m.setUint32(4, e >>> 0, !0), m.setUint32(8, r >>> 0, !0), m.setUint32(12, a >>> 0, !0), m.setUint32(16, o >>> 0, !0), m.setUint32(20, c.length >>> 0, !0), m.setUint8(24, n & 255), m.setUint8(25, l & 255), m.setUint8(26, s & 255);
64
+ let h = 27;
65
+ for (let e = 0; e < c.length; e++) m.setUint32(h, c[e] >>> 0, !0), h += 4;
66
+ return f.set(t, u), p(f);
67
+ }
68
+ async function ne(e) {
69
+ if (typeof DecompressionStream > "u") throw Error("DecompressionStream not supported");
70
+ let t = new DecompressionStream("gzip"), n = t.writable.getWriter(), r = new Uint8Array(e);
71
+ n.write(r).then(() => n.close()).catch(() => void 0);
72
+ let i = t.readable.getReader(), a = [], o = 0;
73
+ for (;;) {
74
+ let { done: e, value: t } = await i.read();
75
+ if (e) break;
76
+ a.push(t), o += t.length;
77
+ }
78
+ let s = new Uint8Array(o), c = 0;
79
+ for (let e = 0; e < a.length; e++) s.set(a[e], c), c += a[e].length;
80
+ return s;
81
+ }
82
+ var v = /* @__PURE__ */ function(e) {
83
+ return e[e.Null = 1] = "Null", e[e.Bool = 2] = "Bool", e[e.Int32 = 3] = "Int32", e[e.Int64 = 4] = "Int64", e[e.UInt32 = 5] = "UInt32", e[e.UInt64 = 6] = "UInt64", e[e.Float32 = 7] = "Float32", e[e.Float64 = 8] = "Float64", e[e.Array = 9] = "Array", e[e.Dict = 10] = "Dict", e[e.Object = 11] = "Object", e[e.String = 12] = "String", e[e.Binary = 13] = "Binary", e[e.Guid = 14] = "Guid", e;
84
+ }({}), y = 161, b = 162, x = new TextEncoder(), re = new TextDecoder("utf-8", { fatal: !0 }), S = class e {
85
+ buffer = new O();
86
+ cached;
87
+ constructor(e = 1) {
88
+ this.buffer.writeByte(y), this.buffer.writeVarUInt(e >>> 0);
89
+ }
90
+ writeInt32Field(e, t) {
91
+ this.writeFixedField(e, 3, () => this.buffer.writeInt32(t | 0));
92
+ }
93
+ writeUInt32Field(e, t) {
94
+ this.writeFixedField(e, 5, () => this.buffer.writeUInt32(t >>> 0));
95
+ }
96
+ writeInt64Field(e, t) {
97
+ this.writeFixedField(e, 4, () => this.buffer.writeBigInt64(t));
98
+ }
99
+ writeUInt64Field(e, t) {
100
+ this.writeFixedField(e, 6, () => this.buffer.writeBigUInt64(t));
101
+ }
102
+ writeFloat32Field(e, t) {
103
+ this.writeFixedField(e, 7, () => this.buffer.writeFloat32(t));
104
+ }
105
+ writeFloat64Field(e, t) {
106
+ this.writeFixedField(e, 8, () => this.buffer.writeFloat64(t));
107
+ }
108
+ writeBoolField(e, t) {
109
+ this.writeFixedField(e, 2, () => this.buffer.writeByte(+!!t));
110
+ }
111
+ writeGuidField(e, t) {
112
+ let n = t instanceof D ? t.asBytes() : t;
113
+ if (n.length !== 16) throw Error("Guid payload must be 16 bytes");
114
+ this.writeFixedField(e, 14, () => this.buffer.writeBytes(n));
115
+ }
116
+ writeStringField(e, t) {
117
+ let n = x.encode(t ?? "");
118
+ this.writeVariableField(e, 12, n);
119
+ }
120
+ writeBinaryField(e, t) {
121
+ this.writeVariableField(e, 13, t);
122
+ }
123
+ writeObjectField(t, n, r) {
124
+ let i = new e(n);
125
+ r(i);
126
+ let a = i.finish();
127
+ this.writeVariableField(t, 11, a);
128
+ }
129
+ writeArrayField(e, t, n) {
130
+ let r = new ie(t);
131
+ n(r);
132
+ let i = r.finish();
133
+ this.writeVariableField(e, 9, i);
134
+ }
135
+ writeDictionaryField(e, t, n, r) {
136
+ let i = new C(t, n);
137
+ r(i);
138
+ let a = i.finish();
139
+ this.writeVariableField(e, 10, a);
140
+ }
141
+ finish() {
142
+ return this.cached ||= (this.buffer.writeByte(b), this.buffer.toUint8Array()), this.cached;
143
+ }
144
+ writeFixedField(e, t, n) {
145
+ this.writeFieldHeader(e, t, 0), n();
146
+ }
147
+ writeVariableField(e, t, n) {
148
+ this.writeFieldHeader(e, t, n.length), this.buffer.writeBytes(n);
149
+ }
150
+ writeFieldHeader(e, t, n) {
151
+ this.buffer.writeUInt32(e >>> 0), this.buffer.writeByte(A(t)), de(t) && this.buffer.writeVarUInt(n >>> 0);
152
+ }
153
+ }, ie = class e {
154
+ elementType;
155
+ payload = new O();
156
+ count = 0;
157
+ constructor(e) {
158
+ this.elementType = e;
159
+ }
160
+ writeInt32(e) {
161
+ this.ensureElementType(3), this.count++, this.payload.writeInt32(e | 0);
162
+ }
163
+ writeUInt32(e) {
164
+ this.ensureElementType(5), this.count++, this.payload.writeUInt32(e >>> 0);
165
+ }
166
+ writeInt64(e) {
167
+ this.ensureElementType(4), this.count++, this.payload.writeBigInt64(e);
168
+ }
169
+ writeUInt64(e) {
170
+ this.ensureElementType(6), this.count++, this.payload.writeBigUInt64(e);
171
+ }
172
+ writeFloat32(e) {
173
+ this.ensureElementType(7), this.count++, this.payload.writeFloat32(e);
174
+ }
175
+ writeFloat64(e) {
176
+ this.ensureElementType(8), this.count++, this.payload.writeFloat64(e);
177
+ }
178
+ writeBool(e) {
179
+ this.ensureElementType(2), this.count++, this.payload.writeByte(+!!e);
180
+ }
181
+ writeGuid(e) {
182
+ this.ensureElementType(14), this.count++;
183
+ let t = e instanceof D ? e.asBytes() : e;
184
+ if (t.length !== 16) throw Error("Guid payload must be 16 bytes");
185
+ this.payload.writeBytes(t);
186
+ }
187
+ writeString(e) {
188
+ this.ensureElementType(12), this.count++;
189
+ let t = x.encode(e ?? "");
190
+ this.payload.writeVarUInt(t.length), this.payload.writeBytes(t);
191
+ }
192
+ writeBinary(e) {
193
+ this.ensureElementType(13), this.count++, this.payload.writeVarUInt(e.length), this.payload.writeBytes(e);
194
+ }
195
+ writeObject(e, t) {
196
+ this.ensureElementType(11), this.count++;
197
+ let n = new S(e);
198
+ t(n);
199
+ let r = n.finish();
200
+ this.payload.writeVarUInt(r.length), this.payload.writeBytes(r);
201
+ }
202
+ writeArray(t, n) {
203
+ this.ensureElementType(9), this.count++;
204
+ let r = new e(t);
205
+ n(r);
206
+ let i = r.finish();
207
+ this.payload.writeBytes(i);
208
+ }
209
+ writeDictionary(e, t, n) {
210
+ this.ensureElementType(10), this.count++;
211
+ let r = new C(e, t);
212
+ n(r);
213
+ let i = r.finish();
214
+ this.payload.writeBytes(i);
215
+ }
216
+ finish() {
217
+ let e = new O();
218
+ return e.writeByte(A(this.elementType)), e.writeVarUInt(this.count), e.writeBytes(this.payload.toUint8Array()), e.toUint8Array();
219
+ }
220
+ ensureElementType(e) {
221
+ if (this.elementType !== e) throw Error(`Array element type is ${v[this.elementType]}, expected ${v[e]}`);
222
+ }
223
+ }, C = class {
224
+ keyType;
225
+ valueType;
226
+ payload = new O();
227
+ count = 0;
228
+ entryOpen = !1;
229
+ constructor(e, t) {
230
+ this.keyType = e, this.valueType = t, M(e);
231
+ }
232
+ beginEntry() {
233
+ if (this.entryOpen) throw Error("Previous dictionary entry not completed");
234
+ return this.count++, this.entryOpen = !0, new ae(this.keyType, this.valueType, this.payload, () => {
235
+ this.entryOpen = !1;
236
+ });
237
+ }
238
+ finish() {
239
+ if (this.entryOpen) throw Error("Dictionary entry not completed");
240
+ let e = new O();
241
+ return e.writeByte(A(this.keyType)), e.writeByte(A(this.valueType)), e.writeVarUInt(this.count), e.writeBytes(this.payload.toUint8Array()), e.toUint8Array();
242
+ }
243
+ }, ae = class {
244
+ keyType;
245
+ valueType;
246
+ payload;
247
+ onComplete;
248
+ keyWritten = !1;
249
+ valueWritten = !1;
250
+ completed = !1;
251
+ constructor(e, t, n, r) {
252
+ this.keyType = e, this.valueType = t, this.payload = n, this.onComplete = r;
253
+ }
254
+ writeKeyInt32(e) {
255
+ this.ensureKeyType(3), this.payload.writeInt32(e | 0), this.keyWritten = !0;
256
+ }
257
+ writeKeyUInt32(e) {
258
+ this.ensureKeyType(5), this.payload.writeUInt32(e >>> 0), this.keyWritten = !0;
259
+ }
260
+ writeKeyInt64(e) {
261
+ this.ensureKeyType(4), this.payload.writeBigInt64(e), this.keyWritten = !0;
262
+ }
263
+ writeKeyUInt64(e) {
264
+ this.ensureKeyType(6), this.payload.writeBigUInt64(e), this.keyWritten = !0;
265
+ }
266
+ writeKeyFloat32(e) {
267
+ this.ensureKeyType(7), this.payload.writeFloat32(e), this.keyWritten = !0;
268
+ }
269
+ writeKeyFloat64(e) {
270
+ this.ensureKeyType(8), this.payload.writeFloat64(e), this.keyWritten = !0;
271
+ }
272
+ writeKeyBool(e) {
273
+ this.ensureKeyType(2), this.payload.writeByte(+!!e), this.keyWritten = !0;
274
+ }
275
+ writeKeyGuid(e) {
276
+ this.ensureKeyType(14);
277
+ let t = e instanceof D ? e.asBytes() : e;
278
+ if (t.length !== 16) throw Error("Guid payload must be 16 bytes");
279
+ this.payload.writeBytes(t), this.keyWritten = !0;
280
+ }
281
+ writeKeyString(e) {
282
+ this.ensureKeyType(12);
283
+ let t = x.encode(e ?? "");
284
+ this.payload.writeVarUInt(t.length), this.payload.writeBytes(t), this.keyWritten = !0;
285
+ }
286
+ writeKeyBinary(e) {
287
+ this.ensureKeyType(13), this.payload.writeVarUInt(e.length), this.payload.writeBytes(e), this.keyWritten = !0;
288
+ }
289
+ writeValueInt32(e) {
290
+ this.ensureValueType(3), this.payload.writeInt32(e | 0), this.valueWritten = !0;
291
+ }
292
+ writeValueUInt32(e) {
293
+ this.ensureValueType(5), this.payload.writeUInt32(e >>> 0), this.valueWritten = !0;
294
+ }
295
+ writeValueInt64(e) {
296
+ this.ensureValueType(4), this.payload.writeBigInt64(e), this.valueWritten = !0;
297
+ }
298
+ writeValueUInt64(e) {
299
+ this.ensureValueType(6), this.payload.writeBigUInt64(e), this.valueWritten = !0;
300
+ }
301
+ writeValueFloat32(e) {
302
+ this.ensureValueType(7), this.payload.writeFloat32(e), this.valueWritten = !0;
303
+ }
304
+ writeValueFloat64(e) {
305
+ this.ensureValueType(8), this.payload.writeFloat64(e), this.valueWritten = !0;
306
+ }
307
+ writeValueBool(e) {
308
+ this.ensureValueType(2), this.payload.writeByte(+!!e), this.valueWritten = !0;
309
+ }
310
+ writeValueGuid(e) {
311
+ this.ensureValueType(14);
312
+ let t = e instanceof D ? e.asBytes() : e;
313
+ if (t.length !== 16) throw Error("Guid payload must be 16 bytes");
314
+ this.payload.writeBytes(t), this.valueWritten = !0;
315
+ }
316
+ writeNullValue() {
317
+ this.ensureValueType(1), this.valueWritten = !0;
318
+ }
319
+ writeValueBinary(e) {
320
+ this.ensureValueType(13), this.payload.writeVarUInt(e.length), this.payload.writeBytes(e), this.valueWritten = !0;
321
+ }
322
+ writeValueString(e) {
323
+ this.ensureValueType(12);
324
+ let t = x.encode(e ?? "");
325
+ this.payload.writeVarUInt(t.length), this.payload.writeBytes(t), this.valueWritten = !0;
326
+ }
327
+ writeValueObject(e, t) {
328
+ this.ensureValueType(11);
329
+ let n = new S(e);
330
+ t(n);
331
+ let r = n.finish();
332
+ this.payload.writeVarUInt(r.length), this.payload.writeBytes(r), this.valueWritten = !0;
333
+ }
334
+ writeValueArray(e, t) {
335
+ this.ensureValueType(9);
336
+ let n = new ie(e);
337
+ t(n);
338
+ let r = n.finish();
339
+ this.payload.writeBytes(r), this.valueWritten = !0;
340
+ }
341
+ writeValueDictionary(e, t, n) {
342
+ this.ensureValueType(10);
343
+ let r = new C(e, t);
344
+ n(r);
345
+ let i = r.finish();
346
+ this.payload.writeBytes(i), this.valueWritten = !0;
347
+ }
348
+ complete() {
349
+ if (!this.completed) {
350
+ if (!this.keyWritten || !this.valueWritten) throw Error("Dictionary entry must write both key and value");
351
+ this.completed = !0, this.onComplete();
352
+ }
353
+ }
354
+ ensureKeyType(e) {
355
+ if (this.keyType !== e) throw Error(`Dictionary key type is ${v[this.keyType]}, expected ${v[e]}`);
356
+ }
357
+ ensureValueType(e) {
358
+ if (this.valueType !== e) throw Error(`Dictionary value type is ${v[this.valueType]}, expected ${v[e]}`);
359
+ }
360
+ }, w = class e {
361
+ buffer;
362
+ version;
363
+ offset;
364
+ end;
365
+ constructor(e, t, n) {
366
+ this.buffer = e, this.version = t, this.offset = n, this.end = e.length - 1;
367
+ }
368
+ static create(t) {
369
+ let n = R(t);
370
+ if (n.length < 2) throw Error("Teleport payload too short");
371
+ if (n[0] !== y || n[n.length - 1] !== b) throw Error("Teleport object missing markers");
372
+ let r = { offset: 1 }, i = k(n, r, "InvalidLength");
373
+ return new e(n, i, r.offset);
374
+ }
375
+ next() {
376
+ if (this.offset >= this.end) return null;
377
+ if (this.offset + 5 > this.buffer.length) throw Error("Teleport object truncated");
378
+ let e = fe(this.buffer, this.offset);
379
+ this.offset += 4;
380
+ let t = this.buffer[this.offset++], n = t >> 4 & 15;
381
+ if (t & 15) throw Error("Teleport field flags must be zero");
382
+ let r = j(n), i;
383
+ if (r >= 0) N(this.buffer, this.offset, r), i = this.buffer.subarray(this.offset, this.offset + r), this.offset += r;
384
+ else {
385
+ let e = { offset: this.offset }, t = k(this.buffer, e, "InvalidLength");
386
+ N(this.buffer, e.offset, t), i = this.buffer.subarray(e.offset, e.offset + t), this.offset = e.offset + t;
387
+ }
388
+ return new oe(e, n, i);
389
+ }
390
+ }, T = class {
391
+ type;
392
+ payload;
393
+ constructor(e, t) {
394
+ this.type = e, this.payload = t;
395
+ }
396
+ asInt32() {
397
+ return this.ensureType(3), new DataView(this.payload.buffer, this.payload.byteOffset, 4).getInt32(0, !0);
398
+ }
399
+ asUInt32() {
400
+ return this.ensureType(5), new DataView(this.payload.buffer, this.payload.byteOffset, 4).getUint32(0, !0);
401
+ }
402
+ asInt64() {
403
+ return this.ensureType(4), new DataView(this.payload.buffer, this.payload.byteOffset, 8).getBigInt64(0, !0);
404
+ }
405
+ asUInt64() {
406
+ return this.ensureType(6), new DataView(this.payload.buffer, this.payload.byteOffset, 8).getBigUint64(0, !0);
407
+ }
408
+ asFloat32() {
409
+ return this.ensureType(7), new DataView(this.payload.buffer, this.payload.byteOffset, 4).getFloat32(0, !0);
410
+ }
411
+ asFloat64() {
412
+ return this.ensureType(8), new DataView(this.payload.buffer, this.payload.byteOffset, 8).getFloat64(0, !0);
413
+ }
414
+ asBool() {
415
+ return this.ensureType(2), this.payload.length > 0 && this.payload[0] !== 0;
416
+ }
417
+ asBinary() {
418
+ return this.ensureType(13), this.payload;
419
+ }
420
+ asUtf8() {
421
+ return this.ensureType(12), this.payload;
422
+ }
423
+ asString() {
424
+ return this.ensureType(12), re.decode(this.payload);
425
+ }
426
+ asGuid() {
427
+ return this.ensureType(14), D.fromBytes(this.payload);
428
+ }
429
+ asObject() {
430
+ return this.ensureType(11), w.create(this.payload);
431
+ }
432
+ asArray() {
433
+ return this.ensureType(9), se.create(this.payload);
434
+ }
435
+ asDictionary() {
436
+ return this.ensureType(10), ce.create(this.payload);
437
+ }
438
+ ensureType(e) {
439
+ if (this.type !== e) throw Error(`Teleport value has type ${v[this.type]}, expected ${v[e]}`);
440
+ }
441
+ }, oe = class extends T {
442
+ fieldId;
443
+ constructor(e, t, n) {
444
+ super(t, n), this.fieldId = e;
445
+ }
446
+ get isNull() {
447
+ return this.type === 1;
448
+ }
449
+ }, se = class e {
450
+ payload;
451
+ elementType;
452
+ count;
453
+ offset;
454
+ index = 0;
455
+ constructor(e) {
456
+ if (this.payload = e, e.length === 0) throw Error("Array payload too short");
457
+ let t = e[0];
458
+ if (this.elementType = t >> 4 & 15, t & 15) throw Error("Array flags must be zero");
459
+ let n = { offset: 1 };
460
+ this.count = k(e, n, "ArrayMalformed"), this.offset = n.offset;
461
+ }
462
+ static create(t) {
463
+ return new e(R(t));
464
+ }
465
+ next() {
466
+ if (this.index >= this.count) {
467
+ if (this.offset !== this.payload.length) throw Error("Array payload contains trailing data");
468
+ return null;
469
+ }
470
+ let e = this.readValue();
471
+ return this.index++, e;
472
+ }
473
+ readValue() {
474
+ switch (this.elementType) {
475
+ case 3:
476
+ case 5:
477
+ case 7:
478
+ case 8:
479
+ case 2:
480
+ case 4:
481
+ case 6:
482
+ case 14: {
483
+ let e = j(this.elementType);
484
+ N(this.payload, this.offset, e);
485
+ let t = this.payload.subarray(this.offset, this.offset + e);
486
+ return this.offset += e, new E(this.elementType, t);
487
+ }
488
+ case 12:
489
+ case 13: {
490
+ let e = { offset: this.offset }, t = k(this.payload, e, "ArrayMalformed");
491
+ N(this.payload, e.offset, t);
492
+ let n = this.payload.subarray(e.offset, e.offset + t);
493
+ return this.offset = e.offset + t, new E(this.elementType, n);
494
+ }
495
+ case 11: {
496
+ let e = { offset: this.offset }, t = k(this.payload, e, "ArrayMalformed");
497
+ N(this.payload, e.offset, t);
498
+ let n = this.payload.subarray(e.offset, e.offset + t);
499
+ return this.offset = e.offset + t, new E(11, n);
500
+ }
501
+ case 9: {
502
+ let e = P(this.payload, this.offset), t = this.payload.subarray(this.offset, this.offset + e);
503
+ return this.offset += e, new E(9, t);
504
+ }
505
+ case 10: {
506
+ let e = F(this.payload, this.offset), t = this.payload.subarray(this.offset, this.offset + e);
507
+ return this.offset += e, new E(10, t);
508
+ }
509
+ default: throw Error(`Unsupported array element type ${v[this.elementType]}`);
510
+ }
511
+ }
512
+ }, E = class extends T {
513
+ constructor(e, t) {
514
+ super(e, t);
515
+ }
516
+ }, ce = class e {
517
+ payload;
518
+ keyType;
519
+ valueType;
520
+ count;
521
+ offset;
522
+ index = 0;
523
+ constructor(e) {
524
+ if (this.payload = e, e.length < 2) throw Error("Dictionary payload too short");
525
+ if (this.keyType = e[0] >> 4 & 15, this.valueType = e[1] >> 4 & 15, e[0] & 15 || e[1] & 15) throw Error("Dictionary key/value flags must be zero");
526
+ M(this.keyType);
527
+ let t = { offset: 2 };
528
+ this.count = k(e, t, "DictMalformed"), this.offset = t.offset;
529
+ }
530
+ static create(t) {
531
+ return new e(R(t));
532
+ }
533
+ next() {
534
+ if (this.index >= this.count) {
535
+ if (this.offset !== this.payload.length) throw Error("Dictionary payload contains trailing data");
536
+ return null;
537
+ }
538
+ let e = this.readKey(), t = this.readValue();
539
+ return this.index++, new le(e, t);
540
+ }
541
+ readKey() {
542
+ let e = j(this.keyType);
543
+ if (e >= 0) {
544
+ N(this.payload, this.offset, e);
545
+ let t = this.payload.subarray(this.offset, this.offset + e);
546
+ return this.offset += e, new T(this.keyType, t);
547
+ }
548
+ if (this.keyType === 12 || this.keyType === 13) {
549
+ let e = { offset: this.offset }, t = k(this.payload, e, "DictMalformed");
550
+ N(this.payload, e.offset, t);
551
+ let n = this.payload.subarray(e.offset, e.offset + t);
552
+ return this.offset = e.offset + t, new T(this.keyType, n);
553
+ }
554
+ throw Error("Unsupported dictionary key type");
555
+ }
556
+ readValue() {
557
+ switch (this.valueType) {
558
+ case 12:
559
+ case 13: {
560
+ let e = { offset: this.offset }, t = k(this.payload, e, "DictMalformed");
561
+ N(this.payload, e.offset, t);
562
+ let n = this.payload.subarray(e.offset, e.offset + t);
563
+ return this.offset = e.offset + t, new T(this.valueType, n);
564
+ }
565
+ case 11: {
566
+ let e = { offset: this.offset }, t = k(this.payload, e, "DictMalformed");
567
+ N(this.payload, e.offset, t);
568
+ let n = this.payload.subarray(e.offset, e.offset + t);
569
+ return this.offset = e.offset + t, new T(11, n);
570
+ }
571
+ case 9: {
572
+ let e = P(this.payload, this.offset), t = this.payload.subarray(this.offset, this.offset + e);
573
+ return this.offset += e, new T(9, t);
574
+ }
575
+ case 10: {
576
+ let e = F(this.payload, this.offset), t = this.payload.subarray(this.offset, this.offset + e);
577
+ return this.offset += e, new T(10, t);
578
+ }
579
+ case 3:
580
+ case 5:
581
+ case 7:
582
+ case 8:
583
+ case 2:
584
+ case 4:
585
+ case 6:
586
+ case 14:
587
+ case 1: {
588
+ let e = j(this.valueType);
589
+ N(this.payload, this.offset, e);
590
+ let t = this.payload.subarray(this.offset, this.offset + e);
591
+ return this.offset += e, new T(this.valueType, t);
592
+ }
593
+ default: throw Error(`Unsupported dictionary value type ${v[this.valueType]}`);
594
+ }
595
+ }
596
+ }, le = class {
597
+ key;
598
+ value;
599
+ constructor(e, t) {
600
+ this.key = e, this.value = t;
601
+ }
602
+ }, D = class e {
603
+ bytes;
604
+ constructor(e) {
605
+ this.bytes = e;
606
+ }
607
+ static fromString(t) {
608
+ if (!t) throw Error("Guid string is empty");
609
+ let n = t.replace(/-/g, "");
610
+ if (n.length !== 32) throw Error("Guid string must be 32 hex characters");
611
+ let r = /* @__PURE__ */ new Uint8Array(16), i = e.parseHexSlice(n, 0, 8), a = e.parseHexSlice(n, 8, 4), o = e.parseHexSlice(n, 12, 4);
612
+ e.writeUInt32LE(r, 0, i), e.writeUInt16LE(r, 4, a), e.writeUInt16LE(r, 6, o);
613
+ for (let t = 0; t < 8; t++) r[8 + t] = e.parseHexSlice(n, 16 + t * 2, 2);
614
+ return new e(r);
615
+ }
616
+ static fromBytes(t) {
617
+ if (t.length !== 16) throw Error("Guid byte array must be 16 bytes");
618
+ return new e(Uint8Array.from(t));
619
+ }
620
+ static createZero() {
621
+ return new e(/* @__PURE__ */ new Uint8Array(16));
622
+ }
623
+ static createRandom() {
624
+ let t = /* @__PURE__ */ new Uint8Array(16), n = globalThis.crypto;
625
+ if (n?.getRandomValues) n.getRandomValues(t);
626
+ else for (let e = 0; e < t.length; e++) t[e] = Math.floor(Math.random() * 256);
627
+ return t[6] = t[6] & 15 | 64, t[8] = t[8] & 63 | 128, new e(t);
628
+ }
629
+ toString() {
630
+ let t = this.bytes;
631
+ return [
632
+ e.toHex(e.readUInt32LE(t, 0), 8),
633
+ e.toHex(e.readUInt16LE(t, 4), 4),
634
+ e.toHex(e.readUInt16LE(t, 6), 4),
635
+ L(t.subarray(8, 10)),
636
+ L(t.subarray(10, 16))
637
+ ].join("-");
638
+ }
639
+ asBytes() {
640
+ return this.bytes.slice();
641
+ }
642
+ static parseHexSlice(e, t, n) {
643
+ let r = e.substr(t, n), i = Number.parseInt(r, 16);
644
+ if (Number.isNaN(i)) throw Error("Guid string contains invalid characters");
645
+ return i >>> 0;
646
+ }
647
+ static writeUInt32LE(e, t, n) {
648
+ let r = n >>> 0;
649
+ e[t] = r & 255, e[t + 1] = r >>> 8 & 255, e[t + 2] = r >>> 16 & 255, e[t + 3] = r >>> 24 & 255;
650
+ }
651
+ static writeUInt16LE(e, t, n) {
652
+ let r = n & 65535;
653
+ e[t] = r & 255, e[t + 1] = r >>> 8 & 255;
654
+ }
655
+ static readUInt32LE(e, t) {
656
+ return (e[t] | e[t + 1] << 8 | e[t + 2] << 16 | e[t + 3] << 24) >>> 0;
657
+ }
658
+ static readUInt16LE(e, t) {
659
+ return (e[t] | e[t + 1] << 8) & 65535;
660
+ }
661
+ static toHex(e, t) {
662
+ return (e >>> 0).toString(16).padStart(t, "0");
663
+ }
664
+ }, O = class e {
665
+ static INITIAL_CAPACITY = 256;
666
+ buffer;
667
+ dataView;
668
+ length = 0;
669
+ constructor() {
670
+ this.buffer = new Uint8Array(e.INITIAL_CAPACITY), this.dataView = new DataView(this.buffer.buffer);
671
+ }
672
+ ensureCapacity(e) {
673
+ let t = this.length + e;
674
+ if (t <= this.buffer.length) return;
675
+ let n = this.buffer.length;
676
+ for (; n < t;) n *= 2;
677
+ let r = new Uint8Array(n);
678
+ r.set(this.buffer.subarray(0, this.length)), this.buffer = r, this.dataView = new DataView(this.buffer.buffer);
679
+ }
680
+ writeByte(e) {
681
+ this.ensureCapacity(1), this.buffer[this.length++] = e & 255;
682
+ }
683
+ writeBytes(e) {
684
+ this.ensureCapacity(e.length), this.buffer.set(e, this.length), this.length += e.length;
685
+ }
686
+ writeUInt32(e) {
687
+ this.ensureCapacity(4), this.dataView.setUint32(this.length, e >>> 0, !0), this.length += 4;
688
+ }
689
+ writeInt32(e) {
690
+ this.ensureCapacity(4), this.dataView.setInt32(this.length, e | 0, !0), this.length += 4;
691
+ }
692
+ writeFloat32(e) {
693
+ this.ensureCapacity(4), this.dataView.setFloat32(this.length, e, !0), this.length += 4;
694
+ }
695
+ writeFloat64(e) {
696
+ this.ensureCapacity(8), this.dataView.setFloat64(this.length, e, !0), this.length += 8;
697
+ }
698
+ writeBigInt64(e) {
699
+ this.ensureCapacity(8), this.dataView.setBigInt64(this.length, e, !0), this.length += 8;
700
+ }
701
+ writeBigUInt64(e) {
702
+ this.ensureCapacity(8), this.dataView.setBigUint64(this.length, e, !0), this.length += 8;
703
+ }
704
+ writeVarUInt(e) {
705
+ let t = e >>> 0;
706
+ for (; t >= 128;) this.ensureCapacity(1), this.buffer[this.length++] = t & 127 | 128, t >>>= 7;
707
+ this.ensureCapacity(1), this.buffer[this.length++] = t & 127;
708
+ }
709
+ toUint8Array() {
710
+ return this.buffer.slice(0, this.length);
711
+ }
712
+ };
713
+ function k(e, t, n) {
714
+ if (t.offset >= e.length) throw Error(n);
715
+ let r = 0, i = 0, a = 0;
716
+ for (; t.offset < e.length;) {
717
+ let n = e[t.offset++];
718
+ if (a++, r |= (n & 127) << i, !(n & 128)) {
719
+ if (a !== ue(r)) throw Error("Teleport VarUInt is not canonical");
720
+ return r >>> 0;
721
+ }
722
+ if (i += 7, i >= 35) throw Error("Teleport VarUInt exceeds 32-bit range");
723
+ }
724
+ throw Error(n);
725
+ }
726
+ function ue(e) {
727
+ return e < 128 ? 1 : e < 16384 ? 2 : e < 2097152 ? 3 : e < 268435456 ? 4 : 5;
728
+ }
729
+ function A(e, t = 0) {
730
+ if (t & 240) throw Error("Teleport flags must fit into 4 bits");
731
+ return (e & 15) << 4 | t & 15;
732
+ }
733
+ function de(e) {
734
+ return e === 12 || e === 13 || e === 9 || e === 11 || e === 10;
735
+ }
736
+ function j(e) {
737
+ switch (e) {
738
+ case 3:
739
+ case 5:
740
+ case 7: return 4;
741
+ case 4:
742
+ case 6:
743
+ case 8: return 8;
744
+ case 14: return 16;
745
+ case 2: return 1;
746
+ case 1: return 0;
747
+ default: return -1;
748
+ }
749
+ }
750
+ function M(e) {
751
+ if (e === 9 || e === 11 || e === 10 || e === 1) throw Error("Dictionary keys must be primitive Teleport types");
752
+ }
753
+ function N(e, t, n) {
754
+ if (t < 0 || n < 0 || t + n > e.length) throw Error("Teleport payload exceeds bounds");
755
+ }
756
+ function fe(e, t) {
757
+ return (e[t] | e[t + 1] << 8 | e[t + 2] << 16 | e[t + 3] << 24) >>> 0;
758
+ }
759
+ function P(e, t) {
760
+ if (t >= e.length) throw Error("Array payload exceeds bounds");
761
+ let n = e[t], r = n >> 4 & 15;
762
+ if (n & 15) throw Error("Array flags must be zero");
763
+ let i = { offset: t + 1 }, a = k(e, i, "ArrayMalformed"), o = j(r);
764
+ if (o >= 0) {
765
+ let n = o * a;
766
+ return N(e, i.offset, n), i.offset + n - t;
767
+ }
768
+ let s = i.offset;
769
+ for (let t = 0; t < a; t++) s = I(r, e, s, "ArrayMalformed");
770
+ return s - t;
771
+ }
772
+ function F(e, t) {
773
+ if (t + 2 > e.length) throw Error("Dictionary payload too short");
774
+ let n = e[t] >> 4 & 15, r = e[t + 1] >> 4 & 15;
775
+ if (e[t] & 15 || e[t + 1] & 15) throw Error("Dictionary key/value flags must be zero");
776
+ M(n);
777
+ let i = { offset: t + 2 }, a = k(e, i, "DictMalformed"), o = i.offset;
778
+ for (let t = 0; t < a; t++) o = I(n, e, o, "DictMalformed"), o = I(r, e, o, "DictMalformed");
779
+ return o - t;
780
+ }
781
+ function I(e, t, n, r) {
782
+ let i = j(e);
783
+ if (i >= 0) return N(t, n, i), n + i;
784
+ switch (e) {
785
+ case 12:
786
+ case 13: {
787
+ let e = { offset: n }, i = k(t, e, r);
788
+ return N(t, e.offset, i), e.offset + i;
789
+ }
790
+ case 11: {
791
+ let e = { offset: n }, i = k(t, e, r);
792
+ return N(t, e.offset, i), e.offset + i;
793
+ }
794
+ case 9: return n + P(t, n);
795
+ case 10: return n + F(t, n);
796
+ default: throw Error(`Unsupported Teleport type ${v[e]}`);
797
+ }
798
+ }
799
+ function L(e) {
800
+ return Array.from(e).map((e) => e.toString(16).padStart(2, "0")).join("");
801
+ }
802
+ function R(e) {
803
+ return e instanceof Uint8Array ? e : new Uint8Array(e);
804
+ }
805
+ //#endregion
806
+ //#region ../../shared/protocol/src/enums/log-type.ts
807
+ var pe = /* @__PURE__ */ function(e) {
808
+ return e[e.None = 0] = "None", e[e.Trace = 1] = "Trace", e[e.Debug = 2] = "Debug", e[e.Info = 4] = "Info", e[e.Warning = 8] = "Warning", e[e.Error = 16] = "Error", e[e.Critical = 32] = "Critical", e[e.Event = 64] = "Event", e[e.Usage = 128] = "Usage", e[e.Exception = 256] = "Exception", e;
809
+ }({}), z = /* @__PURE__ */ function(e) {
810
+ return e[e.Unknown = 0] = "Unknown", e[e.Playing = 1] = "Playing", e[e.Blocked = 2] = "Blocked", e[e.Hidden = 3] = "Hidden", e;
811
+ }({}), me = /* @__PURE__ */ function(e) {
812
+ return e[e.Unknown = 0] = "Unknown", e[e.Opus = 1] = "Opus", e[e.Mp3 = 2] = "Mp3", e[e.RawPcm16 = 3] = "RawPcm16", e;
813
+ }({}), he = /* @__PURE__ */ function(e) {
814
+ return e[e.Unknown = 0] = "Unknown", e[e.MobileWeb = 1] = "MobileWeb", e[e.MobileApp = 2] = "MobileApp", e[e.DesktopWeb = 3] = "DesktopWeb", e[e.DesktopApp = 4] = "DesktopApp", e;
815
+ }({}), ge = /* @__PURE__ */ function(e) {
816
+ return e[e.Unknown = 0] = "Unknown", e[e.Backend = 2] = "Backend", e[e.Server = 4] = "Server", e[e.Plugin = 8] = "Plugin", e[e.Browser = 16] = "Browser", e[e.Native = 32] = "Native", e;
817
+ }({}), _e = /* @__PURE__ */ function(e) {
818
+ return e[e.None = 0] = "None", e[e.WebSocket = 1] = "WebSocket", e[e.WebSocketProxy = 2] = "WebSocketProxy", e[e.WebTransport = 4] = "WebTransport", e[e.WebTransportProxy = 8] = "WebTransportProxy", e[e.Tcp = 16] = "Tcp", e[e.TcpProxy = 32] = "TcpProxy", e[e.Https = 64] = "Https", e[e.WebRTC = 128] = "WebRTC", e[e.TcpTls = 256] = "TcpTls", e[e.Udp = 512] = "Udp", e[e.UdpDtls = 1024] = "UdpDtls", e;
819
+ }({}), ve = /* @__PURE__ */ function(e) {
820
+ return e[e.None = 0] = "None", e[e.SendBackToSender = 1] = "SendBackToSender", e[e.Delayable = 2] = "Delayable", e[e.SendToUser = 4] = "SendToUser", e[e.Compressed = 8] = "Compressed", e[e.Unreliable = 16] = "Unreliable", e;
821
+ }({}), ye = /* @__PURE__ */ function(e) {
822
+ return e[e.Unknown = 0] = "Unknown", e[e.MessagePack = 1] = "MessagePack", e[e.MemoryPack = 2] = "MemoryPack", e[e.Json = 4] = "Json", e[e.Teleport = 8] = "Teleport", e[e.All = 15] = "All", e;
823
+ }({}), be = /* @__PURE__ */ function(e) {
824
+ return e[e.Css = 0] = "Css", e[e.Flutter = 1] = "Flutter", e;
825
+ }({}), xe = /* @__PURE__ */ function(e) {
826
+ return e[e.Unknown = 0] = "Unknown", e[e.Machine = 1] = "Machine", e[e.Human = 2] = "Human", e;
827
+ }({}), B = /* @__PURE__ */ function(e) {
828
+ return e[e.Unknown = 0] = "Unknown", e[e.H264 = 1] = "H264", e[e.Vp8 = 2] = "Vp8", e[e.Vp9 = 3] = "Vp9", e[e.Av1 = 4] = "Av1", e;
829
+ }({}), Se = /* @__PURE__ */ function(e) {
830
+ return e[e.Unknown = 0] = "Unknown", e[e.DotNet = 1] = "DotNet", e[e.TypeScript = 2] = "TypeScript", e[e.Cpp = 3] = "Cpp", e[e.Dart = 4] = "Dart", e[e.Rust = 5] = "Rust", e;
831
+ }({}), Ce = /* @__PURE__ */ function(e) {
832
+ return e[e.Local = 0] = "Local", e[e.Cloud = 1] = "Cloud", e;
833
+ }({}), we = 4194311, Te = 112206629, Ee = 420935914, De = 4086360332, Oe = 4253308507;
834
+ function V(e) {
835
+ let t = {};
836
+ return ke(t), e && Object.assign(t, e), t;
837
+ }
838
+ function ke(e) {
839
+ return e.Epoch = 0, e.PlayedSeconds = 0, e.BufferedMs = 0, e.State = z.Unknown, e;
840
+ }
841
+ function Ae(e) {
842
+ let t = new S(1);
843
+ return je(e, t), t.finish();
844
+ }
845
+ function je(e, t) {
846
+ t.writeFloat32Field(Te, e.BufferedMs), t.writeInt32Field(Ee, e.State), t.writeUInt32Field(De, e.Epoch >>> 0), t.writeFloat64Field(Oe, e.PlayedSeconds);
847
+ }
848
+ function H(e, t, n) {
849
+ return _(we, Ae(e), 1, t, n);
850
+ }
851
+ var Me = 161083277, Ne = 265814330, Pe = 1368629611, Fe = 2431514951, Ie = 2514959030, Le = 2745379226, Re = 3282782683, ze = 3284746250, Be = 4101844078;
852
+ function U(e) {
853
+ let t = {};
854
+ return W(t), e && Object.assign(t, e), t;
855
+ }
856
+ function W(e) {
857
+ return e.StreamId = "", e.Description = "", e.SourceType = "", e.Codec = me.Unknown, e.CodecDetails = "", e.SampleRate = 0, e.Channels = 0, e.ShapeSets = void 0, e.CorrelationId = void 0, e;
858
+ }
859
+ function Ve(e, t) {
860
+ let n = t ?? U();
861
+ return W(n), He(e, n), n;
862
+ }
863
+ function He(e, t) {
864
+ let n;
865
+ for (; (n = e.next()) !== null;) switch (n.fieldId) {
866
+ case Me:
867
+ if (n.isNull) throw Error();
868
+ t.StreamId = n.asString();
869
+ break;
870
+ case Ne:
871
+ if (n.isNull) {
872
+ t.ShapeSets = void 0;
873
+ break;
874
+ }
875
+ t.ShapeSets = Ue(n.asArray());
876
+ break;
877
+ case Pe:
878
+ if (n.isNull) throw Error();
879
+ t.Description = n.asString();
880
+ break;
881
+ case Fe:
882
+ if (n.isNull) throw Error();
883
+ t.Channels = n.asInt32();
884
+ break;
885
+ case Ie:
886
+ if (n.isNull) {
887
+ t.CorrelationId = void 0;
888
+ break;
889
+ }
890
+ t.CorrelationId = n.asString();
891
+ break;
892
+ case Le:
893
+ if (n.isNull) throw Error();
894
+ t.CodecDetails = n.asString();
895
+ break;
896
+ case Re:
897
+ if (n.isNull) throw Error();
898
+ t.SourceType = n.asString();
899
+ break;
900
+ case ze:
901
+ if (n.isNull) throw Error();
902
+ t.Codec = n.asInt32();
903
+ break;
904
+ case Be:
905
+ if (n.isNull) throw Error();
906
+ t.SampleRate = n.asInt32();
907
+ break;
908
+ default: break;
909
+ }
910
+ }
911
+ function Ue(e) {
912
+ let t = [], n;
913
+ for (; (n = e.next()) !== null;) t.push(Je(n.asObject()));
914
+ return t;
915
+ }
916
+ var We = 1107713536, Ge = 1154362099, Ke = 1185721362;
917
+ function qe(e) {
918
+ let t = {};
919
+ return G(t), e && Object.assign(t, e), t;
920
+ }
921
+ function G(e) {
922
+ return e.SetId = 0, e.Name = "", e.ShapeNames = [], e;
923
+ }
924
+ function Je(e, t) {
925
+ let n = t ?? qe();
926
+ return G(n), Ye(e, n), n;
927
+ }
928
+ function Ye(e, t) {
929
+ let n;
930
+ for (; (n = e.next()) !== null;) switch (n.fieldId) {
931
+ case We:
932
+ if (n.isNull) throw Error();
933
+ t.Name = n.asString();
934
+ break;
935
+ case Ge:
936
+ if (n.isNull) throw Error();
937
+ t.SetId = n.asUInt32();
938
+ break;
939
+ case Ke:
940
+ if (n.isNull) throw Error();
941
+ t.ShapeNames = Xe(n.asArray());
942
+ break;
943
+ default: break;
944
+ }
945
+ }
946
+ function Xe(e) {
947
+ let t = [], n;
948
+ for (; (n = e.next()) !== null;) t.push(n.asString());
949
+ return t;
950
+ }
951
+ var Ze = 8388612;
952
+ function Qe(e) {
953
+ let t = {};
954
+ return e && Object.assign(t, e), t;
955
+ }
956
+ function $e(e) {
957
+ return new S(1).finish();
958
+ }
959
+ function et(e, t, n) {
960
+ return _(Ze, $e(e), 1, t, n);
961
+ }
962
+ var tt = 8388609, K = 161083277, q = 164808083, nt = 1368629611, rt = 2514959030, it = 2745379226, at = 2950031986, ot = 3282782683, st = 3284746250, ct = 4065070594;
963
+ function lt(e) {
964
+ let t = {};
965
+ return ut(t), e && Object.assign(t, e), t;
966
+ }
967
+ function ut(e) {
968
+ return e.StreamId = "", e.Description = "", e.SourceType = "", e.Codec = B.H264, e.CodecDetails = "", e.Width = 0, e.Height = 0, e.Framerate = 30, e.CorrelationId = void 0, e;
969
+ }
970
+ function dt(e) {
971
+ let t = new S(6);
972
+ return ft(e, t), t.finish();
973
+ }
974
+ function ft(e, t) {
975
+ t.writeStringField(K, e.StreamId), t.writeFloat64Field(q, e.Framerate), t.writeStringField(nt, e.Description);
976
+ let n = e.CorrelationId;
977
+ n != null && t.writeStringField(rt, n), t.writeStringField(it, e.CodecDetails), t.writeInt32Field(at, e.Height | 0), t.writeStringField(ot, e.SourceType), t.writeInt32Field(st, e.Codec), t.writeInt32Field(ct, e.Width | 0);
978
+ }
979
+ function pt(e, t) {
980
+ let n = t ?? lt();
981
+ return ut(n), mt(e, n), n;
982
+ }
983
+ function mt(e, t) {
984
+ let n;
985
+ for (; (n = e.next()) !== null;) switch (n.fieldId) {
986
+ case K:
987
+ if (n.isNull) throw Error();
988
+ t.StreamId = n.asString();
989
+ break;
990
+ case q:
991
+ if (n.isNull) throw Error();
992
+ t.Framerate = n.asFloat64();
993
+ break;
994
+ case nt:
995
+ if (n.isNull) throw Error();
996
+ t.Description = n.asString();
997
+ break;
998
+ case rt:
999
+ if (n.isNull) {
1000
+ t.CorrelationId = void 0;
1001
+ break;
1002
+ }
1003
+ t.CorrelationId = n.asString();
1004
+ break;
1005
+ case it:
1006
+ if (n.isNull) throw Error();
1007
+ t.CodecDetails = n.asString();
1008
+ break;
1009
+ case at:
1010
+ if (n.isNull) throw Error();
1011
+ t.Height = n.asInt32();
1012
+ break;
1013
+ case ot:
1014
+ if (n.isNull) throw Error();
1015
+ t.SourceType = n.asString();
1016
+ break;
1017
+ case st:
1018
+ if (n.isNull) throw Error();
1019
+ t.Codec = n.asInt32();
1020
+ break;
1021
+ case ct:
1022
+ if (n.isNull) throw Error();
1023
+ t.Width = n.asInt32();
1024
+ break;
1025
+ default: break;
1026
+ }
1027
+ }
1028
+ function ht(e, t, n) {
1029
+ return _(tt, dt(e), 6, t, n);
1030
+ }
1031
+ var gt = 8388610;
1032
+ function _t(e) {
1033
+ let t = {};
1034
+ return e && Object.assign(t, e), t;
1035
+ }
1036
+ function vt(e) {
1037
+ return new S(1).finish();
1038
+ }
1039
+ function yt(e, t, n) {
1040
+ return _(gt, vt(e), 1, t, n);
1041
+ }
1042
+ //#endregion
1043
+ //#region src/media/media-session-helper.ts
1044
+ function bt() {
1045
+ let e = 44100, t = /* @__PURE__ */ new ArrayBuffer(882044), n = new DataView(t), r = (e, t) => {
1046
+ for (let r = 0; r < t.length; r++) n.setUint8(e + r, t.charCodeAt(r));
1047
+ };
1048
+ r(0, "RIFF"), n.setUint32(4, 882036, !0), r(8, "WAVE"), r(12, "fmt "), n.setUint32(16, 16, !0), n.setUint16(20, 1, !0), n.setUint16(22, 1, !0), n.setUint32(24, e, !0), n.setUint32(28, e * 2, !0), n.setUint16(32, 2, !0), n.setUint16(34, 16, !0), r(36, "data"), n.setUint32(40, 441e3 * 2, !0);
1049
+ let i = new Blob([t], { type: "audio/wav" });
1050
+ return URL.createObjectURL(i);
1051
+ }
1052
+ var xt = class {
1053
+ _silentAudioElement = null;
1054
+ get silentAudioElement() {
1055
+ return this._silentAudioElement;
1056
+ }
1057
+ ensureSilentPlaybackElement() {
1058
+ typeof document > "u" || (this._silentAudioElement || (this._silentAudioElement = document.createElement("audio"), this._silentAudioElement.src = bt(), this._silentAudioElement.loop = !0, this._silentAudioElement.volume = .01), this._silentAudioElement.paused && this._silentAudioElement.play().catch(() => {}));
1059
+ }
1060
+ setup(e, t) {
1061
+ if (!(typeof document > "u") && (this.ensureSilentPlaybackElement(), typeof navigator < "u" && "mediaSession" in navigator)) {
1062
+ let n = e ?? {};
1063
+ navigator.mediaSession.metadata = new MediaMetadata({
1064
+ title: n.title || "Audio",
1065
+ artist: n.artist || "Ikon AI App"
1066
+ }), navigator.mediaSession.playbackState = "playing";
1067
+ try {
1068
+ navigator.mediaSession.setPositionState({
1069
+ duration: 86400,
1070
+ playbackRate: 1,
1071
+ position: 0
1072
+ });
1073
+ } catch {}
1074
+ navigator.mediaSession.setActionHandler("play", () => {
1075
+ t.onPlay(), this._silentAudioElement?.play().catch(() => {}), navigator.mediaSession.playbackState = "playing";
1076
+ }), navigator.mediaSession.setActionHandler("pause", () => {
1077
+ t.onPause(), this._silentAudioElement?.pause(), navigator.mediaSession.playbackState = "paused";
1078
+ });
1079
+ }
1080
+ }
1081
+ cleanup() {
1082
+ if (this._silentAudioElement) try {
1083
+ this._silentAudioElement.pause();
1084
+ let e = this._silentAudioElement.src;
1085
+ this._silentAudioElement.src = "", e.startsWith("blob:") && URL.revokeObjectURL(e), this._silentAudioElement = null;
1086
+ } catch {}
1087
+ if (typeof navigator < "u" && "mediaSession" in navigator) try {
1088
+ navigator.mediaSession.setActionHandler("play", null), navigator.mediaSession.setActionHandler("pause", null);
1089
+ } catch {}
1090
+ }
1091
+ };
1092
+ //#endregion
1093
+ //#region src/worker/audio-playback-worker.ts?worker
1094
+ function St(e) {
1095
+ return new Worker("" + new URL("assets/audio-playback-worker-DivBZPfS.js", import.meta.url).href, {
1096
+ type: "module",
1097
+ name: e?.name
1098
+ });
1099
+ }
1100
+ //#endregion
1101
+ //#region src/media/ikon-audio-playback.ts
1102
+ var J = t("IkonAudioPlayback"), Ct = {
1103
+ enabled: !1,
1104
+ statusIntervalMs: 1e3
1105
+ };
1106
+ function Y(e) {
1107
+ return e instanceof Error ? `${e.name}: ${e.message}` : String(e);
1108
+ }
1109
+ function wt(e) {
1110
+ return `
1111
+ // Audio buffer constants
1112
+ const OUTPUT_SAMPLE_RATE_HZ = ${e.outputSampleRateHz};
1113
+ const DEFAULT_FRAME_SAMPLES_PER_CHANNEL = Math.max(1, Math.round(OUTPUT_SAMPLE_RATE_HZ * 0.02));
1114
+ const BUFFER_TARGET_MIN_MS = 120;
1115
+ const BUFFER_TARGET_MAX_MS = 300;
1116
+ const JITTER_MULTIPLIER = 4;
1117
+ const LOCAL_BUFFER_MAX_MS = ${o};
1118
+ const CONTROL_PERIOD_MS = 200;
1119
+ const UNDERRUN_JUMP_MS = 100;
1120
+ const STABLE_TIME_TO_DECREASE_MS = ${a};
1121
+ const DECREASE_STEP_MS = 10;
1122
+ const DEFAULT_STATS_INTERVAL_SAMPLES = OUTPUT_SAMPLE_RATE_HZ;
1123
+ const PLAYBACK_REPORT_INTERVAL_SAMPLES = Math.round(OUTPUT_SAMPLE_RATE_HZ / 2);
1124
+
1125
+ // Pre-computed reciprocals for division-free calculations
1126
+ const INV_OUTPUT_SAMPLE_RATE_HZ = 1 / OUTPUT_SAMPLE_RATE_HZ;
1127
+ const MS_PER_SAMPLE = 1000 * INV_OUTPUT_SAMPLE_RATE_HZ;
1128
+
1129
+ // Fade envelope constants
1130
+ const FADE_IN_DURATION_MS = 100;
1131
+ const FADE_OUT_DURATION_MS = 50;
1132
+ const FADE_IN_DURATION_SAMPLES = Math.round(OUTPUT_SAMPLE_RATE_HZ * FADE_IN_DURATION_MS / 1000);
1133
+ const FADE_OUT_DURATION_SAMPLES = Math.round(OUTPUT_SAMPLE_RATE_HZ * FADE_OUT_DURATION_MS / 1000);
1134
+ const FADE_IN_INCREMENT = 1.0 / FADE_IN_DURATION_SAMPLES;
1135
+ const FADE_OUT_INCREMENT = 1.0 / FADE_OUT_DURATION_SAMPLES;
1136
+
1137
+ // Render quantum size (standard Web Audio API value)
1138
+ const RENDER_QUANTUM_SIZE = 128;
1139
+
1140
+ // Buffer reduction crossfade constants (align to quantum boundary to avoid partial quanta)
1141
+ const BUFFER_REDUCTION_CROSSFADE_MS = 20;
1142
+ const BUFFER_REDUCTION_CROSSFADE_SAMPLES_RAW = Math.round(OUTPUT_SAMPLE_RATE_HZ * BUFFER_REDUCTION_CROSSFADE_MS / 1000);
1143
+ const BUFFER_REDUCTION_CROSSFADE_SAMPLES = Math.ceil(BUFFER_REDUCTION_CROSSFADE_SAMPLES_RAW / RENDER_QUANTUM_SIZE) * RENDER_QUANTUM_SIZE;
1144
+
1145
+ class RingBuffer {
1146
+ static getStorageForCapacity(capacity, type) {
1147
+ const indexBytes = Int32Array.BYTES_PER_ELEMENT * 2;
1148
+ const dataBytes = type.BYTES_PER_ELEMENT * capacity;
1149
+ return new SharedArrayBuffer(indexBytes + dataBytes);
1150
+ }
1151
+
1152
+ constructor(sharedArrayBuffer, type, channels = 1) {
1153
+ this.buf = sharedArrayBuffer;
1154
+ this.readWrite = new Int32Array(sharedArrayBuffer, 0, 2);
1155
+ const dataOffset = Int32Array.BYTES_PER_ELEMENT * 2;
1156
+ const dataLength = (sharedArrayBuffer.byteLength - dataOffset) / Float32Array.BYTES_PER_ELEMENT;
1157
+ this.storage = new type(sharedArrayBuffer, dataOffset, dataLength);
1158
+ this.channels = channels;
1159
+ }
1160
+
1161
+ get capacity() {
1162
+ return this.storage.length;
1163
+ }
1164
+
1165
+ availableRead() {
1166
+ const read = Atomics.load(this.readWrite, 0);
1167
+ const write = Atomics.load(this.readWrite, 1);
1168
+ if (write >= read) return write - read;
1169
+ return this.capacity - (read - write);
1170
+ }
1171
+
1172
+ pop(target) {
1173
+ const rawReadable = Math.min(this.availableRead(), target.length);
1174
+ const readable = Math.floor(rawReadable / this.channels) * this.channels;
1175
+ if (readable <= 0) {
1176
+ target.fill(0);
1177
+ return 0;
1178
+ }
1179
+
1180
+ let read = Atomics.load(this.readWrite, 0);
1181
+ const firstPart = Math.min(readable, this.capacity - read);
1182
+ for (let i = 0; i < firstPart; i++) {
1183
+ target[i] = this.storage[read + i];
1184
+ }
1185
+ const remaining = readable - firstPart;
1186
+ if (remaining > 0) {
1187
+ for (let i = 0; i < remaining; i++) {
1188
+ target[firstPart + i] = this.storage[i];
1189
+ }
1190
+ }
1191
+ if (readable < target.length) {
1192
+ target.fill(0, readable);
1193
+ }
1194
+ read = (read + readable) % this.capacity;
1195
+ Atomics.store(this.readWrite, 0, read);
1196
+ return readable;
1197
+ }
1198
+
1199
+ skip(count) {
1200
+ const rawReadable = Math.min(this.availableRead(), count);
1201
+ const readable = Math.floor(rawReadable / this.channels) * this.channels;
1202
+ if (readable <= 0) return 0;
1203
+
1204
+ let read = Atomics.load(this.readWrite, 0);
1205
+ read = (read + readable) % this.capacity;
1206
+ Atomics.store(this.readWrite, 0, read);
1207
+ return readable;
1208
+ }
1209
+
1210
+ flush() {
1211
+ Atomics.store(this.readWrite, 0, 0);
1212
+ Atomics.store(this.readWrite, 1, 0);
1213
+ }
1214
+ }
1215
+
1216
+ // Simple queue buffer for non-SAB fallback
1217
+ class QueueBuffer {
1218
+ constructor(maxSamples, channels = 1) {
1219
+ this.chunks = [];
1220
+ this.headIndex = 0;
1221
+ this.offset = 0;
1222
+ this.maxSamples = maxSamples;
1223
+ this.totalSamples = 0;
1224
+ this.channels = channels;
1225
+ this.needsCompaction = false;
1226
+ }
1227
+
1228
+ available() {
1229
+ return this.totalSamples;
1230
+ }
1231
+
1232
+ availableRead() {
1233
+ return this.totalSamples;
1234
+ }
1235
+
1236
+ compact() {
1237
+ if (!this.needsCompaction || this.headIndex === 0) return;
1238
+ const activeCount = this.chunks.length - this.headIndex;
1239
+ for (let i = 0; i < activeCount; i++) {
1240
+ this.chunks[i] = this.chunks[this.headIndex + i];
1241
+ }
1242
+ this.chunks.length = activeCount;
1243
+ this.headIndex = 0;
1244
+ this.needsCompaction = false;
1245
+ }
1246
+
1247
+ push(chunk) {
1248
+ this.chunks.push(chunk);
1249
+ this.totalSamples += chunk.length;
1250
+ if (typeof this.maxSamples === 'number' && this.maxSamples > 0) {
1251
+ if (this.totalSamples > this.maxSamples) {
1252
+ this.skip(this.totalSamples - this.maxSamples);
1253
+ }
1254
+ }
1255
+ }
1256
+
1257
+ pop(target) {
1258
+ if (this.totalSamples <= 0) {
1259
+ target.fill(0);
1260
+ return 0;
1261
+ }
1262
+
1263
+ const rawRequired = Math.min(target.length, this.totalSamples);
1264
+ const required = Math.floor(rawRequired / this.channels) * this.channels;
1265
+ if (required <= 0) {
1266
+ target.fill(0);
1267
+ return 0;
1268
+ }
1269
+
1270
+ let written = 0;
1271
+ while (written < required && this.headIndex < this.chunks.length) {
1272
+ const current = this.chunks[this.headIndex];
1273
+ const available = current.length - this.offset;
1274
+ const toCopy = Math.min(available, required - written);
1275
+ for (let i = 0; i < toCopy; i++) {
1276
+ target[written + i] = current[this.offset + i];
1277
+ }
1278
+ written += toCopy;
1279
+ this.offset += toCopy;
1280
+ this.totalSamples -= toCopy;
1281
+ if (this.offset >= current.length) {
1282
+ this.headIndex += 1;
1283
+ this.offset = 0;
1284
+ }
1285
+ }
1286
+
1287
+ if (written < target.length) {
1288
+ target.fill(0, written);
1289
+ }
1290
+
1291
+ if (this.headIndex > 32 && this.headIndex > this.chunks.length / 2) {
1292
+ this.needsCompaction = true;
1293
+ }
1294
+
1295
+ return written;
1296
+ }
1297
+
1298
+ skip(count) {
1299
+ if (this.totalSamples <= 0 || count <= 0) return 0;
1300
+
1301
+ const alignedCount = Math.floor(count / this.channels) * this.channels;
1302
+ if (alignedCount <= 0) return 0;
1303
+
1304
+ let remaining = alignedCount;
1305
+ while (remaining > 0 && this.headIndex < this.chunks.length) {
1306
+ const current = this.chunks[this.headIndex];
1307
+ const available = current.length - this.offset;
1308
+ if (remaining < available) {
1309
+ this.offset += remaining;
1310
+ this.totalSamples -= remaining;
1311
+ return alignedCount;
1312
+ }
1313
+ remaining -= available;
1314
+ this.totalSamples -= available;
1315
+ this.headIndex += 1;
1316
+ this.offset = 0;
1317
+ }
1318
+
1319
+ if (this.headIndex > 32 && this.headIndex > this.chunks.length / 2) {
1320
+ this.needsCompaction = true;
1321
+ }
1322
+ return alignedCount - remaining;
1323
+ }
1324
+
1325
+ flush() {
1326
+ this.chunks.length = 0;
1327
+ this.headIndex = 0;
1328
+ this.offset = 0;
1329
+ this.totalSamples = 0;
1330
+ this.needsCompaction = false;
1331
+ }
1332
+ }
1333
+
1334
+ class IkonAudioPlayerProcessor extends AudioWorkletProcessor {
1335
+ constructor(options) {
1336
+ super();
1337
+ const cfg = options?.processorOptions?.outputAudioConfig || {};
1338
+ const outputChannels = options?.processorOptions?.outputChannels;
1339
+ this.channels = typeof outputChannels === 'number' && outputChannels > 0 ? outputChannels : ${e.outputChannels};
1340
+ this.useSharedArrayBuffer = options?.processorOptions?.useSharedArrayBuffer !== false;
1341
+ this.queueMaxSamples = Math.floor((OUTPUT_SAMPLE_RATE_HZ * this.channels * LOCAL_BUFFER_MAX_MS) / 1000);
1342
+ this.suspendedMaxSamples = Math.floor((OUTPUT_SAMPLE_RATE_HZ * this.channels * BUFFER_TARGET_MIN_MS) / 1000);
1343
+ this.isPlaybackActive = false;
1344
+
1345
+ // Diagnostics config
1346
+ const diagCfg = cfg.diagnostics || {};
1347
+ this.diagnosticsEnabled = diagCfg.enabled === true;
1348
+ const statusIntervalMs = typeof diagCfg.statusIntervalMs === 'number' && diagCfg.statusIntervalMs > 0 ? diagCfg.statusIntervalMs : 1000;
1349
+ this.statsIntervalSamples = Math.max(1, Math.round((OUTPUT_SAMPLE_RATE_HZ * statusIntervalMs) / 1000));
1350
+
1351
+ // Normalization config (hardcoded)
1352
+ this.normalizationEnabled = true;
1353
+
1354
+ // Stream states for audio buffer
1355
+ this.streams = new Map();
1356
+ this.ringBuffers = new Map();
1357
+ this.queueBuffers = new Map();
1358
+ this.tempInterleaved = new Float32Array(RENDER_QUANTUM_SIZE * this.channels);
1359
+
1360
+ // Pre-allocated crossfade buffers for buffer reduction (no allocations in render path)
1361
+ const crossfadeSamples = BUFFER_REDUCTION_CROSSFADE_SAMPLES * this.channels;
1362
+ this.crossfadePreSkip = new Float32Array(crossfadeSamples);
1363
+ this.crossfadePostSkip = new Float32Array(crossfadeSamples);
1364
+ this.crossfadeResult = new Float32Array(crossfadeSamples);
1365
+ this.crossfadeOffset = 0;
1366
+ this.crossfadeRemaining = 0;
1367
+
1368
+ // Stats reporting
1369
+ this.samplesSinceLastStats = 0;
1370
+ this.lastControlTime = 0;
1371
+ this.drainedStreamIds = [];
1372
+ this.activeStreamIds = [];
1373
+ this.activeStreams = [];
1374
+ this.activeStreamIdIndices = new Map();
1375
+
1376
+ // Pre-allocated message objects to avoid allocation in hot path
1377
+ this._drainedMsg = { type: 'streamDrained', streamId: '' };
1378
+ this._statsMsg = { type: 'stats', streamId: '', bufferMs: 0, targetMs: 0, state: '', inputDrained: false, jitterMs: 0, underrunCount: 0, dropCount: 0 };
1379
+ this._playbackMsg = { type: 'playback', streamId: '', epoch: 0, playedSeconds: 0, bufferMs: 0 };
1380
+ this.samplesSincePlaybackReport = 0;
1381
+
1382
+ this.port.onmessage = (event) => {
1383
+ const data = event.data;
1384
+ if (!data || typeof data !== 'object') return;
1385
+
1386
+ if (data.type === 'streamBegin') {
1387
+ const streamId = data.streamId;
1388
+ const sab = data.sharedArrayBuffer;
1389
+ const useSab = this.useSharedArrayBuffer && sab;
1390
+
1391
+ let buffer;
1392
+ if (useSab) {
1393
+ buffer = new RingBuffer(sab, Float32Array, this.channels);
1394
+ this.ringBuffers.set(streamId, buffer);
1395
+ this.queueBuffers.delete(streamId);
1396
+ } else {
1397
+ buffer = new QueueBuffer(this.queueMaxSamples, this.channels);
1398
+ this.queueBuffers.set(streamId, buffer);
1399
+ this.ringBuffers.delete(streamId);
1400
+ }
1401
+
1402
+ // Initialize audio buffer stream state
1403
+ const stream = {
1404
+ buffer,
1405
+ state: 'BUFFERING',
1406
+ currentEpoch: -1,
1407
+ streamEnding: false,
1408
+ inputDrained: false,
1409
+ epochSet: false,
1410
+ epochTimestampInterleavedSamples: 0,
1411
+ playoutInterleavedSamples: 0,
1412
+ jitterMs: 0,
1413
+ bufferTargetMs: BUFFER_TARGET_MIN_MS,
1414
+ stableDurationMs: 0,
1415
+ totalUnderrunCount: 0,
1416
+ totalDropCount: 0,
1417
+ frameSamplesPerChannel: DEFAULT_FRAME_SAMPLES_PER_CHANNEL,
1418
+ lastOut: new Float32Array(RENDER_QUANTUM_SIZE * this.channels),
1419
+ hasLastOut: false,
1420
+ transport: useSab ? 'sab' : 'queue',
1421
+ // Fade envelope state
1422
+ fadeGain: 0.0,
1423
+ fadeDirection: 'none',
1424
+ needsFadeIn: true,
1425
+ needsFadeOut: false,
1426
+ underrunPending: false,
1427
+ // Diagnostic logging counter
1428
+ bufferingLogCounter: 0
1429
+ };
1430
+ this.streams.set(streamId, stream);
1431
+ this.activeStreamIdIndices.set(streamId, this.activeStreamIds.length);
1432
+ this.activeStreamIds.push(streamId);
1433
+ this.activeStreams.push(stream);
1434
+ return;
1435
+ }
1436
+
1437
+ if (data.type === 'streamEnd') {
1438
+ const streamId = data.streamId;
1439
+ const stream = this.streams.get(streamId);
1440
+ if (stream) {
1441
+ stream.streamEnding = true;
1442
+ }
1443
+ return;
1444
+ }
1445
+
1446
+ if (data.type === 'pcm') {
1447
+ const q = this.queueBuffers.get(data.streamId);
1448
+ if (!q) return;
1449
+ q.compact();
1450
+ if (data.pcm instanceof Float32Array) {
1451
+ q.push(data.pcm);
1452
+ // Apply buffer limit based on playback state
1453
+ const maxSamples = this.isPlaybackActive ? this.queueMaxSamples : this.suspendedMaxSamples;
1454
+ if (q.totalSamples > maxSamples) {
1455
+ q.skip(q.totalSamples - maxSamples);
1456
+ }
1457
+ }
1458
+ return;
1459
+ }
1460
+
1461
+ if (data.type === 'frame') {
1462
+ const stream = this.streams.get(data.streamId);
1463
+ if (!stream) return;
1464
+
1465
+ const epoch = data.epoch;
1466
+ const timestampInterleavedSamples = data.timestampInterleavedSamples;
1467
+
1468
+ // Epoch change → reset stream state
1469
+ if (stream.currentEpoch !== epoch) {
1470
+ this.port.postMessage({
1471
+ type: 'log',
1472
+ level: 'info',
1473
+ message: 'Epoch change ' + stream.currentEpoch + '->' + epoch + ', bufferTargetMs=' + stream.bufferTargetMs.toFixed(0) + 'ms (not reset)',
1474
+ streamId: data.streamId
1475
+ });
1476
+ stream.currentEpoch = epoch;
1477
+ stream.epochSet = false;
1478
+ stream.playoutInterleavedSamples = 0;
1479
+ stream.streamEnding = false;
1480
+ stream.inputDrained = false;
1481
+ stream.state = 'BUFFERING';
1482
+ stream.frameSamplesPerChannel = DEFAULT_FRAME_SAMPLES_PER_CHANNEL;
1483
+ stream.hasLastOut = false;
1484
+ // Reset fade state for new epoch
1485
+ stream.fadeGain = 0.0;
1486
+ stream.fadeDirection = 'none';
1487
+ stream.needsFadeIn = true;
1488
+ stream.needsFadeOut = false;
1489
+ stream.underrunPending = false;
1490
+
1491
+ const ring = this.ringBuffers.get(data.streamId);
1492
+ if (ring) ring.flush();
1493
+ const q = this.queueBuffers.get(data.streamId);
1494
+ if (q) q.flush();
1495
+ }
1496
+
1497
+ // Establish initial timestamp mapping
1498
+ if (!stream.epochSet) {
1499
+ stream.epochSet = true;
1500
+ stream.epochTimestampInterleavedSamples = timestampInterleavedSamples;
1501
+ }
1502
+
1503
+ // Update jitter estimate
1504
+ stream.jitterMs = data.jitterMs;
1505
+ if (typeof data.frameSizeInterleavedSamples === 'number' && data.frameSizeInterleavedSamples > 0) {
1506
+ stream.frameSamplesPerChannel = Math.max(1, Math.round(data.frameSizeInterleavedSamples / this.channels));
1507
+ }
1508
+
1509
+ // Trigger fade-in if receiving audio on a drained stream (even without isFirst)
1510
+ if (stream.inputDrained && !data.isFirst) {
1511
+ stream.needsFadeIn = true;
1512
+ stream.needsFadeOut = false;
1513
+ stream.fadeGain = 0.0;
1514
+ stream.fadeDirection = 'in';
1515
+ stream.inputDrained = false;
1516
+ stream.underrunPending = false;
1517
+ }
1518
+
1519
+ // Track isFirst/isLast for fade triggers
1520
+ if (data.isFirst) {
1521
+ // New audio segment starting - fade in from silence
1522
+ stream.needsFadeIn = true;
1523
+ stream.needsFadeOut = false;
1524
+ stream.fadeGain = 0.0;
1525
+ stream.fadeDirection = 'in';
1526
+ stream.inputDrained = false;
1527
+ stream.underrunPending = false;
1528
+ }
1529
+ if (data.isLast) {
1530
+ // Audio segment ending - schedule fade out
1531
+ stream.needsFadeOut = true;
1532
+ stream.inputDrained = true;
1533
+ this.port.postMessage({
1534
+ type: 'log',
1535
+ level: 'info',
1536
+ message: 'Received isLast=true, setting inputDrained=true',
1537
+ streamId: data.streamId
1538
+ });
1539
+ }
1540
+
1541
+ // Enforce buffer limit when not actively playing (for SAB mode)
1542
+ if (!this.isPlaybackActive) {
1543
+ const maxSamples = this.suspendedMaxSamples;
1544
+ const ring = this.ringBuffers.get(data.streamId);
1545
+ if (ring) {
1546
+ const available = ring.availableRead();
1547
+ if (available > maxSamples) {
1548
+ ring.skip(available - maxSamples);
1549
+ }
1550
+ }
1551
+ }
1552
+ return;
1553
+ }
1554
+
1555
+ if (data.type === 'playbackActive') {
1556
+ const wasActive = this.isPlaybackActive;
1557
+ this.isPlaybackActive = data.active;
1558
+
1559
+ // When switching to inactive, trim all buffers to suspended limit
1560
+ if (wasActive && !data.active) {
1561
+ const maxSamples = this.suspendedMaxSamples;
1562
+ for (const q of this.queueBuffers.values()) {
1563
+ if (q.totalSamples > maxSamples) {
1564
+ q.skip(q.totalSamples - maxSamples);
1565
+ }
1566
+ }
1567
+ for (const ring of this.ringBuffers.values()) {
1568
+ const available = ring.availableRead();
1569
+ if (available > maxSamples) {
1570
+ ring.skip(available - maxSamples);
1571
+ }
1572
+ }
1573
+ }
1574
+ return;
1575
+ }
1576
+ };
1577
+ }
1578
+
1579
+ availableSamples(stream) {
1580
+ const buf = stream.buffer;
1581
+ return buf ? buf.availableRead() : 0;
1582
+ }
1583
+
1584
+ skipSamples(stream, count) {
1585
+ const buf = stream.buffer;
1586
+ return buf ? buf.skip(count) : 0;
1587
+ }
1588
+
1589
+ readInto(stream, target) {
1590
+ const buf = stream.buffer;
1591
+ if (buf) return buf.pop(target);
1592
+ target.fill(0);
1593
+ return 0;
1594
+ }
1595
+
1596
+ // Crossfade skip for buffer reduction - smoothly skips samples with crossfade to avoid clicks
1597
+ crossfadeSkipSamples(stream, samplesToSkip) {
1598
+ const fadeLen = BUFFER_REDUCTION_CROSSFADE_SAMPLES * this.channels;
1599
+
1600
+ // 1. Read pre-skip tail into pre-allocated buffer
1601
+ const preRead = this.readInto(stream, this.crossfadePreSkip);
1602
+ if (preRead < fadeLen) return 0;
1603
+
1604
+ // 2. Skip the excess (minus crossfade portion)
1605
+ const skipAmount = samplesToSkip - fadeLen;
1606
+ if (skipAmount > 0) {
1607
+ this.skipSamples(stream, skipAmount);
1608
+ }
1609
+
1610
+ // 3. Read post-skip head into pre-allocated buffer
1611
+ const postRead = this.readInto(stream, this.crossfadePostSkip);
1612
+ if (postRead < fadeLen) {
1613
+ // Copy pre-skip to result buffer, output what we have
1614
+ for (let i = 0; i < fadeLen; i++) {
1615
+ this.crossfadeResult[i] = this.crossfadePreSkip[i];
1616
+ }
1617
+ this.crossfadeOffset = 0;
1618
+ this.crossfadeRemaining = postRead > 0 ? fadeLen : 0;
1619
+ return samplesToSkip;
1620
+ }
1621
+
1622
+ // 4. Linear crossfade in-place to pre-allocated result buffer
1623
+ const invFadeLen = 1.0 / fadeLen;
1624
+ for (let i = 0; i < fadeLen; i++) {
1625
+ const t = i * invFadeLen;
1626
+ this.crossfadeResult[i] = this.crossfadePreSkip[i] * (1 - t) + this.crossfadePostSkip[i] * t;
1627
+ }
1628
+
1629
+ this.crossfadeOffset = 0;
1630
+ this.crossfadeRemaining = fadeLen;
1631
+
1632
+ return samplesToSkip;
1633
+ }
1634
+
1635
+ // Returns the minimum safe audio buffer target based on jitter
1636
+ getBufferFloorMs(stream) {
1637
+ return BUFFER_TARGET_MIN_MS + JITTER_MULTIPLIER * stream.jitterMs;
1638
+ }
1639
+
1640
+ // ALL buffer target changes go through this function
1641
+ updateBufferTarget(stream, action) {
1642
+ const floorMs = this.getBufferFloorMs(stream);
1643
+ let target = Math.max(stream.bufferTargetMs, floorMs);
1644
+
1645
+ if (action === 'underrun') {
1646
+ // Simple: every underrun adds a fixed amount
1647
+ target += UNDERRUN_JUMP_MS;
1648
+ stream.stableDurationMs = 0;
1649
+ } else if (action === 'stable-decrease') {
1650
+ // Decrease: gradual, never below floor
1651
+ target = Math.max(floorMs, target - DECREASE_STEP_MS);
1652
+ }
1653
+ // action === 'enforce-floor' just enforces floor, no other changes
1654
+
1655
+ stream.bufferTargetMs = Math.min(BUFFER_TARGET_MAX_MS, target);
1656
+ }
1657
+
1658
+ // Immediate response to underrun - called from process() after fade-out completes
1659
+ handleUnderrun(streamId, stream) {
1660
+ stream.totalUnderrunCount++;
1661
+ stream.state = 'BUFFERING';
1662
+ stream.needsFadeIn = true;
1663
+ stream.underrunPending = false;
1664
+ // Note: fadeGain/fadeDirection not reset here - fade-out has already completed
1665
+ this.updateBufferTarget(stream, 'underrun');
1666
+
1667
+ this.port.postMessage({
1668
+ type: 'log',
1669
+ level: 'warn',
1670
+ message: 'Buffer underrun, count=' + stream.totalUnderrunCount + ', newTarget=' + stream.bufferTargetMs.toFixed(0) + 'ms',
1671
+ streamId
1672
+ });
1673
+ }
1674
+
1675
+ controlTick(streamId, stream, nowMs) {
1676
+ if (!stream.epochSet || stream.state === 'ENDED') return;
1677
+
1678
+ // Always enforce buffer floor
1679
+ this.updateBufferTarget(stream, 'enforce-floor');
1680
+
1681
+ // Stability tracking and gradual decrease (only when playing, not ending)
1682
+ if (stream.state === 'PLAYING' && !stream.streamEnding) {
1683
+ stream.stableDurationMs += CONTROL_PERIOD_MS;
1684
+
1685
+ if (stream.stableDurationMs >= STABLE_TIME_TO_DECREASE_MS) {
1686
+ this.updateBufferTarget(stream, 'stable-decrease');
1687
+ stream.stableDurationMs = 0;
1688
+ }
1689
+ }
1690
+ }
1691
+
1692
+ process(_inputs, outputs) {
1693
+ const output = outputs[0];
1694
+ if (!output || output.length === 0) {
1695
+ return true;
1696
+ }
1697
+
1698
+ const now = currentTime;
1699
+ const nowMs = now * 1000;
1700
+ const outputChannels = output.length;
1701
+ const requestSamples = RENDER_QUANTUM_SIZE * this.channels;
1702
+
1703
+ for (let ch = 0; ch < outputChannels; ch++) {
1704
+ output[ch].fill(0);
1705
+ }
1706
+
1707
+ // Run control loop periodically
1708
+ if (nowMs - this.lastControlTime >= CONTROL_PERIOD_MS) {
1709
+ for (let i = 0; i < this.activeStreamIds.length; i++) {
1710
+ const streamId = this.activeStreamIds[i];
1711
+ const stream = this.activeStreams[i];
1712
+ if (stream) this.controlTick(streamId, stream, nowMs);
1713
+ }
1714
+ this.lastControlTime = nowMs;
1715
+ }
1716
+
1717
+ let activeStreamCount = 0;
1718
+ const drainedStreamIds = this.drainedStreamIds;
1719
+ drainedStreamIds.length = 0;
1720
+
1721
+ for (let i = 0; i < this.activeStreamIds.length; i++) {
1722
+ const streamId = this.activeStreamIds[i];
1723
+ const stream = this.activeStreams[i];
1724
+ if (!stream || stream.state === 'ENDED') {
1725
+ continue;
1726
+ }
1727
+
1728
+ const available = this.availableSamples(stream);
1729
+
1730
+ if (stream.state === 'BUFFERING') {
1731
+ if (stream.streamEnding) {
1732
+ if (available <= 0) {
1733
+ stream.state = 'ENDED';
1734
+ drainedStreamIds.push(streamId);
1735
+ continue;
1736
+ }
1737
+ this.port.postMessage({
1738
+ type: 'log',
1739
+ level: 'info',
1740
+ message: 'BUFFERING->PLAYING (streamEnding)',
1741
+ streamId
1742
+ });
1743
+ stream.state = 'PLAYING';
1744
+ } else {
1745
+ const bufferSamples = available / this.channels;
1746
+ const bufferLevelMs = bufferSamples * MS_PER_SAMPLE;
1747
+ if (bufferLevelMs >= stream.bufferTargetMs) {
1748
+ this.port.postMessage({
1749
+ type: 'log',
1750
+ level: 'info',
1751
+ message: 'BUFFERING->PLAYING, buffer=' + bufferLevelMs.toFixed(0) + 'ms, target=' + stream.bufferTargetMs.toFixed(0) + 'ms',
1752
+ streamId
1753
+ });
1754
+ stream.state = 'PLAYING';
1755
+ stream.bufferingLogCounter = 0;
1756
+ if (stream.needsFadeIn) {
1757
+ stream.fadeDirection = 'in';
1758
+ } else {
1759
+ stream.fadeGain = 1.0;
1760
+ }
1761
+ } else {
1762
+ // Log periodically when stuck in BUFFERING with data available
1763
+ if (available > 0) {
1764
+ stream.bufferingLogCounter = (stream.bufferingLogCounter || 0) + 1;
1765
+ // Log every ~1 second (approximately 43 calls at 128 samples / 48kHz)
1766
+ if (stream.bufferingLogCounter >= 43) {
1767
+ this.port.postMessage({
1768
+ type: 'log',
1769
+ level: 'info',
1770
+ message: 'BUFFERING: buffer=' + bufferLevelMs.toFixed(0) + 'ms, target=' + stream.bufferTargetMs.toFixed(0) + 'ms, waiting...',
1771
+ streamId
1772
+ });
1773
+ stream.bufferingLogCounter = 0;
1774
+ }
1775
+ }
1776
+ continue;
1777
+ }
1778
+ }
1779
+ }
1780
+
1781
+ // Buffer reduction: if buffer exceeds target max, drain with crossfade
1782
+ // Skip if we're still outputting a previous crossfade
1783
+ const bufferSamplesForReduction = available / this.channels;
1784
+ const bufferLevelMsForReduction = bufferSamplesForReduction * MS_PER_SAMPLE;
1785
+ if (this.crossfadeRemaining <= 0 && bufferLevelMsForReduction > BUFFER_TARGET_MAX_MS && stream.state === 'PLAYING' && !stream.streamEnding) {
1786
+ const excessMs = bufferLevelMsForReduction - BUFFER_TARGET_MAX_MS;
1787
+ const fadeLen = BUFFER_REDUCTION_CROSSFADE_SAMPLES * this.channels;
1788
+ // Need: fadeLen (pre-skip read) + skip amount + fadeLen (post-skip read)
1789
+ // Skip amount must be > fadeLen for crossfade to make sense
1790
+ // Cap at ~2x speed: skip up to 1 quantum worth per render, but at least fadeLen + 1
1791
+ const excessSamples = Math.round((excessMs * OUTPUT_SAMPLE_RATE_HZ * this.channels) / 1000);
1792
+ const samplesToSkip = Math.min(
1793
+ Math.max(fadeLen + RENDER_QUANTUM_SIZE, RENDER_QUANTUM_SIZE * this.channels * 2),
1794
+ excessSamples
1795
+ );
1796
+ // Check we have enough buffer: need fadeLen + samplesToSkip for the operation
1797
+ if (samplesToSkip > fadeLen && available >= fadeLen + samplesToSkip) {
1798
+ this.crossfadeSkipSamples(stream, samplesToSkip);
1799
+ stream.totalDropCount++;
1800
+ }
1801
+ }
1802
+
1803
+ // Output pending crossfade samples first (from processor-level pre-allocated buffer)
1804
+ if (this.crossfadeRemaining > 0) {
1805
+ const framesToOutput = Math.min(Math.floor(this.crossfadeRemaining / this.channels), RENDER_QUANTUM_SIZE);
1806
+ let idx = this.crossfadeOffset;
1807
+ for (let frame = 0; frame < framesToOutput; frame++) {
1808
+ // Update fade envelope during crossfade output (critical for initial unlock scenario)
1809
+ if (stream.fadeDirection === 'in') {
1810
+ stream.fadeGain = Math.min(1.0, stream.fadeGain + FADE_IN_INCREMENT);
1811
+ if (stream.fadeGain >= 1.0) {
1812
+ stream.fadeDirection = 'none';
1813
+ stream.needsFadeIn = false;
1814
+ }
1815
+ } else if (stream.fadeDirection === 'out') {
1816
+ stream.fadeGain = Math.max(0.0, stream.fadeGain - FADE_OUT_INCREMENT);
1817
+ if (stream.fadeGain <= 0.0) {
1818
+ stream.fadeDirection = 'none';
1819
+ stream.needsFadeOut = false;
1820
+ }
1821
+ }
1822
+ for (let ch = 0; ch < this.channels && ch < outputChannels; ch++) {
1823
+ output[ch][frame] += this.crossfadeResult[idx++] * stream.fadeGain;
1824
+ }
1825
+ }
1826
+ this.crossfadeOffset += framesToOutput * this.channels;
1827
+ this.crossfadeRemaining -= framesToOutput * this.channels;
1828
+ // Always continue after crossfade output to avoid overlapping with normal playback
1829
+ activeStreamCount++;
1830
+ continue;
1831
+ }
1832
+
1833
+ const bufferMs = (available / this.channels) * MS_PER_SAMPLE;
1834
+
1835
+ // Start fade-out when buffer is low and isLast was received
1836
+ if (stream.needsFadeOut && stream.fadeDirection !== 'out' && bufferMs <= FADE_OUT_DURATION_MS * 2.0) {
1837
+ stream.fadeDirection = 'out';
1838
+ }
1839
+
1840
+ const countRead = this.readInto(stream, this.tempInterleaved);
1841
+ const framesRead = Math.floor(countRead / this.channels);
1842
+
1843
+ if (countRead >= requestSamples) {
1844
+ stream.lastOut.set(this.tempInterleaved);
1845
+ stream.hasLastOut = true;
1846
+
1847
+ // Apply per-sample fade envelope
1848
+ let sampleIdx = 0;
1849
+ for (let frame = 0; frame < RENDER_QUANTUM_SIZE; frame++) {
1850
+ // Update fade envelope
1851
+ if (stream.fadeDirection === 'in') {
1852
+ stream.fadeGain = Math.min(1.0, stream.fadeGain + FADE_IN_INCREMENT);
1853
+ if (stream.fadeGain >= 1.0) {
1854
+ stream.fadeDirection = 'none';
1855
+ stream.needsFadeIn = false;
1856
+ }
1857
+ } else if (stream.fadeDirection === 'out') {
1858
+ stream.fadeGain = Math.max(0.0, stream.fadeGain - FADE_OUT_INCREMENT);
1859
+ if (stream.fadeGain <= 0.0) {
1860
+ stream.fadeDirection = 'none';
1861
+ stream.needsFadeOut = false;
1862
+ }
1863
+ }
1864
+ for (let ch = 0; ch < this.channels && ch < outputChannels; ch++) {
1865
+ output[ch][frame] += this.tempInterleaved[sampleIdx++] * stream.fadeGain;
1866
+ }
1867
+ }
1868
+ stream.playoutInterleavedSamples += countRead;
1869
+ activeStreamCount++;
1870
+ } else if (countRead > 0) {
1871
+ // Partial data: smooth tail to avoid hard discontinuities
1872
+ const endingOrDrained = stream.streamEnding || stream.inputDrained;
1873
+
1874
+ const holdFrameIndex = Math.max(0, framesRead - 1);
1875
+ const holdBaseIdx = holdFrameIndex * this.channels;
1876
+ let fillIdx = framesRead * this.channels;
1877
+ for (let frame = framesRead; frame < RENDER_QUANTUM_SIZE; frame++) {
1878
+ for (let ch = 0; ch < this.channels; ch++) {
1879
+ this.tempInterleaved[fillIdx++] = this.tempInterleaved[holdBaseIdx + ch];
1880
+ }
1881
+ }
1882
+
1883
+ stream.lastOut.set(this.tempInterleaved);
1884
+ stream.hasLastOut = true;
1885
+
1886
+ // Render valid frames first with the current fade state
1887
+ let sampleIdx = 0;
1888
+ for (let frame = 0; frame < framesRead; frame++) {
1889
+ if (stream.fadeDirection === 'in') {
1890
+ stream.fadeGain = Math.min(1.0, stream.fadeGain + FADE_IN_INCREMENT);
1891
+ if (stream.fadeGain >= 1.0) {
1892
+ stream.fadeDirection = 'none';
1893
+ stream.needsFadeIn = false;
1894
+ }
1895
+ } else if (stream.fadeDirection === 'out') {
1896
+ stream.fadeGain = Math.max(0.0, stream.fadeGain - FADE_OUT_INCREMENT);
1897
+ if (stream.fadeGain <= 0.0) {
1898
+ stream.fadeDirection = 'none';
1899
+ stream.needsFadeOut = false;
1900
+ }
1901
+ }
1902
+ for (let ch = 0; ch < this.channels && ch < outputChannels; ch++) {
1903
+ output[ch][frame] += this.tempInterleaved[sampleIdx++] * stream.fadeGain;
1904
+ }
1905
+ }
1906
+
1907
+ // Tail: force fade-out so the transition remains bounded
1908
+ if (stream.fadeGain > 0.001) {
1909
+ stream.fadeDirection = 'out';
1910
+ sampleIdx = framesRead * this.channels;
1911
+ for (let frame = framesRead; frame < RENDER_QUANTUM_SIZE; frame++) {
1912
+ stream.fadeGain = Math.max(0.0, stream.fadeGain - FADE_OUT_INCREMENT);
1913
+ if (stream.fadeGain <= 0.0) {
1914
+ stream.fadeDirection = 'none';
1915
+ stream.needsFadeOut = false;
1916
+ }
1917
+ for (let ch = 0; ch < this.channels && ch < outputChannels; ch++) {
1918
+ output[ch][frame] += this.tempInterleaved[sampleIdx++] * stream.fadeGain;
1919
+ }
1920
+ }
1921
+ }
1922
+
1923
+ stream.playoutInterleavedSamples += countRead;
1924
+
1925
+ if (endingOrDrained) {
1926
+ if (stream.fadeGain <= 0.001 && stream.streamEnding) {
1927
+ stream.state = 'ENDED';
1928
+ drainedStreamIds.push(streamId);
1929
+ }
1930
+ } else {
1931
+ // Mark underrun pending - will transition to BUFFERING after fade-out completes
1932
+ stream.underrunPending = true;
1933
+ if (stream.fadeGain <= 0.001) {
1934
+ this.handleUnderrun(streamId, stream);
1935
+ }
1936
+ }
1937
+ activeStreamCount++;
1938
+ } else {
1939
+ // No data available
1940
+ const endingOrDrained = stream.streamEnding || stream.inputDrained;
1941
+
1942
+ if (stream.hasLastOut && stream.fadeGain > 0.001) {
1943
+ // Continue fade-out using sample-hold from lastOut
1944
+ stream.fadeDirection = 'out';
1945
+ if (!endingOrDrained) {
1946
+ stream.underrunPending = true;
1947
+ }
1948
+ // Hold the LAST sample constant (not cycling through the buffer)
1949
+ const holdBaseIdx = (RENDER_QUANTUM_SIZE - 1) * this.channels;
1950
+ for (let frame = 0; frame < RENDER_QUANTUM_SIZE; frame++) {
1951
+ stream.fadeGain = Math.max(0.0, stream.fadeGain - FADE_OUT_INCREMENT);
1952
+ if (stream.fadeGain <= 0.0) {
1953
+ stream.fadeDirection = 'none';
1954
+ stream.needsFadeOut = false;
1955
+ }
1956
+ for (let ch = 0; ch < this.channels && ch < outputChannels; ch++) {
1957
+ output[ch][frame] += stream.lastOut[holdBaseIdx + ch] * stream.fadeGain;
1958
+ }
1959
+ }
1960
+ } else if (endingOrDrained && stream.fadeDirection === 'out' && stream.fadeGain > 0.001) {
1961
+ for (let frame = 0; frame < RENDER_QUANTUM_SIZE; frame++) {
1962
+ stream.fadeGain = Math.max(0.0, stream.fadeGain - FADE_OUT_INCREMENT);
1963
+ if (stream.fadeGain <= 0.001) {
1964
+ stream.fadeGain = 0;
1965
+ stream.fadeDirection = 'none';
1966
+ stream.needsFadeOut = false;
1967
+ break;
1968
+ }
1969
+ }
1970
+ }
1971
+
1972
+ if (endingOrDrained) {
1973
+ if (stream.fadeGain <= 0.001) {
1974
+ if (stream.streamEnding) {
1975
+ stream.state = 'ENDED';
1976
+ drainedStreamIds.push(streamId);
1977
+ } else if (stream.inputDrained) {
1978
+ this.port.postMessage({
1979
+ type: 'log',
1980
+ level: 'info',
1981
+ message: 'PLAYING->BUFFERING (inputDrained), bufferTargetMs=' + stream.bufferTargetMs.toFixed(0) + 'ms',
1982
+ streamId
1983
+ });
1984
+ stream.state = 'BUFFERING';
1985
+ stream.needsFadeIn = true;
1986
+ }
1987
+ }
1988
+ } else if (stream.underrunPending && stream.fadeGain <= 0.001) {
1989
+ // Fade-out complete, now transition to BUFFERING
1990
+ this.handleUnderrun(streamId, stream);
1991
+ } else if (!stream.underrunPending && !stream.hasLastOut) {
1992
+ // No lastOut to fade from, immediate underrun
1993
+ stream.underrunPending = true;
1994
+ this.handleUnderrun(streamId, stream);
1995
+ }
1996
+ }
1997
+ }
1998
+
1999
+ // Cleanup drained streams
2000
+ if (drainedStreamIds.length > 0) {
2001
+ for (let i = 0; i < drainedStreamIds.length; i++) {
2002
+ const streamId = drainedStreamIds[i];
2003
+ // Remove from activeStreamIds/activeStreams using O(1) index lookup + swap-remove
2004
+ const idx = this.activeStreamIdIndices.get(streamId);
2005
+ if (idx !== undefined) {
2006
+ const lastIdx = this.activeStreamIds.length - 1;
2007
+ if (idx !== lastIdx) {
2008
+ const lastStreamId = this.activeStreamIds[lastIdx];
2009
+ this.activeStreamIds[idx] = lastStreamId;
2010
+ this.activeStreams[idx] = this.activeStreams[lastIdx];
2011
+ this.activeStreamIdIndices.set(lastStreamId, idx);
2012
+ }
2013
+ this.activeStreamIds.length--;
2014
+ this.activeStreams.length--;
2015
+ this.activeStreamIdIndices.delete(streamId);
2016
+ }
2017
+ this.ringBuffers.delete(streamId);
2018
+ this.queueBuffers.delete(streamId);
2019
+ this.streams.delete(streamId);
2020
+ try {
2021
+ this._drainedMsg.streamId = streamId;
2022
+ this.port.postMessage(this._drainedMsg);
2023
+ } catch {
2024
+ // ignore
2025
+ }
2026
+ }
2027
+ }
2028
+
2029
+ // Combined normalization + soft clipping (single pass)
2030
+ const needsNormalization = this.normalizationEnabled && activeStreamCount > 1;
2031
+ const normFactor = needsNormalization ? 1.0 / Math.sqrt(activeStreamCount) : 1.0;
2032
+ for (let frame = 0; frame < RENDER_QUANTUM_SIZE; frame++) {
2033
+ for (let ch = 0; ch < outputChannels; ch++) {
2034
+ let v = output[ch][frame];
2035
+ if (needsNormalization) {
2036
+ v *= normFactor;
2037
+ }
2038
+ // NaN check: v !== v is true only for NaN; clipping handles ±Infinity
2039
+ if (v !== v) v = 0;
2040
+ output[ch][frame] = v > 1 ? 1 : v < -1 ? -1 : v;
2041
+ }
2042
+ }
2043
+
2044
+ // Periodic playback-position reports (always on — feeds client->server audibility
2045
+ // reporting, which apps use to pace speech to what listeners actually heard)
2046
+ this.samplesSincePlaybackReport += RENDER_QUANTUM_SIZE;
2047
+ if (this.samplesSincePlaybackReport >= PLAYBACK_REPORT_INTERVAL_SAMPLES) {
2048
+ this.samplesSincePlaybackReport = 0;
2049
+
2050
+ for (let i = 0; i < this.activeStreamIds.length; i++) {
2051
+ const stream = this.activeStreams[i];
2052
+ if (!stream || !stream.epochSet) continue;
2053
+
2054
+ this._playbackMsg.streamId = this.activeStreamIds[i];
2055
+ this._playbackMsg.epoch = stream.currentEpoch;
2056
+ this._playbackMsg.playedSeconds = stream.playoutInterleavedSamples / (OUTPUT_SAMPLE_RATE_HZ * this.channels);
2057
+ this._playbackMsg.bufferMs = (this.availableSamples(stream) / this.channels) * MS_PER_SAMPLE;
2058
+ this.port.postMessage(this._playbackMsg);
2059
+ }
2060
+ }
2061
+
2062
+ // Stats reporting
2063
+ if (!this.diagnosticsEnabled) {
2064
+ return true;
2065
+ }
2066
+
2067
+ this.samplesSinceLastStats += RENDER_QUANTUM_SIZE;
2068
+ if (this.samplesSinceLastStats >= this.statsIntervalSamples) {
2069
+ this.samplesSinceLastStats = 0;
2070
+
2071
+ for (let i = 0; i < this.activeStreamIds.length; i++) {
2072
+ const streamId = this.activeStreamIds[i];
2073
+ const stream = this.activeStreams[i];
2074
+ if (!stream) continue;
2075
+ const bufferSamples = this.availableSamples(stream) / this.channels;
2076
+ const bufferMs = bufferSamples * MS_PER_SAMPLE;
2077
+
2078
+ this._statsMsg.streamId = streamId;
2079
+ this._statsMsg.bufferMs = bufferMs;
2080
+ this._statsMsg.targetMs = stream.bufferTargetMs;
2081
+ this._statsMsg.state = stream.state;
2082
+ this._statsMsg.inputDrained = stream.inputDrained;
2083
+ this._statsMsg.jitterMs = stream.jitterMs;
2084
+ this._statsMsg.underrunCount = stream.totalUnderrunCount;
2085
+ this._statsMsg.dropCount = stream.totalDropCount;
2086
+ this.port.postMessage(this._statsMsg);
2087
+ }
2088
+ }
2089
+
2090
+ return true;
2091
+ }
2092
+ }
2093
+
2094
+ registerProcessor('ikon-audio-player', IkonAudioPlayerProcessor);
2095
+ `;
2096
+ }
2097
+ var Tt = class t {
2098
+ client;
2099
+ config;
2100
+ enabled = !1;
2101
+ stateUnsubscribe = null;
2102
+ isMobile = c();
2103
+ boundVisibilityChange = null;
2104
+ boundPageHide = null;
2105
+ unlockHandler = null;
2106
+ awaitingUnlock = !1;
2107
+ recoveryTask = null;
2108
+ static RESUME_TIMEOUT_MS = 100;
2109
+ static MAX_WORKER_REBUILD_ATTEMPTS = 3;
2110
+ workerRebuildAttempts = 0;
2111
+ audioContext = null;
2112
+ outputSampleRateHz = e;
2113
+ audioWorkletNode = null;
2114
+ scriptProcessorNode = null;
2115
+ scriptDiagnosticsTimer = null;
2116
+ workletModuleUrl = null;
2117
+ workletModuleKey = null;
2118
+ workletReady = null;
2119
+ pendingWorkletMessages = [];
2120
+ audioWorker = null;
2121
+ protocolPort = null;
2122
+ mediaSessionHelper = new xt();
2123
+ hiddenMuted = !1;
2124
+ lastReportedPlaybackState = null;
2125
+ lastPlaybackByStream = /* @__PURE__ */ new Map();
2126
+ fallbackQueues = /* @__PURE__ */ new Map();
2127
+ activeStreams = /* @__PURE__ */ new Set();
2128
+ mediaSessionConfig = null;
2129
+ constructor(e, t) {
2130
+ this.client = e, this.config = t ?? {}, J.debug(`Audio playback initialized, isMobile=${this.isMobile}`);
2131
+ }
2132
+ setMediaSession(e) {
2133
+ this.mediaSessionConfig = e;
2134
+ }
2135
+ async setEnabled(e, t) {
2136
+ if (e && t?.userGesture && this.audioContext) try {
2137
+ await this.resumeWithTimeout(this.audioContext), this.audioContext.state === "running" && this.removeUnlockHandler();
2138
+ } catch {}
2139
+ if (e === this.enabled) {
2140
+ e && (await this.ensureAudioGraph(t, { promptForUnlock: !0 }), await this.ensureWorkers());
2141
+ return;
2142
+ }
2143
+ if (this.enabled = e, this.ensureStateSubscription(), !e) {
2144
+ this.stopAudioGraph(), this.stopWorkers();
2145
+ return;
2146
+ }
2147
+ await this.ensureAudioGraph(t, { promptForUnlock: !0 }), await this.ensureWorkers();
2148
+ }
2149
+ enable(e) {
2150
+ return this.setEnabled(!0, e);
2151
+ }
2152
+ disable() {
2153
+ return this.setEnabled(!1);
2154
+ }
2155
+ pause() {
2156
+ if (this.audioContext) {
2157
+ this.notifyPlaybackActive(!1);
2158
+ try {
2159
+ this.audioWorkletNode && this.audioWorkletNode.disconnect(), this.scriptProcessorNode && this.scriptProcessorNode.disconnect();
2160
+ } catch {}
2161
+ }
2162
+ }
2163
+ resume() {
2164
+ if (!this.audioContext) return;
2165
+ try {
2166
+ this.audioWorkletNode && this.audioWorkletNode.connect(this.audioContext.destination), this.scriptProcessorNode && this.scriptProcessorNode.connect(this.audioContext.destination);
2167
+ } catch {}
2168
+ let e = this.getContextState(this.audioContext) === "running";
2169
+ this.notifyPlaybackActive(e);
2170
+ }
2171
+ requestRecovery() {
2172
+ if (this.enabled && r() && this.audioContext) {
2173
+ this.stopAudioGraph();
2174
+ try {
2175
+ let t = new AudioContext({
2176
+ sampleRate: e,
2177
+ latencyHint: n
2178
+ });
2179
+ this.audioContext = t, this.outputSampleRateHz = t.sampleRate, t.resume().catch(() => void 0), this.setupAudioGraphAfterRecreate();
2180
+ } catch (e) {
2181
+ J.error(`iOS audio recovery failed: ${Y(e)}`), this.addUnlockHandler();
2182
+ }
2183
+ return;
2184
+ }
2185
+ }
2186
+ async setupAudioGraphAfterRecreate() {
2187
+ if (!this.audioContext) return;
2188
+ let e = this.audioContext, t = this.resolveDiagnosticsConfig();
2189
+ try {
2190
+ await this.ensureAudioWorklet(e, t, void 0, { promptForUnlock: !1 });
2191
+ } catch {
2192
+ this.ensureScriptProcessor(e, t);
2193
+ }
2194
+ }
2195
+ syncStreamsFromGlobalState(e) {
2196
+ if (!this.audioWorker) return;
2197
+ let t = Object.values(e.AudioStreams ?? {}).map((e) => ({
2198
+ senderId: e.ClientSessionId,
2199
+ trackId: e.TrackId,
2200
+ info: e.Info
2201
+ }));
2202
+ if (t.length !== 0) try {
2203
+ this.audioWorker.postMessage({
2204
+ type: "syncStreams",
2205
+ streams: t
2206
+ });
2207
+ } catch {}
2208
+ }
2209
+ setWebRtcActive(e) {
2210
+ if (this.audioWorker) try {
2211
+ this.audioWorker.postMessage({
2212
+ type: "setWebRtcEnabled",
2213
+ enabled: e
2214
+ });
2215
+ } catch {}
2216
+ }
2217
+ handleProtocolTransportLost() {
2218
+ this.audioWorker && this.stopWorkers();
2219
+ }
2220
+ dispose() {
2221
+ this.enabled = !1, this.activeStreams.clear(), this.removeUnlockHandler(), this.removeVisibilityHandler(), this.stateUnsubscribe?.(), this.stateUnsubscribe = null, this.stopAudioGraph(), this.stopWorkers();
2222
+ }
2223
+ resolveDiagnosticsConfig() {
2224
+ let e = this.config.diagnostics ?? {};
2225
+ return {
2226
+ enabled: e.enabled ?? Ct.enabled,
2227
+ statusIntervalMs: e.statusIntervalMs ?? Ct.statusIntervalMs
2228
+ };
2229
+ }
2230
+ resolveBackgroundConfig() {
2231
+ let e = this.config.background ?? {};
2232
+ return {
2233
+ allowOnDesktop: e.allowOnDesktop ?? !0,
2234
+ allowOnMobile: e.allowOnMobile ?? !1
2235
+ };
2236
+ }
2237
+ async ensureAudioGraph(t, i) {
2238
+ if (typeof window > "u") return;
2239
+ if (!this.audioContext) {
2240
+ let t = e, r = n;
2241
+ try {
2242
+ this.audioContext = new AudioContext({
2243
+ sampleRate: t,
2244
+ latencyHint: r
2245
+ });
2246
+ } catch (e) {
2247
+ try {
2248
+ this.audioContext = new AudioContext({ latencyHint: r });
2249
+ } catch (e) {
2250
+ try {
2251
+ this.audioContext = new AudioContext();
2252
+ } catch (e) {
2253
+ J.warn(`Failed to create AudioContext: ${Y(e)}`), this.audioContext = null;
2254
+ return;
2255
+ }
2256
+ J.debug(`AudioContext latencyHint unsupported: ${Y(e)}`);
2257
+ }
2258
+ J.debug(`AudioContext sample rate request unsupported: ${Y(e)}`);
2259
+ }
2260
+ this.outputSampleRateHz = this.audioContext.sampleRate || 48e3, this.outputSampleRateHz !== t && J.debug(`AudioContext running at ${this.outputSampleRateHz}Hz (requested ${t}Hz)`), this.attachAudioContextHealthHandlers(this.audioContext);
2261
+ }
2262
+ r() && this.mediaSessionHelper.ensureSilentPlaybackElement();
2263
+ let a = this.audioContext, o = this.resolveDiagnosticsConfig();
2264
+ if ((this.config.performance?.preferAudioWorklet ?? !0) && s(a)) {
2265
+ await this.ensureAudioWorklet(a, o, t, i);
2266
+ return;
2267
+ }
2268
+ this.ensureScriptProcessor(a, o), await this.resumeAudioContextIfPossible(a, t, i), this.rebindWorkerTransportIfNeeded();
2269
+ }
2270
+ attachAudioContextHealthHandlers(e) {
2271
+ e.__ikonAudioHealthAttached !== !0 && (Object.defineProperty(e, "__ikonAudioHealthAttached", {
2272
+ value: !0,
2273
+ enumerable: !1
2274
+ }), e.onstatechange = () => {
2275
+ let t = this.getContextState(e), n = t === "running";
2276
+ this.notifyPlaybackActive(n), this.enabled && !n && (J.warn(`AudioContext state changed to '${t}', attempting recovery`), this.recoverAudioContext({ reason: "statechange" }));
2277
+ }, this.attachVisibilityHandler());
2278
+ }
2279
+ attachVisibilityHandler() {
2280
+ if (this.boundVisibilityChange || typeof document > "u") return;
2281
+ let e = this.resolveBackgroundConfig(), t = this.isMobile ? e.allowOnMobile : e.allowOnDesktop;
2282
+ t && this.setupMediaSession();
2283
+ let n = () => {
2284
+ if (!(!this.enabled || !this.audioContext || t)) {
2285
+ this.hiddenMuted = !0, this.notifyPlaybackActive(!1);
2286
+ try {
2287
+ this.audioWorkletNode && this.audioWorkletNode.disconnect(), this.scriptProcessorNode && this.scriptProcessorNode.disconnect();
2288
+ } catch {}
2289
+ }
2290
+ }, r = () => {
2291
+ if (!this.enabled || !this.audioContext) return;
2292
+ this.hiddenMuted = !1;
2293
+ try {
2294
+ this.audioWorkletNode && this.audioWorkletNode.connect(this.audioContext.destination), this.scriptProcessorNode && this.scriptProcessorNode.connect(this.audioContext.destination);
2295
+ } catch {}
2296
+ let e = this.getContextState(this.audioContext) === "running";
2297
+ this.notifyPlaybackActive(e);
2298
+ }, i = () => {
2299
+ if (this.enabled) {
2300
+ if (document.visibilityState === "visible") {
2301
+ r(), this.audioContext && this.getContextState(this.audioContext) !== "running" && this.recoverAudioContext({ reason: "visibilityVisible" });
2302
+ return;
2303
+ }
2304
+ n();
2305
+ }
2306
+ }, a = () => {
2307
+ n();
2308
+ };
2309
+ this.boundVisibilityChange = i, this.boundPageHide = a, document.addEventListener("visibilitychange", i), t || window.addEventListener("pagehide", a);
2310
+ }
2311
+ setupMediaSession() {
2312
+ this.mediaSessionHelper.setup(this.mediaSessionConfig ?? void 0, {
2313
+ onPlay: () => this.resume(),
2314
+ onPause: () => this.pause()
2315
+ });
2316
+ }
2317
+ cleanupMediaSession() {
2318
+ this.mediaSessionHelper.cleanup();
2319
+ }
2320
+ removeVisibilityHandler() {
2321
+ typeof document < "u" && this.boundVisibilityChange && document.removeEventListener("visibilitychange", this.boundVisibilityChange), typeof window < "u" && this.boundPageHide && (window.removeEventListener("pagehide", this.boundPageHide), window.removeEventListener("blur", this.boundPageHide)), this.boundVisibilityChange = null, this.boundPageHide = null, this.cleanupMediaSession();
2322
+ }
2323
+ getContextState(e) {
2324
+ return e?.state ?? "suspended";
2325
+ }
2326
+ addUnlockHandler() {
2327
+ if (this.enabled && !this.awaitingUnlock && !(typeof document > "u")) {
2328
+ this.awaitingUnlock = !0, this.unlockHandler = () => {
2329
+ let e = this.audioContext;
2330
+ if (e && this.getContextState(e) !== "running") try {
2331
+ e.resume().catch(() => void 0);
2332
+ } catch {}
2333
+ let t = this.mediaSessionHelper.silentAudioElement;
2334
+ if (t && t.paused) try {
2335
+ t.play().catch(() => void 0);
2336
+ } catch {}
2337
+ if ((e ? this.getContextState(e) : "suspended") === "running") {
2338
+ this.removeUnlockHandler();
2339
+ return;
2340
+ }
2341
+ this.recoveryTask || this.recoverAudioContext({
2342
+ reason: "gesture",
2343
+ fromUserGesture: !0
2344
+ }).finally(() => {
2345
+ let e = this.audioContext;
2346
+ e && this.getContextState(e) === "running" && this.removeUnlockHandler();
2347
+ });
2348
+ };
2349
+ for (let e of [
2350
+ "touchstart",
2351
+ "touchend",
2352
+ "click",
2353
+ "pointerdown",
2354
+ "pointerup",
2355
+ "keydown",
2356
+ "mouseup"
2357
+ ]) document.addEventListener(e, this.unlockHandler, { capture: !0 });
2358
+ }
2359
+ }
2360
+ removeUnlockHandler() {
2361
+ if (!this.unlockHandler) {
2362
+ this.awaitingUnlock = !1;
2363
+ return;
2364
+ }
2365
+ if (typeof document < "u") for (let e of [
2366
+ "touchstart",
2367
+ "touchend",
2368
+ "click",
2369
+ "pointerdown",
2370
+ "pointerup",
2371
+ "keydown",
2372
+ "mouseup"
2373
+ ]) document.removeEventListener(e, this.unlockHandler, { capture: !0 });
2374
+ this.unlockHandler = null, this.awaitingUnlock = !1, this.reportPlaybackStateIfChanged();
2375
+ }
2376
+ async resumeWithTimeout(e) {
2377
+ let n = e.resume(), r = new Promise((e, n) => {
2378
+ setTimeout(() => n(/* @__PURE__ */ Error("AudioContext.resume() timed out")), t.RESUME_TIMEOUT_MS);
2379
+ });
2380
+ await Promise.race([n, r]);
2381
+ }
2382
+ async ensureAudioWorklet(e, t, n, r) {
2383
+ if (this.audioWorkletNode) {
2384
+ await this.resumeAudioContextIfPossible(e, n, r);
2385
+ return;
2386
+ }
2387
+ if (n?.userGesture && this.getContextState(e) !== "running") try {
2388
+ await this.resumeWithTimeout(e), this.getContextState(e) === "running" && this.removeUnlockHandler();
2389
+ } catch {}
2390
+ let a = `${this.outputSampleRateHz}_2`;
2391
+ if (!this.workletModuleUrl || this.workletModuleKey !== a) {
2392
+ if (this.workletModuleUrl) try {
2393
+ URL.revokeObjectURL(this.workletModuleUrl);
2394
+ } catch {}
2395
+ this.workletModuleKey = a, this.workletReady = null, this.workletModuleUrl = URL.createObjectURL(new Blob([wt({
2396
+ outputSampleRateHz: this.outputSampleRateHz,
2397
+ outputChannels: 2
2398
+ })], { type: "text/javascript" }));
2399
+ }
2400
+ this.workletReady ||= e.audioWorklet.addModule(this.workletModuleUrl).catch((e) => {
2401
+ throw J.warn(`Failed to load AudioWorklet module, falling back: ${Y(e)}`), e;
2402
+ });
2403
+ try {
2404
+ await this.workletReady;
2405
+ } catch {
2406
+ this.workletReady = null, this.ensureScriptProcessor(e, t);
2407
+ return;
2408
+ }
2409
+ this.stopScriptProcessor();
2410
+ let o = (this.config.performance?.preferSharedArrayBuffer ?? !0) && i();
2411
+ this.audioWorkletNode = new AudioWorkletNode(e, "ikon-audio-player", {
2412
+ numberOfInputs: 0,
2413
+ numberOfOutputs: 1,
2414
+ outputChannelCount: [2],
2415
+ processorOptions: {
2416
+ outputAudioConfig: { diagnostics: t },
2417
+ useSharedArrayBuffer: o,
2418
+ outputChannels: 2
2419
+ }
2420
+ }), this.audioWorkletNode.onprocessorerror = () => {
2421
+ J.error("Audio playback worklet processor crashed, rebuilding audio pipeline"), this.stopAudioWorklet(), this.setupAudioGraphAfterRecreate();
2422
+ }, this.audioWorkletNode.port.onmessage = (e) => {
2423
+ let n = e.data;
2424
+ if (!(!n || typeof n != "object")) {
2425
+ if (n.type === "log") {
2426
+ let e = n, t = e.streamId ? ` [stream=${e.streamId.substring(0, 8)}]` : "";
2427
+ e.level === "error" ? J.error(`[Worklet]${t} ${e.message}`) : e.level === "warn" ? J.warn(`[Worklet]${t} ${e.message}`) : J.info(`[Worklet]${t} ${e.message}`);
2428
+ return;
2429
+ }
2430
+ if (n.type === "stats") {
2431
+ if (!t.enabled) return;
2432
+ let e = n;
2433
+ J.debug(`[Audio] stream=${e.streamId.substring(0, 8)} state=${e.state} buffer=${e.bufferMs.toFixed(0)}ms target=${e.targetMs.toFixed(0)}ms jitter=${e.jitterMs.toFixed(1)}ms underrun=${e.underrunCount} drop=${e.dropCount}`);
2434
+ return;
2435
+ }
2436
+ if (n.type === "playback") {
2437
+ let e = n;
2438
+ this.lastPlaybackByStream.set(e.streamId, {
2439
+ epoch: e.epoch,
2440
+ playedSeconds: e.playedSeconds,
2441
+ bufferMs: e.bufferMs
2442
+ }), this.sendPlaybackReport(e.streamId, e.epoch, e.playedSeconds, e.bufferMs);
2443
+ return;
2444
+ }
2445
+ if (n.type === "streamDrained" && "streamId" in n && typeof n.streamId == "string") {
2446
+ let e = n.streamId;
2447
+ this.activeStreams.delete(e), this.lastPlaybackByStream.delete(e), this.activeStreams.size === 0 && this.removeUnlockHandler();
2448
+ return;
2449
+ }
2450
+ }
2451
+ }, this.audioWorkletNode.connect(e.destination), this.stopScriptProcessorDiagnostics(), await this.resumeAudioContextIfPossible(e, n, r), this.rebindWorkerTransportIfNeeded();
2452
+ let s = this.pendingWorkletMessages.splice(0);
2453
+ for (let e of s) this.postToWorklet(e);
2454
+ let c = this.getContextState(e) === "running";
2455
+ this.notifyPlaybackActive(c);
2456
+ }
2457
+ ensureScriptProcessor(e, t) {
2458
+ if (this.scriptProcessorNode) return;
2459
+ this.stopAudioWorklet(), this.pendingWorkletMessages = [];
2460
+ let n = 1024, r = new Float32Array(n * 2), i = Math.round(this.outputSampleRateHz * 100 / 1e3), a = Math.round(this.outputSampleRateHz * 50 / 1e3), o = 1 / i, s = 1 / a, c = [, ,], l = [], u = e.createScriptProcessor(n, 0, 2);
2461
+ u.onaudioprocess = (e) => {
2462
+ let t = e.outputBuffer, n = t.numberOfChannels, i = t.length;
2463
+ for (let e = 0; e < n; e++) {
2464
+ let n = t.getChannelData(e);
2465
+ n.fill(0), c[e] = n;
2466
+ }
2467
+ let a = 0;
2468
+ l.length = 0;
2469
+ for (let [e, t] of this.fallbackQueues.entries()) {
2470
+ t.totalSamples > 0 && (a += 1), (t.ending || t.inputDrained) && t.fadeDirection !== "out" && t.totalSamples / n * 1e3 / this.outputSampleRateHz <= 100 && (t.fadeDirection = "out"), this.readFromFallbackQueue(t, r), (t.totalSamples > 0 || !t.hasLastOut) && ((!t.lastOut || t.lastOut.length !== r.length) && (t.lastOut = new Float32Array(r.length)), t.lastOut.set(r), t.hasLastOut = !0);
2471
+ for (let e = 0; e < i; e++) {
2472
+ t.fadeDirection === "in" ? (t.fadeGain = Math.min(1, t.fadeGain + o), t.fadeGain >= 1 && (t.fadeDirection = "none")) : t.fadeDirection === "out" && (t.fadeGain = Math.max(0, t.fadeGain - s), t.fadeGain <= 0 && (t.fadeDirection = "none"));
2473
+ for (let i = 0; i < n; i++) {
2474
+ let a = r[e * n + i];
2475
+ c[i][e] += a * t.fadeGain;
2476
+ }
2477
+ }
2478
+ let u = t.ending || t.inputDrained;
2479
+ if (u && t.totalSamples <= 0) {
2480
+ if (t.fadeDirection === "out" && t.fadeGain > .001) {
2481
+ for (let e = 0; e < i; e++) if (t.fadeGain = Math.max(0, t.fadeGain - s), t.fadeGain <= .001) {
2482
+ t.fadeGain = 0, t.fadeDirection = "none";
2483
+ break;
2484
+ }
2485
+ }
2486
+ t.fadeGain <= .001 && t.ending && l.push(e);
2487
+ } else if (!u && t.totalSamples <= 0 && t.hasLastOut && t.lastOut && t.fadeGain > .001) {
2488
+ t.fadeDirection = "out", t.underrunPending = !0;
2489
+ let e = t.lastOut, r = i - 1;
2490
+ for (let a = 0; a < i; a++) {
2491
+ t.fadeGain = Math.max(0, t.fadeGain - s), t.fadeGain <= 0 && (t.fadeDirection = "none");
2492
+ for (let i = 0; i < n; i++) {
2493
+ let o = e[r * n + i];
2494
+ c[i][a] += o * t.fadeGain;
2495
+ }
2496
+ }
2497
+ }
2498
+ }
2499
+ if (l.length > 0) {
2500
+ for (let e of l) this.fallbackQueues.delete(e), this.activeStreams.delete(e);
2501
+ this.activeStreams.size === 0 && this.removeUnlockHandler();
2502
+ }
2503
+ if (a > 1) {
2504
+ let e = 1 / Math.sqrt(a);
2505
+ for (let t = 0; t < i; t++) for (let r = 0; r < n; r++) c[r][t] *= e;
2506
+ }
2507
+ for (let e = 0; e < i; e++) for (let t = 0; t < n; t++) {
2508
+ let n = c[t][e];
2509
+ c[t][e] = n > 1 ? 1 : n < -1 ? -1 : n;
2510
+ }
2511
+ }, u.connect(e.destination), this.scriptProcessorNode = u, this.ensureScriptProcessorDiagnostics(t);
2512
+ }
2513
+ compactFallbackQueue(e) {
2514
+ if (e.headIndex <= 32 || e.headIndex <= e.chunks.length / 2) return;
2515
+ let t = e.chunks.length - e.headIndex;
2516
+ for (let n = 0; n < t; n++) e.chunks[n] = e.chunks[e.headIndex + n];
2517
+ e.chunks.length = t, e.headIndex = 0;
2518
+ }
2519
+ skipFromFallbackQueue(e, t) {
2520
+ if (t <= 0) return;
2521
+ let n = t;
2522
+ for (; n > 0 && e.headIndex < e.chunks.length;) {
2523
+ let t = e.chunks[e.headIndex], r = t.length - e.offset, i = Math.min(r, n);
2524
+ e.offset += i, e.totalSamples -= i, n -= i, e.offset >= t.length && (e.headIndex += 1, e.offset = 0);
2525
+ }
2526
+ e.totalSamples < 0 && (e.totalSamples = 0), this.compactFallbackQueue(e);
2527
+ }
2528
+ trimFallbackQueue(e) {
2529
+ let t = Math.floor(this.outputSampleRateHz * 2 * o / 1e3), n = e.totalSamples - t;
2530
+ n <= 0 || this.skipFromFallbackQueue(e, n);
2531
+ }
2532
+ stopScriptProcessorDiagnostics() {
2533
+ this.scriptDiagnosticsTimer &&= (clearInterval(this.scriptDiagnosticsTimer), null);
2534
+ }
2535
+ ensureScriptProcessorDiagnostics(e) {
2536
+ if (!e.enabled) {
2537
+ this.stopScriptProcessorDiagnostics();
2538
+ return;
2539
+ }
2540
+ if (this.scriptDiagnosticsTimer) return;
2541
+ let t = Math.max(250, e.statusIntervalMs);
2542
+ this.scriptDiagnosticsTimer = setInterval(() => {
2543
+ if (this.scriptProcessorNode) for (let [e, t] of this.fallbackQueues.entries()) {
2544
+ if (t.totalSamples <= 0) continue;
2545
+ let n = t.totalSamples / 2 * 1e3 / this.outputSampleRateHz;
2546
+ J.debug(`[Audio] stream=${e.substring(0, 8)} buffer=${n.toFixed(0)}ms renderer=ScriptProcessor`);
2547
+ }
2548
+ }, t);
2549
+ }
2550
+ readFromFallbackQueue(e, t) {
2551
+ let n = 0;
2552
+ for (; n < t.length && e.headIndex < e.chunks.length;) {
2553
+ let r = e.chunks[e.headIndex], i = r.length - e.offset, a = Math.min(i, t.length - n);
2554
+ t.set(r.subarray(e.offset, e.offset + a), n), n += a, e.offset += a, e.totalSamples = Math.max(0, e.totalSamples - a), e.offset >= r.length && (e.headIndex += 1, e.offset = 0);
2555
+ }
2556
+ n < t.length && t.fill(0, n), this.compactFallbackQueue(e);
2557
+ }
2558
+ async resumeAudioContextIfPossible(e, t, n) {
2559
+ if (this.getContextState(e) === "running") {
2560
+ this.removeUnlockHandler();
2561
+ return;
2562
+ }
2563
+ let r = n?.promptForUnlock ?? !0;
2564
+ if (!(t?.userGesture ?? !1)) {
2565
+ if (!r) return;
2566
+ this.addUnlockHandler();
2567
+ return;
2568
+ }
2569
+ try {
2570
+ await this.resumeWithTimeout(e), this.removeUnlockHandler();
2571
+ } catch (e) {
2572
+ J.warn(`Failed to resume AudioContext: ${Y(e)}`), this.addUnlockHandler();
2573
+ }
2574
+ }
2575
+ async recoverAudioContext(e) {
2576
+ if (e.fromUserGesture && this.recoveryTask && this.audioContext) try {
2577
+ if (await this.resumeWithTimeout(this.audioContext), this.audioContext.state === "running") {
2578
+ this.removeUnlockHandler();
2579
+ return;
2580
+ }
2581
+ } catch {}
2582
+ if (this.recoveryTask) {
2583
+ await this.recoveryTask;
2584
+ return;
2585
+ }
2586
+ this.recoveryTask = this.recoverAudioContextInternal(e).finally(() => {
2587
+ this.recoveryTask = null;
2588
+ }), await this.recoveryTask;
2589
+ }
2590
+ async recoverAudioContextInternal(e) {
2591
+ if (!this.enabled) return;
2592
+ let t = this.activeStreams.size > 0, n = this.audioContext;
2593
+ if (!n) {
2594
+ if (!t) return;
2595
+ await this.ensureAudioGraph(void 0, { promptForUnlock: !0 });
2596
+ return;
2597
+ }
2598
+ if (n.sampleRate !== this.outputSampleRateHz) {
2599
+ J.warn(`AudioContext sample rate drifted (${n.sampleRate} Hz vs worklet ${this.outputSampleRateHz} Hz), rebuilding audio graph`), this.stopAudioGraph(), t && await this.ensureAudioGraph(void 0, { promptForUnlock: !0 });
2600
+ return;
2601
+ }
2602
+ if (this.getContextState(n) === "running") {
2603
+ this.removeUnlockHandler();
2604
+ return;
2605
+ }
2606
+ if (t) {
2607
+ try {
2608
+ if (await this.resumeWithTimeout(n), this.getContextState(n) === "running") {
2609
+ this.removeUnlockHandler();
2610
+ return;
2611
+ }
2612
+ } catch (t) {
2613
+ J.debug(`AudioContext resume failed or timed out (${e.reason}): ${t}`);
2614
+ }
2615
+ this.addUnlockHandler();
2616
+ }
2617
+ }
2618
+ rebindWorkerTransportIfNeeded() {
2619
+ if (!this.audioWorker || !this.audioContext) return;
2620
+ let e = (this.config.performance?.preferSharedArrayBuffer ?? !0) && this.audioWorkletNode && i() ? "sab" : "pcm";
2621
+ try {
2622
+ this.audioWorker.postMessage({
2623
+ type: "rebind",
2624
+ transport: e,
2625
+ outputSampleRate: this.outputSampleRateHz,
2626
+ outputChannels: 2
2627
+ });
2628
+ } catch {}
2629
+ }
2630
+ async ensureWorkers() {
2631
+ if (this.audioWorker) return;
2632
+ if (typeof Worker > "u") {
2633
+ J.warn("Audio decode worker unavailable (Workers not supported)");
2634
+ return;
2635
+ }
2636
+ let e = this.client.createProtocolMessagePort({ opcodeGroupsMask: l.GROUP_AUDIO });
2637
+ if (!e) return;
2638
+ this.protocolPort = e;
2639
+ try {
2640
+ this.audioWorker = new St();
2641
+ } catch (e) {
2642
+ J.warn(`Failed to create audio worker: ${Y(e)}`), this.protocolPort.close(), this.protocolPort = null;
2643
+ return;
2644
+ }
2645
+ let t = this.audioWorker;
2646
+ t.addEventListener("message", (e) => this.onAudioWorkerMessage(e.data)), t.addEventListener("error", (e) => {
2647
+ let t = [
2648
+ e.message,
2649
+ e.filename,
2650
+ e.lineno,
2651
+ e.colno
2652
+ ].filter((e) => e != null && String(e).length > 0);
2653
+ J.error(`Audio worker failed: ${t.join(" ")}`.trim()), this.handleWorkerCrash();
2654
+ });
2655
+ let n = (this.config.performance?.preferSharedArrayBuffer ?? !0) && !!this.audioWorkletNode && i(), r = this.config.performance?.preferWebCodecs ?? !0, a = this.client.isWebRtcMediaActive, o = {
2656
+ type: "configure",
2657
+ transport: n ? "sab" : "pcm",
2658
+ outputSampleRate: this.outputSampleRateHz,
2659
+ outputChannels: 2,
2660
+ preferWebCodecs: r,
2661
+ webRtcEnabled: a
2662
+ };
2663
+ t.postMessage(o), t.postMessage({
2664
+ type: "attachProtocolPort",
2665
+ port: e.port
2666
+ }, [e.port]);
2667
+ let s = this.client.globalState;
2668
+ s && this.syncStreamsFromGlobalState(s);
2669
+ let c = this.audioWorkletNode ? "AudioWorklet" : "ScriptProcessor", u = n ? "SharedArrayBuffer" : "MessagePort", d = a ? "WebRTC (native)" : r && typeof AudioDecoder < "u" ? "WebCodecs" : "WASM Opus";
2670
+ J.debug(`Audio initialized: renderer=${c}, transport=${u}, decoder=${d}, output=${this.outputSampleRateHz}Hz/2ch`);
2671
+ }
2672
+ onAudioWorkerMessage(e) {
2673
+ if (e.type === "streamBegin") {
2674
+ this.activeStreams.add(e.streamId);
2675
+ let t = this.audioContext;
2676
+ if (this.enabled && t && this.getContextState(t) !== "running" && this.recoverAudioContext({ reason: "streamBegin" }), this.audioWorkletNode) {
2677
+ this.postToWorklet({
2678
+ type: "streamBegin",
2679
+ streamId: e.streamId,
2680
+ sharedArrayBuffer: e.sharedArrayBuffer
2681
+ });
2682
+ return;
2683
+ }
2684
+ this.fallbackQueues.set(e.streamId, {
2685
+ chunks: [],
2686
+ headIndex: 0,
2687
+ offset: 0,
2688
+ totalSamples: 0,
2689
+ ending: !1,
2690
+ inputDrained: !1,
2691
+ fadeGain: 0,
2692
+ fadeDirection: "in",
2693
+ lastOut: null,
2694
+ hasLastOut: !1,
2695
+ underrunPending: !1
2696
+ });
2697
+ return;
2698
+ }
2699
+ if (e.type === "streamEnd") {
2700
+ let t = this.fallbackQueues.get(e.streamId);
2701
+ if (t) {
2702
+ t.ending = !0, t.totalSamples <= 0 && (this.fallbackQueues.delete(e.streamId), this.activeStreams.delete(e.streamId), this.activeStreams.size === 0 && this.removeUnlockHandler());
2703
+ return;
2704
+ }
2705
+ if (this.audioWorkletNode) {
2706
+ this.postToWorklet({
2707
+ type: "streamEnd",
2708
+ streamId: e.streamId
2709
+ });
2710
+ return;
2711
+ }
2712
+ this.activeStreams.delete(e.streamId), this.activeStreams.size === 0 && this.removeUnlockHandler();
2713
+ return;
2714
+ }
2715
+ if (e.type === "pcm") {
2716
+ if (this.audioWorkletNode) {
2717
+ this.postToWorklet({
2718
+ type: "pcm",
2719
+ streamId: e.streamId,
2720
+ pcm: e.pcm
2721
+ }, [e.pcm.buffer]);
2722
+ return;
2723
+ }
2724
+ let t = this.fallbackQueues.get(e.streamId);
2725
+ t && (t.underrunPending && t.totalSamples === 0 && (t.fadeGain = 0, t.fadeDirection = "in", t.underrunPending = !1), t.chunks.push(e.pcm), t.totalSamples += e.pcm.length, this.trimFallbackQueue(t));
2726
+ return;
2727
+ }
2728
+ if (e.type === "frame") {
2729
+ let t = this.fallbackQueues.get(e.streamId);
2730
+ t && (t.inputDrained && !e.isFirst && (t.inputDrained = !1, t.fadeGain = 0, t.fadeDirection = "in", t.underrunPending = !1), e.isFirst && (t.inputDrained = !1, t.fadeGain = 0, t.fadeDirection = "in", t.underrunPending = !1), e.isLast && (t.inputDrained = !0)), this.postToWorklet({
2731
+ type: "frame",
2732
+ streamId: e.streamId,
2733
+ epoch: e.epoch,
2734
+ timestampInterleavedSamples: e.timestampInterleavedSamples,
2735
+ frameSizeInterleavedSamples: e.frameSizeInterleavedSamples,
2736
+ isFirst: e.isFirst,
2737
+ isLast: e.isLast,
2738
+ jitterMs: e.jitterMs
2739
+ });
2740
+ return;
2741
+ }
2742
+ e.type === "error" && J.warn(`Audio worker error${e.streamId ? ` (stream ${e.streamId})` : ""}: ${e.error.message}`);
2743
+ }
2744
+ postToWorklet(e, t) {
2745
+ if (!this.audioWorkletNode) {
2746
+ if (this.scriptProcessorNode) return;
2747
+ this.enqueuePendingWorkletMessage(e);
2748
+ return;
2749
+ }
2750
+ try {
2751
+ this.audioWorkletNode.port.postMessage(e, t ?? []);
2752
+ } catch {
2753
+ this.enqueuePendingWorkletMessage(e);
2754
+ }
2755
+ }
2756
+ notifyPlaybackActive(e) {
2757
+ this.postToWorklet({
2758
+ type: "playbackActive",
2759
+ active: e
2760
+ }), this.reportPlaybackStateIfChanged();
2761
+ }
2762
+ computePlaybackState() {
2763
+ let e = this.audioContext;
2764
+ return !e || this.getContextState(e) !== "running" || this.awaitingUnlock ? z.Blocked : this.hiddenMuted ? z.Hidden : z.Playing;
2765
+ }
2766
+ sendPlaybackReport(e, t, n, r) {
2767
+ let i = e.indexOf("_");
2768
+ if (i <= 0) return;
2769
+ let a = Number(e.substring(0, i)), o = Number(e.substring(i + 1));
2770
+ if (!Number.isFinite(a) || !Number.isFinite(o)) return;
2771
+ let s = this.computePlaybackState();
2772
+ this.lastReportedPlaybackState = s;
2773
+ try {
2774
+ let e = V({
2775
+ Epoch: t,
2776
+ PlayedSeconds: n,
2777
+ BufferedMs: r,
2778
+ State: s
2779
+ });
2780
+ this.client.sendProtocolMessage(H(e, this.client.sessionId ?? 0, {
2781
+ trackId: o,
2782
+ targetIds: [a]
2783
+ }));
2784
+ } catch {}
2785
+ }
2786
+ reportPlaybackStateIfChanged() {
2787
+ let e = this.computePlaybackState();
2788
+ if (e !== this.lastReportedPlaybackState) {
2789
+ for (let [e, t] of this.lastPlaybackByStream) this.sendPlaybackReport(e, t.epoch, t.playedSeconds, t.bufferMs);
2790
+ this.lastReportedPlaybackState = e;
2791
+ }
2792
+ }
2793
+ enqueuePendingWorkletMessage(e) {
2794
+ if (e.type === "streamBegin") {
2795
+ let t = this.pendingWorkletMessages.findIndex((t) => t.type === "streamBegin" && t.streamId === e.streamId);
2796
+ t >= 0 && this.pendingWorkletMessages.splice(t, 1);
2797
+ }
2798
+ if (this.pendingWorkletMessages.push(e), !(this.pendingWorkletMessages.length <= 512)) for (; !(this.pendingWorkletMessages.length <= 512);) {
2799
+ let e = this.pendingWorkletMessages.findIndex((e) => e.type !== "streamBegin");
2800
+ if (e < 0) break;
2801
+ this.pendingWorkletMessages.splice(e, 1);
2802
+ }
2803
+ }
2804
+ stopAudioWorklet() {
2805
+ if (this.audioWorkletNode) {
2806
+ try {
2807
+ this.audioWorkletNode.disconnect();
2808
+ } catch {}
2809
+ this.audioWorkletNode = null;
2810
+ }
2811
+ }
2812
+ stopScriptProcessor() {
2813
+ if (this.scriptProcessorNode) {
2814
+ try {
2815
+ this.scriptProcessorNode.disconnect();
2816
+ } catch {}
2817
+ this.scriptProcessorNode.onaudioprocess = null, this.scriptProcessorNode = null;
2818
+ }
2819
+ this.stopScriptProcessorDiagnostics();
2820
+ }
2821
+ stopAudioGraph() {
2822
+ if (this.stopAudioWorklet(), this.stopScriptProcessor(), this.fallbackQueues.clear(), this.audioContext &&= (this.audioContext.close().catch(() => void 0), null), this.workletModuleUrl) {
2823
+ try {
2824
+ URL.revokeObjectURL(this.workletModuleUrl);
2825
+ } catch {}
2826
+ this.workletModuleUrl = null, this.workletModuleKey = null;
2827
+ }
2828
+ this.workletReady = null, this.pendingWorkletMessages = [];
2829
+ }
2830
+ handleWorkerCrash() {
2831
+ if (this.stopWorkers(), this.workerRebuildAttempts >= t.MAX_WORKER_REBUILD_ATTEMPTS) {
2832
+ J.error("Audio worker crashed repeatedly, giving up on rebuilds");
2833
+ return;
2834
+ }
2835
+ this.workerRebuildAttempts++, this.ensureWorkers().then(() => {
2836
+ let e = this.client.globalState;
2837
+ e && this.syncStreamsFromGlobalState(e);
2838
+ });
2839
+ }
2840
+ stopWorkers() {
2841
+ let e = Array.from(this.activeStreams);
2842
+ if (this.audioWorker) {
2843
+ try {
2844
+ this.audioWorker.postMessage({ type: "dispose" });
2845
+ } catch {}
2846
+ try {
2847
+ this.audioWorker.terminate();
2848
+ } catch {}
2849
+ this.audioWorker = null;
2850
+ }
2851
+ if (this.protocolPort &&= (this.protocolPort.close(), null), this.audioWorkletNode) for (let t of e) this.postToWorklet({
2852
+ type: "streamEnd",
2853
+ streamId: t
2854
+ });
2855
+ this.activeStreams.clear(), this.fallbackQueues.clear(), this.removeUnlockHandler();
2856
+ }
2857
+ ensureStateSubscription() {
2858
+ this.stateUnsubscribe ||= this.client.subscribeToState((e) => {
2859
+ if (this.enabled) {
2860
+ if (e === "connected" || e === "reconnecting") {
2861
+ this.ensureWorkers();
2862
+ return;
2863
+ }
2864
+ (e === "idle" || e === "offline" || e === "offlineError" || e === "connecting" || e === "connectingSlow") && this.stopWorkers();
2865
+ }
2866
+ });
2867
+ }
2868
+ };
2869
+ //#endregion
2870
+ //#region src/worker/video-playback-worker.ts?worker
2871
+ function Et(e) {
2872
+ return new Worker("" + new URL("assets/video-playback-worker-tC5nUVnw.js", import.meta.url).href, {
2873
+ type: "module",
2874
+ name: e?.name
2875
+ });
2876
+ }
2877
+ //#endregion
2878
+ //#region src/media/ikon-video-playback.ts
2879
+ var X = t("IkonVideoPlayback");
2880
+ function Z(e) {
2881
+ return e instanceof Error ? `${e.name}: ${e.message}` : String(e);
2882
+ }
2883
+ var Q = class e {
2884
+ static MAX_WORKER_REBUILD_ATTEMPTS = 3;
2885
+ client;
2886
+ config;
2887
+ enabled = !0;
2888
+ workerRebuildAttempts = 0;
2889
+ stateUnsubscribe = null;
2890
+ worker = null;
2891
+ protocolPort = null;
2892
+ surfaces = /* @__PURE__ */ new Map();
2893
+ constructor(e, t) {
2894
+ this.client = e, this.config = t ?? {}, this.ensureStateSubscription();
2895
+ }
2896
+ async attachCanvas(e, t) {
2897
+ if (!e || typeof e != "string") throw Error("streamId is required");
2898
+ this.detachCanvas(e);
2899
+ let n = this.canUseOffscreenCanvas(t) ? "offscreen" : "bitmap", r = null, i = null;
2900
+ if (n === "bitmap" && (i = t.getContext("bitmaprenderer", { alpha: !1 }), i || (r = t.getContext("2d", {
2901
+ alpha: !1,
2902
+ desynchronized: !0
2903
+ }))), this.surfaces.set(e, {
2904
+ canvas: t,
2905
+ ctx2d: r,
2906
+ ctxBitmap: i,
2907
+ mode: n,
2908
+ attachedToWorker: !1
2909
+ }), this.ensureStateSubscription(), !this.enabled) {
2910
+ X.warn(`Canvas for stream ${e} not attached to the worker: video playback is disabled`);
2911
+ return;
2912
+ }
2913
+ await this.ensureWorker(), this.ensureProtocolPort(), this.syncStreamToWorker(e), this.surfaces.get(e)?.attachedToWorker || X.warn(`Canvas for stream ${e} did not reach the worker; it will stay blank`);
2914
+ }
2915
+ detachCanvas(e) {
2916
+ let t = this.surfaces.get(e);
2917
+ if (t && (this.surfaces.delete(e), this.worker)) try {
2918
+ this.worker.postMessage({
2919
+ type: "unwatchStream",
2920
+ streamId: e
2921
+ }), t.mode === "offscreen" && this.worker.postMessage({
2922
+ type: "detachSurface",
2923
+ streamId: e
2924
+ });
2925
+ } catch {}
2926
+ }
2927
+ async setEnabled(e) {
2928
+ if (this.enabled = e, this.ensureStateSubscription(), !e) {
2929
+ if (this.worker) try {
2930
+ this.worker.postMessage({
2931
+ type: "setEnabled",
2932
+ enabled: !1
2933
+ });
2934
+ } catch {}
2935
+ this.detachProtocolPort();
2936
+ return;
2937
+ }
2938
+ if (await this.ensureWorker(), this.ensureProtocolPort(), this.worker) {
2939
+ try {
2940
+ this.worker.postMessage({
2941
+ type: "setEnabled",
2942
+ enabled: !0
2943
+ });
2944
+ } catch {}
2945
+ this.syncAllStreamsToWorker();
2946
+ }
2947
+ }
2948
+ dispose() {
2949
+ this.enabled = !1, this.stateUnsubscribe?.(), this.stateUnsubscribe = null, this.surfaces.clear(), this.stopWorker();
2950
+ }
2951
+ canUseOffscreenCanvas(e) {
2952
+ return this.config.performance?.preferOffscreenCanvas ?? !0 ? "transferControlToOffscreen" in e && typeof e.transferControlToOffscreen == "function" : !1;
2953
+ }
2954
+ async ensureWorker() {
2955
+ if (!this.enabled || this.worker) return;
2956
+ if (typeof Worker > "u") {
2957
+ X.warn("Video decode worker unavailable (Workers not supported)");
2958
+ return;
2959
+ }
2960
+ let e;
2961
+ try {
2962
+ e = new Et();
2963
+ } catch (e) {
2964
+ X.warn(`Failed to create video worker: ${Z(e)}`);
2965
+ return;
2966
+ }
2967
+ this.worker = e, e.addEventListener("message", (e) => this.onWorkerMessage(e.data)), e.addEventListener("error", (e) => {
2968
+ let t = [
2969
+ e.message,
2970
+ e.filename,
2971
+ e.lineno,
2972
+ e.colno
2973
+ ].filter((e) => e != null && String(e).length > 0);
2974
+ X.error(`Video worker failed: ${t.join(" ")}`.trim()), this.handleWorkerCrash();
2975
+ }), e.postMessage({
2976
+ type: "configure",
2977
+ webRtcEnabled: this.client.isWebRtcMediaActive
2978
+ }), e.postMessage({
2979
+ type: "setEnabled",
2980
+ enabled: this.enabled
2981
+ }), this.syncAllStreamsToWorker();
2982
+ let t = this.client.globalState;
2983
+ t && this.syncStreamsFromGlobalState(t);
2984
+ }
2985
+ onWorkerMessage(e) {
2986
+ if (e.type !== "streamBegin" && e.type !== "streamEnd") {
2987
+ if (e.type === "frame") {
2988
+ let t = this.surfaces.get(e.streamId);
2989
+ if (!t || t.mode !== "bitmap") {
2990
+ try {
2991
+ e.bitmap.close();
2992
+ } catch {}
2993
+ return;
2994
+ }
2995
+ if (t.ctxBitmap) {
2996
+ try {
2997
+ t.ctxBitmap.transferFromImageBitmap(e.bitmap);
2998
+ } catch {}
2999
+ return;
3000
+ }
3001
+ if (!t.ctx2d) {
3002
+ try {
3003
+ e.bitmap.close();
3004
+ } catch {}
3005
+ return;
3006
+ }
3007
+ (t.canvas.width !== e.width || t.canvas.height !== e.height) && (t.canvas.width = e.width, t.canvas.height = e.height);
3008
+ try {
3009
+ t.ctx2d.drawImage(e.bitmap, 0, 0, e.width, e.height);
3010
+ } finally {
3011
+ try {
3012
+ e.bitmap.close();
3013
+ } catch {}
3014
+ }
3015
+ return;
3016
+ }
3017
+ if (e.type === "requestIdr") {
3018
+ this.requestIdrForStream(e.senderId, e.trackId);
3019
+ return;
3020
+ }
3021
+ if (e.type === "error") {
3022
+ X.warn(`Video worker error: ${e.error.message}`);
3023
+ return;
3024
+ }
3025
+ e.type === "log" ? X.info(e.message) : e.type === "debug" && X.debug(e.message);
3026
+ }
3027
+ }
3028
+ syncAllStreamsToWorker() {
3029
+ for (let e of this.surfaces.keys()) this.syncStreamToWorker(e);
3030
+ }
3031
+ syncStreamToWorker(e) {
3032
+ if (!this.worker) return;
3033
+ let t = this.surfaces.get(e);
3034
+ if (t && !t.attachedToWorker) {
3035
+ if (t.mode === "offscreen") {
3036
+ try {
3037
+ let n = t.canvas.transferControlToOffscreen();
3038
+ this.worker.postMessage({
3039
+ type: "attachSurface",
3040
+ streamId: e,
3041
+ canvas: n
3042
+ }, [n]), t.attachedToWorker = !0;
3043
+ } catch (n) {
3044
+ X.warn(`Failed to attach offscreen canvas for stream ${e}: ${Z(n)}`), t.mode = "bitmap", t.ctxBitmap = t.canvas.getContext("bitmaprenderer", { alpha: !1 }), t.ctxBitmap || (t.ctx2d = t.canvas.getContext("2d", {
3045
+ alpha: !1,
3046
+ desynchronized: !0
3047
+ }));
3048
+ try {
3049
+ this.worker.postMessage({
3050
+ type: "watchStream",
3051
+ streamId: e
3052
+ }), t.attachedToWorker = !0;
3053
+ } catch {}
3054
+ }
3055
+ return;
3056
+ }
3057
+ try {
3058
+ this.worker.postMessage({
3059
+ type: "watchStream",
3060
+ streamId: e
3061
+ }), t.attachedToWorker = !0;
3062
+ } catch {}
3063
+ }
3064
+ }
3065
+ requestIdrForStream(e, t) {
3066
+ let n = et(Qe(), this.client.sessionId ?? 0, {
3067
+ trackId: t,
3068
+ targetIds: [e]
3069
+ });
3070
+ this.client.sendProtocolMessage(n);
3071
+ }
3072
+ detachProtocolPort() {
3073
+ if (this.protocolPort) {
3074
+ try {
3075
+ this.protocolPort.close();
3076
+ } catch {}
3077
+ this.protocolPort = null;
3078
+ }
3079
+ }
3080
+ ensureProtocolPort() {
3081
+ if (!this.worker || this.protocolPort) return;
3082
+ let e = this.client.createProtocolMessagePort({ opcodeGroupsMask: l.GROUP_VIDEO });
3083
+ if (e) {
3084
+ this.protocolPort = e;
3085
+ try {
3086
+ this.worker.postMessage({
3087
+ type: "attachProtocolPort",
3088
+ port: e.port
3089
+ }, [e.port]);
3090
+ } catch (e) {
3091
+ X.warn(`Failed to attach video protocol port: ${Z(e)}`), this.protocolPort.close(), this.protocolPort = null;
3092
+ }
3093
+ }
3094
+ }
3095
+ syncStreamsFromGlobalState(e) {
3096
+ if (!this.worker) return;
3097
+ let t = Object.values(e.VideoStreams ?? {}).map((e) => ({
3098
+ senderId: e.ClientSessionId,
3099
+ trackId: e.TrackId,
3100
+ info: e.Info
3101
+ }));
3102
+ if (t.length !== 0) try {
3103
+ this.worker.postMessage({
3104
+ type: "syncStreams",
3105
+ streams: t
3106
+ });
3107
+ } catch {}
3108
+ }
3109
+ setWebRtcActive(e) {
3110
+ if (this.worker) try {
3111
+ this.worker.postMessage({
3112
+ type: "setWebRtcEnabled",
3113
+ enabled: e
3114
+ });
3115
+ } catch {}
3116
+ }
3117
+ handleProtocolTransportLost() {
3118
+ this.worker && this.stopWorker();
3119
+ }
3120
+ handleWorkerCrash() {
3121
+ if (this.stopWorker(), this.workerRebuildAttempts >= e.MAX_WORKER_REBUILD_ATTEMPTS) {
3122
+ X.error("Video worker crashed repeatedly, giving up on rebuilds");
3123
+ return;
3124
+ }
3125
+ this.workerRebuildAttempts++, this.ensureWorker().then(() => this.ensureProtocolPort());
3126
+ }
3127
+ stopWorker() {
3128
+ if (this.detachProtocolPort(), this.worker) {
3129
+ try {
3130
+ this.worker.postMessage({ type: "dispose" });
3131
+ } catch {}
3132
+ try {
3133
+ this.worker.terminate();
3134
+ } catch {}
3135
+ this.worker = null;
3136
+ }
3137
+ }
3138
+ ensureStateSubscription() {
3139
+ this.stateUnsubscribe ||= this.client.subscribeToState((e) => {
3140
+ if (this.enabled) {
3141
+ if (e === "connected" || e === "reconnecting") {
3142
+ this.ensureWorker().then(() => this.ensureProtocolPort());
3143
+ return;
3144
+ }
3145
+ (e === "idle" || e === "offline" || e === "offlineError" || e === "connecting" || e === "connectingSlow") && this.detachProtocolPort();
3146
+ }
3147
+ });
3148
+ }
3149
+ }, Dt = /* @__PURE__ */ d({ IkonMedia: () => $ }), $ = class {
3150
+ audio;
3151
+ video;
3152
+ constructor(e, t) {
3153
+ this.audio = new Tt(e, t?.audio), this.video = new Q(e, t?.video), t?.mediaSession && this.audio.setMediaSession(t.mediaSession);
3154
+ }
3155
+ dispose() {
3156
+ this.audio.dispose(), this.video.dispose();
3157
+ }
3158
+ };
3159
+ //#endregion
3160
+ export { v as A, Ce as C, D, B as E, g as F, ee as I, te as L, _ as M, m as N, w as O, h as P, d as R, Se as S, xe as T, ge as _, xt as a, ve as b, pt as c, V as d, U as f, he as g, z as h, Tt as i, p as j, S as k, ht as l, H as m, Dt as n, lt as o, Ve as p, Q as r, _t as s, $ as t, yt as u, _e as v, be as w, ye as x, pe as y };