@moq/watch 0.2.17 → 0.2.19

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 (57) hide show
  1. package/README.md +4 -3
  2. package/audio/backend.d.ts +11 -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 +8 -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 +22 -18
  19. package/backend.d.ts.map +1 -1
  20. package/{broadcast-80k1hgfM.js → broadcast-Duw8w3D_.js} +446 -439
  21. package/broadcast-Duw8w3D_.js.map +1 -0
  22. package/broadcast.d.ts +15 -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 +65 -36
  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 +10 -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 +1 -1
  55. package/video/renderer.d.ts.map +1 -1
  56. package/video/source.d.ts.map +1 -1
  57. 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 __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, { get: all[name], enumerable: true });\n};\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// 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,215 @@ 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
+ if (e.codec === "mp3") {
728
+ let t = e.sampleRate >= 32e3 ? 1152 : 576;
729
+ return Math.ceil(t / e.sampleRate * 1e3);
730
+ }
731
+ }
732
+ //#endregion
733
+ //#region src/audio/index.ts
734
+ var L = /* @__PURE__ */ f({
735
+ Decoder: () => O,
736
+ Emitter: () => M,
737
+ Mse: () => P,
738
+ Source: () => F
739
+ }), R = /* @__PURE__ */ f({ Muxer: () => z }), z = class {
471
740
  element;
472
741
  paused;
473
742
  #e;
@@ -502,11 +771,8 @@ var O = .001, k = .2, A = class {
502
771
  let t = e.get(this.element);
503
772
  if (!t) return;
504
773
  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
- }
774
+ n && e.interval(() => {
775
+ for (let e of n.sourceBuffers) e.updating || t.currentTime > 10 && e.remove(0, t.currentTime - 10);
510
776
  }, 1e3);
511
777
  }
512
778
  #o(e) {
@@ -528,91 +794,7 @@ var O = .001, k = .2, A = class {
528
794
  close() {
529
795
  this.#n.close();
530
796
  }
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 {
797
+ }, B = 500, V = 100, H = class {
616
798
  enabled;
617
799
  source;
618
800
  #e = new a(void 0);
@@ -635,7 +817,7 @@ var O = .001, k = .2, A = class {
635
817
  }), this.#n.set(void 0);
636
818
  }
637
819
  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));
820
+ 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
821
  }
640
822
  #l(e) {
641
823
  let t = e.getAll([
@@ -653,7 +835,7 @@ var O = .001, k = .2, A = class {
653
835
  this.#e.set(void 0), this.#c(), this.#o.set([]);
654
836
  return;
655
837
  }
656
- let s = new F({
838
+ let s = new U({
657
839
  source: this.source,
658
840
  broadcast: o,
659
841
  track: i,
@@ -665,7 +847,7 @@ var O = .001, k = .2, A = class {
665
847
  let t = e.get(this.#e);
666
848
  if (t) {
667
849
  let n = e.get(s.timestamp), r = e.get(t.timestamp);
668
- if (!n || r && r > n + se) return;
850
+ if (!n || r && r > n + V) return;
669
851
  }
670
852
  this.#e.set(s), s = void 0, e.close();
671
853
  });
@@ -706,13 +888,13 @@ var O = .001, k = .2, A = class {
706
888
  }
707
889
  this.#i.set(!1), e.timer(() => {
708
890
  this.#i.set(!0);
709
- }, oe);
891
+ }, B);
710
892
  }
711
893
  }
712
894
  close() {
713
895
  this.#c(), this.#s.close();
714
896
  }
715
- }, F = class {
897
+ }, U = class {
716
898
  source;
717
899
  broadcast;
718
900
  track;
@@ -722,23 +904,24 @@ var O = .001, k = .2, A = class {
722
904
  frame = new a(void 0);
723
905
  buffered = new a([]);
724
906
  #e = new a([]);
907
+ #t = 0;
725
908
  signals = new i();
726
909
  constructor(e) {
727
910
  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));
911
+ 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
912
  }
730
- #t(e) {
913
+ #n(e) {
731
914
  let t = this.broadcast.subscribe(this.track, o.PRIORITY.video);
732
915
  e.cleanup(() => t.close());
