@moq/watch 0.2.17 → 0.2.18

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 (56) hide show
  1. package/README.md +1 -1
  2. package/audio/backend.d.ts +1 -1
  3. package/audio/backend.d.ts.map +1 -1
  4. package/audio/buffer.d.ts +10 -1
  5. package/audio/buffer.d.ts.map +1 -1
  6. package/audio/decoder.d.ts +2 -1
  7. package/audio/decoder.d.ts.map +1 -1
  8. package/audio/emitter.d.ts.map +1 -1
  9. package/audio/mse.d.ts +1 -1
  10. package/audio/mse.d.ts.map +1 -1
  11. package/audio/render.d.ts +6 -1
  12. package/audio/render.d.ts.map +1 -1
  13. package/audio/ring-buffer.d.ts +2 -0
  14. package/audio/ring-buffer.d.ts.map +1 -1
  15. package/audio/shared-ring-buffer.d.ts +8 -1
  16. package/audio/shared-ring-buffer.d.ts.map +1 -1
  17. package/audio/source.d.ts.map +1 -1
  18. package/backend.d.ts +17 -18
  19. package/backend.d.ts.map +1 -1
  20. package/{broadcast-80k1hgfM.js → broadcast-oQKyLEQ1.js} +435 -436
  21. package/broadcast-oQKyLEQ1.js.map +1 -0
  22. package/broadcast.d.ts +3 -3
  23. package/broadcast.d.ts.map +1 -1
  24. package/buffered.d.ts +19 -0
  25. package/buffered.d.ts.map +1 -0
  26. package/element.d.ts +22 -4
  27. package/element.d.ts.map +1 -1
  28. package/element.js +59 -35
  29. package/element.js.map +1 -1
  30. package/index.d.ts +1 -0
  31. package/index.d.ts.map +1 -1
  32. package/index.js +7 -6
  33. package/mse.d.ts.map +1 -1
  34. package/package.json +6 -6
  35. package/support/element.d.ts.map +1 -1
  36. package/support/element.js.map +1 -1
  37. package/support/index.js.map +1 -1
  38. package/sync-D67IxmxJ.js +128 -0
  39. package/sync-D67IxmxJ.js.map +1 -0
  40. package/sync.d.ts +27 -5
  41. package/sync.d.ts.map +1 -1
  42. package/sync.test.d.ts +2 -0
  43. package/sync.test.d.ts.map +1 -0
  44. package/ui/components/latency.d.ts.map +1 -1
  45. package/ui/element.d.ts.map +1 -1
  46. package/ui/element.js +215 -178
  47. package/ui/element.js.map +1 -1
  48. package/video/backend.d.ts +1 -1
  49. package/video/backend.d.ts.map +1 -1
  50. package/video/decoder.d.ts +1 -1
  51. package/video/decoder.d.ts.map +1 -1
  52. package/video/mse.d.ts +3 -3
  53. package/video/mse.d.ts.map +1 -1
  54. package/video/renderer.d.ts.map +1 -1
  55. package/video/source.d.ts.map +1 -1
  56. package/broadcast-80k1hgfM.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import * as e from "@moq/json";
1
+ import { t as e } from "./sync-D67IxmxJ.js";
2
2
  import * as t from "@moq/net";
3
3
  import { Path as n, Time as r } from "@moq/net";
4
4
  import { Effect as i, Signal as a } from "@moq/signals";
