@norskvideo/moq-watch 0.1.2 → 0.1.4

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 (102) hide show
  1. package/README.md +1 -2
  2. package/audio/buffer.d.ts +8 -0
  3. package/audio/buffer.d.ts.map +1 -1
  4. package/audio/decoder.d.ts +25 -15
  5. package/audio/decoder.d.ts.map +1 -1
  6. package/audio/emitter.d.ts +13 -10
  7. package/audio/emitter.d.ts.map +1 -1
  8. package/audio/handover.d.ts +16 -0
  9. package/audio/handover.d.ts.map +1 -0
  10. package/audio/index.d.ts +0 -2
  11. package/audio/index.d.ts.map +1 -1
  12. package/audio/render.d.ts +10 -1
  13. package/audio/render.d.ts.map +1 -1
  14. package/audio/ring-buffer.d.ts +7 -0
  15. package/audio/ring-buffer.d.ts.map +1 -1
  16. package/audio/shared-ring-buffer.d.ts +30 -2
  17. package/audio/shared-ring-buffer.d.ts.map +1 -1
  18. package/audio/source.d.ts +21 -17
  19. package/audio/source.d.ts.map +1 -1
  20. package/audio/subscription.d.ts +10 -0
  21. package/audio/subscription.d.ts.map +1 -0
  22. package/audio/terminal.d.ts +36 -0
  23. package/audio/terminal.d.ts.map +1 -0
  24. package/audio/unlock.d.ts +20 -0
  25. package/audio/unlock.d.ts.map +1 -0
  26. package/audio/warmup.d.ts +9 -0
  27. package/audio/warmup.d.ts.map +1 -0
  28. package/broadcast.d.ts +21 -61
  29. package/broadcast.d.ts.map +1 -1
  30. package/element.d.ts +45 -12
  31. package/element.d.ts.map +1 -1
  32. package/element.js +190 -95
  33. package/element.js.map +1 -1
  34. package/index.d.ts +9 -6
  35. package/index.d.ts.map +1 -1
  36. package/index.js +24 -5
  37. package/index.js.map +1 -0
  38. package/msf.d.ts.map +1 -1
  39. package/package.json +5 -6
  40. package/source-lWNGIbmd.js +1722 -0
  41. package/source-lWNGIbmd.js.map +1 -0
  42. package/support/element.d.ts.map +1 -1
  43. package/support/element.js.map +1 -1
  44. package/support/index.d.ts +3 -3
  45. package/support/index.d.ts.map +1 -1
  46. package/support/index.js +18 -17
  47. package/support/index.js.map +1 -1
  48. package/sync-DNQkgnlX.js +132 -0
  49. package/sync-DNQkgnlX.js.map +1 -0
  50. package/sync.d.ts +42 -22
  51. package/sync.d.ts.map +1 -1
  52. package/ui/components/buffer-control.d.ts +1 -1
  53. package/ui/components/buffer-control.d.ts.map +1 -1
  54. package/ui/components/fullscreen-button.d.ts +1 -2
  55. package/ui/components/fullscreen-button.d.ts.map +1 -1
  56. package/ui/components/play-pause.d.ts +1 -1
  57. package/ui/components/play-pause.d.ts.map +1 -1
  58. package/ui/element.d.ts.map +1 -1
  59. package/ui/element.js +269 -265
  60. package/ui/element.js.map +1 -1
  61. package/ui/fullscreen.d.ts +1 -2
  62. package/ui/fullscreen.d.ts.map +1 -1
  63. package/video/config.d.ts +17 -0
  64. package/video/config.d.ts.map +1 -0
  65. package/video/decoder.d.ts +42 -16
  66. package/video/decoder.d.ts.map +1 -1
  67. package/video/index.d.ts +0 -2
  68. package/video/index.d.ts.map +1 -1
  69. package/video/playhead.d.ts +31 -0
  70. package/video/playhead.d.ts.map +1 -0
  71. package/video/presentation.d.ts +16 -0
  72. package/video/presentation.d.ts.map +1 -0
  73. package/video/renderer.d.ts +14 -17
  74. package/video/renderer.d.ts.map +1 -1
  75. package/video/source.d.ts +23 -20
  76. package/video/source.d.ts.map +1 -1
  77. package/audio/backend.d.ts +0 -26
  78. package/audio/backend.d.ts.map +0 -1
  79. package/audio/mse.d.ts +0 -22
  80. package/audio/mse.d.ts.map +0 -1
  81. package/audio/ring-buffer.test.d.ts +0 -2
  82. package/audio/ring-buffer.test.d.ts.map +0 -1
  83. package/audio/shared-ring-buffer.test.d.ts +0 -2
  84. package/audio/shared-ring-buffer.test.d.ts.map +0 -1
  85. package/backend.d.ts +0 -65
  86. package/backend.d.ts.map +0 -1
  87. package/broadcast-BY0pAaOe.js +0 -1706
  88. package/broadcast-BY0pAaOe.js.map +0 -1
  89. package/buffered.d.ts +0 -19
  90. package/buffered.d.ts.map +0 -1
  91. package/mse.d.ts +0 -19
  92. package/mse.d.ts.map +0 -1
  93. package/sync-D67IxmxJ.js +0 -128
  94. package/sync-D67IxmxJ.js.map +0 -1
  95. package/sync.test.d.ts +0 -2
  96. package/sync.test.d.ts.map +0 -1
  97. package/video/backend.d.ts +0 -25
  98. package/video/backend.d.ts.map +0 -1
  99. package/video/mse.d.ts +0 -23
  100. package/video/mse.d.ts.map +0 -1
  101. package/video/source.test.d.ts +0 -2
  102. package/video/source.test.d.ts.map +0 -1
package/element.d.ts CHANGED
@@ -1,11 +1,18 @@
1
+ /**
2
+ * The `<moq-watch>` custom element: a broadcast player driven by HTML attributes.
3
+ *
4
+ * Side-effectful: importing this registers the element.
5
+ *
6
+ * @module
7
+ */
1
8
  import type * as Catalog from "@norskvideo/moq-hang/catalog";
2
9
  import type { Time } from "@norskvideo/moq-net";
3
10
  import * as Moq from "@norskvideo/moq-net";
4
- import { Effect } from "@norskvideo/moq-signals";
5
- import { MultiBackend } from "./backend";
11
+ import { Effect, Signal } from "@norskvideo/moq-signals";
12
+ import * as Audio from "./audio";
6
13
  import { Broadcast, type CatalogFormat } from "./broadcast";
7
- import { type Bound, type Latency } from "./sync";
8
- import type { Visible } from "./video";
14
+ import { type Bound, type Latency, Sync } from "./sync";
15
+ import * as Video from "./video";
9
16
  declare const OBSERVED: readonly ["url", "name", "paused", "volume", "muted", "visible", "reload", "latency", "latency-min", "latency-max", "jitter", "catalog-format"];
10
17
  type Observed = (typeof OBSERVED)[number];