733
916
  let n = new VideoDecoder({
734
917
  output: async (t) => {
735
918
  try {
736
- let n = r.Milli.fromMicro(t.timestamp);
737
- if (n < (this.timestamp.peek() ?? 0)) return;
919
+ let n = this.#t, i = r.Milli.fromMicro(t.timestamp);
920
+ if (i < (this.timestamp.peek() ?? 0)) return;
738
921
  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()));
922
+ let a = this.source.sync.wait(i).then(() => !0);
923
+ if (!await Promise.race([a, e.cancel]) || n !== this.#t || i < (this.timestamp.peek() ?? 0)) return;
924
+ this.timestamp.set(i), this.#s(i), this.frame.update((e) => (e?.close(), t.clone()));
742
925
  } finally {
743
926
  t.close();
744
927
  }
@@ -749,9 +932,9 @@ var O = .001, k = .2, A = class {
749
932
  });
750
933
  e.cleanup(() => {
751
934
  n.state !== "closed" && n.close();
752
- }), this.config.container.kind === "cmaf" ? this.#r(e, t, n) : this.#n(e, t, n);
935
+ }), this.config.container.kind === "cmaf" ? this.#i(e, t, n) : this.#r(e, t, n);
753
936
  }
754
- #n(e, t, n) {
937
+ #r(e, t, n) {
755
938
  let i = this.config.container.kind === "loc" ? new c.Loc.Format() : new c.Legacy.Format(), a = new c.Consumer(t, {
756
939
  format: i,
757
940
  latency: this.source.sync.buffer
@@ -770,6 +953,7 @@ var O = .001, k = .2, A = class {
770
953
  for (;;) {
771
954
  let e = await a.next();
772
955
  if (!e) break;
956
+ this.#a(e.discontinuity) && (o = void 0);
773
957
  let { frame: t, group: i } = e;
774
958
  if (!t) {
775
959
  o && (o.final = !0);
@@ -789,7 +973,7 @@ var O = .001, k = .2, A = class {
789
973
  let l = o;
790
974
  if (l && (l.group === i || l.final && l.group + 1 === i)) {
791
975
  let e = r.Milli.fromMicro(l.timestamp), n = r.Milli.fromMicro(t.timestamp);
792
- this.#i(e, n);
976
+ this.#o(e, n);
793
977
  }
794
978
  o = {
795
979
  timestamp: t.timestamp,
@@ -799,7 +983,7 @@ var O = .001, k = .2, A = class {
799
983
  }
800
984
  });
801
985
  }
802
- #r(e, t, n) {
986
+ #i(e, t, n) {
803
987
  if (this.config.container.kind !== "cmaf") return;
804
988
  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
989
  format: new c.Cmaf.Format(a),
@@ -819,6 +1003,7 @@ var O = .001, k = .2, A = class {
819
1003
  for (;;) {
820
1004
  let e = await s.next();
821
1005
  if (!e) break;
1006
+ this.#a(e.discontinuity) && (u = void 0);
822
1007
  let { frame: t, group: i } = e;
823
1008
  if (!t) {
824
1009
  u && (u.final = !0);
@@ -832,7 +1017,7 @@ var O = .001, k = .2, A = class {
832
1017
  let o = u;
833
1018
  if (o && (o.group === i || o.final && o.group + 1 === i)) {
834
1019
  let e = r.Milli.fromMicro(o.timestamp), n = r.Milli.fromMicro(t.timestamp);
835
- this.#i(e, n);
1020
+ this.#o(e, n);
836
1021
  }
837
1022
  if (u = {
838
1023
  timestamp: t.timestamp,
@@ -847,7 +1032,10 @@ var O = .001, k = .2, A = class {
847
1032
  }
848
1033
  });
849
1034
  }
850
- #i(e, t) {
1035
+ #a(e) {
1036
+ return e === this.#t ? !1 : (this.#t = e, this.timestamp.set(void 0), this.#e.set([]), this.source.sync.reset(), !0);
1037
+ }
1038
+ #o(e, t) {
851
1039
  e > t || this.#e.mutate((n) => {
852
1040
  for (let i of n) if (i.start <= t && i.end >= e) {
853
1041
  i.start = r.Milli.min(i.start, e), i.end = r.Milli.max(i.end, t);
@@ -859,7 +1047,7 @@ var O = .001, k = .2, A = class {
859
1047
  }), n.sort((e, t) => e.start - t.start);
860
1048
  });
861
1049
  }
862
- #a(e) {
1050
+ #s(e) {
863
1051
  this.#e.mutate((t) => {
864
1052
  for (; t.length > 0;) {
865
1053
  if (t[0].end >= e) {
@@ -876,7 +1064,7 @@ var O = .001, k = .2, A = class {
876
1064
  });
877
1065
  }
878
1066
  };
879
- async function I(e) {
1067
+ async function W(e) {
880
1068
  let t;
881
1069
  if (e.description) t = l.Hex.toBytes(e.description);
882
1070
  else if (e.container.kind === "cmaf") try {
@@ -902,7 +1090,7 @@ async function I(e) {
902
1090
  }
903
1091
  //#endregion
904
1092
  //#region src/video/mse.ts
905
- var L = class {
1093
+ var G = class {
906
1094
  muxer;
907
1095
  source;
908
1096
  #e = new a(void 0);
@@ -915,7 +1103,7 @@ var L = class {
915
1103
  timestamp = this.#r;
916
1104
  signals = new i();
917
1105
  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));
1106
+ 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
1107
  }
920
1108
  #i(e) {
921
1109
  let t = e.get(this.muxer.element);
@@ -936,7 +1124,7 @@ var L = class {
936
1124
  }), e.event(c, "error", (e) => {
937
1125
  console.error("[MSE] SourceBuffer error:", e);
938
1126
  }), e.event(c, "updateend", () => {
939
- this.#t.set(q(c.buffered));
1127
+ this.#t.set(N(c.buffered));
940
1128
  }), o.container.kind === "cmaf" ? this.#o(e, i, a, o, c, t) : this.#s(e, i, a, o, c, t);
941
1129
  }
942
1130
  async #a(e, t) {
@@ -1023,12 +1211,12 @@ var L = class {
1023
1211
  this.source.close(), this.signals.close();
1024
1212
  }
1025
1213
  };
1026
- async function R(e) {
1214
+ async function K(e) {
1027
1215
  return MediaSource.isTypeSupported(`video/mp4; codecs="${e.codec}"`);
1028
1216
  }
1029
1217
  //#endregion
1030
1218
  //#region src/video/renderer.ts
1031
- var z = .01, B = class {
1219
+ var q = .01, J = class {
1032
1220
  decoder;
1033
1221
  canvas;
1034
1222
  paused;
@@ -1039,7 +1227,7 @@ var z = .01, B = class {
1039
1227
  #t = new a(!1);
1040
1228
  #n = new i();
1041
1229
  constructor(e, t) {
1042
- this.decoder = e, this.canvas = a.from(t?.canvas), this.paused = a.from(t?.paused ?? !1), this.visible = a.from(t?.visible ?? "0px"), this.#n.run((e) => {
1230
+ this.decoder = e, this.canvas = a.from(t?.canvas), this.paused = a.from(t?.paused ?? !1), this.visible = a.from(t?.visible ?? "20%"), this.#n.run((e) => {
1043
1231
  let t = e.get(this.canvas);
1044
1232
  this.#e.set(t?.getContext("2d") ?? void 0);
1045
1233
  }), this.#n.run(this.#i.bind(this)), this.#n.run(this.#a.bind(this)), this.#n.run(this.#o.bind(this)), this.#n.run(this.#r.bind(this));
@@ -1072,11 +1260,11 @@ var z = .01, B = class {
1072
1260
  }, o;
1073
1261
  try {
1074
1262
  o = new IntersectionObserver(a, {
1075
- threshold: z,
1263
+ threshold: q,
1076
1264
  rootMargin: t
1077
1265
  });
1078
1266
  } catch {
1079
- console.warn(`moq-watch: invalid visible margin "${t}", using "0px"`), o = new IntersectionObserver(a, { threshold: z });
1267
+ console.warn(`moq-watch: invalid visible margin "${t}", using "0px"`), o = new IntersectionObserver(a, { threshold: q });
1080
1268
  }
1081
1269
  i(), e.event(document, "visibilitychange", i), o.observe(n), e.cleanup(() => o.disconnect()), e.cleanup(() => this.#t.set(!1));
1082
1270
  }
@@ -1116,7 +1304,7 @@ var z = .01, B = class {
1116
1304
  };
1117
1305
  //#endregion
1118
1306
  //#region src/video/source.ts
1119
- function V(e) {
1307
+ function oe(e) {
1120
1308
  return (t) => {
1121
1309
  let n = [], r = [];
1122
1310
  for (let [i, a] of t) if (a.codedWidth && a.codedHeight) {
@@ -1132,7 +1320,7 @@ function V(e) {
1132
1320
  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
1321
  };
1134
1322
  }
1135
- function H(e, t) {
1323
+ function se(e, t) {
1136
1324
  return (n) => {
1137
1325
  let r = [], i = [];
1138
1326
  for (let [a, o] of n) {
@@ -1149,7 +1337,7 @@ function H(e, t) {
1149
1337
  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
1338
  };
1151
1339
  }
1152
- function U(e) {
1340
+ function ce(e) {
1153
1341
  return (t) => {
1154
1342
  let n = [], r = [];
1155
1343
  for (let [i, a] of t) a.bitrate != null && a.bitrate <= e ? n.push({
@@ -1162,7 +1350,7 @@ function U(e) {
1162
1350
  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
1351
  };
1164
1352
  }
1165
- function W(e) {
1353
+ function le(e) {
1166
1354
  let t = e[0];
1167
1355
  for (let n of e) {
1168
1356
  let [, e] = n, [, r] = t, i = (e.codedWidth ?? 0) * (e.codedHeight ?? 0), a = (r.codedWidth ?? 0) * (r.codedHeight ?? 0);
@@ -1174,7 +1362,7 @@ function W(e) {
1174
1362
  }
1175
1363
  return t[0];
1176
1364
  }
1177
- var G = class {
1365
+ var Y = class {
1178
1366
  broadcast;
1179
1367
  target;
1180
1368
  catalog;
@@ -1238,7 +1426,7 @@ var G = class {
1238
1426
  if (n.length === 0) return;
1239
1427
  if (n.length === 1) return n[0][0];
1240
1428
  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);
1429
+ 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
1430
  let i = r.map((e) => e(n)), a = i.map((e) => new Set(e));
1243
1431
  for (let e of i[0]) if (a.every((t) => t.has(e))) return e;
1244
1432
  console.warn("conflicting rendition filters, no rendition satisfies all criteria");
@@ -1246,26 +1434,12 @@ var G = class {
1246
1434
  close() {
1247
1435
  this.#r.close();
1248
1436
  }
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 {
1437
+ }, ue = /* @__PURE__ */ f({
1438
+ Decoder: () => H,
1439
+ Mse: () => G,
1440
+ Renderer: () => J,
1441
+ Source: () => Y
1442
+ }), de = class {
1269
1443
  source;
1270
1444
  stats = new a(void 0);
1271
1445
  stalled = new a(!1);
@@ -1274,7 +1448,7 @@ var ce = class {
1274
1448
  constructor(e) {
1275
1449
  this.source = e;
1276
1450
  }
1277
- }, le = class {
1451
+ }, fe = class {
1278
1452
  source;
1279
1453
  volume = new a(.5);
1280
1454
  muted = new a(!1);
@@ -1284,48 +1458,52 @@ var ce = class {
1284
1458
  constructor(e) {
1285
1459
  this.source = e;
1286
1460
  }
1287
- }, ue = class {
1461
+ }, pe = class {
1288
1462
  element = new a(void 0);
1289
1463
  broadcast;
1290
1464
  latency;
1291
1465
  jitter;
1466
+ buffered;
1292
1467
  paused;
1293
1468
  visible;
1294
1469
  video;
1295
1470
  #e;
1296
1471
  audio;
1297
1472
  #t;
1473
+ #n;
1298
1474
  sync;
1299
1475
  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));
1476
+ constructor(t) {
1477
+ this.element = a.from(t?.element), this.broadcast = a.from(t?.broadcast), this.sync = new e({
1478
+ latency: t?.latency,
1479
+ connection: t?.connection
1480
+ }), 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 ?? "20%"), this.signals.run(this.#r.bind(this));
1305
1481
  }
1306
- #n(e) {
1482
+ #r(e) {
1307
1483
  let t = e.get(this.element);
1308
- t && (t instanceof HTMLCanvasElement ? this.#r(e, t) : t instanceof HTMLVideoElement && this.#i(e, t));
1484
+ t && (t instanceof HTMLCanvasElement ? this.#i(e, t) : t instanceof HTMLVideoElement && this.#a(e, t));
1309
1485
  }
1310
- #r(e, t) {
1311
- let n = new P(this.#e), r = new E(this.#t), i = new A(r, {
1486
+ #i(e, t) {
1487
+ let n = new H(this.#e), r = new O(this.#t);
1488
+ this.#n = r;
1489
+ let i = new M(r, {
1312
1490
  volume: this.audio.volume,
1313
1491
  muted: this.audio.muted,
1314
1492
  paused: this.paused
1315
- }), a = new B(n, {
1493
+ }), a = new J(n, {
1316
1494
  canvas: t,
1317
1495
  paused: this.paused,
1318
1496
  visible: this.visible
1319
1497
  });
1320
1498
  e.cleanup(() => {
1321
- n.close(), r.close(), i.close(), a.close();
1499
+ n.close(), r.close(), i.close(), a.close(), this.#n = void 0;
1322
1500
  }), 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
1501
  }
1324
- #i(e, t) {
1325
- let n = new j(this.sync, {
1502
+ #a(e, t) {
1503
+ let n = new z(this.sync, {
1326
1504
  paused: this.paused,
1327
1505
  element: t
1328
- }), r = new L(n, this.#e), i = new J(n, this.#t, {
1506
+ }), r = new G(n, this.#e), i = new P(n, this.#t, {
1329
1507
  volume: this.audio.volume,
1330
1508
  muted: this.audio.muted
1331
1509
  });
@@ -1333,198 +1511,19 @@ var ce = class {
1333
1511
  r.close(), i.close(), n.close();
1334
1512
  }), 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
1513
  }
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
- });
1514
+ reset() {
1515
+ this.sync.reset(), this.#n?.reset();
1439
1516
  }
1440
1517
  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
- }
1505
- }
1506
- close() {
1507
- this.#r.close();
1518
+ this.signals.close(), this.#e.close(), this.#t.close(), this.sync.close();
1508
1519
  }
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) {
1520
+ }, me = 48e3, X = 2;
1521
+ function he(e) {
1523
1522
  let t = "";
1524
1523
  for (let n = 0; n < e.length; n++) t += e[n].toString(16).padStart(2, "0");
1525
1524
  return t;
1526
1525
  }
1527
- function Q(e) {
1526
+ function Z(e) {
1528
1527
  let t;
1529
1528
  try {
1530
1529
  t = e.initData ? p(e.initData) : void 0;
@@ -1539,12 +1538,12 @@ function Q(e) {
1539
1538
  description: void 0
1540
1539
  } : {
1541
1540
  container: { kind: "legacy" },
1542
- description: t ? Z(t) : void 0
1541
+ description: t ? he(t) : void 0
1543
1542
  };
1544
1543
  }
1545
1544
  function ge(e) {
1546
1545
  if (!e.codec) return;
1547
- let { container: t, description: n } = Q(e);
1546
+ let { container: t, description: n } = Z(e);
1548
1547
  return {
1549
1548
  codec: e.codec,
1550
1549
  container: t,
@@ -1562,18 +1561,18 @@ function _e(e) {
1562
1561
  if (!e.channelConfig) return X;
1563
1562
  let t = Number.parseInt(e.channelConfig, 10);
1564
1563
  return Number.isFinite(t) ? t : X;
1565
- })(), { container: n, description: r } = Q(e);
1564
+ })(), { container: n, description: r } = Z(e);
1566
1565
  return {
1567
1566
  codec: e.codec,
1568
1567
  container: n,
1569
1568
  description: r,
1570
- sampleRate: s(e.samplerate ?? he),
1569
+ sampleRate: s(e.samplerate ?? me),
1571
1570
  numberOfChannels: s(t),
1572
1571
  bitrate: e.bitrate == null ? void 0 : s(e.bitrate),
1573
1572
  jitter: e.jitter == null ? void 0 : s(e.jitter)
1574
1573
  };
1575
1574
  }
1576
- function ve(e) {
1575
+ function Q(e) {
1577
1576
  let t = {}, n = {};
1578
1577
  for (let r of e.tracks) if (r.role === "video") {
1579
1578
  let e = ge(r);
@@ -1587,11 +1586,15 @@ function ve(e) {
1587
1586
  }
1588
1587
  //#endregion
1589
1588
  //#region src/broadcast.ts
1590
- var $ = [...o.FORMATS, "manual"];
1591
- function ye(e) {
1589
+ var $ = [
1590
+ ...o.FORMATS,
1591
+ "hangz",
1592
+ "manual"
1593
+ ];
1594
+ function ve(e) {
1592
1595
  if (e !== null) return $.find((t) => t === e);
1593
1596
  }
1594
- var be = class {
1597
+ var ye = class {
1595
1598
  connection;
1596
1599
  enabled;
1597
1600
  name;
@@ -1605,15 +1608,19 @@ var be = class {
1605
1608
  #n = new a(!1);
1606
1609
  signals = new i();
1607
1610
  constructor(e) {
1608
- this.connection = a.from(e?.connection), this.name = a.from(e?.name ?? n.empty()), this.enabled = a.from(e?.enabled ?? !1), this.reload = a.from(e?.reload ?? !1), this.catalogFormat = a.from(e?.catalogFormat), this.catalog = a.from(e?.catalog), this.#t = e?.announced ?? new a(/* @__PURE__ */ new Set()), this.signals.run(this.#r.bind(this)), this.signals.run(this.#i.bind(this)), this.signals.run(this.#a.bind(this));
1611
+ this.connection = a.from(e?.connection), this.name = a.from(e?.name ?? n.empty()), this.enabled = a.from(e?.enabled ?? !1), this.reload = a.from(e?.reload ?? !0), this.catalogFormat = a.from(e?.catalogFormat), this.catalog = a.from(e?.catalog), this.#t = e?.announced, this.signals.run(this.#r.bind(this)), this.signals.run(this.#i.bind(this)), this.signals.run(this.#a.bind(this));
1609
1612
  }
1610
1613
  #r(e) {
1611
1614
  if (!e.get(this.reload)) {
1612
1615
  this.#n.set(!0);
1613
1616
  return;
1614
1617
  }
1618
+ if (!this.#t) {
1619
+ this.#n.set(!0);
1620
+ return;
1621
+ }
1615
1622
  if (e.get(this.connection)?.url.hostname.endsWith("mediaoverquic.com")) {
1616
- console.warn("Cloudflare relay does not support broadcast discovery yet; ignoring reload signal."), this.#n.set(!0);
1623
+ this.#n.set(!0);
1617
1624
  return;
1618
1625
  }
1619
1626
  let t = e.get(this.name), n = e.get(this.#t);
@@ -1626,33 +1633,33 @@ var be = class {
1626
1633
  let n = e.get(this.name), r = t.consume(n);
1627
1634
  e.cleanup(() => r.close()), e.set(this.#e, r, void 0);
1628
1635
  }
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");
1636
+ #a(e) {
1637
+ if (!e.get(this.enabled)) return;
1638
+ let t = e.get(this.catalogFormat), n = e.get(this.name), r = t ?? o.detectFormat(n) ?? o.DEFAULT_FORMAT;
1639
+ if (r === "manual") {
1640
+ let t = e.get(this.catalog);
1641
+ this.status.set(t ? "live" : "loading");
1635
1642
  return;
1636
1643
  }
1637
- let a = t.get(this.active);
1638
- if (!a) return;
1644
+ let i = e.get(this.active);
1645
+ if (!i) return;
1639
1646
  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;
1647
+ let a = r === "hang" ? o.TRACK : r === "hangz" ? o.TRACK_COMPRESSED : "catalog", s = i.subscribe(a, o.PRIORITY.catalog);
1648
+ e.cleanup(() => s.close());
1649
+ let c;
1650
+ if (r === "hang" || r === "hangz") {
1651
+ let e = new o.Consumer(s, { compression: r === "hangz" });
1652
+ c = () => e.next();
1653
+ } else c = async () => {
1654
+ let e = await u.fetch(s);
1655
+ return e ? Q(e) : void 0;
1649
1656
  };
1650
- t.spawn(async () => {
1657
+ e.spawn(async () => {
1651
1658
  try {
1652
1659
  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");
1660
+ let t = await Promise.race([e.cancel, c()]);
1661
+ if (!t) break;
1662
+ console.debug("received catalog", r, this.name.peek(), t), this.catalog.set(t), this.status.set("live");
1656
1663
  }
1657
1664
  } catch (e) {
1658
1665
  console.warn("error fetching catalog", this.name.peek(), e);
@@ -1675,6 +1682,6 @@ var be = class {
1675
1682
  }
1676
1683
  };
1677
1684
  //#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 };
1685
+ 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
1686
 
1680
- //# sourceMappingURL=broadcast-80k1hgfM.js.map
1687
+ //# sourceMappingURL=broadcast-Duw8w3D_.js.map