@@ -26,17 +26,18 @@ function p(e) {
26
26
  //#endregion
27
27
  //#region src/audio/shared-ring-buffer.ts
28
28
  var m = 0, h = 1, g = 2, _ = 3, v = 4;
29
- function y(e, t, n) {
29
+ function y(e, t, n, r = !1) {
30
30
  if (e <= 0) throw Error("invalid channels");
31
31
  if (t <= 0) throw Error("invalid capacity");
32
32
  if (n <= 0) throw Error("invalid sample rate");
33
- let r = new SharedArrayBuffer(e * t * Float32Array.BYTES_PER_ELEMENT), i = new SharedArrayBuffer(v * Int32Array.BYTES_PER_ELEMENT), a = new Int32Array(i);
34
- return Atomics.store(a, _, 1), {
33
+ let i = new SharedArrayBuffer(e * t * Float32Array.BYTES_PER_ELEMENT), a = new SharedArrayBuffer(v * Int32Array.BYTES_PER_ELEMENT), o = new Int32Array(a);
34
+ return Atomics.store(o, _, 1), {
35
35
  channels: e,
36
36
  capacity: t,
37
37
  rate: n,
38
- samples: r,
39
- control: i
38
+ samples: i,
39
+ control: a,
40
+ buffered: r
40
41
  };
41
42
  }
42
43
  function b(e, t) {
@@ -56,16 +57,20 @@ var C = class e {
56
57
  channels;
57
58
  capacity;
58
59
  rate;
60
+ buffered;
59
61
  init;
60
62
  #e;
61
63
  #t;
64
+ #n = !1;
62
65
  constructor(e) {
63
- this.channels = e.channels, this.capacity = e.capacity, this.rate = e.rate, this.init = e, this.#e = new Int32Array(e.control), this.#t = [];
66
+ this.channels = e.channels, this.capacity = e.capacity, this.rate = e.rate, this.buffered = e.buffered, this.init = e, this.#e = new Int32Array(e.control), this.#t = [];
64
67
  for (let t = 0; t < this.channels; t++) this.#t.push(new Float32Array(e.samples, t * this.capacity * Float32Array.BYTES_PER_ELEMENT, this.capacity));
65
68
  }
66
69
  insert(e, t) {
67
70
  if (t.length !== this.channels) throw Error("wrong number of channels");
68
- let n = Math.round(r.Second.fromMicro(e) * this.rate), i = t[0].length, a = 0, o = n + i | 0, s = Atomics.load(this.#e, h), c = s - n | 0;
71
+ let n = Math.round(r.Second.fromMicro(e) * this.rate), i = t[0].length, a = 0;
72
+ this.buffered && !this.#n && (Atomics.store(this.#e, h, n | 0), Atomics.store(this.#e, m, n | 0), this.#n = !0);
73
+ let o = n + i | 0, s = Atomics.load(this.#e, h), c = s - n | 0;
69
74
  if (c > 0) {
70
75
  if (c >= i) return;
71
76
  a = c, n = n + c | 0;
@@ -91,7 +96,7 @@ var C = class e {
91
96
  read(e) {
92
97
  if (Atomics.load(this.#e, _) === 1) return 0;
93
98
  let t = Atomics.load(this.#e, h), n = Atomics.load(this.#e, m), r = Atomics.load(this.#e, g), i = n - t | 0;
94
- if (r > 0 && i > r) {
99
+ if (!this.buffered && r > 0 && i > r) {
95
100
  let e = n - r | 0;
96
101
  t = S(this.#e, h, e);
97
102
  }
@@ -106,8 +111,15 @@ var C = class e {
106
111
  setLatency(e) {
107
112
  Atomics.store(this.#e, g, e);
108
113
  }
114
+ reset() {
115
+ this.#n = !1, Atomics.store(this.#e, _, 1);
116
+ let e = Atomics.load(this.#e, m);
117
+ Atomics.store(this.#e, h, e);
118
+ }
109
119
  resize(t) {
110
- let n = new e(y(this.channels, t, this.rate)), r = Atomics.load(this.#e, h), i = Atomics.load(this.#e, m), a = Atomics.load(this.#e, g), o = Atomics.load(this.#e, _), s = i - r | 0, c = Math.max(0, Math.min(s, n.capacity)), l = i - c | 0;
120
+ let n = new e(y(this.channels, t, this.rate, this.buffered));
121
+ n.#n = this.#n;
122
+ let r = Atomics.load(this.#e, h), i = Atomics.load(this.#e, m), a = Atomics.load(this.#e, g), o = Atomics.load(this.#e, _), s = i - r | 0, c = Math.max(0, Math.min(s, n.capacity)), l = i - c | 0;
111
123
  for (let e = 0; e < this.channels; e++) {
112
124
  let t = this.#t[e], r = n.#t[e];
113
125
  for (let e = 0; e < c; e++) {
@@ -127,14 +139,38 @@ var C = class e {
127
139
  get length() {
128
140
  return Atomics.load(this.#e, m) - Atomics.load(this.#e, h) | 0;
129
141
  }
142
+ }, w = class {
143
+ #e;
144
+ #t;
145
+ #n = [];
146
+ constructor(e, t) {
147
+ this.#e = e, this.#t = t;
148
+ }
149
+ setHeadroom(e) {
150
+ this.#t = e;
151
+ }
152
+ wait(e, t) {
153
+ return !this.#e || t >= (e - this.#t | 0) ? Promise.resolve() : new Promise((t) => this.#n.push({
154
+ timestamp: e,
155
+ resolve: t
156
+ }));
157
+ }
158
+ advance(e) {
159
+ this.#n.length !== 0 && (this.#n = this.#n.filter(({ timestamp: t, resolve: n }) => e < (t - this.#t | 0) ? !0 : (n(), !1)));
160
+ }
161
+ flush() {
162
+ for (let { resolve: e } of this.#n) e();
163
+ this.#n = [];
164
+ }
130
165
  };
131
- //#endregion
132
- //#region src/audio/buffer.ts
133
- function w() {
166
+ function T(e, t) {
167
+ return r.Micro.fromSecond(e / t);
168
+ }
169
+ function E() {
134
170
  return !(typeof SharedArrayBuffer > "u" || typeof crossOriginIsolated < "u" && !crossOriginIsolated);
135
171
  }
136
- function ee(e, t, n, r) {
137
- return w() ? (console.log("[audio] using SharedArrayBuffer audio buffer"), new te(e, t, n, r)) : (console.log("[audio] using postMessage audio buffer (SharedArrayBuffer unavailable)"), new ne(e, t, n, r));
172
+ function ee(e, t, n, r, i = !1) {
173
+ return E() ? (console.log("[audio] using SharedArrayBuffer audio buffer"), new te(e, t, n, r, i)) : (console.log("[audio] using postMessage audio buffer (SharedArrayBuffer unavailable)"), new ne(e, t, n, r, i));
138
174
  }
139
175
  var te = class {
140
176
  rate;
@@ -145,24 +181,28 @@ var te = class {
145
181
  timestamp = this.#n;
146
182
  #r = new a(!0);
147
183
  stalled = this.#r;
148
- #i = new i();
149
- constructor(e, t, n, r) {
184
+ #i;
185
+ #a = new i();
186
+ constructor(e, t, n, r, i) {
150
187
  this.#e = e, this.channels = t, this.rate = n;
151
- let i = y(t, Math.max(n, r * 2), n);
152
- this.#t = new C(i), this.#t.setLatency(r);
153
- let a = {
188
+ let a = Math.max(n, r * 2);
189
+ this.#i = new w(i, T(r, n));
190
+ let o = y(t, a, n, i);
191
+ this.#t = new C(o), this.#t.setLatency(r);
192
+ let s = {
154
193
  type: "init-shared",
155
- ...i
194
+ ...o
156
195
  };
157
- e.port.postMessage(a), this.#i.interval(() => {
158
- this.#n.set(this.#t.timestamp), this.#r.set(this.#t.stalled);
196
+ e.port.postMessage(s), this.#a.interval(() => {
197
+ let e = this.#t.stalled;
198
+ this.#n.set(this.#t.timestamp), this.#r.set(e), e ? this.#i.flush() : this.#i.advance(this.#t.timestamp);
159
199
  }, 50);
160
200
  }
161
201
  insert(e, t) {
162
202
  this.#t.insert(e, t);
163
203
  }
164
204
  setLatency(e) {
165
- if (this.#t.capacity < e * 1.5) {
205
+ if (this.#i.setHeadroom(T(e, this.rate)), this.#t.capacity < e * 1.5) {
166
206
  let t = Math.max(this.rate, e * 2);
167
207
  this.#t = this.#t.resize(t), this.#t.setLatency(e);
168
208
  let n = {
@@ -172,8 +212,14 @@ var te = class {
172
212
  this.#e.port.postMessage(n);
173
213
  } else this.#t.setLatency(e);
174
214
  }
215
+ reset() {
216
+ this.#t.reset(), this.#i.flush();
217
+ }
218
+ wait(e) {
219
+ return this.#t.stalled ? Promise.resolve() : this.#i.wait(e, this.#t.timestamp);
220
+ }
175
221
  close() {
176
- this.#i.close();
222
+ this.#i.flush(), this.#a.close();
177
223
  }
178
224
  }, ne = class {
179
225
  rate;
@@ -183,18 +229,20 @@ var te = class {
183
229
  timestamp = this.#t;
184
230
  #n = new a(!0);
185
231
  stalled = this.#n;
186
- #r = new i();
187
- constructor(e, t, n, i) {
188
- this.#e = e, this.channels = t, this.rate = n;
189
- let a = {
232
+ #r;
233
+ #i = new i();
234
+ constructor(e, t, n, i, a) {
235
+ this.#e = e, this.channels = t, this.rate = n, this.#r = new w(a, T(i, n));
236
+ let o = {
190
237
  type: "init-post",
191
238
  channels: t,
192
239
  rate: n,
193
- latency: r.Milli.fromSecond(i / n)
240
+ latency: r.Milli.fromSecond(i / n),
241
+ buffered: a
194
242
  };
195
- e.port.postMessage(a), this.#r.event(e.port, "message", (e) => {
243
+ e.port.postMessage(o), this.#i.event(e.port, "message", (e) => {
196
244
  let t = e.data;
197
- t?.type === "state" && (this.#t.set(t.timestamp), this.#n.set(t.stalled));
245
+ t?.type === "state" && (this.#t.set(t.timestamp), this.#n.set(t.stalled), t.stalled ? this.#r.flush() : this.#r.advance(t.timestamp));
198
246
  }), e.port.start();
199
247
  }
200
248
  insert(e, t) {
@@ -206,16 +254,23 @@ var te = class {
206
254
  this.#e.port.postMessage(n, t.map((e) => e.buffer));
207
255
  }
208
256
  setLatency(e) {
257
+ this.#r.setHeadroom(T(e, this.rate));
209
258
  let t = {
210
259
  type: "latency",
211
260
  latency: r.Milli.fromSecond(e / this.rate)
212
261
  };
213
262
  this.#e.port.postMessage(t);
214
263
  }
264
+ reset() {
265
+ this.#e.port.postMessage({ type: "reset" }), this.#r.flush();
266
+ }
267
+ wait(e) {
268
+ return this.#n.peek() ? Promise.resolve() : this.#r.wait(e, this.#t.peek());
269
+ }
215
270
  close() {
216
- this.#r.close();
271
+ this.#r.flush(), this.#i.close();
217
272
  }
218
- }, re = new Blob(["var __defProp = Object.defineProperty;\nvar __getOwnPropNames = Object.getOwnPropertyNames;\nvar __esm = (fn, res) => function __init() {\n return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;\n};\nvar __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, { get: all[name], enumerable: true });\n};\n\n// ../net/src/path.ts\nvar init_path = __esm({\n \"../net/src/path.ts\"() {\n \"use strict\";\n }\n});\n\n// ../net/src/varint.ts\nvar MAX_U6, MAX_U14, MAX_U30, MAX_U53, MAX_U64, MAX_U62;\nvar init_varint = __esm({\n \"../net/src/varint.ts\"() {\n \"use strict\";\n MAX_U6 = 2 ** 6 - 1;\n MAX_U14 = 2 ** 14 - 1;\n MAX_U30 = 2 ** 30 - 1;\n MAX_U53 = Number.MAX_SAFE_INTEGER;\n MAX_U64 = (1n << 64n) - 1n;\n MAX_U62 = 2n ** 62n - 1n;\n }\n});\n\n// ../net/src/index.ts\ninit_path();\n\n// ../net/src/time.ts\nvar time_exports = {};\n__export(time_exports, {\n Micro: () => Micro,\n Milli: () => Milli,\n Nano: () => Nano,\n Second: () => Second\n});\nvar Nano = {\n zero: 0,\n fromMicro: (us) => us * 1e3,\n fromMilli: (ms) => ms * 1e6,\n fromSecond: (s) => s * 1e9,\n toMicro: (ns) => ns / 1e3,\n toMilli: (ns) => ns / 1e6,\n toSecond: (ns) => ns / 1e9,\n now: () => performance.now() * 1e6,\n add: (a, b) => a + b,\n sub: (a, b) => a - b,\n mul: (a, b) => a * b,\n div: (a, b) => a / b,\n max: (a, b) => Math.max(a, b),\n min: (a, b) => Math.min(a, b)\n};\nvar Micro = {\n zero: 0,\n fromNano: (ns) => ns / 1e3,\n fromMilli: (ms) => ms * 1e3,\n fromSecond: (s) => s * 1e6,\n toNano: (us) => us * 1e3,\n toMilli: (us) => us / 1e3,\n toSecond: (us) => us / 1e6,\n now: () => performance.now() * 1e3,\n add: (a, b) => a + b,\n sub: (a, b) => a - b,\n mul: (a, b) => a * b,\n div: (a, b) => a / b,\n max: (a, b) => Math.max(a, b),\n min: (a, b) => Math.min(a, b)\n};\nvar Milli = {\n zero: 0,\n fromNano: (ns) => ns / 1e6,\n fromMicro: (us) => us / 1e3,\n fromSecond: (s) => s * 1e3,\n toNano: (ms) => ms * 1e6,\n toMicro: (ms) => ms * 1e3,\n toSecond: (ms) => ms / 1e3,\n now: () => performance.now(),\n add: (a, b) => a + b,\n sub: (a, b) => a - b,\n mul: (a, b) => a * b,\n div: (a, b) => a / b,\n max: (a, b) => Math.max(a, b),\n min: (a, b) => Math.min(a, b)\n};\nvar Second = {\n zero: 0,\n fromNano: (ns) => ns / 1e9,\n fromMicro: (us) => us / 1e6,\n fromMilli: (ms) => ms / 1e3,\n toNano: (s) => s * 1e9,\n toMicro: (s) => s * 1e6,\n toMilli: (s) => s * 1e3,\n now: () => performance.now() / 1e3,\n add: (a, b) => a + b,\n sub: (a, b) => a - b,\n mul: (a, b) => a * b,\n div: (a, b) => a / b,\n max: (a, b) => Math.max(a, b),\n min: (a, b) => Math.min(a, b)\n};\n\n// ../net/src/index.ts\ninit_varint();\n\n// src/audio/ring-buffer.ts\nvar AudioRingBuffer = class {\n #buffer;\n #writeIndex = 0;\n #readIndex = 0;\n rate;\n channels;\n #stalled = true;\n constructor(props) {\n if (props.channels <= 0) throw new Error(\"invalid channels\");\n if (props.rate <= 0) throw new Error(\"invalid sample rate\");\n if (props.latency <= 0) throw new Error(\"invalid latency\");\n const samples = Math.ceil(props.rate * time_exports.Second.fromMilli(props.latency));\n if (samples === 0) throw new Error(\"empty buffer\");\n this.rate = props.rate;\n this.channels = props.channels;\n this.#buffer = [];\n for (let i = 0; i < this.channels; i++) {\n this.#buffer[i] = new Float32Array(samples);\n }\n }\n get stalled() {\n return this.#stalled;\n }\n get timestamp() {\n return time_exports.Micro.fromSecond(this.#readIndex / this.rate);\n }\n get length() {\n return this.#writeIndex - this.#readIndex;\n }\n get capacity() {\n return this.#buffer[0]?.length;\n }\n resize(latency) {\n const newCapacity = Math.ceil(this.rate * time_exports.Second.fromMilli(latency));\n if (newCapacity === this.capacity) return;\n if (newCapacity === 0) throw new Error(\"empty buffer\");\n const newBuffer = [];\n for (let i = 0; i < this.channels; i++) {\n newBuffer[i] = new Float32Array(newCapacity);\n }\n const samplesToKeep = Math.min(this.length, newCapacity);\n if (samplesToKeep > 0) {\n const copyStart = this.#writeIndex - samplesToKeep;\n for (let channel = 0; channel < this.channels; channel++) {\n const src = this.#buffer[channel];\n const dst = newBuffer[channel];\n for (let i = 0; i < samplesToKeep; i++) {\n const srcPos = (copyStart + i) % src.length;\n const dstPos = (copyStart + i) % dst.length;\n dst[dstPos] = src[srcPos];\n }\n }\n }\n this.#buffer = newBuffer;\n this.#readIndex = this.#writeIndex - samplesToKeep;\n if (samplesToKeep === 0) this.#stalled = true;\n }\n write(timestamp, data) {\n if (data.length !== this.channels) throw new Error(\"wrong number of channels\");\n let start = Math.round(time_exports.Second.fromMicro(timestamp) * this.rate);\n let samples = data[0].length;\n let offset = this.#readIndex - start;\n if (offset > samples) {\n return;\n } else if (offset > 0) {\n samples -= offset;\n start += offset;\n } else {\n offset = 0;\n }\n const end = start + samples;\n const overflow = end - this.#readIndex - this.#buffer[0].length;\n if (overflow >= 0) {\n this.#stalled = false;\n this.#readIndex += overflow;\n }\n if (start > this.#writeIndex) {\n const gapSize = Math.min(start - this.#writeIndex, this.#buffer[0].length);\n if (gapSize === 1) {\n console.warn(\"floating point inaccuracy detected\");\n }\n for (let channel = 0; channel < this.channels; channel++) {\n const dst = this.#buffer[channel];\n for (let i = 0; i < gapSize; i++) {\n const writePos = (this.#writeIndex + i) % dst.length;\n dst[writePos] = 0;\n }\n }\n }\n for (let channel = 0; channel < this.channels; channel++) {\n let src = data[channel];\n src = src.subarray(src.length - samples);\n const dst = this.#buffer[channel];\n if (src.length !== samples) throw new Error(\"mismatching number of samples\");\n for (let i = 0; i < samples; i++) {\n const writePos = (start + i) % dst.length;\n dst[writePos] = src[i];\n }\n }\n if (end > this.#writeIndex) {\n this.#writeIndex = end;\n }\n }\n read(output) {\n if (output.length !== this.channels) throw new Error(\"wrong number of channels\");\n if (this.#stalled) return 0;\n const samples = Math.min(this.#writeIndex - this.#readIndex, output[0].length);\n if (samples === 0) return 0;\n for (let channel = 0; channel < this.channels; channel++) {\n const dst = output[channel];\n const src = this.#buffer[channel];\n if (dst.length !== output[0].length) throw new Error(\"mismatching number of samples\");\n for (let i = 0; i < samples; i++) {\n const readPos = (this.#readIndex + i) % src.length;\n dst[i] = src[readPos];\n }\n }\n this.#readIndex += samples;\n return samples;\n }\n};\n\n// src/audio/shared-ring-buffer.ts\nvar WRITE = 0;\nvar READ = 1;\nvar LATENCY = 2;\nvar STALLED = 3;\nvar CONTROL_SLOTS = 4;\nfunction allocSharedRingBuffer(channels, capacity, rate) {\n if (channels <= 0) throw new Error(\"invalid channels\");\n if (capacity <= 0) throw new Error(\"invalid capacity\");\n if (rate <= 0) throw new Error(\"invalid sample rate\");\n const samples = new SharedArrayBuffer(channels * capacity * Float32Array.BYTES_PER_ELEMENT);\n const control = new SharedArrayBuffer(CONTROL_SLOTS * Int32Array.BYTES_PER_ELEMENT);\n const ctrl = new Int32Array(control);\n Atomics.store(ctrl, STALLED, 1);\n return { channels, capacity, rate, samples, control };\n}\nfunction i32Max(a, b) {\n return (a - b | 0) > 0 ? a : b;\n}\nfunction slot(idx, capacity) {\n return (idx % capacity + capacity) % capacity;\n}\nfunction casAdvance(arr, idx, candidate) {\n for (; ; ) {\n const current = Atomics.load(arr, idx);\n if ((candidate - current | 0) <= 0) return current;\n const witnessed = Atomics.compareExchange(arr, idx, current, candidate);\n if (witnessed === current) return candidate;\n }\n}\nvar SharedRingBuffer = class _SharedRingBuffer {\n channels;\n capacity;\n rate;\n init;\n #control;\n #samples;\n constructor(init) {\n this.channels = init.channels;\n this.capacity = init.capacity;\n this.rate = init.rate;\n this.init = init;\n this.#control = new Int32Array(init.control);\n this.#samples = [];\n for (let i = 0; i < this.channels; i++) {\n this.#samples.push(\n new Float32Array(init.samples, i * this.capacity * Float32Array.BYTES_PER_ELEMENT, this.capacity)\n );\n }\n }\n /**\n * Insert audio samples at the given timestamp.\n * Main thread only. Handles out-of-order writes, gap filling, and overflow.\n */\n insert(timestamp, data) {\n if (data.length !== this.channels) throw new Error(\"wrong number of channels\");\n let start = Math.round(time_exports.Second.fromMicro(timestamp) * this.rate);\n const originalLength = data[0].length;\n let offset = 0;\n const end = start + originalLength | 0;\n const read = Atomics.load(this.#control, READ);\n const behind = read - start | 0;\n if (behind > 0) {\n if (behind >= originalLength) {\n return;\n }\n offset = behind;\n start = start + behind | 0;\n }\n const samples = originalLength - offset;\n if ((end - read | 0) > this.capacity) {\n casAdvance(this.#control, READ, end - this.capacity | 0);\n }\n const write = Atomics.load(this.#control, WRITE);\n const gap = start - write | 0;\n if (gap > 0) {\n const gapSize = Math.min(gap, this.capacity);\n for (let channel = 0; channel < this.channels; channel++) {\n const dst = this.#samples[channel];\n for (let i = 0; i < gapSize; i++) {\n dst[slot(write + i | 0, this.capacity)] = 0;\n }\n }\n }\n for (let channel = 0; channel < this.channels; channel++) {\n const src = data[channel];\n const dst = this.#samples[channel];\n for (let i = 0; i < samples; i++) {\n dst[slot(start + i | 0, this.capacity)] = src[offset + i];\n }\n }\n Atomics.store(this.#control, WRITE, i32Max(Atomics.load(this.#control, WRITE), end));\n const currentRead = Atomics.load(this.#control, READ);\n const currentWrite = Atomics.load(this.#control, WRITE);\n const latency = Atomics.load(this.#control, LATENCY);\n if ((currentWrite - currentRead | 0) >= latency && latency > 0) {\n Atomics.store(this.#control, STALLED, 0);\n }\n }\n /**\n * Read audio samples into the output buffers.\n * AudioWorklet only. Returns the number of samples read.\n */\n read(output) {\n if (Atomics.load(this.#control, STALLED) === 1) return 0;\n let read = Atomics.load(this.#control, READ);\n const write = Atomics.load(this.#control, WRITE);\n const latency = Atomics.load(this.#control, LATENCY);\n const buffered = write - read | 0;\n if (latency > 0 && buffered > latency) {\n const skipTo = write - latency | 0;\n read = casAdvance(this.#control, READ, skipTo);\n }\n const available = write - read | 0;\n const count = Math.min(available, output[0].length);\n if (count <= 0) return 0;\n for (let channel = 0; channel < this.channels; channel++) {\n const src = this.#samples[channel];\n const dst = output[channel];\n for (let i = 0; i < count; i++) {\n dst[i] = src[slot(read + i | 0, this.capacity)];\n }\n }\n casAdvance(this.#control, READ, read + count | 0);\n return count;\n }\n /** Update the target latency in samples. */\n setLatency(samples) {\n Atomics.store(this.#control, LATENCY, samples);\n }\n /**\n * Allocate a new ring with `newCapacity` samples and copy the unread window\n * [READ, WRITE) plus control state into it. Used when growing capacity so\n * we don't drop buffered audio. If `newCapacity` is smaller than the unread\n * span, the oldest samples are truncated.\n *\n * Main thread only. `resize()` reads from the source `SharedRingBuffer` and\n * writes into a freshly allocated buffer from `allocSharedRingBuffer`, so it\n * relies on the same invariant as `insert()`: no concurrent main-thread\n * writers. The AudioWorklet reader is tolerated via the CAS discipline used\n * by READ/WRITE elsewhere.\n */\n resize(newCapacity) {\n const init = allocSharedRingBuffer(this.channels, newCapacity, this.rate);\n const dst = new _SharedRingBuffer(init);\n const read = Atomics.load(this.#control, READ);\n const write = Atomics.load(this.#control, WRITE);\n const latency = Atomics.load(this.#control, LATENCY);\n const stalled = Atomics.load(this.#control, STALLED);\n const available = write - read | 0;\n const copyCount = Math.max(0, Math.min(available, dst.capacity));\n const copyStart = write - copyCount | 0;\n for (let channel = 0; channel < this.channels; channel++) {\n const src = this.#samples[channel];\n const out = dst.#samples[channel];\n for (let i = 0; i < copyCount; i++) {\n const idx = copyStart + i | 0;\n out[slot(idx, dst.capacity)] = src[slot(idx, this.capacity)];\n }\n }\n Atomics.store(dst.#control, READ, copyStart);\n Atomics.store(dst.#control, WRITE, write);\n Atomics.store(dst.#control, LATENCY, latency);\n Atomics.store(dst.#control, STALLED, stalled);\n return dst;\n }\n /** Current playback timestamp derived from READ position. */\n get timestamp() {\n const read = Atomics.load(this.#control, READ);\n return time_exports.Micro.fromSecond(read / this.rate);\n }\n /** Whether the buffer is stalled (waiting to fill). */\n get stalled() {\n return Atomics.load(this.#control, STALLED) === 1;\n }\n /**\n * Number of buffered samples (WRITE - READ).\n *\n * Non-atomic: WRITE and READ are loaded separately, so a concurrent\n * writer/reader can make the two loads inconsistent. Intended for\n * tests and diagnostics, not control-flow decisions.\n */\n get length() {\n return Atomics.load(this.#control, WRITE) - Atomics.load(this.#control, READ) | 0;\n }\n};\n\n// src/audio/render-worklet.ts\nvar Render = class extends AudioWorkletProcessor {\n // Set after init, depending on which path the main thread chose.\n #backend;\n #underflow = 0;\n #stateCounter = 0;\n constructor() {\n super();\n this.port.onmessage = (event) => {\n const msg = event.data;\n if (msg.type === \"init-shared\") {\n console.log(\"[audio-worklet] init-shared: using SharedArrayBuffer path\");\n this.#backend = new SharedRingBuffer(msg);\n this.#underflow = 0;\n } else if (msg.type === \"init-post\") {\n console.log(\"[audio-worklet] init-post: using postMessage path\");\n this.#backend = new AudioRingBuffer(msg);\n this.#underflow = 0;\n } else if (msg.type === \"data\") {\n if (this.#backend instanceof AudioRingBuffer) this.#backend.write(msg.timestamp, msg.data);\n } else if (msg.type === \"latency\") {\n if (this.#backend instanceof AudioRingBuffer) this.#backend.resize(msg.latency);\n }\n };\n }\n process(_inputs, outputs, _parameters) {\n const output = outputs[0];\n const backend = this.#backend;\n const samplesRead = backend?.read(output) ?? 0;\n if (samplesRead < output[0].length) {\n this.#underflow += output[0].length - samplesRead;\n } else if (this.#underflow > 0 && backend) {\n console.debug(`audio underflow: ${Math.round(1e3 * this.#underflow / backend.rate)}ms`);\n this.#underflow = 0;\n }\n if (backend instanceof AudioRingBuffer) {\n this.#stateCounter++;\n if (this.#stateCounter >= 5) {\n this.#stateCounter = 0;\n const state = {\n type: \"state\",\n timestamp: backend.timestamp,\n stalled: backend.stalled\n };\n this.port.postMessage(state);\n }\n }\n return true;\n }\n};\nregisterProcessor(\"render\", Render);\n"], { type: "application/javascript" }), T = URL.createObjectURL(re), E = class {
273
+ }, re = new Blob(["var __defProp = Object.defineProperty;\nvar __getOwnPropNames = Object.getOwnPropertyNames;\nvar __esm = (fn, res, err) => function __init() {\n if (err) throw err[0];\n try {\n return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;\n } catch (e) {\n throw err = [e], e;\n }\n};\nvar __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, { get: all[name], enumerable: true });\n};\n\n// ../net/src/path.ts\nvar init_path = __esm({\n \"../net/src/path.ts\"() {\n \"use strict\";\n }\n});\n\n// ../net/src/varint.ts\nvar MAX_U6, MAX_U14, MAX_U30, MAX_U53, MAX_U64, MAX_U62;\nvar init_varint = __esm({\n \"../net/src/varint.ts\"() {\n \"use strict\";\n MAX_U6 = 2 ** 6 - 1;\n MAX_U14 = 2 ** 14 - 1;\n MAX_U30 = 2 ** 30 - 1;\n MAX_U53 = Number.MAX_SAFE_INTEGER;\n MAX_U64 = (1n << 64n) - 1n;\n MAX_U62 = 2n ** 62n - 1n;\n }\n});\n\n// ../net/src/index.ts\ninit_path();\n\n// ../net/src/time.ts\nvar time_exports = {};\n__export(time_exports, {\n Micro: () => Micro,\n Milli: () => Milli,\n Nano: () => Nano,\n Second: () => Second\n});\nvar Nano = {\n zero: 0,\n fromMicro: (us) => us * 1e3,\n fromMilli: (ms) => ms * 1e6,\n fromSecond: (s) => s * 1e9,\n toMicro: (ns) => ns / 1e3,\n toMilli: (ns) => ns / 1e6,\n toSecond: (ns) => ns / 1e9,\n now: () => performance.now() * 1e6,\n add: (a, b) => a + b,\n sub: (a, b) => a - b,\n mul: (a, b) => a * b,\n div: (a, b) => a / b,\n max: (a, b) => Math.max(a, b),\n min: (a, b) => Math.min(a, b)\n};\nvar Micro = {\n zero: 0,\n fromNano: (ns) => ns / 1e3,\n fromMilli: (ms) => ms * 1e3,\n fromSecond: (s) => s * 1e6,\n toNano: (us) => us * 1e3,\n toMilli: (us) => us / 1e3,\n toSecond: (us) => us / 1e6,\n now: () => performance.now() * 1e3,\n add: (a, b) => a + b,\n sub: (a, b) => a - b,\n mul: (a, b) => a * b,\n div: (a, b) => a / b,\n max: (a, b) => Math.max(a, b),\n min: (a, b) => Math.min(a, b)\n};\nvar Milli = {\n zero: 0,\n fromNano: (ns) => ns / 1e6,\n fromMicro: (us) => us / 1e3,\n fromSecond: (s) => s * 1e3,\n toNano: (ms) => ms * 1e6,\n toMicro: (ms) => ms * 1e3,\n toSecond: (ms) => ms / 1e3,\n now: () => performance.now(),\n add: (a, b) => a + b,\n sub: (a, b) => a - b,\n mul: (a, b) => a * b,\n div: (a, b) => a / b,\n max: (a, b) => Math.max(a, b),\n min: (a, b) => Math.min(a, b)\n};\nvar Second = {\n zero: 0,\n fromNano: (ns) => ns / 1e9,\n fromMicro: (us) => us / 1e6,\n fromMilli: (ms) => ms / 1e3,\n toNano: (s) => s * 1e9,\n toMicro: (s) => s * 1e6,\n toMilli: (s) => s * 1e3,\n now: () => performance.now() / 1e3,\n add: (a, b) => a + b,\n sub: (a, b) => a - b,\n mul: (a, b) => a * b,\n div: (a, b) => a / b,\n max: (a, b) => Math.max(a, b),\n min: (a, b) => Math.min(a, b)\n};\n\n// ../net/src/index.ts\ninit_varint();\n\n// src/audio/ring-buffer.ts\nvar AudioRingBuffer = class {\n #buffer;\n #writeIndex = 0;\n #readIndex = 0;\n rate;\n channels;\n #stalled = true;\n // Buffered mode: anchor to the first sample, play through without skipping ahead.\n #buffered;\n // Un-stall threshold in samples (how much to buffer before playback starts).\n #latencySamples;\n // Whether the read/write indices have been anchored to the first inserted sample.\n #anchored = false;\n constructor(props) {\n if (props.channels <= 0) throw new Error(\"invalid channels\");\n if (props.rate <= 0) throw new Error(\"invalid sample rate\");\n if (props.latency <= 0) throw new Error(\"invalid latency\");\n this.#latencySamples = Math.ceil(props.rate * time_exports.Second.fromMilli(props.latency));\n if (this.#latencySamples === 0) throw new Error(\"empty buffer\");\n this.rate = props.rate;\n this.channels = props.channels;\n this.#buffered = props.buffered ?? false;\n const capacity = this.#capacityFor(this.#latencySamples);\n this.#buffer = [];\n for (let i = 0; i < this.channels; i++) {\n this.#buffer[i] = new Float32Array(capacity);\n }\n }\n #capacityFor(latencySamples) {\n return this.#buffered ? latencySamples * 2 : latencySamples;\n }\n get stalled() {\n return this.#stalled;\n }\n get timestamp() {\n return time_exports.Micro.fromSecond(this.#readIndex / this.rate);\n }\n get length() {\n return this.#writeIndex - this.#readIndex;\n }\n get capacity() {\n return this.#buffer[0]?.length;\n }\n resize(latency) {\n this.#latencySamples = Math.ceil(this.rate * time_exports.Second.fromMilli(latency));\n const newCapacity = this.#capacityFor(this.#latencySamples);\n if (newCapacity === this.capacity) return;\n if (newCapacity === 0) throw new Error(\"empty buffer\");\n const newBuffer = [];\n for (let i = 0; i < this.channels; i++) {\n newBuffer[i] = new Float32Array(newCapacity);\n }\n const samplesToKeep = Math.min(this.length, newCapacity);\n if (samplesToKeep > 0) {\n const copyStart = this.#writeIndex - samplesToKeep;\n for (let channel = 0; channel < this.channels; channel++) {\n const src = this.#buffer[channel];\n const dst = newBuffer[channel];\n for (let i = 0; i < samplesToKeep; i++) {\n const srcPos = (copyStart + i) % src.length;\n const dstPos = (copyStart + i) % dst.length;\n dst[dstPos] = src[srcPos];\n }\n }\n }\n this.#buffer = newBuffer;\n this.#readIndex = this.#writeIndex - samplesToKeep;\n if (samplesToKeep === 0) this.#stalled = true;\n }\n write(timestamp, data) {\n if (data.length !== this.channels) throw new Error(\"wrong number of channels\");\n let start = Math.round(time_exports.Second.fromMicro(timestamp) * this.rate);\n let samples = data[0].length;\n if (this.#buffered && !this.#anchored) {\n this.#readIndex = start;\n this.#writeIndex = start;\n this.#anchored = true;\n }\n let offset = this.#readIndex - start;\n if (offset > samples) {\n return;\n } else if (offset > 0) {\n samples -= offset;\n start += offset;\n } else {\n offset = 0;\n }\n const end = start + samples;\n const overflow = end - this.#readIndex - this.#buffer[0].length;\n if (overflow >= 0) {\n this.#stalled = false;\n this.#readIndex += overflow;\n }\n if (start > this.#writeIndex) {\n const gapSize = Math.min(start - this.#writeIndex, this.#buffer[0].length);\n if (gapSize === 1) {\n console.warn(\"floating point inaccuracy detected\");\n }\n for (let channel = 0; channel < this.channels; channel++) {\n const dst = this.#buffer[channel];\n for (let i = 0; i < gapSize; i++) {\n const writePos = (this.#writeIndex + i) % dst.length;\n dst[writePos] = 0;\n }\n }\n }\n for (let channel = 0; channel < this.channels; channel++) {\n let src = data[channel];\n src = src.subarray(src.length - samples);\n const dst = this.#buffer[channel];\n if (src.length !== samples) throw new Error(\"mismatching number of samples\");\n for (let i = 0; i < samples; i++) {\n const writePos = (start + i) % dst.length;\n dst[writePos] = src[i];\n }\n }\n if (end > this.#writeIndex) {\n this.#writeIndex = end;\n }\n if (this.#buffered && this.length >= this.#latencySamples) {\n this.#stalled = false;\n }\n }\n // Flush all buffered samples and re-stall, ready to anchor the next utterance.\n reset() {\n this.#readIndex = 0;\n this.#writeIndex = 0;\n this.#stalled = true;\n this.#anchored = false;\n }\n read(output) {\n if (output.length !== this.channels) throw new Error(\"wrong number of channels\");\n if (this.#stalled) return 0;\n const samples = Math.min(this.#writeIndex - this.#readIndex, output[0].length);\n if (samples === 0) return 0;\n for (let channel = 0; channel < this.channels; channel++) {\n const dst = output[channel];\n const src = this.#buffer[channel];\n if (dst.length !== output[0].length) throw new Error(\"mismatching number of samples\");\n for (let i = 0; i < samples; i++) {\n const readPos = (this.#readIndex + i) % src.length;\n dst[i] = src[readPos];\n }\n }\n this.#readIndex += samples;\n return samples;\n }\n};\n\n// src/audio/shared-ring-buffer.ts\nvar WRITE = 0;\nvar READ = 1;\nvar LATENCY = 2;\nvar STALLED = 3;\nvar CONTROL_SLOTS = 4;\nfunction allocSharedRingBuffer(channels, capacity, rate, buffered = false) {\n if (channels <= 0) throw new Error(\"invalid channels\");\n if (capacity <= 0) throw new Error(\"invalid capacity\");\n if (rate <= 0) throw new Error(\"invalid sample rate\");\n const samples = new SharedArrayBuffer(channels * capacity * Float32Array.BYTES_PER_ELEMENT);\n const control = new SharedArrayBuffer(CONTROL_SLOTS * Int32Array.BYTES_PER_ELEMENT);\n const ctrl = new Int32Array(control);\n Atomics.store(ctrl, STALLED, 1);\n return { channels, capacity, rate, samples, control, buffered };\n}\nfunction i32Max(a, b) {\n return (a - b | 0) > 0 ? a : b;\n}\nfunction slot(idx, capacity) {\n return (idx % capacity + capacity) % capacity;\n}\nfunction casAdvance(arr, idx, candidate) {\n for (; ; ) {\n const current = Atomics.load(arr, idx);\n if ((candidate - current | 0) <= 0) return current;\n const witnessed = Atomics.compareExchange(arr, idx, current, candidate);\n if (witnessed === current) return candidate;\n }\n}\nvar SharedRingBuffer = class _SharedRingBuffer {\n channels;\n capacity;\n rate;\n buffered;\n init;\n #control;\n #samples;\n // Whether READ/WRITE have been anchored to the first inserted sample (buffered mode).\n #anchored = false;\n constructor(init) {\n this.channels = init.channels;\n this.capacity = init.capacity;\n this.rate = init.rate;\n this.buffered = init.buffered;\n this.init = init;\n this.#control = new Int32Array(init.control);\n this.#samples = [];\n for (let i = 0; i < this.channels; i++) {\n this.#samples.push(\n new Float32Array(init.samples, i * this.capacity * Float32Array.BYTES_PER_ELEMENT, this.capacity)\n );\n }\n }\n /**\n * Insert audio samples at the given timestamp.\n * Main thread only. Handles out-of-order writes, gap filling, and overflow.\n */\n insert(timestamp, data) {\n if (data.length !== this.channels) throw new Error(\"wrong number of channels\");\n let start = Math.round(time_exports.Second.fromMicro(timestamp) * this.rate);\n const originalLength = data[0].length;\n let offset = 0;\n if (this.buffered && !this.#anchored) {\n Atomics.store(this.#control, READ, start | 0);\n Atomics.store(this.#control, WRITE, start | 0);\n this.#anchored = true;\n }\n const end = start + originalLength | 0;\n const read = Atomics.load(this.#control, READ);\n const behind = read - start | 0;\n if (behind > 0) {\n if (behind >= originalLength) {\n return;\n }\n offset = behind;\n start = start + behind | 0;\n }\n const samples = originalLength - offset;\n if ((end - read | 0) > this.capacity) {\n casAdvance(this.#control, READ, end - this.capacity | 0);\n }\n const write = Atomics.load(this.#control, WRITE);\n const gap = start - write | 0;\n if (gap > 0) {\n const gapSize = Math.min(gap, this.capacity);\n for (let channel = 0; channel < this.channels; channel++) {\n const dst = this.#samples[channel];\n for (let i = 0; i < gapSize; i++) {\n dst[slot(write + i | 0, this.capacity)] = 0;\n }\n }\n }\n for (let channel = 0; channel < this.channels; channel++) {\n const src = data[channel];\n const dst = this.#samples[channel];\n for (let i = 0; i < samples; i++) {\n dst[slot(start + i | 0, this.capacity)] = src[offset + i];\n }\n }\n Atomics.store(this.#control, WRITE, i32Max(Atomics.load(this.#control, WRITE), end));\n const currentRead = Atomics.load(this.#control, READ);\n const currentWrite = Atomics.load(this.#control, WRITE);\n const latency = Atomics.load(this.#control, LATENCY);\n if ((currentWrite - currentRead | 0) >= latency && latency > 0) {\n Atomics.store(this.#control, STALLED, 0);\n }\n }\n /**\n * Read audio samples into the output buffers.\n * AudioWorklet only. Returns the number of samples read.\n */\n read(output) {\n if (Atomics.load(this.#control, STALLED) === 1) return 0;\n let read = Atomics.load(this.#control, READ);\n const write = Atomics.load(this.#control, WRITE);\n const latency = Atomics.load(this.#control, LATENCY);\n const buffered = write - read | 0;\n if (!this.buffered && latency > 0 && buffered > latency) {\n const skipTo = write - latency | 0;\n read = casAdvance(this.#control, READ, skipTo);\n }\n const available = write - read | 0;\n const count = Math.min(available, output[0].length);\n if (count <= 0) return 0;\n for (let channel = 0; channel < this.channels; channel++) {\n const src = this.#samples[channel];\n const dst = output[channel];\n for (let i = 0; i < count; i++) {\n dst[i] = src[slot(read + i | 0, this.capacity)];\n }\n }\n casAdvance(this.#control, READ, read + count | 0);\n return count;\n }\n /** Update the target latency in samples. */\n setLatency(samples) {\n Atomics.store(this.#control, LATENCY, samples);\n }\n /**\n * Flush buffered samples and re-stall, ready to anchor the next utterance (buffered mode).\n * Main thread only. The worklet reader sees STALLED and stops until the next insert.\n */\n reset() {\n this.#anchored = false;\n Atomics.store(this.#control, STALLED, 1);\n const write = Atomics.load(this.#control, WRITE);\n Atomics.store(this.#control, READ, write);\n }\n /**\n * Allocate a new ring with `newCapacity` samples and copy the unread window\n * [READ, WRITE) plus control state into it. Used when growing capacity so\n * we don't drop buffered audio. If `newCapacity` is smaller than the unread\n * span, the oldest samples are truncated.\n *\n * Main thread only. `resize()` reads from the source `SharedRingBuffer` and\n * writes into a freshly allocated buffer from `allocSharedRingBuffer`, so it\n * relies on the same invariant as `insert()`: no concurrent main-thread\n * writers. The AudioWorklet reader is tolerated via the CAS discipline used\n * by READ/WRITE elsewhere.\n */\n resize(newCapacity) {\n const init = allocSharedRingBuffer(this.channels, newCapacity, this.rate, this.buffered);\n const dst = new _SharedRingBuffer(init);\n dst.#anchored = this.#anchored;\n const read = Atomics.load(this.#control, READ);\n const write = Atomics.load(this.#control, WRITE);\n const latency = Atomics.load(this.#control, LATENCY);\n const stalled = Atomics.load(this.#control, STALLED);\n const available = write - read | 0;\n const copyCount = Math.max(0, Math.min(available, dst.capacity));\n const copyStart = write - copyCount | 0;\n for (let channel = 0; channel < this.channels; channel++) {\n const src = this.#samples[channel];\n const out = dst.#samples[channel];\n for (let i = 0; i < copyCount; i++) {\n const idx = copyStart + i | 0;\n out[slot(idx, dst.capacity)] = src[slot(idx, this.capacity)];\n }\n }\n Atomics.store(dst.#control, READ, copyStart);\n Atomics.store(dst.#control, WRITE, write);\n Atomics.store(dst.#control, LATENCY, latency);\n Atomics.store(dst.#control, STALLED, stalled);\n return dst;\n }\n /** Current playback timestamp derived from READ position. */\n get timestamp() {\n const read = Atomics.load(this.#control, READ);\n return time_exports.Micro.fromSecond(read / this.rate);\n }\n /** Whether the buffer is stalled (waiting to fill). */\n get stalled() {\n return Atomics.load(this.#control, STALLED) === 1;\n }\n /**\n * Number of buffered samples (WRITE - READ).\n *\n * Non-atomic: WRITE and READ are loaded separately, so a concurrent\n * writer/reader can make the two loads inconsistent. Intended for\n * tests and diagnostics, not control-flow decisions.\n */\n get length() {\n return Atomics.load(this.#control, WRITE) - Atomics.load(this.#control, READ) | 0;\n }\n};\n\n// src/audio/render-worklet.ts\nvar Render = class extends AudioWorkletProcessor {\n // Set after init, depending on which path the main thread chose.\n #backend;\n #underflow = 0;\n #stateCounter = 0;\n constructor() {\n super();\n this.port.onmessage = (event) => {\n const msg = event.data;\n if (msg.type === \"init-shared\") {\n console.log(\"[audio-worklet] init-shared: using SharedArrayBuffer path\");\n this.#backend = new SharedRingBuffer(msg);\n this.#underflow = 0;\n } else if (msg.type === \"init-post\") {\n console.log(\"[audio-worklet] init-post: using postMessage path\");\n this.#backend = new AudioRingBuffer(msg);\n this.#underflow = 0;\n } else if (msg.type === \"data\") {\n if (this.#backend instanceof AudioRingBuffer) this.#backend.write(msg.timestamp, msg.data);\n } else if (msg.type === \"latency\") {\n if (this.#backend instanceof AudioRingBuffer) this.#backend.resize(msg.latency);\n } else if (msg.type === \"reset\") {\n if (this.#backend instanceof AudioRingBuffer) this.#backend.reset();\n }\n };\n }\n process(_inputs, outputs, _parameters) {\n const output = outputs[0];\n const backend = this.#backend;\n const samplesRead = backend?.read(output) ?? 0;\n if (samplesRead < output[0].length) {\n this.#underflow += output[0].length - samplesRead;\n } else if (this.#underflow > 0 && backend) {\n console.debug(`audio underflow: ${Math.round(1e3 * this.#underflow / backend.rate)}ms`);\n this.#underflow = 0;\n }\n if (backend instanceof AudioRingBuffer) {\n this.#stateCounter++;\n if (this.#stateCounter >= 5) {\n this.#stateCounter = 0;\n const state = {\n type: \"state\",\n timestamp: backend.timestamp,\n stalled: backend.stalled\n };\n this.port.postMessage(state);\n }\n }\n return true;\n }\n};\nregisterProcessor(\"render\", Render);\n"], { type: "application/javascript" }), D = URL.createObjectURL(re), O = class {
219
274
  source;
220
275
  enabled;
221
276
  #e = new a(void 0);
@@ -234,11 +289,15 @@ var te = class {
234
289
  #s = new a([]);
235
290
  buffered = this.#s;
236
291
  #c;
237
- #l = new i();
292
+ #l = 0;
293
+ #u = new i();
294
+ #d = new a(r.Milli.zero);
238
295
  constructor(e, t) {
239
- this.source = e, this.source.supported.set(D), this.enabled = a.from(t?.enabled ?? !1), this.#l.run(this.#u.bind(this)), this.#l.run(this.#d.bind(this)), this.#l.run(this.#f.bind(this)), this.#l.run(this.#p.bind(this));
296
+ this.source = e, this.source.supported.set(k), this.enabled = a.from(t?.enabled ?? !1), this.#u.run((e) => {
297
+ this.#d.set(e.get(this.source.sync.maxBuffer));
298
+ }), this.#u.run(this.#f.bind(this)), this.#u.run(this.#p.bind(this)), this.#u.run(this.#m.bind(this)), this.#u.run(this.#h.bind(this));
240
299
  }
241
- #u(e) {
300
+ #f(e) {
242
301
  let t = e.get(this.source.config);
243
302
  if (!t) return;
244
303
  let n = t.sampleRate, i = t.numberOfChannels, a = new AudioContext({
@@ -246,38 +305,38 @@ var te = class {
246
305
  sampleRate: n
247
306
  });
248
307
  e.set(this.#e, a), e.cleanup(() => a.close()), e.spawn(async () => {
249
- if (await a.audioWorklet.addModule(T), a.state === "closed") return;
308
+ if (await a.audioWorklet.addModule(D), a.state === "closed") return;
250
309
  let t = new AudioWorkletNode(a, "render", {
251
310
  channelCount: i,
252
311
  channelCountMode: "explicit",
253
312
  outputChannelCount: [i]
254
313
  });
255
314
  e.cleanup(() => t.disconnect());
256
- let o = this.source.sync.buffer.peek(), s = ee(t, i, n, Math.ceil(n * r.Second.fromMilli(o)));
315
+ let o = this.source.sync.buffer.peek(), s = ee(t, i, n, Math.ceil(n * r.Second.fromMilli(o)), this.source.sync.buffered.peek());
257
316
  this.#c = s, e.cleanup(() => {
258
317
  s.close(), this.#c = void 0;
259
318
  }), e.run((e) => {
260
319
  let t = r.Milli.fromMicro(e.get(s.timestamp));
261
- this.#i.set(t), this.#v(t);
320
+ this.#i.set(t), this.#b(t);
262
321
  }), e.run((e) => {
263
322
  this.#a.set(e.get(s.stalled));
264
323
  }), e.set(this.#t, t);
265
324
  });
266
325
  }
267
- #d(e) {
326
+ #p(e) {
268
327
  let t = e.getAll([this.enabled, this.#e]);
269
328
  if (!t) return;
270
329
  let [n, r] = t;
271
330
  r.resume();
272
331
  }
273
- #f(e) {
332
+ #m(e) {
274
333
  if (!e.get(this.#t)) return;
275
334
  let t = this.#c;
276
335
  if (!t) return;
277
336
  let n = e.get(this.source.sync.buffer), i = Math.ceil(t.rate * r.Second.fromMilli(n));
278
337
  t.setLatency(i);
279
338
  }
280
- #p(e) {
339
+ #h(e) {
281
340
  if (!e.get(this.enabled)) return;
282
341
  let t = e.get(this.source.broadcast);
283
342
  if (!t) return;
@@ -288,12 +347,12 @@ var te = class {
288
347
  let i = e.get(t.active);
289
348
  if (!i) return;
290
349
  let a = i.subscribe(n, o.PRIORITY.audio);
291
- e.cleanup(() => a.close()), r.container.kind === "cmaf" ? this.#h(e, a, r) : this.#m(e, a, r);
350
+ e.cleanup(() => a.close()), r.container.kind === "cmaf" ? this.#_(e, a, r) : this.#g(e, a, r);
292
351
  }
293
- #m(e, t, n) {
352
+ #g(e, t, n) {
294
353
  let i = n.container.kind === "loc" ? new c.Loc.Format() : new c.Legacy.Format(), a = new c.Consumer(t, {
295
354
  format: i,
296
- latency: this.source.sync.buffer
355
+ latency: this.#d
297
356
  });
298
357
  e.cleanup(() => a.close()), e.run((e) => {
299
358
  let t = e.get(a.buffered), n = e.get(this.#o);
@@ -306,7 +365,7 @@ var te = class {
306
365
  e.close();
307
366
  return;
308
367
  }
309
- this.#g(e);
368
+ this.#v(e);
310
369
  },
311
370
  error: (e) => console.error(e)
312
371
  });
@@ -320,10 +379,11 @@ var te = class {
320
379
  });;) {
321
380
  let e = await a.next();
322
381
  if (!e) break;
382
+ this.#x(e.discontinuity);
323
383
  let { frame: t } = e;
324
384
  if (!t) continue;
325
385
  let n = r.Milli.fromMicro(t.timestamp);
326
- this.source.sync.received(n, "audio"), this.#r.update((e) => ({ bytesReceived: (e?.bytesReceived ?? 0) + t.data.byteLength }));
386
+ this.source.sync.received(n, "audio"), this.#r.update((e) => ({ bytesReceived: (e?.bytesReceived ?? 0) + t.data.byteLength })), await this.#c?.wait(t.timestamp);
327
387
  let o = new EncodedAudioChunk({
328
388
  type: t.keyframe ? "key" : "delta",
329
389
  data: t.data,
@@ -333,11 +393,11 @@ var te = class {
333
393
  }
334
394
  });
335
395
  }
336
- #h(e, t, n) {
396
+ #_(e, t, n) {
337
397
  if (n.container.kind !== "cmaf") return;
338
398
  let i = p(n.container.init), a = c.Cmaf.decodeInitSegment(i), o = n.codec === "opus" ? void 0 : n.description ? l.Hex.toBytes(n.description) : a.description, s = new c.Consumer(t, {
339
399
  format: new c.Cmaf.Format(a),
340
- latency: this.source.sync.buffer
400
+ latency: this.#d
341
401
  });
342
402
  e.cleanup(() => s.close()), e.run((e) => {
343
403
  let t = e.get(s.buffered), n = e.get(this.#o);
@@ -345,7 +405,7 @@ var te = class {
345
405
  }), e.spawn(async () => {
346
406
  if (!await l.Libav.polyfill()) return;
347
407
  let t = new AudioDecoder({
348
- output: (e) => this.#g(e),
408
+ output: (e) => this.#v(e),
349
409
  error: (e) => console.error(e)
350
410
  });
351
411
  for (e.cleanup(() => {
@@ -358,10 +418,11 @@ var te = class {
358
418
  });;) {
359
419
  let e = await s.next();
360
420
  if (!e) break;
421
+ this.#x(e.discontinuity);
361
422
  let { frame: n } = e;
362
423
  if (!n) continue;
363
424
  let i = r.Milli.fromMicro(n.timestamp);
364
- if (this.source.sync.received(i, "audio"), this.#r.update((e) => ({ bytesReceived: (e?.bytesReceived ?? 0) + n.data.byteLength })), t.state === "closed") break;
425
+ if (this.source.sync.received(i, "audio"), this.#r.update((e) => ({ bytesReceived: (e?.bytesReceived ?? 0) + n.data.byteLength })), await this.#c?.wait(n.timestamp), t.state === "closed") break;
365
426
  t.decode(new EncodedAudioChunk({
366
427
  type: n.keyframe ? "key" : "delta",
367
428
  data: n.data,
@@ -370,14 +431,14 @@ var te = class {
370
431
  }
371
432
  });
372
433
  }
373
- #g(e) {
434
+ #v(e) {
374
435
  let t = e.timestamp, n = r.Milli.fromMicro(t), i = this.#c;
375
436
  if (!i) {
376
437
  e.close();
377
438
  return;
378
439
  }
379
440
  let a = e.numberOfFrames / e.sampleRate * 1e6, o = r.Milli.fromMicro(a), s = r.Milli.add(n, o);
380
- this.#_(n, s);
441
+ this.#y(n, s);
381
442
  let c = Math.min(e.numberOfChannels, i.channels), l = [];
382
443
  for (let t = 0; t < c; t++) {
383
444
  let n = new Float32Array(e.numberOfFrames);
@@ -388,7 +449,7 @@ var te = class {
388
449
  }
389
450
  i.insert(t, l), e.close();
390
451
  }
391
- #_(e, t) {
452
+ #y(e, t) {
392
453
  e > t || this.#o.mutate((n) => {
393
454
  for (let i of n) if (e <= i.end + 1 && t >= i.start) {
394
455
  i.start = r.Milli.min(i.start, e), i.end = r.Milli.max(i.end, t);
@@ -400,7 +461,7 @@ var te = class {
400
461
  }), n.sort((e, t) => e.start - t.start);
401
462
  });
402
463
  }
403
- #v(e) {
464
+ #b(e) {
404
465
  this.#o.mutate((t) => {
405
466
  for (; t.length > 0;) {
406
467
  if (t[0].end >= e) {
@@ -411,11 +472,17 @@ var te = class {
411
472
  }
412
473
  });
413
474
  }
475
+ reset() {
476
+ this.#c?.reset();
477
+ }
478
+ #x(e) {
479
+ e !== this.#l && (this.#l = e, this.#c?.reset(), this.source.sync.reset());
480
+ }
414
481
  close() {
415
- this.#l.close();
482
+ this.#u.close();
416
483
  }
417
484
  };
418
- async function D(e) {
485
+ async function k(e) {
419
486
  let t;
420
487
  if (e.codec !== "opus") {
421
488
  if (e.description) t = l.Hex.toBytes(e.description);
@@ -432,7 +499,7 @@ async function D(e) {
432
499
  }
433
500
  //#endregion
434
501
  //#region src/audio/emitter.ts
435
- var O = .001, k = .2, A = class {
502
+ var A = .001, j = .2, M = class {
436
503
  source;
437
504
  volume;
438
505
  muted;
@@ -461,13 +528,211 @@ var O = .001, k = .2, A = class {
461
528
  if (!t) return;
462
529
  e.cleanup(() => t.gain.cancelScheduledValues(t.context.currentTime));
463
530
  let n = e.get(this.volume);
464
- n < O ? (t.gain.exponentialRampToValueAtTime(O, t.context.currentTime + k), t.gain.setValueAtTime(0, t.context.currentTime + k + .01)) : t.gain.exponentialRampToValueAtTime(n, t.context.currentTime + k);
531
+ n < A ? (t.gain.exponentialRampToValueAtTime(A, t.context.currentTime + j), t.gain.setValueAtTime(0, t.context.currentTime + j + .01)) : t.gain.exponentialRampToValueAtTime(n, t.context.currentTime + j);
465
532
  });
466
533
  }
467
534
  close() {
468
535
  this.#e.close();
469
536
  }
470
- }, ie = /* @__PURE__ */ f({ Muxer: () => j }), j = class {
537
+ };
538
+ //#endregion
539
+ //#region src/buffered.ts
540
+ function N(e) {
541
+ let n = [];
542
+ for (let r = 0; r < e.length; r++) {
543
+ let i = t.Time.Milli.fromSecond(e.start(r)), a = t.Time.Milli.fromSecond(e.end(r));
544
+ n.push({
545
+ start: i,
546
+ end: a
547
+ });
548
+ }
549
+ return n;
550
+ }
551
+ //#endregion
552
+ //#region src/audio/mse.ts
553
+ var P = class {
554
+ muxer;
555
+ source;
556
+ volume;
557
+ muted;
558
+ #e = new a(void 0);
559
+ stats = this.#e;
560
+ #t = new a([]);
561
+ buffered = this.#t;
562
+ context = new a(void 0);
563
+ #n = new i();
564
+ constructor(e, t, n) {
565
+ this.muxer = e, this.source = t, this.source.supported.set(ie), this.volume = a.from(n?.volume ?? .5), this.muted = a.from(n?.muted ?? !1), this.#n.run(this.#r.bind(this)), this.#n.run(this.#s.bind(this));
566
+ }
567
+ #r(e) {
568
+ let t = e.get(this.muxer.element);
569
+ if (!t) return;
570
+ let n = e.get(this.muxer.mediaSource);
571
+ if (!n) return;
572
+ let r = e.get(this.source.broadcast);
573
+ if (!r) return;
574
+ let i = e.get(r.active);
575
+ if (!i) return;
576
+ let a = e.get(this.source.track);
577
+ if (!a) return;
578
+ let s = e.get(this.source.config);
579
+ if (!s) return;
580
+ let c = `audio/mp4; codecs="${s.codec}"`, l = n.addSourceBuffer(c);
581
+ e.cleanup(() => {
582
+ n.removeSourceBuffer(l), l.abort();
583
+ }), e.event(l, "error", (e) => {
584
+ console.error("[MSE] SourceBuffer error:", e);
585
+ }), e.event(l, "updateend", () => {
586
+ this.#t.set(N(l.buffered));
587
+ });
588
+ let u = i.subscribe(a, o.PRIORITY.audio);
589
+ e.cleanup(() => u.close()), s.container.kind === "cmaf" ? this.#a(e, u, s, l, t) : this.#o(e, u, s, l, t);
590
+ }
591
+ async #i(e, t) {
592
+ for (; e.updating;) await new Promise((t) => e.addEventListener("updateend", t, { once: !0 }));
593
+ for (e.appendBuffer(t); e.updating;) await new Promise((t) => e.addEventListener("updateend", t, { once: !0 }));
594
+ }
595
+ #a(e, n, r, i, a) {
596
+ if (r.container.kind !== "cmaf") throw Error("unreachable");
597
+ let o = p(r.container.init), s = c.Cmaf.decodeInitSegment(o);
598
+ e.spawn(async () => {
599
+ for (await this.#i(i, o);;) {
600
+ let e = await n.readFrame();
601
+ if (!e) return;
602
+ let r = c.Cmaf.decodeTimestamp(e, s);
603
+ this.source.sync.received(t.Time.Milli.fromMicro(r), "audio"), await this.#i(i, e), a.buffered.length > 0 && a.currentTime < a.buffered.start(0) && (a.currentTime = a.buffered.start(0));
604
+ }
605
+ });
606
+ }
607
+ #o(e, n, r, i, a) {
608
+ let o = r.container.kind === "loc" ? new c.Loc.Format() : new c.Legacy.Format(), s = new c.Consumer(n, {
609
+ format: o,
610
+ latency: this.source.sync.buffer
611
+ });
612
+ e.cleanup(() => s.close()), e.spawn(async () => {
613
+ let e = c.Cmaf.createAudioInitSegment(r);
614
+ await this.#i(i, e);
615
+ let n = 1, o, l;
616
+ for (;;) {
617
+ let e = await s.next();
618
+ if (!e) return;
619
+ if (!e.frame) continue;
620
+ l = e.frame;
621
+ let n = t.Time.Milli.fromMicro(l.timestamp);
622
+ this.source.sync.received(n, "audio");
623
+ break;
624
+ }
625
+ for (;;) {
626
+ let e = await s.next();
627
+ if (e && !e.frame) continue;
628
+ let r = e?.frame;
629
+ if (r) {
630
+ o = t.Time.Micro.sub(r.timestamp, l.timestamp);
631
+ let e = t.Time.Milli.fromMicro(r.timestamp);
632
+ this.source.sync.received(e, "audio");
633
+ }
634
+ let u = c.Cmaf.encodeDataSegment({
635
+ data: l.data,
636
+ timestamp: l.timestamp,
637
+ duration: o ?? 0,
638
+ keyframe: l.keyframe,
639
+ sequence: n++
640
+ });
641
+ if (await this.#i(i, u), a.buffered.length > 0 && a.currentTime < a.buffered.start(0) && (a.currentTime = a.buffered.start(0)), !r) return;
642
+ l = r;
643
+ }
644
+ });
645
+ }
646
+ #s(e) {
647
+ let t = e.get(this.muxer.element);
648
+ if (!t) return;
649
+ let n = e.get(this.volume), r = e.get(this.muted);
650
+ r && !t.muted ? t.muted = !0 : !r && t.muted && (t.muted = !1), n !== t.volume && (t.volume = n), e.event(t, "volumechange", () => {
651
+ this.volume.set(t.volume);
652
+ });
653
+ }
654
+ close() {
655
+ this.#n.close();
656
+ }
657
+ };
658
+ async function ie(e) {
659
+ return MediaSource.isTypeSupported(`audio/mp4; codecs="${e.codec}"`);
660
+ }
661
+ //#endregion
662
+ //#region src/audio/source.ts
663
+ var ae = 128, F = class {
664
+ broadcast;
665
+ target;
666
+ catalog;
667
+ #e = new a({});
668
+ available = this.#e;
669
+ #t = new a(void 0);
670
+ track = this.#t;
671
+ #n = new a(void 0);
672
+ config = this.#n;
673
+ supported;
674
+ sync;
675
+ #r = new i();
676
+ constructor(e, t) {
677
+ this.sync = e, this.broadcast = a.from(t?.broadcast), this.target = a.from(t?.target), this.supported = a.from(t?.supported), this.catalog = this.#r.computed((e) => {
678
+ let t = e.get(this.broadcast);
679
+ return t ? e.get(t.catalog)?.audio : void 0;
680
+ }), this.#r.run(this.#i.bind(this)), this.#r.run(this.#a.bind(this));
681
+ }
682
+ #i(e) {
683
+ let t = e.get(this.catalog)?.renditions ?? {}, n = e.get(this.supported);
684
+ n && e.spawn(async () => {
685
+ let e = {};
686
+ for (let [r, i] of Object.entries(t)) await n(i) && (e[r] = i);
687
+ Object.keys(e).length === 0 && Object.keys(t).length > 0 && console.warn("no supported audio renditions found:", t), this.#e.set(e);
688
+ });
689
+ }
690
+ #a(e) {
691
+ let t = e.get(this.#e);
692
+ if (Object.keys(t).length === 0) return;
693
+ let n = e.get(this.target), r;
694
+ if (n?.name && n.name in t) r = {
695
+ track: n.name,
696
+ config: t[n.name]
697
+ };
698
+ else if (r = this.#o(t), !r) return;
699
+ e.set(this.#t, r.track), e.set(this.#n, r.config);
700
+ let i = (r.config.jitter ?? I(r.config) ?? 0) + Math.ceil(ae / r.config.sampleRate * 1e3);
701
+ e.set(this.sync.audio, i);
702
+ }
703
+ #o(e) {
704
+ let t = Object.entries(e);
705
+ if (t.length !== 0) {
706
+ for (let [e, n] of t) if (n.container.kind === "legacy") return {
707
+ track: e,
708
+ config: n
709
+ };
710
+ for (let [e, n] of t) if (n.container.kind === "loc") return {
711
+ track: e,
712
+ config: n
713
+ };
714
+ for (let [e, n] of t) if (n.container.kind === "cmaf") return {
715
+ track: e,
716
+ config: n
717
+ };
718
+ }
719
+ }
720
+ close() {
721
+ this.#r.close();
722
+ }
723
+ };
724
+ function I(e) {
725
+ if (e.codec.startsWith("opus")) return 20;
726
+ if (e.codec.startsWith("mp4a")) return Math.ceil(1024 / e.sampleRate * 1e3);
727
+ }
728
+ //#endregion
729
+ //#region src/audio/index.ts
730
+ var L = /* @__PURE__ */ f({
731
+ Decoder: () => O,
732
+ Emitter: () => M,
733
+ Mse: () => P,
734
+ Source: () => F
735
+ }), R = /* @__PURE__ */ f({ Muxer: () => z }), z = class {
471
736
  element;
472
737
  paused;
473
738
  #e;
@@ -502,11 +767,8 @@ var O = .001, k = .2, A = class {
502
767
  let t = e.get(this.element);
503
768
  if (!t) return;
504
769
  let n = e.get(this.mediaSource);
505
- n && e.interval(async () => {
506
- for (let e of n.sourceBuffers) {
507
- for (; e.updating;) await new Promise((t) => e.addEventListener("updateend", t, { once: !0 }));
508
- t.currentTime > 10 && e.remove(0, t.currentTime - 10);
509
- }
770
+ n && e.interval(() => {
771
+ for (let e of n.sourceBuffers) e.updating || t.currentTime > 10 && e.remove(0, t.currentTime - 10);
510
772
  }, 1e3);
511
773
  }
512
774
  #o(e) {
@@ -528,91 +790,7 @@ var O = .001, k = .2, A = class {
528
790
  close() {
529
791
  this.#n.close();
530
792
  }
531
- }, ae = 20, M = 100, N = class e {
532
- #e = new a(void 0);
533
- reference = this.#e;
534
- latency;
535
- jitter;
536
- audio;
537
- video;
538
- #t = new a(r.Milli.zero);
539
- buffer = this.#t;
540
- #n;
541
- timestamp = new a(void 0);
542
- #r = /* @__PURE__ */ new Map();
543
- #i;
544
- #a;
545
- signals = new i();
546
- constructor(e) {
547
- this.latency = a.from(e?.latency ?? "real-time"), this.jitter = new a(M), this.#i = e?.connection, this.audio = a.from(e?.audio), this.video = a.from(e?.video), this.#n = Promise.withResolvers(), this.signals.run(this.#o.bind(this)), this.signals.run(this.#s.bind(this));
548
- }
549
- #o(e) {
550
- let t = e.get(this.latency);
551
- if (typeof t == "number") {
552
- this.#a = void 0, this.jitter.set(t);
553
- return;
554
- }
555
- let n = (this.#i ? e.get(this.#i) : void 0)?.rtt, r = n ? e.get(n) : void 0;
556
- if (r !== void 0) {
557
- this.#a = this.#a === void 0 ? r : Math.min(this.#a, r);
558
- let e = Math.max(ae, this.#a * 1.25);
559
- this.jitter.set(e);
560
- return;
561
- }
562
- this.#a = void 0, this.jitter.set(M);
563
- }
564
- #s(e) {
565
- let t = e.get(this.jitter), n = e.get(this.video) ?? r.Milli.zero, i = e.get(this.audio) ?? r.Milli.zero, a = r.Milli.add(r.Milli.max(n, i), t);
566
- this.#t.set(a), this.#n.resolve(), this.#n = Promise.withResolvers();
567
- }
568
- received(t, n = "") {
569
- this.timestamp.update((e) => e === void 0 || t > e ? t : e);
570
- let i = r.Milli.now(), a = r.Milli.sub(i, t), o = this.#e.peek();
571
- if (o !== void 0) {
572
- let t = r.Milli.add(r.Milli.sub(o, a), this.#t.peek());
573
- if (t < 0) {
574
- let e = this.#r.get(n);
575
- e ? (e.count++, e.maxMs = Math.max(e.maxMs, -t)) : this.#r.set(n, {
576
- count: 1,
577
- maxMs: -t
578
- });
579
- } else {
580
- let t = this.#r.get(n);
581
- if (t) {
582
- let r = n ? `sync[${n}]` : "sync", i = e.#c(t.maxMs);
583
- console.debug(`${r}: ${t.count} late frame(s), max ${i} behind`), this.#r.delete(n);
584
- }
585
- }
586
- if (a >= o) return;
587
- }
588
- this.#e.set(a), this.#n.resolve(), this.#n = Promise.withResolvers();
589
- }
590
- now() {
591
- let e = this.#e.peek();
592
- if (e !== void 0) return r.Milli.sub(r.Milli.sub(r.Milli.now(), e), this.#t.peek());
593
- }
594
- async wait(e) {
595
- if (this.#e.peek() === void 0) throw Error("reference not set; call update() first");
596
- for (;;) {
597
- let t = r.Milli.now(), n = r.Milli.sub(t, e), i = this.#e.peek();
598
- if (i === void 0) return;
599
- let a = r.Milli.add(r.Milli.sub(i, n), this.#t.peek());
600
- if (a <= 0 || a < 5) return;
601
- let o = new Promise((e) => setTimeout(e, a)).then(() => !0);
602
- if (await Promise.race([this.#n.promise, o])) return;
603
- }
604
- }
605
- static #c(e) {
606
- if (e = Math.round(e), e < 1e3) return `${e}ms`;
607
- let t = e / 1e3;
608
- if (t < 60) return `${Math.round(t * 10) / 10}s`;
609
- let n = t / 60;
610
- return `${Math.round(n * 10) / 10}m`;
611
- }
612
- close() {
613
- this.signals.close();
614
- }
615
- }, oe = 500, se = 100, P = class {
793
+ }, B = 500, V = 100, H = class {
616
794
  enabled;
617
795
  source;
618
796
  #e = new a(void 0);
@@ -635,7 +813,7 @@ var O = .001, k = .2, A = class {
635
813
  }), this.#n.set(void 0);
636
814
  }
637
815
  constructor(e, t) {
638
- this.enabled = a.from(t?.enabled ?? !1), this.source = e, this.source.supported.set(I), this.#s.run(this.#l.bind(this)), this.#s.run(this.#u.bind(this)), this.#s.run(this.#d.bind(this)), this.#s.run(this.#f.bind(this));
816
+ this.enabled = a.from(t?.enabled ?? !1), this.source = e, this.source.supported.set(W), this.#s.run(this.#l.bind(this)), this.#s.run(this.#u.bind(this)), this.#s.run(this.#d.bind(this)), this.#s.run(this.#f.bind(this));
639
817
  }
640
818
  #l(e) {
641
819
  let t = e.getAll([
@@ -653,7 +831,7 @@ var O = .001, k = .2, A = class {
653
831
  this.#e.set(void 0), this.#c(), this.#o.set([]);
654
832
  return;
655
833
  }
656
- let s = new F({
834
+ let s = new U({
657
835
  source: this.source,
658
836
  broadcast: o,
659
837
  track: i,
@@ -665,7 +843,7 @@ var O = .001, k = .2, A = class {
665
843
  let t = e.get(this.#e);
666
844
  if (t) {
667
845
  let n = e.get(s.timestamp), r = e.get(t.timestamp);
668
- if (!n || r && r > n + se) return;
846
+ if (!n || r && r > n + V) return;
669
847
  }
670
848
  this.#e.set(s), s = void 0, e.close();
671
849
  });
@@ -706,13 +884,13 @@ var O = .001, k = .2, A = class {
706
884
  }
707
885
  this.#i.set(!1), e.timer(() => {
708
886
  this.#i.set(!0);
709
- }, oe);
887
+ }, B);
710
888
  }
711
889
  }
712
890
  close() {
713
891
  this.#c(), this.#s.close();
714
892
  }
715
- }, F = class {
893
+ }, U = class {
716
894
  source;
717
895
  broadcast;
718
896
  track;
@@ -722,23 +900,24 @@ var O = .001, k = .2, A = class {
722
900
  frame = new a(void 0);
723
901
  buffered = new a([]);
724
902
  #e = new a([]);
903
+ #t = 0;
725
904
  signals = new i();
726
905
  constructor(e) {
727
906
  let { codedWidth: t, codedHeight: n, ...r } = e.config;
728
- this.source = e.source, this.broadcast = e.broadcast, this.track = e.track, this.config = r, this.stats = e.stats, this.signals.run(this.#t.bind(this));
907
+ this.source = e.source, this.broadcast = e.broadcast, this.track = e.track, this.config = r, this.stats = e.stats, this.signals.run(this.#n.bind(this));
729
908
  }
730
- #t(e) {
909
+ #n(e) {
731
910
  let t = this.broadcast.subscribe(this.track, o.PRIORITY.video);
732
911
  e.cleanup(() => t.close());
733
912
  let n = new VideoDecoder({
734
913
  output: async (t) => {
735
914
  try {
736
- let n = r.Milli.fromMicro(t.timestamp);
737
- if (n < (this.timestamp.peek() ?? 0)) return;
915
+ let n = this.#t, i = r.Milli.fromMicro(t.timestamp);
916
+ if (i < (this.timestamp.peek() ?? 0)) return;
738
917
  this.frame.peek() === void 0 && this.frame.set(t.clone());
739
- let i = this.source.sync.wait(n).then(() => !0);
740
- if (!await Promise.race([i, e.cancel]) || n < (this.timestamp.peek() ?? 0)) return;
741
- this.timestamp.set(n), this.#a(n), this.frame.update((e) => (e?.close(), t.clone()));
918
+ let a = this.source.sync.wait(i).then(() => !0);
919
+ if (!await Promise.race([a, e.cancel]) || n !== this.#t || i < (this.timestamp.peek() ?? 0)) return;
920
+ this.timestamp.set(i), this.#s(i), this.frame.update((e) => (e?.close(), t.clone()));
742
921
  } finally {
743
922
  t.close();
744
923
  }
@@ -749,9 +928,9 @@ var O = .001, k = .2, A = class {
749
928
  });
750
929
  e.cleanup(() => {
751
930
  n.state !== "closed" && n.close();
752
- }), this.config.container.kind === "cmaf" ? this.#r(e, t, n) : this.#n(e, t, n);
931
+ }), this.config.container.kind === "cmaf" ? this.#i(e, t, n) : this.#r(e, t, n);
753
932
  }
754
- #n(e, t, n) {
933
+ #r(e, t, n) {
755
934
  let i = this.config.container.kind === "loc" ? new c.Loc.Format() : new c.Legacy.Format(), a = new c.Consumer(t, {
756
935
  format: i,
757
936
  latency: this.source.sync.buffer
@@ -770,6 +949,7 @@ var O = .001, k = .2, A = class {
770
949
  for (;;) {
771
950
  let e = await a.next();
772
951
  if (!e) break;
952
+ this.#a(e.discontinuity) && (o = void 0);
773
953
  let { frame: t, group: i } = e;
774
954
  if (!t) {
775
955
  o && (o.final = !0);
@@ -789,7 +969,7 @@ var O = .001, k = .2, A = class {
789
969
  let l = o;
790
970
  if (l && (l.group === i || l.final && l.group + 1 === i)) {
791
971
  let e = r.Milli.fromMicro(l.timestamp), n = r.Milli.fromMicro(t.timestamp);
792
- this.#i(e, n);
972
+ this.#o(e, n);
793
973
  }
794
974
  o = {
795
975
  timestamp: t.timestamp,
@@ -799,7 +979,7 @@ var O = .001, k = .2, A = class {
799
979
  }
800
980
  });
801
981
  }
802
- #r(e, t, n) {
982
+ #i(e, t, n) {
803
983
  if (this.config.container.kind !== "cmaf") return;
804
984
  let i = p(this.config.container.init), a = c.Cmaf.decodeInitSegment(i), o = this.config.description ? l.Hex.toBytes(this.config.description) : a.description, s = new c.Consumer(t, {
805
985
  format: new c.Cmaf.Format(a),
@@ -819,6 +999,7 @@ var O = .001, k = .2, A = class {
819
999
  for (;;) {
820
1000
  let e = await s.next();
821
1001
  if (!e) break;
1002
+ this.#a(e.discontinuity) && (u = void 0);
822
1003
  let { frame: t, group: i } = e;
823
1004
  if (!t) {
824
1005
  u && (u.final = !0);
@@ -832,7 +1013,7 @@ var O = .001, k = .2, A = class {
832
1013
  let o = u;
833
1014
  if (o && (o.group === i || o.final && o.group + 1 === i)) {
834
1015
  let e = r.Milli.fromMicro(o.timestamp), n = r.Milli.fromMicro(t.timestamp);
835
- this.#i(e, n);
1016
+ this.#o(e, n);
836
1017
  }
837
1018
  if (u = {
838
1019
  timestamp: t.timestamp,
@@ -847,7 +1028,10 @@ var O = .001, k = .2, A = class {
847
1028
  }
848
1029
  });
849
1030
  }
850
- #i(e, t) {
1031
+ #a(e) {
1032
+ return e === this.#t ? !1 : (this.#t = e, this.timestamp.set(void 0), this.#e.set([]), this.source.sync.reset(), !0);
1033
+ }
1034
+ #o(e, t) {
851
1035
  e > t || this.#e.mutate((n) => {
852
1036
  for (let i of n) if (i.start <= t && i.end >= e) {
853
1037
  i.start = r.Milli.min(i.start, e), i.end = r.Milli.max(i.end, t);
@@ -859,7 +1043,7 @@ var O = .001, k = .2, A = class {
859
1043
  }), n.sort((e, t) => e.start - t.start);
860
1044
  });
861
1045
  }
862
- #a(e) {
1046
+ #s(e) {
863
1047
  this.#e.mutate((t) => {
864
1048
  for (; t.length > 0;) {
865
1049
  if (t[0].end >= e) {
@@ -876,7 +1060,7 @@ var O = .001, k = .2, A = class {
876
1060
  });
877
1061
  }
878
1062
  };
879
- async function I(e) {
1063
+ async function W(e) {
880
1064
  let t;
881
1065
  if (e.description) t = l.Hex.toBytes(e.description);
882
1066
  else if (e.container.kind === "cmaf") try {
@@ -902,7 +1086,7 @@ async function I(e) {
902
1086
  }
903
1087
  //#endregion
904
1088
  //#region src/video/mse.ts
905
- var L = class {
1089
+ var G = class {
906
1090
  muxer;
907
1091
  source;
908
1092
  #e = new a(void 0);
@@ -915,7 +1099,7 @@ var L = class {
915
1099
  timestamp = this.#r;
916
1100
  signals = new i();
917
1101
  constructor(e, t) {
918
- this.muxer = e, this.source = t, this.source.supported.set(R), this.signals.run(this.#i.bind(this)), this.signals.run(this.#c.bind(this)), this.signals.run(this.#l.bind(this));
1102
+ this.muxer = e, this.source = t, this.source.supported.set(K), this.signals.run(this.#i.bind(this)), this.signals.run(this.#c.bind(this)), this.signals.run(this.#l.bind(this));
919
1103
  }
920
1104
  #i(e) {
921
1105
  let t = e.get(this.muxer.element);
@@ -936,7 +1120,7 @@ var L = class {
936
1120
  }), e.event(c, "error", (e) => {
937
1121
  console.error("[MSE] SourceBuffer error:", e);
938
1122
  }), e.event(c, "updateend", () => {
939
- this.#t.set(q(c.buffered));
1123
+ this.#t.set(N(c.buffered));
940
1124
  }), o.container.kind === "cmaf" ? this.#o(e, i, a, o, c, t) : this.#s(e, i, a, o, c, t);
941
1125
  }
942
1126
  async #a(e, t) {
@@ -1023,12 +1207,12 @@ var L = class {
1023
1207
  this.source.close(), this.signals.close();
1024
1208
  }
1025
1209
  };
1026
- async function R(e) {
1210
+ async function K(e) {
1027
1211
  return MediaSource.isTypeSupported(`video/mp4; codecs="${e.codec}"`);
1028
1212
  }
1029
1213
  //#endregion
1030
1214
  //#region src/video/renderer.ts
1031
- var z = .01, B = class {
1215
+ var q = .01, J = class {
1032
1216
  decoder;
1033
1217
  canvas;
1034
1218
  paused;
@@ -1072,11 +1256,11 @@ var z = .01, B = class {
1072
1256
  }, o;
1073
1257
  try {
1074
1258
  o = new IntersectionObserver(a, {
1075
- threshold: z,
1259
+ threshold: q,
1076
1260
  rootMargin: t
1077
1261
  });
1078
1262
  } catch {
1079
- console.warn(`moq-watch: invalid visible margin "${t}", using "0px"`), o = new IntersectionObserver(a, { threshold: z });
1263
+ console.warn(`moq-watch: invalid visible margin "${t}", using "0px"`), o = new IntersectionObserver(a, { threshold: q });
1080
1264
  }
1081
1265
  i(), e.event(document, "visibilitychange", i), o.observe(n), e.cleanup(() => o.disconnect()), e.cleanup(() => this.#t.set(!1));
1082
1266
  }
@@ -1116,7 +1300,7 @@ var z = .01, B = class {
1116
1300
  };
1117
1301
  //#endregion
1118
1302
  //#region src/video/source.ts
1119
- function V(e) {
1303
+ function oe(e) {
1120
1304
  return (t) => {
1121
1305
  let n = [], r = [];
1122
1306
  for (let [i, a] of t) if (a.codedWidth && a.codedHeight) {
@@ -1132,7 +1316,7 @@ function V(e) {
1132
1316
  return n.sort((e, t) => t.size - e.size), n.length > 0 ? n.map((e) => e.name) : r.length > 0 ? (r.sort((e, t) => e.size - t.size), [r[0].name]) : t.map(([e]) => e);
1133
1317
  };
1134
1318
  }
1135
- function H(e, t) {
1319
+ function se(e, t) {
1136
1320
  return (n) => {
1137
1321
  let r = [], i = [];
1138
1322
  for (let [a, o] of n) {
@@ -1149,7 +1333,7 @@ function H(e, t) {
1149
1333
  return r.sort((e, t) => t.size - e.size), r.length > 0 ? r.map((e) => e.name) : i.length > 0 ? (i.sort((e, t) => e.size - t.size), [i[0].name]) : n.map(([e]) => e);
1150
1334
  };
1151
1335
  }
1152
- function U(e) {
1336
+ function ce(e) {
1153
1337
  return (t) => {
1154
1338
  let n = [], r = [];
1155
1339
  for (let [i, a] of t) a.bitrate != null && a.bitrate <= e ? n.push({
@@ -1162,7 +1346,7 @@ function U(e) {
1162
1346
  return n.sort((e, t) => t.bitrate - e.bitrate), n.length > 0 ? n.map((e) => e.name) : r.length > 0 ? (r.sort((e, t) => e.bitrate - t.bitrate), [r[0].name]) : t.map(([e]) => e);
1163
1347
  };
1164
1348
  }
1165
- function W(e) {
1349
+ function le(e) {
1166
1350
  let t = e[0];
1167
1351
  for (let n of e) {
1168
1352
  let [, e] = n, [, r] = t, i = (e.codedWidth ?? 0) * (e.codedHeight ?? 0), a = (r.codedWidth ?? 0) * (r.codedHeight ?? 0);
@@ -1174,7 +1358,7 @@ function W(e) {
1174
1358
  }
1175
1359
  return t[0];
1176
1360
  }
1177
- var G = class {
1361
+ var Y = class {
1178
1362
  broadcast;
1179
1363
  target;
1180
1364
  catalog;
@@ -1238,7 +1422,7 @@ var G = class {
1238
1422
  if (n.length === 0) return;
1239
1423
  if (n.length === 1) return n[0][0];
1240
1424
  let r = [];
1241
- if (t?.pixels != null && r.push(V(t.pixels)), (t?.width != null || t?.height != null) && r.push(H(t.width, t.height)), t?.bitrate != null && r.push(U(t.bitrate)), r.length === 0) return W(n);
1425
+ if (t?.pixels != null && r.push(oe(t.pixels)), (t?.width != null || t?.height != null) && r.push(se(t.width, t.height)), t?.bitrate != null && r.push(ce(t.bitrate)), r.length === 0) return le(n);
1242
1426
  let i = r.map((e) => e(n)), a = i.map((e) => new Set(e));
1243
1427
  for (let e of i[0]) if (a.every((t) => t.has(e))) return e;
1244
1428
  console.warn("conflicting rendition filters, no rendition satisfies all criteria");
@@ -1246,26 +1430,12 @@ var G = class {
1246
1430
  close() {
1247
1431
  this.#r.close();
1248
1432
  }
1249
- }, K = /* @__PURE__ */ f({
1250
- Decoder: () => P,
1251
- Mse: () => L,
1252
- Renderer: () => B,
1253
- Source: () => G
1254
- });
1255
- //#endregion
1256
- //#region src/backend.ts
1257
- function q(e) {
1258
- let n = [];
1259
- for (let r = 0; r < e.length; r++) {
1260
- let i = t.Time.Milli.fromSecond(e.start(r)), a = t.Time.Milli.fromSecond(e.end(r));
1261
- n.push({
1262
- start: i,
1263
- end: a
1264
- });
1265
- }
1266
- return n;
1267
- }
1268
- var ce = class {
1433
+ }, ue = /* @__PURE__ */ f({
1434
+ Decoder: () => H,
1435
+ Mse: () => G,
1436
+ Renderer: () => J,
1437
+ Source: () => Y
1438
+ }), de = class {
1269
1439
  source;
1270
1440
  stats = new a(void 0);
1271
1441
  stalled = new a(!1);
@@ -1274,7 +1444,7 @@ var ce = class {
1274
1444
  constructor(e) {
1275
1445
  this.source = e;
1276
1446
  }
1277
- }, le = class {
1447
+ }, fe = class {
1278
1448
  source;
1279
1449
  volume = new a(.5);
1280
1450
  muted = new a(!1);
@@ -1284,48 +1454,52 @@ var ce = class {
1284
1454
  constructor(e) {
1285
1455
  this.source = e;
1286
1456
  }
1287
- }, ue = class {
1457
+ }, pe = class {
1288
1458
  element = new a(void 0);
1289
1459
  broadcast;
1290
1460
  latency;
1291
1461
  jitter;
1462
+ buffered;
1292
1463
  paused;
1293
1464
  visible;
1294
1465
  video;
1295
1466
  #e;
1296
1467
  audio;
1297
1468
  #t;
1469
+ #n;
1298
1470
  sync;
1299
1471
  signals = new i();
1300
- constructor(e) {
1301
- this.element = a.from(e?.element), this.broadcast = a.from(e?.broadcast), this.sync = new N({
1302
- latency: e?.latency,
1303
- connection: e?.connection
1304
- }), this.latency = this.sync.latency, this.jitter = this.sync.jitter, this.#e = new G(this.sync, { broadcast: this.broadcast }), this.#t = new Y(this.sync, { broadcast: this.broadcast }), this.video = new ce(this.#e), this.audio = new le(this.#t), this.paused = a.from(e?.paused ?? !1), this.visible = a.from(e?.visible ?? "0px"), this.signals.run(this.#n.bind(this));
1472
+ constructor(t) {
1473
+ this.element = a.from(t?.element), this.broadcast = a.from(t?.broadcast), this.sync = new e({
1474
+ latency: t?.latency,
1475
+ connection: t?.connection
1476
+ }), this.latency = this.sync.latency, this.jitter = this.sync.jitter, this.buffered = this.sync.buffered, this.#e = new Y(this.sync, { broadcast: this.broadcast }), this.#t = new F(this.sync, { broadcast: this.broadcast }), this.video = new de(this.#e), this.audio = new fe(this.#t), this.paused = a.from(t?.paused ?? !1), this.visible = a.from(t?.visible ?? "0px"), this.signals.run(this.#r.bind(this));
1305
1477
  }
1306
- #n(e) {
1478
+ #r(e) {
1307
1479
  let t = e.get(this.element);
1308
- t && (t instanceof HTMLCanvasElement ? this.#r(e, t) : t instanceof HTMLVideoElement && this.#i(e, t));
1480
+ t && (t instanceof HTMLCanvasElement ? this.#i(e, t) : t instanceof HTMLVideoElement && this.#a(e, t));
1309
1481
  }
1310
- #r(e, t) {
1311
- let n = new P(this.#e), r = new E(this.#t), i = new A(r, {
1482
+ #i(e, t) {
1483
+ let n = new H(this.#e), r = new O(this.#t);
1484
+ this.#n = r;
1485
+ let i = new M(r, {
1312
1486
  volume: this.audio.volume,
1313
1487
  muted: this.audio.muted,
1314
1488
  paused: this.paused
1315
- }), a = new B(n, {
1489
+ }), a = new J(n, {
1316
1490
  canvas: t,
1317
1491
  paused: this.paused,
1318
1492
  visible: this.visible
1319
1493
  });
1320
1494
  e.cleanup(() => {
1321
- n.close(), r.close(), i.close(), a.close();
1495
+ n.close(), r.close(), i.close(), a.close(), this.#n = void 0;
1322
1496
  }), e.proxy(this.video.stats, n.stats), e.proxy(this.video.buffered, n.buffered), e.proxy(this.video.stalled, n.stalled), e.proxy(this.video.timestamp, n.timestamp), e.proxy(this.audio.stats, r.stats), e.proxy(this.audio.buffered, r.buffered), e.proxy(this.audio.context, r.context);
1323
1497
  }
1324
- #i(e, t) {
1325
- let n = new j(this.sync, {
1498
+ #a(e, t) {
1499
+ let n = new z(this.sync, {
1326
1500
  paused: this.paused,
1327
1501
  element: t
1328
- }), r = new L(n, this.#e), i = new J(n, this.#t, {
1502
+ }), r = new G(n, this.#e), i = new P(n, this.#t, {
1329
1503
  volume: this.audio.volume,
1330
1504
  muted: this.audio.muted
1331
1505
  });
@@ -1333,198 +1507,19 @@ var ce = class {
1333
1507
  r.close(), i.close(), n.close();
1334
1508
  }), e.proxy(this.video.stats, r.stats), e.proxy(this.video.buffered, r.buffered), e.proxy(this.video.stalled, r.stalled), e.proxy(this.video.timestamp, r.timestamp), e.proxy(this.audio.stats, i.stats), e.proxy(this.audio.buffered, i.buffered), e.proxy(this.audio.context, i.context);
1335
1509
  }
1336
- close() {
1337
- this.signals.close(), this.#e.close(), this.#t.close(), this.sync.close();
1338
- }
1339
- }, J = class {
1340
- muxer;
1341
- source;
1342
- volume;
1343
- muted;
1344
- #e = new a(void 0);
1345
- stats = this.#e;
1346
- #t = new a([]);
1347
- buffered = this.#t;
1348
- context = new a(void 0);
1349
- #n = new i();
1350
- constructor(e, t, n) {
1351
- this.muxer = e, this.source = t, this.source.supported.set(de), this.volume = a.from(n?.volume ?? .5), this.muted = a.from(n?.muted ?? !1), this.#n.run(this.#r.bind(this)), this.#n.run(this.#s.bind(this));
1352
- }
1353
- #r(e) {
1354
- let t = e.get(this.muxer.element);
1355
- if (!t) return;
1356
- let n = e.get(this.muxer.mediaSource);
1357
- if (!n) return;
1358
- let r = e.get(this.source.broadcast);
1359
- if (!r) return;
1360
- let i = e.get(r.active);
1361
- if (!i) return;
1362
- let a = e.get(this.source.track);
1363
- if (!a) return;
1364
- let s = e.get(this.source.config);
1365
- if (!s) return;
1366
- let c = `audio/mp4; codecs="${s.codec}"`, l = n.addSourceBuffer(c);
1367
- e.cleanup(() => {
1368
- n.removeSourceBuffer(l), l.abort();
1369
- }), e.event(l, "error", (e) => {
1370
- console.error("[MSE] SourceBuffer error:", e);
1371
- }), e.event(l, "updateend", () => {
1372
- this.#t.set(q(l.buffered));
1373
- });
1374
- let u = i.subscribe(a, o.PRIORITY.audio);
1375
- e.cleanup(() => u.close()), s.container.kind === "cmaf" ? this.#a(e, u, s, l, t) : this.#o(e, u, s, l, t);
1376
- }
1377
- async #i(e, t) {
1378
- for (; e.updating;) await new Promise((t) => e.addEventListener("updateend", t, { once: !0 }));
1379
- for (e.appendBuffer(t); e.updating;) await new Promise((t) => e.addEventListener("updateend", t, { once: !0 }));
1380
- }
1381
- #a(e, n, r, i, a) {
1382
- if (r.container.kind !== "cmaf") throw Error("unreachable");
1383
- let o = p(r.container.init), s = c.Cmaf.decodeInitSegment(o);
1384
- e.spawn(async () => {
1385
- for (await this.#i(i, o);;) {
1386
- let e = await n.readFrame();
1387
- if (!e) return;
1388
- let r = c.Cmaf.decodeTimestamp(e, s);
1389
- this.source.sync.received(t.Time.Milli.fromMicro(r), "audio"), await this.#i(i, e), a.buffered.length > 0 && a.currentTime < a.buffered.start(0) && (a.currentTime = a.buffered.start(0));
1390
- }
1391
- });
1392
- }
1393
- #o(e, n, r, i, a) {
1394
- let o = r.container.kind === "loc" ? new c.Loc.Format() : new c.Legacy.Format(), s = new c.Consumer(n, {
1395
- format: o,
1396
- latency: this.source.sync.buffer
1397
- });
1398
- e.cleanup(() => s.close()), e.spawn(async () => {
1399
- let e = c.Cmaf.createAudioInitSegment(r);
1400
- await this.#i(i, e);
1401
- let n = 1, o, l;
1402
- for (;;) {
1403
- let e = await s.next();
1404
- if (!e) return;
1405
- if (!e.frame) continue;
1406
- l = e.frame;
1407
- let n = t.Time.Milli.fromMicro(l.timestamp);
1408
- this.source.sync.received(n, "audio");
1409
- break;
1410
- }
1411
- for (;;) {
1412
- let e = await s.next();
1413
- if (e && !e.frame) continue;
1414
- let r = e?.frame;
1415
- if (r) {
1416
- o = t.Time.Micro.sub(r.timestamp, l.timestamp);
1417
- let e = t.Time.Milli.fromMicro(r.timestamp);
1418
- this.source.sync.received(e, "audio");
1419
- }
1420
- let u = c.Cmaf.encodeDataSegment({
1421
- data: l.data,
1422
- timestamp: l.timestamp,
1423
- duration: o ?? 0,
1424
- keyframe: l.keyframe,
1425
- sequence: n++
1426
- });
1427
- if (await this.#i(i, u), a.buffered.length > 0 && a.currentTime < a.buffered.start(0) && (a.currentTime = a.buffered.start(0)), !r) return;
1428
- l = r;
1429
- }
1430
- });
1431
- }
1432
- #s(e) {
1433
- let t = e.get(this.muxer.element);
1434
- if (!t) return;
1435
- let n = e.get(this.volume), r = e.get(this.muted);
1436
- r && !t.muted ? t.muted = !0 : !r && t.muted && (t.muted = !1), n !== t.volume && (t.volume = n), e.event(t, "volumechange", () => {
1437
- this.volume.set(t.volume);
1438
- });
1439
- }
1440
- close() {
1441
- this.#n.close();
1442
- }
1443
- };
1444
- async function de(e) {
1445
- return MediaSource.isTypeSupported(`audio/mp4; codecs="${e.codec}"`);
1446
- }
1447
- //#endregion
1448
- //#region src/audio/source.ts
1449
- var fe = 128, Y = class {
1450
- broadcast;
1451
- target;
1452
- catalog;
1453
- #e = new a({});
1454
- available = this.#e;
1455
- #t = new a(void 0);
1456
- track = this.#t;
1457
- #n = new a(void 0);
1458
- config = this.#n;
1459
- supported;
1460
- sync;
1461
- #r = new i();
1462
- constructor(e, t) {
1463
- this.sync = e, this.broadcast = a.from(t?.broadcast), this.target = a.from(t?.target), this.supported = a.from(t?.supported), this.catalog = this.#r.computed((e) => {
1464
- let t = e.get(this.broadcast);
1465
- return t ? e.get(t.catalog)?.audio : void 0;
1466
- }), this.#r.run(this.#i.bind(this)), this.#r.run(this.#a.bind(this));
1467
- }
1468
- #i(e) {
1469
- let t = e.get(this.catalog)?.renditions ?? {}, n = e.get(this.supported);
1470
- n && e.spawn(async () => {
1471
- let e = {};
1472
- for (let [r, i] of Object.entries(t)) await n(i) && (e[r] = i);
1473
- Object.keys(e).length === 0 && Object.keys(t).length > 0 && console.warn("no supported audio renditions found:", t), this.#e.set(e);
1474
- });
1475
- }
1476
- #a(e) {
1477
- let t = e.get(this.#e);
1478
- if (Object.keys(t).length === 0) return;
1479
- let n = e.get(this.target), r;
1480
- if (n?.name && n.name in t) r = {
1481
- track: n.name,
1482
- config: t[n.name]
1483
- };
1484
- else if (r = this.#o(t), !r) return;
1485
- e.set(this.#t, r.track), e.set(this.#n, r.config);
1486
- let i = (r.config.jitter ?? pe(r.config) ?? 0) + Math.ceil(fe / r.config.sampleRate * 1e3);
1487
- e.set(this.sync.audio, i);
1488
- }
1489
- #o(e) {
1490
- let t = Object.entries(e);
1491
- if (t.length !== 0) {
1492
- for (let [e, n] of t) if (n.container.kind === "legacy") return {
1493
- track: e,
1494
- config: n
1495
- };
1496
- for (let [e, n] of t) if (n.container.kind === "loc") return {
1497
- track: e,
1498
- config: n
1499
- };
1500
- for (let [e, n] of t) if (n.container.kind === "cmaf") return {
1501
- track: e,
1502
- config: n
1503
- };
1504
- }
1510
+ reset() {
1511
+ this.sync.reset(), this.#n?.reset();
1505
1512
  }
1506
1513
  close() {
1507
- this.#r.close();
1514
+ this.signals.close(), this.#e.close(), this.#t.close(), this.sync.close();
1508
1515
  }
1509
- };
1510
- function pe(e) {
1511
- if (e.codec.startsWith("opus")) return 20;
1512
- if (e.codec.startsWith("mp4a")) return Math.ceil(1024 / e.sampleRate * 1e3);
1513
- }
1514
- //#endregion
1515
- //#region src/audio/index.ts
1516
- var me = /* @__PURE__ */ f({
1517
- Decoder: () => E,
1518
- Emitter: () => A,
1519
- Mse: () => J,
1520
- Source: () => Y
1521
- }), he = 48e3, X = 2;
1522
- function Z(e) {
1516
+ }, me = 48e3, X = 2;
1517
+ function he(e) {
1523
1518
  let t = "";
1524
1519
  for (let n = 0; n < e.length; n++) t += e[n].toString(16).padStart(2, "0");
1525
1520
  return t;
1526
1521
  }
1527
- function Q(e) {
1522
+ function Z(e) {
1528
1523
  let t;
1529
1524
  try {
1530
1525
  t = e.initData ? p(e.initData) : void 0;
@@ -1539,12 +1534,12 @@ function Q(e) {
1539
1534
  description: void 0
1540
1535
  } : {
1541
1536
  container: { kind: "legacy" },
1542
- description: t ? Z(t) : void 0
1537
+ description: t ? he(t) : void 0
1543
1538
  };
1544
1539
  }
1545
1540
  function ge(e) {
1546
1541
  if (!e.codec) return;
1547
- let { container: t, description: n } = Q(e);
1542
+ let { container: t, description: n } = Z(e);
1548
1543
  return {
1549
1544
  codec: e.codec,
1550
1545
  container: t,
@@ -1562,18 +1557,18 @@ function _e(e) {
1562
1557
  if (!e.channelConfig) return X;
1563
1558
  let t = Number.parseInt(e.channelConfig, 10);
1564
1559
  return Number.isFinite(t) ? t : X;
1565
- })(), { container: n, description: r } = Q(e);
1560
+ })(), { container: n, description: r } = Z(e);
1566
1561
  return {
1567
1562
  codec: e.codec,
1568
1563
  container: n,
1569
1564
  description: r,
1570
- sampleRate: s(e.samplerate ?? he),
1565
+ sampleRate: s(e.samplerate ?? me),
1571
1566
  numberOfChannels: s(t),
1572
1567
  bitrate: e.bitrate == null ? void 0 : s(e.bitrate),
1573
1568
  jitter: e.jitter == null ? void 0 : s(e.jitter)
1574
1569
  };
1575
1570
  }
1576
- function ve(e) {
1571
+ function Q(e) {
1577
1572
  let t = {}, n = {};
1578
1573
  for (let r of e.tracks) if (r.role === "video") {
1579
1574
  let e = ge(r);
@@ -1587,11 +1582,15 @@ function ve(e) {
1587
1582
  }
1588
1583
  //#endregion
1589
1584
  //#region src/broadcast.ts
1590
- var $ = [...o.FORMATS, "manual"];
1591
- function ye(e) {
1585
+ var $ = [
1586
+ ...o.FORMATS,
1587
+ "hangz",
1588
+ "manual"
1589
+ ];
1590
+ function ve(e) {
1592
1591
  if (e !== null) return $.find((t) => t === e);
1593
1592
  }
1594
- var be = class {
1593
+ var ye = class {
1595
1594
  connection;
1596
1595
  enabled;
1597
1596
  name;
@@ -1626,33 +1625,33 @@ var be = class {
1626
1625
  let n = e.get(this.name), r = t.consume(n);
1627
1626
  e.cleanup(() => r.close()), e.set(this.#e, r, void 0);
1628
1627
  }
1629
- #a(t) {
1630
- if (!t.get(this.enabled)) return;
1631
- let n = t.get(this.catalogFormat), r = t.get(this.name), i = n ?? o.detectFormat(r) ?? o.DEFAULT_FORMAT;
1632
- if (i === "manual") {
1633
- let e = t.get(this.catalog);
1634
- this.status.set(e ? "live" : "loading");
1628
+ #a(e) {
1629
+ if (!e.get(this.enabled)) return;
1630
+ let t = e.get(this.catalogFormat), n = e.get(this.name), r = t ?? o.detectFormat(n) ?? o.DEFAULT_FORMAT;
1631
+ if (r === "manual") {
1632
+ let t = e.get(this.catalog);
1633
+ this.status.set(t ? "live" : "loading");
1635
1634
  return;
1636
1635
  }
1637
- let a = t.get(this.active);
1638
- if (!a) return;
1636
+ let i = e.get(this.active);
1637
+ if (!i) return;
1639
1638
  this.status.set("loading");
1640
- let s = i === "hang" ? "catalog.json" : "catalog", c = a.subscribe(s, o.PRIORITY.catalog);
1641
- t.cleanup(() => c.close());
1642
- let l;
1643
- if (i === "hang") {
1644
- let t = new e.Consumer(c, { schema: o.RootSchema });
1645
- l = () => t.next();
1646
- } else l = async () => {
1647
- let e = await u.fetch(c);
1648
- return e ? ve(e) : void 0;
1639
+ let a = r === "hang" ? o.TRACK : r === "hangz" ? o.TRACK_COMPRESSED : "catalog", s = i.subscribe(a, o.PRIORITY.catalog);
1640
+ e.cleanup(() => s.close());
1641
+ let c;
1642
+ if (r === "hang" || r === "hangz") {
1643
+ let e = new o.Consumer(s, { compression: r === "hangz" });
1644
+ c = () => e.next();
1645
+ } else c = async () => {
1646
+ let e = await u.fetch(s);
1647
+ return e ? Q(e) : void 0;
1649
1648
  };
1650
- t.spawn(async () => {
1649
+ e.spawn(async () => {
1651
1650
  try {
1652
1651
  for (;;) {
1653
- let e = await Promise.race([t.cancel, l()]);
1654
- if (!e) break;
1655
- console.debug("received catalog", i, this.name.peek(), e), this.catalog.set(e), this.status.set("live");
1652
+ let t = await Promise.race([e.cancel, c()]);
1653
+ if (!t) break;
1654
+ console.debug("received catalog", r, this.name.peek(), t), this.catalog.set(t), this.status.set("live");
1656
1655
  }
1657
1656
  } catch (e) {
1658
1657
  console.warn("error fetching catalog", this.name.peek(), e);
@@ -1675,6 +1674,6 @@ var be = class {
1675
1674
  }
1676
1675
  };
1677
1676
  //#endregion
1678
- export { ue as a, N as c, me as i, ie as l, $ as n, q as o, ye as r, K as s, be as t };
1677
+ export { ue as a, N as c, pe as i, $ as n, R as o, ve as r, L as s, ye as t };
1679
1678
 
1680
- //# sourceMappingURL=broadcast-80k1hgfM.js.map
1679
+ //# sourceMappingURL=broadcast-oQKyLEQ1.js.map