11
18
  export default class MoqWatch extends HTMLElement {
@@ -13,8 +20,31 @@ export default class MoqWatch extends HTMLElement {
13
20
  static observedAttributes: readonly ["url", "name", "paused", "volume", "muted", "visible", "reload", "latency", "latency-min", "latency-max", "jitter", "catalog-format"];
14
21
  connection: Moq.Connection.Reload;
15
22
  broadcast: Broadcast;
16
- backend: MultiBackend;
17
- signals: Effect;
23
+ /** Downloads and decodes the video track. `video.source` picks the rendition. */
24
+ video: Video.Decoder;
25
+ /** Downloads and decodes the audio track. `audio.source` picks the rendition. */
26
+ audio: Audio.Decoder;
27
+ /** Paints decoded frames to the nested <canvas>. */
28
+ renderer: Video.Renderer;
29
+ /** Plays decoded samples through the speakers. */
30
+ emitter: Audio.Emitter;
31
+ /** Keeps audio and video playing at the target latency. */
32
+ sync: Sync;
33
+ readonly controls: {
34
+ paused: Signal<boolean>;
35
+ volume: Signal<number>;
36
+ muted: Signal<boolean>;
37
+ visible: Signal<Video.Visible>;
38
+ latency: Signal<Latency>;
39
+ target: Signal<Video.Target | undefined>;
40
+ };
41
+ /**
42
+ * Effects scoped to this element's lifetime, closed on disconnect.
43
+ *
44
+ * Public because the element is the top of the tree: it's where an application hangs its own
45
+ * reactivity. The components underneath keep theirs private, so `close()` is the only handle.
46
+ */
47
+ readonly signals: Effect;
18
48
  constructor();
19
49
  connectedCallback(): void;
20
50
  disconnectedCallback(): void;
@@ -29,14 +59,16 @@ export default class MoqWatch extends HTMLElement {
29
59
  set volume(value: number);
30
60
  get muted(): boolean;
31
61
  set muted(value: boolean);
32
- /** When video is downloaded relative to the canvas position. See {@link Visible}. */
33
- get visible(): Visible;
34
- set visible(value: Visible);
62
+ get visible(): Video.Visible;
63
+ set visible(value: Video.Visible);
35
64
  get reload(): boolean;
36
65
  set reload(value: boolean);
37
66
  /**
38
67
  * The latency target. Assign a scalar (or `"real-time"`) to minimize latency, or an object
39
68
  * `{ min, max }` to open a range and buffer future-dated frames. See {@link Latency}.
69
+ *
70
+ * `"instant"` drops the clock instead: video paints the moment it decodes and audio is
71
+ * disabled. Assigning `latencyMin` or `latencyMax` leaves that mode, since both write a range.
40
72
  */
41
73
  get latency(): Latency;
42
74
  set latency(value: Latency);
@@ -53,9 +85,10 @@ export default class MoqWatch extends HTMLElement {
53
85
  set latencyMax(value: Bound);
54
86
  /** The jitter buffer in milliseconds. */
55
87
  get jitter(): Time.Milli;
56
- /** @deprecated Use `latency = <number>` instead. */
57
- set jitter(value: number);
58
- /** Re-anchor playback and flush the audio buffer at an utterance boundary (buffered mode). */
88
+ /**
89
+ * Re-anchor playback at an utterance boundary in buffered mode: reset the sync reference
90
+ * and flush the audio buffer so the next utterance plays from its own first frame.
91
+ */
59
92
  reset(): void;
60
93
  get catalogFormat(): CatalogFormat | undefined;
61
94
  set catalogFormat(value: CatalogFormat | undefined);
package/element.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"element.d.ts","sourceRoot":"","sources":["../src/element.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"element.d.ts","sourceRoot":"","sources":["../src/element.ts"],"names":[],"mappings":"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"}
package/element.js CHANGED
@@ -1,9 +1,9 @@
1
- import { i as e, r as t, t as n } from "./broadcast-BY0pAaOe.js";
2
- import { n as r, r as i } from "./sync-D67IxmxJ.js";
3
- import * as a from "@norskvideo/moq-net";
4
- import { Effect as o, Signal as s } from "@norskvideo/moq-signals";
1
+ import { c as e, i as t, l as n, n as r, o as i, r as a, s as o, t as s } from "./source-lWNGIbmd.js";
2
+ import { n as c, r as l, t as u } from "./sync-DNQkgnlX.js";
3
+ import * as d from "@norskvideo/moq-net";
4
+ import { Effect as f, Signal as p } from "@norskvideo/moq-signals";
5
5
  //#region src/element.ts
6
- var c = [
6
+ var m = [
7
7
  "url",
8
8
  "name",
9
9
  "paused",
@@ -17,106 +17,204 @@ var c = [
17
17
  "jitter",
18
18
  "catalog-format"
19
19
  ];
20
- function l(e) {
20
+ function h(e) {
21
21
  let t = e?.trim();
22
22
  return t ? t === "never" || t === "always" || /^-?\d+(\.\d+)?(px|%)$/.test(t) ? t : /^-?\d+(\.\d+)?$/.test(t) ? `${t}px` : (console.warn(`moq-watch: invalid visible="${e}", expected "never", "always", or a CSS length like "200px"`), "20%") : "20%";
23
23
  }
24
- function u(e, t) {
24
+ function g(e, t) {
25
25
  if (e === null) return t;
26
26
  let n = e.trim().toLowerCase();
27
27
  return n !== "false" && n !== "0";
28
28
  }
29
- var d = new FinalizationRegistry((e) => e.close()), f = class extends HTMLElement {
30
- static observedAttributes = c;
29
+ var _ = new FinalizationRegistry((e) => e.close()), v = class extends HTMLElement {
30
+ static observedAttributes = m;
31
31
  connection;
32
32
  broadcast;
33
- backend;
34
- #e = new s(!1);
35
- signals = new o();
33
+ video;
34
+ audio;
35
+ renderer;
36
+ emitter;
37
+ sync;
38
+ controls = {
39
+ paused: new p(!1),
40
+ volume: new p(.5),
41
+ muted: new p(!1),
42
+ visible: new p("20%"),
43
+ latency: new p("real-time"),
44
+ target: new p(void 0)
45
+ };
46
+ #e = new p(d.Path.empty());
47
+ #t = new p(!0);
48
+ #n = new p(void 0);
49
+ #r = new p(void 0);
50
+ #i = new p(void 0);
51
+ #a = new p(!1);
52
+ #o = new p(!1);
53
+ #s = !1;
54
+ #c = new p(!0);
55
+ #l = new p("real-time");
56
+ #u = new p(!1);
57
+ #d = .5;
58
+ signals = new f();
36
59
  constructor() {
37
- super(), d.register(this, this.signals), this.connection = new a.Connection.Reload({ enabled: this.#e }), this.signals.cleanup(() => this.connection.close()), this.broadcast = new n({
60
+ super(), _.register(this, this.signals), this.connection = new d.Connection.Reload({ enabled: this.#u }), this.signals.cleanup(() => this.connection.close()), this.broadcast = new t({
38
61
  connection: this.connection.established,
39
- announced: this.connection.announced,
40
- enabled: this.#e
41
- }), this.signals.cleanup(() => this.broadcast.close()), this.backend = new e({
62
+ enabled: this.#u,
63
+ name: this.#e,
64
+ reload: this.#t,
65
+ catalogFormat: this.#n,
66
+ catalog: this.#r
67
+ }), this.signals.cleanup(() => this.broadcast.close());
68
+ let i = new s({
42
69
  broadcast: this.broadcast,
43
- connection: this.connection.established
44
- }), this.signals.cleanup(() => this.backend.close());
45
- let t = () => {
46
- let e = this.querySelector("canvas"), t = this.querySelector("video");
47
- if (e && t) throw Error("Cannot have both canvas and video elements");
48
- this.backend.element.set(e ?? t);
49
- }, i = new MutationObserver(t);
50
- i.observe(this, {
70
+ target: this.controls.target,
71
+ supported: a.supported
72
+ }), l = new o({
73
+ broadcast: this.broadcast,
74
+ supported: n.supported
75
+ });
76
+ this.signals.cleanup(() => {
77
+ i.close(), l.close();
78
+ }), this.signals.run((e) => {
79
+ let t = e.get(this.controls.latency);
80
+ this.#l.set(t === "instant" ? d.Time.Milli.zero : t);
81
+ }), this.sync = new u({
82
+ latency: this.#l,
83
+ connection: this.connection.established,
84
+ video: i.out.jitter,
85
+ audio: l.out.jitter
86
+ }), this.signals.cleanup(() => this.sync.close()), this.signals.run((e) => {
87
+ this.#c.set(e.get(this.controls.latency) !== "instant");
88
+ }), this.video = new a(i, this.sync, {
89
+ enabled: this.#a,
90
+ paced: this.#c
91
+ }), this.audio = new n(l, this.sync, { enabled: this.#o }), this.signals.cleanup(() => {
92
+ this.video.close(), this.audio.close();
93
+ }), this.emitter = new e(this.audio, {
94
+ volume: this.controls.volume,
95
+ muted: this.controls.muted,
96
+ paused: this.controls.paused
97
+ }), this.renderer = new r(this.video, {
98
+ canvas: this.#i,
99
+ visible: this.controls.visible
100
+ }), this.signals.cleanup(() => {
101
+ this.emitter.close(), this.renderer.close();
102
+ }), this.signals.run((e) => {
103
+ let t = e.get(this.emitter.out.enabled);
104
+ this.#o.set(t && e.get(this.controls.latency) !== "instant");
105
+ }), this.signals.run((e) => {
106
+ e.get(this.controls.latency) === "instant" && this.audio.reset();
107
+ }), this.signals.run((e) => {
108
+ let t = e.get(this.renderer.out.visible);
109
+ if (!e.get(this.controls.paused)) {
110
+ this.#a.set(t);
111
+ return;
112
+ }
113
+ let n = e.get(this.renderer.out.frame);
114
+ this.#a.set(t && !n);
115
+ }), this.signals.run((e) => {
116
+ e.get(this.controls.muted) ? (this.#d = this.controls.volume.peek() || .5, this.controls.volume.set(0)) : this.controls.volume.set(this.#d);
117
+ }), this.signals.run((e) => {
118
+ let t = e.get(this.controls.volume);
119
+ this.controls.muted.set(t === 0);
120
+ });
121
+ let f = () => {
122
+ let e = this.querySelector("canvas") ?? void 0;
123
+ !e && this.querySelector("video") && console.warn("moq-watch: rendering requires a <canvas> child; a <video> child does nothing."), this.#i.set(e);
124
+ }, p = new MutationObserver(f);
125
+ p.observe(this, {
51
126
  childList: !0,
52
127
  subtree: !0
53
- }), this.signals.cleanup(() => i.disconnect()), t(), this.signals.run((e) => {
128
+ }), this.signals.cleanup(() => p.disconnect()), f(), this.signals.run((e) => {
54
129
  let t = e.get(this.connection.url);
55
130
  t ? this.setAttribute("url", t.toString()) : this.removeAttribute("url");
56
131
  }), this.signals.run((e) => {
57
- let t = e.get(this.broadcast.name);
132
+ let t = e.get(this.#e);
58
133
  this.setAttribute("name", t.toString());
59
134
  }), this.signals.run((e) => {
60
- e.get(this.backend.audio.muted) ? this.setAttribute("muted", "") : this.removeAttribute("muted");
135
+ e.get(this.controls.muted) ? this.setAttribute("muted", "") : this.removeAttribute("muted");
61
136
  }), this.signals.run((e) => {
62
- e.get(this.backend.paused) ? this.setAttribute("paused", "true") : this.removeAttribute("paused");
137
+ e.get(this.controls.paused) ? this.setAttribute("paused", "") : this.removeAttribute("paused");
63
138
  }), this.signals.run((e) => {
64
- let t = e.get(this.backend.visible);
65
- this.setAttribute("visible", t);
66
- }), this.signals.run((e) => {
67
- let t = e.get(this.backend.audio.volume);
139
+ let t = e.get(this.controls.volume);
68
140
  this.setAttribute("volume", t.toString());
69
141
  }), this.signals.run((e) => {
70
- let { min: t, max: n } = r(e.get(this.backend.latency));
71
- if (t === n) if (t === "real-time") this.setAttribute("latency", "real-time");
142
+ let t = e.get(this.controls.visible);
143
+ this.setAttribute("visible", t);
144
+ }), this.signals.run((e) => {
145
+ let t = e.get(this.controls.latency);
146
+ if (t === "instant") {
147
+ this.#p("instant");
148
+ return;
149
+ }
150
+ let { min: n, max: r } = c(t);
151
+ if (n !== r) {
152
+ this.getAttribute("latency") === "instant" && this.#p(void 0);
153
+ return;
154
+ }
155
+ if (n === "real-time") this.#p("real-time");
72
156
  else {
73
- let t = Math.floor(e.get(this.backend.jitter));
74
- this.setAttribute("latency", t.toString());
157
+ let t = Math.floor(e.get(this.sync.out.jitter));
158
+ this.#p(t.toString());
75
159
  }
76
160
  });
77
- let o = (e, t) => {
161
+ let m = (e, t) => {
78
162
  if (e <= 0 || t <= 0) return;
79
163
  let n = window.devicePixelRatio || 1;
80
- this.backend.video.source.target.update((r) => ({
164
+ this.controls.target.update((r) => ({
81
165
  ...r,
82
166
  width: Math.round(e * n),
83
167
  height: Math.round(t * n)
84
168
  }));
85
- }, s = new ResizeObserver((e) => {
169
+ }, h = new ResizeObserver((e) => {
86
170
  let t = e[0];
87
- t && o(t.contentRect.width, t.contentRect.height);
171
+ t && m(t.contentRect.width, t.contentRect.height);
88
172
  });
89
- s.observe(this), this.signals.cleanup(() => s.disconnect());
90
- let c = this.getBoundingClientRect();
91
- o(c.width, c.height);
173
+ h.observe(this), this.signals.cleanup(() => h.disconnect());
174
+ let g = this.getBoundingClientRect();
175
+ m(g.width, g.height);
92
176
  }
93
177
  connectedCallback() {
94
- this.#e.set(!0), this.style.display = "block", this.style.position = "relative";
178
+ this.#u.set(!0), this.style.display = "block", this.style.position = "relative";
95
179
  }
96
180
  disconnectedCallback() {
97
- this.#e.set(!1);
181
+ this.#u.set(!1);
98
182
  }
99
- #t(e) {
183
+ #f(e) {
100
184
  if (!e || e === "real-time") return "real-time";
185
+ if (e === "instant") return console.warn("moq-watch: \"instant\" is not a bound, use latency=\"instant\""), "real-time";
101
186
  let t = Number.parseFloat(e);
102
- return Number.isFinite(t) ? t : 100;
103
- }
104
- attributeChangedCallback(e, n, r) {
105
- if (n !== r) if (e === "url") this.connection.url.set(r ? new URL(r) : void 0);
106
- else if (e === "name") this.broadcast.name.set(a.Path.from(r ?? ""));
107
- else if (e === "paused") this.backend.paused.set(r !== null);
108
- else if (e === "volume") {
109
- let e = r ? Number.parseFloat(r) : .5;
110
- this.backend.audio.volume.set(e);
111
- } else if (e === "muted") this.backend.audio.muted.set(r !== null);
112
- else if (e === "visible") this.backend.visible.set(l(r));
113
- else if (e === "reload") this.broadcast.reload.set(u(r, !0));
114
- else if (e === "latency") this.latency = this.#t(r);
115
- else if (e === "latency-min") this.latencyMin = this.#t(r);
116
- else if (e === "latency-max") this.latencyMax = this.#t(r);
117
- else if (e === "jitter") this.latency = this.#t(r);
118
- else if (e === "catalog-format") this.broadcast.catalogFormat.set(t(r));
119
- else throw Error(`Invalid attribute: ${e}`);
187
+ return d.Time.Milli(Number.isFinite(t) ? t : 100);
188
+ }
189
+ #p(e) {
190
+ this.#s = !0;
191
+ try {
192
+ e === void 0 ? this.removeAttribute("latency") : this.setAttribute("latency", e);
193
+ } finally {
194
+ this.#s = !1;
195
+ }
196
+ }
197
+ #m(e) {
198
+ return e?.trim() === "instant" ? "instant" : this.#f(e);
199
+ }
200
+ attributeChangedCallback(e, t, n) {
201
+ if (t !== n) {
202
+ if (e === "url") this.connection.url.set(n ? new URL(n) : void 0);
203
+ else if (e === "name") this.#e.set(d.Path.from(n ?? ""));
204
+ else if (e === "paused") this.controls.paused.set(g(n, !1));
205
+ else if (e === "volume") {
206
+ let e = n ? Number.parseFloat(n) : .5;
207
+ this.controls.volume.set(e);
208
+ } else if (e === "muted") this.controls.muted.set(g(n, !1));
209
+ else if (e === "visible") this.controls.visible.set(h(n));
210
+ else if (e === "reload") this.#t.set(g(n, !0));
211
+ else if (e === "latency") this.#s || (this.latency = this.#m(n));
212
+ else if (e === "latency-min") this.latencyMin = this.#f(n);
213
+ else if (e === "latency-max") this.latencyMax = this.#f(n);
214
+ else if (e === "jitter") this.latency = this.#f(n);
215
+ else if (e === "catalog-format") this.#n.set(i(n));
216
+ else throw Error(`Invalid attribute: ${e}`);
217
+ }
120
218
  }
121
219
  get url() {
122
220
  return this.connection.url.peek();
@@ -125,85 +223,82 @@ var d = new FinalizationRegistry((e) => e.close()), f = class extends HTMLElemen
125
223
  this.connection.url.set(e ? new URL(e) : void 0);
126
224
  }
127
225
  get name() {
128
- return this.broadcast.name.peek();
226
+ return this.#e.peek();
129
227
  }
130
228
  set name(e) {
131
- this.broadcast.name.set(a.Path.from(e));
229
+ this.#e.set(d.Path.from(e));
132
230
  }
133
231
  get paused() {
134
- return this.backend.paused.peek();
232
+ return this.controls.paused.peek();
135
233
  }
136
234
  set paused(e) {
137
- this.backend.paused.set(e);
235
+ this.controls.paused.set(e);
138
236
  }
139
237
  get volume() {
140
- return this.backend.audio.volume.peek();
238
+ return this.controls.volume.peek();
141
239
  }
142
240
  set volume(e) {
143
- this.backend.audio.volume.set(e);
241
+ this.controls.volume.set(e);
144
242
  }
145
243
  get muted() {
146
- return this.backend.audio.muted.peek();
244
+ return this.controls.muted.peek();
147
245
  }
148
246
  set muted(e) {
149
- this.backend.audio.muted.set(e);
247
+ this.controls.muted.set(e);
150
248
  }
151
249
  get visible() {
152
- return this.backend.visible.peek();
250
+ return this.controls.visible.peek();
153
251
  }
154
252
  set visible(e) {
155
- this.backend.visible.set(e);
253
+ this.controls.visible.set(e);
156
254
  }
157
255
  get reload() {
158
- return this.broadcast.reload.peek();
256
+ return this.#t.peek();
159
257
  }
160
258
  set reload(e) {
161
- this.broadcast.reload.set(e);
259
+ this.#t.set(e);
162
260
  }
163
261
  get latency() {
164
- return this.backend.latency.peek();
262
+ return this.controls.latency.peek();
165
263
  }
166
264
  set latency(e) {
167
- this.backend.latency.set(e);
265
+ this.controls.latency.set(e);
168
266
  }
169
267
  get latencyMin() {
170
- return r(this.backend.latency.peek()).min;
268
+ return c(this.controls.latency.peek()).min;
171
269
  }
172
270
  set latencyMin(e) {
173
- let { max: t } = r(this.backend.latency.peek());
174
- this.backend.latency.set(i(e, t));
271
+ let { max: t } = c(this.controls.latency.peek());
272
+ this.controls.latency.set(l(e, t));
175
273
  }
176
274
  get latencyMax() {
177
- return r(this.backend.latency.peek()).max;
275
+ return c(this.controls.latency.peek()).max;
178
276
  }
179
277
  set latencyMax(e) {
180
- let { min: t } = r(this.backend.latency.peek());
181
- this.backend.latency.set(i(t, e));
278
+ let { min: t } = c(this.controls.latency.peek());
279
+ this.controls.latency.set(l(t, e));
182
280
  }
183
281
  get jitter() {
184
- return this.backend.jitter.peek();
185
- }
186
- set jitter(e) {
187
- this.latency = e;
282
+ return this.sync.out.jitter.peek();
188
283
  }
189
284
  reset() {
190
- this.backend.reset();
285
+ this.sync.reset(), this.audio.reset();
191
286
  }
192
287
  get catalogFormat() {
193
- return this.broadcast.catalogFormat.peek();
288
+ return this.#n.peek();
194
289
  }
195
290
  set catalogFormat(e) {
196
- this.broadcast.catalogFormat.set(e);
291
+ this.#n.set(e);
197
292
  }
198
293
  get catalog() {
199
- return this.broadcast.catalog.peek();
294
+ return this.broadcast.out.catalog.peek();
200
295
  }
201
296
  set catalog(e) {
202
- this.broadcast.catalog.set(e);
297
+ this.#r.set(e);
203
298
  }
204
299
  };
205
- customElements.define("moq-watch", f);
300
+ customElements.define("moq-watch", v);
206
301
  //#endregion
207
- export { f as default };
302
+ export { v as default };
208
303
 
209
304
  //# sourceMappingURL=element.js.map
package/element.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"element.js","names":["#enabled","#parseBound"],"sources":["../src/element.ts"],"sourcesContent":["import type * as Catalog from \"@moq/hang/catalog\";\nimport type { Time } from \"@moq/net\";\nimport * as Moq from \"@moq/net\";\nimport { Effect, Signal } from \"@moq/signals\";\nimport { MultiBackend } from \"./backend\";\nimport { Broadcast, type CatalogFormat, parseCatalogFormat } from \"./broadcast\";\nimport { type Bound, type Latency, latencyBounds, latencyFromBounds } from \"./sync\";\nimport type { Visible } from \"./video\";\n\nconst OBSERVED = [\n\t\"url\",\n\t\"name\",\n\t\"paused\",\n\t\"volume\",\n\t\"muted\",\n\t\"visible\",\n\t\"reload\",\n\t\"latency\",\n\t\"latency-min\",\n\t\"latency-max\",\n\t\"jitter\",\n\t\"catalog-format\",\n] as const;\ntype Observed = (typeof OBSERVED)[number];\n\n// Parse the `visible` attribute into a Visible value, falling back to \"20%\".\nfunction parseVisible(value: string | null): Visible {\n\tconst trimmed = value?.trim();\n\tif (!trimmed) return \"20%\";\n\tif (trimmed === \"never\" || trimmed === \"always\") return trimmed;\n\t// A CSS length usable as an IntersectionObserver rootMargin (px or %).\n\tif (/^-?\\d+(\\.\\d+)?(px|%)$/.test(trimmed)) return trimmed;\n\t// Allow a bare number as a px convenience (e.g. visible=\"200\").\n\tif (/^-?\\d+(\\.\\d+)?$/.test(trimmed)) return `${trimmed}px`;\n\tconsole.warn(`moq-watch: invalid visible=\"${value}\", expected \"never\", \"always\", or a CSS length like \"200px\"`);\n\treturn \"20%\";\n}\n\nfunction parseBoolean(value: string | null, defaultValue: boolean): boolean {\n\tif (value === null) return defaultValue;\n\tconst normalized = value.trim().toLowerCase();\n\treturn normalized !== \"false\" && normalized !== \"0\";\n}\n\n// Close everything when this element is garbage collected.\n// This is primarily to avoid a console.warn that we didn't close() before GC.\n// There's no destructor for web components so this is the best we can do.\nconst cleanup = new FinalizationRegistry<Effect>((signals) => signals.close());\n\n// An optional web component that wraps a <canvas>\nexport default class MoqWatch extends HTMLElement {\n\tstatic observedAttributes = OBSERVED;\n\n\t// The connection to the moq-relay server.\n\tconnection: Moq.Connection.Reload;\n\n\t// The broadcast being watched.\n\tbroadcast: Broadcast;\n\n\t// The backend that powers this element.\n\tbackend: MultiBackend;\n\n\t// Set when the element is connected to the DOM.\n\t#enabled = new Signal(false);\n\n\t// Expose the Effect class, so users can easily create effects scoped to this element.\n\tsignals = new Effect();\n\n\tconstructor() {\n\t\tsuper();\n\n\t\tcleanup.register(this, this.signals);\n\n\t\tthis.connection = new Moq.Connection.Reload({\n\t\t\tenabled: this.#enabled,\n\t\t});\n\t\tthis.signals.cleanup(() => this.connection.close());\n\n\t\tthis.broadcast = new Broadcast({\n\t\t\tconnection: this.connection.established,\n\t\t\tannounced: this.connection.announced,\n\t\t\tenabled: this.#enabled,\n\t\t});\n\t\tthis.signals.cleanup(() => this.broadcast.close());\n\n\t\tthis.backend = new MultiBackend({\n\t\t\tbroadcast: this.broadcast,\n\t\t\tconnection: this.connection.established,\n\t\t});\n\t\tthis.signals.cleanup(() => this.backend.close());\n\n\t\t// Watch to see if the canvas element is added or removed.\n\t\tconst setElement = () => {\n\t\t\tconst canvas = this.querySelector(\"canvas\") as HTMLCanvasElement | undefined;\n\t\t\tconst video = this.querySelector(\"video\") as HTMLVideoElement | undefined;\n\t\t\tif (canvas && video) {\n\t\t\t\tthrow new Error(\"Cannot have both canvas and video elements\");\n\t\t\t}\n\t\t\tthis.backend.element.set(canvas ?? video);\n\t\t};\n\n\t\tconst observer = new MutationObserver(setElement);\n\t\tobserver.observe(this, { childList: true, subtree: true });\n\t\tthis.signals.cleanup(() => observer.disconnect());\n\t\tsetElement();\n\n\t\t// Optionally update attributes to match the library state.\n\t\t// This is kind of dangerous because it can create loops.\n\t\t// NOTE: This only runs when the element is connected to the DOM, which is not obvious.\n\t\t// This is because there's no destructor for web components to clean up our effects.\n\t\tthis.signals.run((effect) => {\n\t\t\tconst url = effect.get(this.connection.url);\n\t\t\tif (url) {\n\t\t\t\tthis.setAttribute(\"url\", url.toString());\n\t\t\t} else {\n\t\t\t\tthis.removeAttribute(\"url\");\n\t\t\t}\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tconst name = effect.get(this.broadcast.name);\n\t\t\tthis.setAttribute(\"name\", name.toString());\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tconst muted = effect.get(this.backend.audio.muted);\n\t\t\tif (muted) {\n\t\t\t\tthis.setAttribute(\"muted\", \"\");\n\t\t\t} else {\n\t\t\t\tthis.removeAttribute(\"muted\");\n\t\t\t}\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tconst paused = effect.get(this.backend.paused);\n\t\t\tif (paused) {\n\t\t\t\tthis.setAttribute(\"paused\", \"true\");\n\t\t\t} else {\n\t\t\t\tthis.removeAttribute(\"paused\");\n\t\t\t}\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tconst visible = effect.get(this.backend.visible);\n\t\t\tthis.setAttribute(\"visible\", visible);\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tconst volume = effect.get(this.backend.audio.volume);\n\t\t\tthis.setAttribute(\"volume\", volume.toString());\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tconst { min, max } = latencyBounds(effect.get(this.backend.latency));\n\t\t\t// Only reflect the collapsed `latency` sugar attribute when the range is actually\n\t\t\t// collapsed. An open range is expressed via latency-min/latency-max, and writing\n\t\t\t// `latency` here would round-trip back through attributeChangedCallback and collapse it.\n\t\t\tif (min !== max) return;\n\t\t\tif (min === \"real-time\") {\n\t\t\t\tthis.setAttribute(\"latency\", \"real-time\");\n\t\t\t} else {\n\t\t\t\tconst jitter = Math.floor(effect.get(this.backend.jitter));\n\t\t\t\tthis.setAttribute(\"latency\", jitter.toString());\n\t\t\t}\n\t\t});\n\n\t\t// Track the element's rendered size and feed it into the rendition picker,\n\t\t// scaled by devicePixelRatio so high-DPI screens still get sharp renditions.\n\t\tconst updateDimensions = (width: number, height: number) => {\n\t\t\tif (width <= 0 || height <= 0) return;\n\t\t\tconst dpr = window.devicePixelRatio || 1;\n\t\t\tthis.backend.video.source.target.update((prev) => ({\n\t\t\t\t...prev,\n\t\t\t\twidth: Math.round(width * dpr),\n\t\t\t\theight: Math.round(height * dpr),\n\t\t\t}));\n\t\t};\n\n\t\tconst resizeObserver = new ResizeObserver((entries) => {\n\t\t\tconst entry = entries[0];\n\t\t\tif (!entry) return;\n\t\t\tupdateDimensions(entry.contentRect.width, entry.contentRect.height);\n\t\t});\n\t\tresizeObserver.observe(this);\n\t\tthis.signals.cleanup(() => resizeObserver.disconnect());\n\n\t\t// Seed with the current size in case the observer doesn't fire immediately\n\t\t// (e.g. the element is still 0x0 when we attach).\n\t\tconst rect = this.getBoundingClientRect();\n\t\tupdateDimensions(rect.width, rect.height);\n\t}\n\n\t// Annoyingly, we have to use these callbacks to figure out when the element is connected to the DOM.\n\t// This wouldn't be so bad if there was a destructor for web components to clean up our effects.\n\tconnectedCallback() {\n\t\tthis.#enabled.set(true);\n\t\tthis.style.display = \"block\";\n\t\tthis.style.position = \"relative\";\n\t}\n\n\tdisconnectedCallback() {\n\t\t// Stop everything but don't actually cleanup just in case we get added back to the DOM.\n\t\tthis.#enabled.set(false);\n\t}\n\n\t// Parse a single latency bound: absent or \"real-time\" is adaptive, otherwise a fixed ms value.\n\t#parseBound(value: string | null): Bound {\n\t\tif (!value || value === \"real-time\") return \"real-time\";\n\t\tconst parsed = Number.parseFloat(value);\n\t\treturn (Number.isFinite(parsed) ? parsed : 100) as Time.Milli;\n\t}\n\n\tattributeChangedCallback(name: Observed, oldValue: string | null, newValue: string | null) {\n\t\tif (oldValue === newValue) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (name === \"url\") {\n\t\t\tthis.connection.url.set(newValue ? new URL(newValue) : undefined);\n\t\t} else if (name === \"name\") {\n\t\t\tthis.broadcast.name.set(Moq.Path.from(newValue ?? \"\"));\n\t\t} else if (name === \"paused\") {\n\t\t\tthis.backend.paused.set(newValue !== null);\n\t\t} else if (name === \"volume\") {\n\t\t\tconst volume = newValue ? Number.parseFloat(newValue) : 0.5;\n\t\t\tthis.backend.audio.volume.set(volume);\n\t\t} else if (name === \"muted\") {\n\t\t\tthis.backend.audio.muted.set(newValue !== null);\n\t\t} else if (name === \"visible\") {\n\t\t\tthis.backend.visible.set(parseVisible(newValue));\n\t\t} else if (name === \"reload\") {\n\t\t\tthis.broadcast.reload.set(parseBoolean(newValue, true));\n\t\t} else if (name === \"latency\") {\n\t\t\t// Sugar: collapse the floor and ceiling to a single value.\n\t\t\tthis.latency = this.#parseBound(newValue);\n\t\t} else if (name === \"latency-min\") {\n\t\t\tthis.latencyMin = this.#parseBound(newValue);\n\t\t} else if (name === \"latency-max\") {\n\t\t\tthis.latencyMax = this.#parseBound(newValue);\n\t\t} else if (name === \"jitter\") {\n\t\t\t// Deprecated: use latency=\"<number>\" instead.\n\t\t\tthis.latency = this.#parseBound(newValue);\n\t\t} else if (name === \"catalog-format\") {\n\t\t\tthis.broadcast.catalogFormat.set(parseCatalogFormat(newValue));\n\t\t} else {\n\t\t\tconst exhaustive: never = name;\n\t\t\tthrow new Error(`Invalid attribute: ${exhaustive}`);\n\t\t}\n\t}\n\n\tget url(): URL | undefined {\n\t\treturn this.connection.url.peek();\n\t}\n\n\tset url(value: string | URL | undefined) {\n\t\tthis.connection.url.set(value ? new URL(value) : undefined);\n\t}\n\n\tget name(): Moq.Path.Valid {\n\t\treturn this.broadcast.name.peek();\n\t}\n\n\tset name(value: string | Moq.Path.Valid) {\n\t\tthis.broadcast.name.set(Moq.Path.from(value));\n\t}\n\n\tget paused(): boolean {\n\t\treturn this.backend.paused.peek();\n\t}\n\n\tset paused(value: boolean) {\n\t\tthis.backend.paused.set(value);\n\t}\n\n\tget volume(): number {\n\t\treturn this.backend.audio.volume.peek();\n\t}\n\n\tset volume(value: number) {\n\t\tthis.backend.audio.volume.set(value);\n\t}\n\n\tget muted(): boolean {\n\t\treturn this.backend.audio.muted.peek();\n\t}\n\n\tset muted(value: boolean) {\n\t\tthis.backend.audio.muted.set(value);\n\t}\n\n\t/** When video is downloaded relative to the canvas position. See {@link Visible}. */\n\tget visible(): Visible {\n\t\treturn this.backend.visible.peek();\n\t}\n\n\tset visible(value: Visible) {\n\t\tthis.backend.visible.set(value);\n\t}\n\n\tget reload(): boolean {\n\t\treturn this.broadcast.reload.peek();\n\t}\n\n\tset reload(value: boolean) {\n\t\tthis.broadcast.reload.set(value);\n\t}\n\n\t/**\n\t * The latency target. Assign a scalar (or `\"real-time\"`) to minimize latency, or an object\n\t * `{ min, max }` to open a range and buffer future-dated frames. See {@link Latency}.\n\t */\n\tget latency(): Latency {\n\t\treturn this.backend.latency.peek();\n\t}\n\n\tset latency(value: Latency) {\n\t\tthis.backend.latency.set(value);\n\t}\n\n\t/** The latency floor (jitter/startup buffer). Read-modify-writes `latency`, leaving the ceiling. */\n\tget latencyMin(): Bound {\n\t\treturn latencyBounds(this.backend.latency.peek()).min;\n\t}\n\n\tset latencyMin(value: Bound) {\n\t\tconst { max } = latencyBounds(this.backend.latency.peek());\n\t\tthis.backend.latency.set(latencyFromBounds(value, max));\n\t}\n\n\t/**\n\t * The latency ceiling: `\"real-time\"` (default) minimizes, a number caps at that many ms. A\n\t * ceiling above the floor enables buffered playback: build up a buffer from future-dated frames\n\t * (e.g. TTS written faster than real-time) and only skip ahead past the cap. Call `reset()` at\n\t * each utterance boundary. Read-modify-writes `latency`, leaving the floor untouched.\n\t */\n\tget latencyMax(): Bound {\n\t\treturn latencyBounds(this.backend.latency.peek()).max;\n\t}\n\n\tset latencyMax(value: Bound) {\n\t\tconst { min } = latencyBounds(this.backend.latency.peek());\n\t\tthis.backend.latency.set(latencyFromBounds(min, value));\n\t}\n\n\t/** The jitter buffer in milliseconds. */\n\tget jitter(): Time.Milli {\n\t\treturn this.backend.jitter.peek();\n\t}\n\n\t/** @deprecated Use `latency = <number>` instead. */\n\tset jitter(value: number) {\n\t\tthis.latency = value as Time.Milli;\n\t}\n\n\t/** Re-anchor playback and flush the audio buffer at an utterance boundary (buffered mode). */\n\treset(): void {\n\t\tthis.backend.reset();\n\t}\n\n\tget catalogFormat(): CatalogFormat | undefined {\n\t\treturn this.broadcast.catalogFormat.peek();\n\t}\n\n\tset catalogFormat(value: CatalogFormat | undefined) {\n\t\tthis.broadcast.catalogFormat.set(value);\n\t}\n\n\t/**\n\t * The active catalog. Assign directly when `catalogFormat` is `\"manual\"`;\n\t * for `\"hang\"` and `\"msf\"` this is overwritten by the fetch loop.\n\t */\n\tget catalog(): Catalog.Root | undefined {\n\t\treturn this.broadcast.catalog.peek();\n\t}\n\n\tset catalog(value: Catalog.Root | undefined) {\n\t\tthis.broadcast.catalog.set(value);\n\t}\n}\n\ncustomElements.define(\"moq-watch\", MoqWatch);\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t\"moq-watch\": MoqWatch;\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"element.js","names":["#enabled","#name","#reload","#catalogFormat","#catalog","#syncLatency","#videoPaced","#videoEnabled","#audioEnabled","#canvas","#unmuteVolume","#reflectLatency","#reflectingLatency","#parseBound","#parseLatency"],"sources":["../src/element.ts"],"sourcesContent":["/**\n * The `<moq-watch>` custom element: a broadcast player driven by HTML attributes.\n *\n * Side-effectful: importing this registers the element.\n *\n * @module\n */\nimport type * as Catalog from \"@moq/hang/catalog\";\nimport type { Time } from \"@moq/net\";\nimport * as Moq from \"@moq/net\";\nimport { Effect, Signal } from \"@moq/signals\";\nimport * as Audio from \"./audio\";\nimport { Broadcast, type CatalogFormat, parseCatalogFormat } from \"./broadcast\";\nimport { type Bound, type Latency, latencyBounds, latencyFromBounds, type Paced, Sync } from \"./sync\";\nimport * as Video from \"./video\";\n\nconst OBSERVED = [\n\t\"url\",\n\t\"name\",\n\t\"paused\",\n\t\"volume\",\n\t\"muted\",\n\t\"visible\",\n\t\"reload\",\n\t\"latency\",\n\t\"latency-min\",\n\t\"latency-max\",\n\t\"jitter\",\n\t\"catalog-format\",\n] as const;\ntype Observed = (typeof OBSERVED)[number];\n\n// Parse the `visible` attribute into a Visible value, falling back to \"20%\".\nfunction parseVisible(value: string | null): Video.Visible {\n\tconst trimmed = value?.trim();\n\tif (!trimmed) return \"20%\";\n\tif (trimmed === \"never\" || trimmed === \"always\") return trimmed;\n\t// A CSS length usable as an IntersectionObserver rootMargin (px or %).\n\tif (/^-?\\d+(\\.\\d+)?(px|%)$/.test(trimmed)) return trimmed;\n\t// Allow a bare number as a px convenience (e.g. visible=\"200\").\n\tif (/^-?\\d+(\\.\\d+)?$/.test(trimmed)) return `${trimmed}px`;\n\tconsole.warn(`moq-watch: invalid visible=\"${value}\", expected \"never\", \"always\", or a CSS length like \"200px\"`);\n\treturn \"20%\";\n}\n\n/**\n * Parse a boolean attribute: absent uses `defaultValue`, bare presence is true, and an explicit\n * `\"false\"`/`\"0\"` is false. Presence alone can't express false, and attributes that default to\n * true (`reload`) need to, so every boolean attribute accepts the explicit form.\n */\nfunction parseBoolean(value: string | null, defaultValue: boolean): boolean {\n\tif (value === null) return defaultValue;\n\tconst normalized = value.trim().toLowerCase();\n\treturn normalized !== \"false\" && normalized !== \"0\";\n}\n\n// Close everything when this element is garbage collected.\n// This is primarily to avoid a console.warn that we didn't close() before GC.\n// There's no destructor for web components so this is the best we can do.\nconst cleanup = new FinalizationRegistry<Effect>((signals) => signals.close());\n\n// An optional web component that wraps a <canvas>\nexport default class MoqWatch extends HTMLElement {\n\tstatic observedAttributes = OBSERVED;\n\n\t// The connection to the moq-relay server.\n\tconnection: Moq.Connection.Reload;\n\n\t// The broadcast being watched.\n\tbroadcast: Broadcast;\n\n\t/** Downloads and decodes the video track. `video.source` picks the rendition. */\n\tvideo: Video.Decoder;\n\n\t/** Downloads and decodes the audio track. `audio.source` picks the rendition. */\n\taudio: Audio.Decoder;\n\n\t/** Paints decoded frames to the nested <canvas>. */\n\trenderer: Video.Renderer;\n\n\t/** Plays decoded samples through the speakers. */\n\temitter: Audio.Emitter;\n\n\t/** Keeps audio and video playing at the target latency. */\n\tsync: Sync;\n\n\t// The mutable user controls. As the top of the tree, this element owns the\n\t// writable Signals and wires read-only views into the pipeline. The UI and\n\t// the attribute/property accessors read and write these directly.\n\treadonly controls = {\n\t\tpaused: new Signal(false),\n\t\tvolume: new Signal(0.5),\n\t\tmuted: new Signal(false),\n\t\t// When video is downloaded relative to the canvas position. See {@link Video.Visible}.\n\t\tvisible: new Signal<Video.Visible>(\"20%\"),\n\t\tlatency: new Signal<Latency>(\"real-time\"),\n\t\t// The desired video rendition (resolution/bitrate cap).\n\t\ttarget: new Signal<Video.Target | undefined>(undefined),\n\t};\n\n\t// Broadcast configuration owned here and wired into `broadcast` as inputs.\n\t#name = new Signal<Moq.Path.Valid>(Moq.Path.empty());\n\t#reload = new Signal(true);\n\t#catalogFormat = new Signal<CatalogFormat | undefined>(undefined);\n\t#catalog = new Signal<Catalog.Root | undefined>(undefined);\n\n\t// The canvas element to render into.\n\t#canvas = new Signal<HTMLCanvasElement | undefined>(undefined);\n\n\t// Whether to download. Driven by the renderer/emitter policy, read by the decoders.\n\t#videoEnabled = new Signal(false);\n\t#audioEnabled = new Signal(false);\n\n\t// Set while this element writes the `latency` attribute itself, so attributeChangedCallback\n\t// ignores the echo. Dropping a stale value would otherwise parse back as \"real-time\" and\n\t// clobber the range that replaced it.\n\t#reflectingLatency = false;\n\n\t// Whether video paces off the clock, cleared while the latency is \"instant\".\n\t#videoPaced = new Signal(true);\n\n\t// The latency handed to Sync, which has no way to express \"instant\".\n\t#syncLatency = new Signal<Paced>(\"real-time\");\n\n\t// Set when the element is connected to the DOM.\n\t#enabled = new Signal(false);\n\n\t// Stashed volume to restore on unmute.\n\t#unmuteVolume = 0.5;\n\n\t/**\n\t * Effects scoped to this element's lifetime, closed on disconnect.\n\t *\n\t * Public because the element is the top of the tree: it's where an application hangs its own\n\t * reactivity. The components underneath keep theirs private, so `close()` is the only handle.\n\t */\n\treadonly signals = new Effect();\n\n\tconstructor() {\n\t\tsuper();\n\n\t\tcleanup.register(this, this.signals);\n\n\t\tthis.connection = new Moq.Connection.Reload({\n\t\t\tenabled: this.#enabled,\n\t\t});\n\t\tthis.signals.cleanup(() => this.connection.close());\n\n\t\tthis.broadcast = new Broadcast({\n\t\t\tconnection: this.connection.established,\n\t\t\tenabled: this.#enabled,\n\t\t\tname: this.#name,\n\t\t\treload: this.#reload,\n\t\t\tcatalogFormat: this.#catalogFormat,\n\t\t\tcatalog: this.#catalog,\n\t\t});\n\t\tthis.signals.cleanup(() => this.broadcast.close());\n\n\t\t// The decoders' support probes drive rendition selection: anything WebCodecs can't play is filtered out.\n\t\tconst videoSource = new Video.Source({\n\t\t\tbroadcast: this.broadcast,\n\t\t\ttarget: this.controls.target,\n\t\t\tsupported: Video.Decoder.supported,\n\t\t});\n\t\tconst audioSource = new Audio.Source({\n\t\t\tbroadcast: this.broadcast,\n\t\t\tsupported: Audio.Decoder.supported,\n\t\t});\n\t\tthis.signals.cleanup(() => {\n\t\t\tvideoSource.close();\n\t\t\taudioSource.close();\n\t\t});\n\n\t\t// Sources produce the per-rendition jitter that Sync reads, so they're created\n\t\t// before Sync to avoid a construction cycle.\n\t\t//\n\t\t// Sync can't implement \"instant\" on its own: not pacing video and disabling audio happens\n\t\t// here. Hand it a zero buffer instead.\n\t\tthis.signals.run((effect) => {\n\t\t\tconst latency = effect.get(this.controls.latency);\n\t\t\tthis.#syncLatency.set(latency === \"instant\" ? Moq.Time.Milli.zero : latency);\n\t\t});\n\n\t\tthis.sync = new Sync({\n\t\t\tlatency: this.#syncLatency,\n\t\t\tconnection: this.connection.established,\n\t\t\tvideo: videoSource.out.jitter,\n\t\t\taudio: audioSource.out.jitter,\n\t\t});\n\t\tthis.signals.cleanup(() => this.sync.close());\n\n\t\t// `latency=\"instant\"` stops video waiting on the clock, so frames paint the moment they\n\t\t// decode. The clock stays wired: it still tracks receipts and rewinds.\n\t\tthis.signals.run((effect) => {\n\t\t\tthis.#videoPaced.set(effect.get(this.controls.latency) !== \"instant\");\n\t\t});\n\n\t\tthis.video = new Video.Decoder(videoSource, this.sync, {\n\t\t\tenabled: this.#videoEnabled,\n\t\t\tpaced: this.#videoPaced,\n\t\t});\n\t\tthis.audio = new Audio.Decoder(audioSource, this.sync, { enabled: this.#audioEnabled });\n\t\tthis.signals.cleanup(() => {\n\t\t\tthis.video.close();\n\t\t\tthis.audio.close();\n\t\t});\n\n\t\tthis.emitter = new Audio.Emitter(this.audio, {\n\t\t\tvolume: this.controls.volume,\n\t\t\tmuted: this.controls.muted,\n\t\t\tpaused: this.controls.paused,\n\t\t});\n\t\tthis.renderer = new Video.Renderer(this.video, {\n\t\t\tcanvas: this.#canvas,\n\t\t\tvisible: this.controls.visible,\n\t\t});\n\t\tthis.signals.cleanup(() => {\n\t\t\tthis.emitter.close();\n\t\t\tthis.renderer.close();\n\t\t});\n\n\t\t// Audio download follows the emitter's enable policy (paused/muted), except an instant\n\t\t// latency turns it off outright: the ring needs a target depth to avoid underrunning, and\n\t\t// unpaced video has nothing pulling it back toward the audio clock.\n\t\tthis.signals.run((effect) => {\n\t\t\tconst enabled = effect.get(this.emitter.out.enabled);\n\t\t\tthis.#audioEnabled.set(enabled && effect.get(this.controls.latency) !== \"instant\");\n\t\t});\n\n\t\t// Stopping the download leaves the ring holding a floor's worth of PCM, and the emitter\n\t\t// stays connected to drain it. Flush on the way in so audio stops now instead of playing\n\t\t// against video that just jumped to the live edge.\n\t\tthis.signals.run((effect) => {\n\t\t\tif (effect.get(this.controls.latency) !== \"instant\") return;\n\t\t\tthis.audio.reset();\n\t\t});\n\n\t\t// Video downloads while playing and on-screen. When paused, keep downloading only\n\t\t// until a frame is on the canvas, then stop: a cold paused start still shows a poster\n\t\t// instead of black, without streaming while paused. Read the rendered frame only in\n\t\t// the paused branch so playback doesn't re-run this every painted frame.\n\t\tthis.signals.run((effect) => {\n\t\t\tconst visible = effect.get(this.renderer.out.visible);\n\t\t\tif (!effect.get(this.controls.paused)) {\n\t\t\t\tthis.#videoEnabled.set(visible);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst frame = effect.get(this.renderer.out.frame);\n\t\t\tthis.#videoEnabled.set(visible && !frame);\n\t\t});\n\n\t\t// Mute/volume coupling. The element owns the writable volume/muted Signals, so\n\t\t// the policy lives here: muting stashes and zeroes the volume; a zero volume\n\t\t// reports as muted.\n\t\tthis.signals.run((effect) => {\n\t\t\tconst muted = effect.get(this.controls.muted);\n\t\t\tif (muted) {\n\t\t\t\tthis.#unmuteVolume = this.controls.volume.peek() || 0.5;\n\t\t\t\tthis.controls.volume.set(0);\n\t\t\t} else {\n\t\t\t\tthis.controls.volume.set(this.#unmuteVolume);\n\t\t\t}\n\t\t});\n\t\tthis.signals.run((effect) => {\n\t\t\tconst volume = effect.get(this.controls.volume);\n\t\t\tthis.controls.muted.set(volume === 0);\n\t\t});\n\n\t\t// Watch to see if the canvas element is added or removed.\n\t\tconst setCanvas = () => {\n\t\t\tconst canvas = this.querySelector(\"canvas\") ?? undefined;\n\n\t\t\t// A <video> child used to render via MSE. Nothing renders it now, and audio still plays,\n\t\t\t// so the failure looks like a bug in the page instead of a removed feature.\n\t\t\tif (!canvas && this.querySelector(\"video\")) {\n\t\t\t\tconsole.warn(\"moq-watch: rendering requires a <canvas> child; a <video> child does nothing.\");\n\t\t\t}\n\n\t\t\tthis.#canvas.set(canvas);\n\t\t};\n\n\t\tconst observer = new MutationObserver(setCanvas);\n\t\tobserver.observe(this, { childList: true, subtree: true });\n\t\tthis.signals.cleanup(() => observer.disconnect());\n\t\tsetCanvas();\n\n\t\t// Optionally update attributes to match the library state.\n\t\t// This is kind of dangerous because it can create loops.\n\t\t// NOTE: This only runs when the element is connected to the DOM, which is not obvious.\n\t\t// This is because there's no destructor for web components to clean up our effects.\n\t\tthis.signals.run((effect) => {\n\t\t\tconst url = effect.get(this.connection.url);\n\t\t\tif (url) {\n\t\t\t\tthis.setAttribute(\"url\", url.toString());\n\t\t\t} else {\n\t\t\t\tthis.removeAttribute(\"url\");\n\t\t\t}\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tconst name = effect.get(this.#name);\n\t\t\tthis.setAttribute(\"name\", name.toString());\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tconst muted = effect.get(this.controls.muted);\n\t\t\tif (muted) {\n\t\t\t\tthis.setAttribute(\"muted\", \"\");\n\t\t\t} else {\n\t\t\t\tthis.removeAttribute(\"muted\");\n\t\t\t}\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tconst paused = effect.get(this.controls.paused);\n\t\t\tif (paused) {\n\t\t\t\tthis.setAttribute(\"paused\", \"\");\n\t\t\t} else {\n\t\t\t\tthis.removeAttribute(\"paused\");\n\t\t\t}\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tconst volume = effect.get(this.controls.volume);\n\t\t\tthis.setAttribute(\"volume\", volume.toString());\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tconst visible = effect.get(this.controls.visible);\n\t\t\tthis.setAttribute(\"visible\", visible);\n\t\t});\n\n\t\tthis.signals.run((effect) => {\n\t\t\tconst latency = effect.get(this.controls.latency);\n\t\t\tif (latency === \"instant\") {\n\t\t\t\tthis.#reflectLatency(\"instant\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst { min, max } = latencyBounds(latency);\n\t\t\t// Only reflect the collapsed `latency` sugar attribute when the range is actually\n\t\t\t// collapsed. An open range is expressed via latency-min/latency-max, and writing\n\t\t\t// `latency` here would round-trip back through attributeChangedCallback and collapse it.\n\t\t\tif (min !== max) {\n\t\t\t\t// Still drop a stale \"instant\": leaving it would advertise a mode the element is no\n\t\t\t\t// longer in, and a clone would come back up in it.\n\t\t\t\tif (this.getAttribute(\"latency\") === \"instant\") this.#reflectLatency(undefined);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (min === \"real-time\") {\n\t\t\t\tthis.#reflectLatency(\"real-time\");\n\t\t\t} else {\n\t\t\t\tconst jitter = Math.floor(effect.get(this.sync.out.jitter));\n\t\t\t\tthis.#reflectLatency(jitter.toString());\n\t\t\t}\n\t\t});\n\n\t\t// Track the element's rendered size and feed it into the rendition picker,\n\t\t// scaled by devicePixelRatio so high-DPI screens still get sharp renditions.\n\t\tconst updateDimensions = (width: number, height: number) => {\n\t\t\tif (width <= 0 || height <= 0) return;\n\t\t\tconst dpr = window.devicePixelRatio || 1;\n\t\t\tthis.controls.target.update((prev) => ({\n\t\t\t\t...prev,\n\t\t\t\twidth: Math.round(width * dpr),\n\t\t\t\theight: Math.round(height * dpr),\n\t\t\t}));\n\t\t};\n\n\t\tconst resizeObserver = new ResizeObserver((entries) => {\n\t\t\tconst entry = entries[0];\n\t\t\tif (!entry) return;\n\t\t\tupdateDimensions(entry.contentRect.width, entry.contentRect.height);\n\t\t});\n\t\tresizeObserver.observe(this);\n\t\tthis.signals.cleanup(() => resizeObserver.disconnect());\n\n\t\t// Seed with the current size in case the observer doesn't fire immediately\n\t\t// (e.g. the element is still 0x0 when we attach).\n\t\tconst rect = this.getBoundingClientRect();\n\t\tupdateDimensions(rect.width, rect.height);\n\t}\n\n\t// Annoyingly, we have to use these callbacks to figure out when the element is connected to the DOM.\n\t// This wouldn't be so bad if there was a destructor for web components to clean up our effects.\n\tconnectedCallback() {\n\t\tthis.#enabled.set(true);\n\t\tthis.style.display = \"block\";\n\t\tthis.style.position = \"relative\";\n\t}\n\n\tdisconnectedCallback() {\n\t\t// Stop everything but don't actually cleanup just in case we get added back to the DOM.\n\t\tthis.#enabled.set(false);\n\t}\n\n\t// Parse a single latency bound: absent or \"real-time\" is adaptive, otherwise a fixed ms value.\n\t#parseBound(value: string | null): Bound {\n\t\tif (!value || value === \"real-time\") return \"real-time\";\n\t\tif (value === \"instant\") {\n\t\t\tconsole.warn('moq-watch: \"instant\" is not a bound, use latency=\"instant\"');\n\t\t\treturn \"real-time\";\n\t\t}\n\t\tconst parsed = Number.parseFloat(value);\n\t\treturn Moq.Time.Milli(Number.isFinite(parsed) ? parsed : 100);\n\t}\n\n\t// Write the `latency` attribute (undefined removes it) without the echo coming back through\n\t// attributeChangedCallback. Custom element reactions run before setAttribute/removeAttribute\n\t// returns, so the flag still covers the callback.\n\t#reflectLatency(value: string | undefined): void {\n\t\tthis.#reflectingLatency = true;\n\t\ttry {\n\t\t\tif (value === undefined) this.removeAttribute(\"latency\");\n\t\t\telse this.setAttribute(\"latency\", value);\n\t\t} finally {\n\t\t\tthis.#reflectingLatency = false;\n\t\t}\n\t}\n\n\t// Parse the `latency` attribute, which also accepts the range-replacing \"instant\".\n\t#parseLatency(value: string | null): Latency {\n\t\treturn value?.trim() === \"instant\" ? \"instant\" : this.#parseBound(value);\n\t}\n\n\tattributeChangedCallback(name: Observed, oldValue: string | null, newValue: string | null) {\n\t\tif (oldValue === newValue) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (name === \"url\") {\n\t\t\tthis.connection.url.set(newValue ? new URL(newValue) : undefined);\n\t\t} else if (name === \"name\") {\n\t\t\tthis.#name.set(Moq.Path.from(newValue ?? \"\"));\n\t\t} else if (name === \"paused\") {\n\t\t\tthis.controls.paused.set(parseBoolean(newValue, false));\n\t\t} else if (name === \"volume\") {\n\t\t\tconst volume = newValue ? Number.parseFloat(newValue) : 0.5;\n\t\t\tthis.controls.volume.set(volume);\n\t\t} else if (name === \"muted\") {\n\t\t\tthis.controls.muted.set(parseBoolean(newValue, false));\n\t\t} else if (name === \"visible\") {\n\t\t\tthis.controls.visible.set(parseVisible(newValue));\n\t\t} else if (name === \"reload\") {\n\t\t\tthis.#reload.set(parseBoolean(newValue, true));\n\t\t} else if (name === \"latency\") {\n\t\t\t// Sugar: collapse the floor and ceiling to a single value.\n\t\t\tif (!this.#reflectingLatency) this.latency = this.#parseLatency(newValue);\n\t\t} else if (name === \"latency-min\") {\n\t\t\tthis.latencyMin = this.#parseBound(newValue);\n\t\t} else if (name === \"latency-max\") {\n\t\t\tthis.latencyMax = this.#parseBound(newValue);\n\t\t} else if (name === \"jitter\") {\n\t\t\t// Deprecated: use latency=\"<number>\" instead.\n\t\t\tthis.latency = this.#parseBound(newValue);\n\t\t} else if (name === \"catalog-format\") {\n\t\t\tthis.#catalogFormat.set(parseCatalogFormat(newValue));\n\t\t} else {\n\t\t\tconst exhaustive: never = name;\n\t\t\tthrow new Error(`Invalid attribute: ${exhaustive}`);\n\t\t}\n\t}\n\n\tget url(): URL | undefined {\n\t\treturn this.connection.url.peek();\n\t}\n\n\tset url(value: string | URL | undefined) {\n\t\tthis.connection.url.set(value ? new URL(value) : undefined);\n\t}\n\n\tget name(): Moq.Path.Valid {\n\t\treturn this.#name.peek();\n\t}\n\n\tset name(value: string | Moq.Path.Valid) {\n\t\tthis.#name.set(Moq.Path.from(value));\n\t}\n\n\tget paused(): boolean {\n\t\treturn this.controls.paused.peek();\n\t}\n\n\tset paused(value: boolean) {\n\t\tthis.controls.paused.set(value);\n\t}\n\n\tget volume(): number {\n\t\treturn this.controls.volume.peek();\n\t}\n\n\tset volume(value: number) {\n\t\tthis.controls.volume.set(value);\n\t}\n\n\tget muted(): boolean {\n\t\treturn this.controls.muted.peek();\n\t}\n\n\tset muted(value: boolean) {\n\t\tthis.controls.muted.set(value);\n\t}\n\n\tget visible(): Video.Visible {\n\t\treturn this.controls.visible.peek();\n\t}\n\n\tset visible(value: Video.Visible) {\n\t\tthis.controls.visible.set(value);\n\t}\n\n\tget reload(): boolean {\n\t\treturn this.#reload.peek();\n\t}\n\n\tset reload(value: boolean) {\n\t\tthis.#reload.set(value);\n\t}\n\n\t/**\n\t * The latency target. Assign a scalar (or `\"real-time\"`) to minimize latency, or an object\n\t * `{ min, max }` to open a range and buffer future-dated frames. See {@link Latency}.\n\t *\n\t * `\"instant\"` drops the clock instead: video paints the moment it decodes and audio is\n\t * disabled. Assigning `latencyMin` or `latencyMax` leaves that mode, since both write a range.\n\t */\n\tget latency(): Latency {\n\t\treturn this.controls.latency.peek();\n\t}\n\n\tset latency(value: Latency) {\n\t\tthis.controls.latency.set(value);\n\t}\n\n\t/** The latency floor (jitter/startup buffer). Read-modify-writes `latency`, leaving the ceiling. */\n\tget latencyMin(): Bound {\n\t\treturn latencyBounds(this.controls.latency.peek()).min;\n\t}\n\n\tset latencyMin(value: Bound) {\n\t\tconst { max } = latencyBounds(this.controls.latency.peek());\n\t\tthis.controls.latency.set(latencyFromBounds(value, max));\n\t}\n\n\t/**\n\t * The latency ceiling: `\"real-time\"` (default) minimizes, a number caps at that many ms. A\n\t * ceiling above the floor enables buffered playback: build up a buffer from future-dated frames\n\t * (e.g. TTS written faster than real-time) and only skip ahead past the cap. Call `reset()` at\n\t * each utterance boundary. Read-modify-writes `latency`, leaving the floor untouched.\n\t */\n\tget latencyMax(): Bound {\n\t\treturn latencyBounds(this.controls.latency.peek()).max;\n\t}\n\n\tset latencyMax(value: Bound) {\n\t\tconst { min } = latencyBounds(this.controls.latency.peek());\n\t\tthis.controls.latency.set(latencyFromBounds(min, value));\n\t}\n\n\t/** The jitter buffer in milliseconds. */\n\tget jitter(): Time.Milli {\n\t\treturn this.sync.out.jitter.peek();\n\t}\n\n\t/**\n\t * Re-anchor playback at an utterance boundary in buffered mode: reset the sync reference\n\t * and flush the audio buffer so the next utterance plays from its own first frame.\n\t */\n\treset(): void {\n\t\tthis.sync.reset();\n\t\tthis.audio.reset();\n\t}\n\n\tget catalogFormat(): CatalogFormat | undefined {\n\t\treturn this.#catalogFormat.peek();\n\t}\n\n\tset catalogFormat(value: CatalogFormat | undefined) {\n\t\tthis.#catalogFormat.set(value);\n\t}\n\n\t/**\n\t * The active catalog. Assign directly when `catalogFormat` is `\"manual\"`;\n\t * for `\"hang\"` and `\"msf\"` this is overwritten by the fetch loop.\n\t */\n\tget catalog(): Catalog.Root | undefined {\n\t\treturn this.broadcast.out.catalog.peek();\n\t}\n\n\tset catalog(value: Catalog.Root | undefined) {\n\t\tthis.#catalog.set(value);\n\t}\n}\n\ncustomElements.define(\"moq-watch\", MoqWatch);\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t\"moq-watch\": MoqWatch;\n\t}\n}\n"],"mappings":"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"}
package/index.d.ts CHANGED
@@ -1,14 +1,17 @@
1
+ /**
2
+ * Subscribe to a broadcast, decode it, and render it.
3
+ *
4
+ * The JS API: compose {@link Broadcast}, {@link Sync}, and the per-track `Video`/`Audio` pipelines
5
+ * yourself. For a drop-in element, import `@norskvideo/moq-watch/element` instead.
6
+ *
7
+ * @module
8
+ */
1
9
  export * as Hang from "@norskvideo/moq-hang";
2
- export * as Json from "@norskvideo/moq-json";
10
+ export type { BufferedRange, BufferedRanges } from "@norskvideo/moq-hang/container";
3
11
  export * as Net from "@norskvideo/moq-net";
4
- /** @deprecated Use `Net` instead. */
5
- export * as Lite from "@norskvideo/moq-net";
6
12
  export * as Signals from "@norskvideo/moq-signals";
7
13
  export * as Audio from "./audio";
8
- export * from "./backend";
9
14
  export * from "./broadcast";
10
- export * from "./buffered";
11
- export * as Mse from "./mse";
12
15
  export * from "./sync";
13
16
  export * as Video from "./video";
14
17
  //# sourceMappingURL=index.d.ts.map