@streaming-cdn/rtc-web 1.3.15 → 1.3.17

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.
@@ -1,25 +1,25 @@
1
- const Ce = 0.02, Ie = 1.4;
2
- function ie(r, e, t = 0.02, i = 1.4) {
3
- const n = r.filter((o) => o.level >= t);
1
+ const qe = 0.02, Oe = 1.4;
2
+ function oe(a, e, t = 0.02, i = 1.4) {
3
+ const n = a.filter((r) => r.level >= t);
4
4
  if (n.length === 0) return null;
5
- const s = n.reduce((o, c) => c.level > o.level ? c : o), a = n.find((o) => o.participantId === e);
6
- return a ? s.participantId === e ? e : s.level >= a.level * i ? s.participantId : e : s.participantId;
5
+ const s = n.reduce((r, c) => c.level > r.level ? c : r), o = n.find((r) => r.participantId === e);
6
+ return o ? s.participantId === e ? e : s.level >= o.level * i ? s.participantId : e : s.participantId;
7
7
  }
8
- function ne(r) {
8
+ function ce(a) {
9
9
  const e = [];
10
- for (const t of r) {
10
+ for (const t of a) {
11
11
  if (String(t.type ?? "") !== "inbound-rtp" || String(t.kind ?? t.mediaType ?? "") !== "audio") continue;
12
12
  const i = Number(t.audioLevel ?? NaN);
13
13
  Number.isFinite(i) && e.push({ mid: typeof t.mid == "string" ? t.mid : null, level: i });
14
14
  }
15
15
  return e;
16
16
  }
17
- const M = {
17
+ const N = {
18
18
  audio: { active: !1, maxBitrate: 0, maxFramerate: 0, scaleResolutionDownBy: 4 },
19
19
  low: { active: !0, maxBitrate: 35e4, maxFramerate: 15, scaleResolutionDownBy: 4 },
20
20
  medium: { active: !0, maxBitrate: 11e5, maxFramerate: 24, scaleResolutionDownBy: 2 },
21
21
  high: { active: !0, maxBitrate: 28e5, maxFramerate: 30, scaleResolutionDownBy: 1 }
22
- }, ae = {
22
+ }, he = {
23
23
  warmupSamples: 3,
24
24
  poorSamplesToStepDown: 2,
25
25
  goodSamplesToStepUp: 6,
@@ -31,45 +31,45 @@ const M = {
31
31
  goodRttMs: 250,
32
32
  sustainRatio: 0.8
33
33
  };
34
- function re(r) {
35
- const e = String(r ?? "").trim().toLowerCase();
34
+ function ue(a) {
35
+ const e = String(a ?? "").trim().toLowerCase();
36
36
  return !e || e === "none" ? "" : e === "cpu" || e === "bandwidth" ? e : "other";
37
37
  }
38
- function oe(r, e, t, i, n = {}) {
39
- const s = { ...ae, ...n.thresholds }, a = ["low", "medium", "high"], o = n.ceiling && n.ceiling !== "audio" ? n.ceiling : "high", c = a.indexOf(o), d = a.indexOf(r), l = (n.samplesSeen ?? s.warmupSamples) + 1, h = l <= s.warmupSamples, v = re(e.qualityLimitationReason), g = e.packetLossPct ?? 0, k = e.rttMs ?? 0, p = e.availableOutgoingBitrateBps, C = M[r].maxBitrate, S = g >= s.criticalLossPct || k >= s.criticalRttMs || v === "cpu", y = S || g >= s.poorLossPct || k >= s.poorRttMs || v === "bandwidth", T = p == null || p >= C * s.sustainRatio, I = g < s.goodLossPct && k < s.goodRttMs && !v && T, R = y ? t + 1 : 0, E = I ? i + 1 : 0, f = { quality: r, poorSamples: R, goodSamples: E, samplesSeen: l, changed: !1 };
40
- return d > c ? { quality: a[c], poorSamples: 0, goodSamples: 0, samplesSeen: l, changed: !0 } : h ? f : S && d > 0 ? { quality: a[d - 1], poorSamples: 0, goodSamples: 0, samplesSeen: l, changed: !0 } : R >= s.poorSamplesToStepDown && d > 0 ? { quality: a[d - 1], poorSamples: 0, goodSamples: 0, samplesSeen: l, changed: !0 } : E >= s.goodSamplesToStepUp && d < c ? { quality: a[d + 1], poorSamples: 0, goodSamples: 0, samplesSeen: l, changed: !0 } : f;
38
+ function me(a, e, t, i, n = {}) {
39
+ const s = { ...he, ...n.thresholds }, o = ["low", "medium", "high"], r = n.ceiling && n.ceiling !== "audio" ? n.ceiling : "high", c = o.indexOf(r), l = o.indexOf(a), d = (n.samplesSeen ?? s.warmupSamples) + 1, u = d <= s.warmupSamples, S = ue(e.qualityLimitationReason), f = e.packetLossPct ?? 0, v = e.rttMs ?? 0, I = e.availableOutgoingBitrateBps, y = N[a].maxBitrate, k = f >= s.criticalLossPct || v >= s.criticalRttMs || S === "cpu", p = k || f >= s.poorLossPct || v >= s.poorRttMs || S === "bandwidth", C = I == null || I >= y * s.sustainRatio, P = f < s.goodLossPct && v < s.goodRttMs && !S && C, L = p ? t + 1 : 0, M = P ? i + 1 : 0, A = { quality: a, poorSamples: L, goodSamples: M, samplesSeen: d, changed: !1 };
40
+ return l > c ? { quality: o[c], poorSamples: 0, goodSamples: 0, samplesSeen: d, changed: !0 } : u ? A : k && l > 0 ? { quality: o[l - 1], poorSamples: 0, goodSamples: 0, samplesSeen: d, changed: !0 } : L >= s.poorSamplesToStepDown && l > 0 ? { quality: o[l - 1], poorSamples: 0, goodSamples: 0, samplesSeen: d, changed: !0 } : M >= s.goodSamplesToStepUp && l < c ? { quality: o[l + 1], poorSamples: 0, goodSamples: 0, samplesSeen: d, changed: !0 } : A;
41
41
  }
42
- function ce(r, e) {
43
- const t = r instanceof Map ? r : new Map(r);
44
- let i = 0, n = 0, s = null, a = null, o = null;
45
- for (const l of t.values()) {
46
- const h = String(l.type ?? "");
47
- if (h === "outbound-rtp" && String(l.kind ?? "") === "video" && (i += Number(l.packetsSent ?? 0), s = l.qualityLimitationReason ?? s), h === "remote-inbound-rtp" && String(l.kind ?? "") === "video") {
48
- n += Number(l.packetsLost ?? 0);
49
- const v = Number(l.roundTripTime ?? NaN);
50
- Number.isFinite(v) && (a = v * 1e3);
42
+ function pe(a, e) {
43
+ const t = a instanceof Map ? a : new Map(a);
44
+ let i = 0, n = 0, s = null, o = null, r = null;
45
+ for (const d of t.values()) {
46
+ const u = String(d.type ?? "");
47
+ if (u === "outbound-rtp" && String(d.kind ?? "") === "video" && (i += Number(d.packetsSent ?? 0), s = d.qualityLimitationReason ?? s), u === "remote-inbound-rtp" && String(d.kind ?? "") === "video") {
48
+ n += Number(d.packetsLost ?? 0);
49
+ const S = Number(d.roundTripTime ?? NaN);
50
+ Number.isFinite(S) && (o = S * 1e3);
51
51
  }
52
- if (h === "candidate-pair" && (l.nominated === !0 || String(l.state ?? "") === "succeeded")) {
53
- const v = Number(l.currentRoundTripTime ?? NaN);
54
- a == null && Number.isFinite(v) && (a = v * 1e3);
55
- const g = Number(l.availableOutgoingBitrate ?? NaN);
56
- Number.isFinite(g) && (o = g);
52
+ if (u === "candidate-pair" && (d.nominated === !0 || String(d.state ?? "") === "succeeded")) {
53
+ const S = Number(d.currentRoundTripTime ?? NaN);
54
+ o == null && Number.isFinite(S) && (o = S * 1e3);
55
+ const f = Number(d.availableOutgoingBitrate ?? NaN);
56
+ Number.isFinite(f) && (r = f);
57
57
  }
58
58
  }
59
59
  const c = { packetsSent: i, packetsLost: n, timestamp: Date.now() };
60
- let d = null;
60
+ let l = null;
61
61
  if (e) {
62
- const l = i - e.packetsSent, h = n - e.packetsLost;
63
- l > 0 && h >= 0 && (d = h / (l + h) * 100);
62
+ const d = i - e.packetsSent, u = n - e.packetsLost;
63
+ d > 0 && u >= 0 && (l = u / (d + u) * 100);
64
64
  }
65
65
  return {
66
- sample: { packetLossPct: d, rttMs: a, availableOutgoingBitrateBps: o, qualityLimitationReason: s },
66
+ sample: { packetLossPct: l, rttMs: o, availableOutgoingBitrateBps: r, qualityLimitationReason: s },
67
67
  counters: c
68
68
  };
69
69
  }
70
- const le = {
70
+ const fe = {
71
71
  audio: { active: !1, maxBitrate: 0, maxFramerate: 0, scaleResolutionDownBy: 4 }
72
- }, de = { active: !0, maxBitrate: 4e6, maxFramerate: 30, scaleResolutionDownBy: 1 };
72
+ }, ge = { active: !0, maxBitrate: 4e6, maxFramerate: 30, scaleResolutionDownBy: 1 };
73
73
  class Q {
74
74
  options;
75
75
  socket = null;
@@ -153,14 +153,14 @@ class Q {
153
153
  async setDevice(e) {
154
154
  const t = { deviceId: { exact: e.deviceId } }, i = await navigator.mediaDevices.getUserMedia(e.kind === "audioinput" ? { audio: t } : { video: { ...t, width: { ideal: 1920 }, height: { ideal: 1080 }, frameRate: { ideal: 30, max: 30 } } }), n = e.kind === "audioinput" ? i.getAudioTracks()[0] : i.getVideoTracks()[0];
155
155
  if (!n) throw new Error("Selected media device is unavailable");
156
- const s = e.kind === "audioinput" ? "audio" : "video", a = this.localStream?.getTracks().find((o) => o.kind === s);
157
- if (this.localStream || (this.localStream = new MediaStream()), a && (this.localStream.removeTrack(a), a.stop()), this.localStream.addTrack(n), s === "video") {
158
- this.screenStream?.getVideoTracks().some((o) => o.readyState === "live") || await this.replaceVideoTrack(n);
156
+ const s = e.kind === "audioinput" ? "audio" : "video", o = this.localStream?.getTracks().find((r) => r.kind === s);
157
+ if (this.localStream || (this.localStream = new MediaStream()), o && (this.localStream.removeTrack(o), o.stop()), this.localStream.addTrack(n), s === "video") {
158
+ this.screenStream?.getVideoTracks().some((r) => r.readyState === "live") || await this.replaceVideoTrack(n);
159
159
  return;
160
160
  }
161
- for (const o of this.peers.values()) {
162
- const c = o.connection.getSenders().find((d) => d.track?.kind === s);
163
- c ? await c.replaceTrack(n) : o.connection.addTrack(n, this.localStream);
161
+ for (const r of this.peers.values()) {
162
+ const c = r.connection.getSenders().find((l) => l.track?.kind === s);
163
+ c ? await c.replaceTrack(n) : r.connection.addTrack(n, this.localStream);
164
164
  }
165
165
  await this.applyVideoQuality();
166
166
  }
@@ -214,31 +214,31 @@ class Q {
214
214
  async openSocket(e) {
215
215
  const { credential: t } = this.options, i = new URL(t.signalingUrl, globalThis.location?.href);
216
216
  i.protocol = i.protocol === "https:" ? "wss:" : "ws:";
217
- const n = new Promise((c, d) => {
218
- this.resolveJoin = c, this.rejectJoin = d;
219
- }), s = new WebSocket(i, ["rtc-client", t.signalingToken]), a = ++this.socketGeneration;
217
+ const n = new Promise((c, l) => {
218
+ this.resolveJoin = c, this.rejectJoin = l;
219
+ }), s = new WebSocket(i, ["rtc-client", t.signalingToken]), o = ++this.socketGeneration;
220
220
  this.socket = s;
221
- const o = setTimeout(() => this.rejectJoin?.(new Error("RTC signaling timed out")), 15e3);
221
+ const r = setTimeout(() => this.rejectJoin?.(new Error("RTC signaling timed out")), 15e3);
222
222
  s.addEventListener("open", () => {
223
223
  this.heartbeatDeadline = Date.now() + 45e3, this.startHeartbeat(s), this.startQualitySampling(), this.send({ type: "rtc.room.join" });
224
224
  }), s.addEventListener("message", (c) => {
225
- const d = String(c.data);
225
+ const l = String(c.data);
226
226
  this.signalQueue = this.signalQueue.then(async () => {
227
- this.socket !== s || this.socketGeneration !== a || await this.handleSignal(d);
228
- }).catch((l) => {
229
- const h = l instanceof Error ? l.message : "RTC signaling processing failed";
230
- this.options.onEvent("signalingerror", { message: h }), this.joined || this.rejectJoin?.(new Error(h));
227
+ this.socket !== s || this.socketGeneration !== o || await this.handleSignal(l);
228
+ }).catch((d) => {
229
+ const u = d instanceof Error ? d.message : "RTC signaling processing failed";
230
+ this.options.onEvent("signalingerror", { message: u }), this.joined || this.rejectJoin?.(new Error(u));
231
231
  });
232
232
  }), s.addEventListener("error", () => this.rejectJoin?.(new Error("RTC signaling connection failed"))), s.addEventListener("close", (c) => {
233
233
  if (this.socket !== s) return;
234
234
  this.socket = null, this.stopHeartbeat(), this.stopQualitySampling(), !this.joined && !e && this.rejectJoin?.(new Error(c.reason || "RTC signaling connection closed"));
235
- const d = !this.stopped && (this.joined || e);
236
- this.joined = !1, d ? (this.resetPeers(), this.options.onEvent("reconnecting", { reason: c.reason, code: c.code, attempt: this.reconnectAttempt + 1 }), this.scheduleReconnect()) : this.stopped || this.options.onEvent("disconnected", { reason: c.reason, code: c.code });
235
+ const l = !this.stopped && (this.joined || e);
236
+ this.joined = !1, l ? (this.resetPeers(), this.options.onEvent("reconnecting", { reason: c.reason, code: c.code, attempt: this.reconnectAttempt + 1 }), this.scheduleReconnect()) : this.stopped || this.options.onEvent("disconnected", { reason: c.reason, code: c.code });
237
237
  });
238
238
  try {
239
239
  await n, this.joined = !0, this.options.onEvent("connected", { participantId: t.participantId, resumed: e });
240
240
  } finally {
241
- clearTimeout(o), this.resolveJoin = null, this.rejectJoin = null;
241
+ clearTimeout(r), this.resolveJoin = null, this.rejectJoin = null;
242
242
  }
243
243
  }
244
244
  async handleSignal(e) {
@@ -270,11 +270,11 @@ class Q {
270
270
  if (i === "rtc.offer" && t.sender && t.description) {
271
271
  const n = this.ensurePeer(t.sender);
272
272
  n.offerTask && await n.offerTask;
273
- const s = n.connection.signalingState !== "stable", a = !this.shouldInitiateOffer(t.sender);
274
- if (s && !a) return;
273
+ const s = n.connection.signalingState !== "stable", o = !this.shouldInitiateOffer(t.sender);
274
+ if (s && !o) return;
275
275
  s && await n.connection.setLocalDescription({ type: "rollback" }), await n.connection.setRemoteDescription(t.description);
276
- const o = await n.connection.createAnswer();
277
- await n.connection.setLocalDescription(o), this.send({
276
+ const r = await n.connection.createAnswer();
277
+ await n.connection.setLocalDescription(r), this.send({
278
278
  type: "rtc.answer",
279
279
  targetParticipantId: t.sender.participantId,
280
280
  negotiationId: t.negotiationId,
@@ -321,11 +321,11 @@ class Q {
321
321
  };
322
322
  this.peers.set(e.participantId, s);
323
323
  for (const c of this.localStream?.getAudioTracks() ?? []) i.addTrack(c, this.localStream);
324
- const a = this.screenStream?.getVideoTracks().some((c) => c.readyState === "live") ? this.screenStream : this.localStream, o = a?.getVideoTracks()[0];
325
- return o && a && (s.videoSender = i.addTrack(o, a)), !this.localStream?.getAudioTracks().length && this.options.receiveAudio && i.addTransceiver("audio", { direction: "recvonly" }), !o && this.options.receiveVideo && (s.videoTransceiver = i.addTransceiver("video", { direction: "recvonly" }), s.videoSender = s.videoTransceiver.sender), this.applyCodecPreference(i), this.applyVideoQuality(i), i.addEventListener("icecandidate", (c) => {
324
+ const o = this.screenStream?.getVideoTracks().some((c) => c.readyState === "live") ? this.screenStream : this.localStream, r = o?.getVideoTracks()[0];
325
+ return r && o && (s.videoSender = i.addTrack(r, o)), !this.localStream?.getAudioTracks().length && this.options.receiveAudio && i.addTransceiver("audio", { direction: "recvonly" }), !r && this.options.receiveVideo && (s.videoTransceiver = i.addTransceiver("video", { direction: "recvonly" }), s.videoSender = s.videoTransceiver.sender), this.applyCodecPreference(i), this.applyVideoQuality(i), i.addEventListener("icecandidate", (c) => {
326
326
  c.candidate && this.send({ type: "rtc.ice", targetParticipantId: e.participantId, candidate: c.candidate.toJSON() });
327
327
  }), i.addEventListener("track", (c) => {
328
- n.getTracks().some((d) => d.id === c.track.id) || n.addTrack(c.track), this.options.onEvent("remotestream", { participant: e, stream: n, track: c.track });
328
+ n.getTracks().some((l) => l.id === c.track.id) || n.addTrack(c.track), this.options.onEvent("remotestream", { participant: e, stream: n, track: c.track });
329
329
  }), i.addEventListener("connectionstatechange", () => {
330
330
  ["failed", "closed"].includes(i.connectionState) && this.removePeer(e.participantId), this.options.onEvent("peerconnectionstate", { participant: e, state: i.connectionState });
331
331
  }), s;
@@ -351,7 +351,7 @@ class Q {
351
351
  });
352
352
  if (e.connection.signalingState !== "stable") return;
353
353
  await e.connection.setLocalDescription(t);
354
- const i = H();
354
+ const i = _();
355
355
  e.activeNegotiationId = i, this.send({
356
356
  type: "rtc.offer",
357
357
  targetParticipantId: e.identity.participantId,
@@ -371,10 +371,10 @@ class Q {
371
371
  async replaceVideoTrack(e) {
372
372
  let t = !1;
373
373
  for (const i of this.peers.values()) {
374
- const n = i.videoSender ?? i.connection.getSenders().find((a) => a.track?.kind === "video") ?? i.connection.getTransceivers().find((a) => a.receiver.track.kind === "video")?.sender, s = i.videoTransceiver ?? i.connection.getTransceivers().find((a) => a.sender === n);
374
+ const n = i.videoSender ?? i.connection.getSenders().find((o) => o.track?.kind === "video") ?? i.connection.getTransceivers().find((o) => o.receiver.track.kind === "video")?.sender, s = i.videoTransceiver ?? i.connection.getTransceivers().find((o) => o.sender === n);
375
375
  if (n) {
376
- const a = e ? this.options.receiveVideo ? "sendrecv" : "sendonly" : this.options.receiveVideo ? "recvonly" : "inactive";
377
- s && s.direction !== a && (s.direction = a, t = !0), await n.replaceTrack(e), i.videoSender = n, s && (i.videoTransceiver = s);
376
+ const o = e ? this.options.receiveVideo ? "sendrecv" : "sendonly" : this.options.receiveVideo ? "recvonly" : "inactive";
377
+ s && s.direction !== o && (s.direction = o, t = !0), await n.replaceTrack(e), i.videoSender = n, s && (i.videoTransceiver = s);
378
378
  } else e && (i.videoSender = i.connection.addTrack(e, this.screenStream ?? this.localStream ?? new MediaStream([e])), t = !0);
379
379
  }
380
380
  t && await this.renegotiateAll(), await this.applyVideoQuality();
@@ -391,8 +391,8 @@ class Q {
391
391
  async applyPeerVideoQuality(e) {
392
392
  const t = this.screenStream?.getVideoTracks()[0]?.id;
393
393
  for (const i of e.connection.getSenders().filter((n) => n.track?.kind === "video")) {
394
- const n = !!(t && i.track?.id === t), s = n ? de : this.quality === "audio" ? le.audio : M[e.quality], a = i.getParameters(), o = a.encodings?.length ? a.encodings : [{}];
395
- a.encodings = o.map((c) => ({ ...c, ...s })), a.degradationPreference = n ? "maintain-resolution" : "maintain-framerate", await i.setParameters(a);
394
+ const n = !!(t && i.track?.id === t), s = n ? ge : this.quality === "audio" ? fe.audio : N[e.quality], o = i.getParameters(), r = o.encodings?.length ? o.encodings : [{}];
395
+ o.encodings = r.map((c) => ({ ...c, ...s })), o.degradationPreference = n ? "maintain-resolution" : "maintain-framerate", await i.setParameters(o);
396
396
  }
397
397
  }
398
398
  startQualitySampling() {
@@ -411,11 +411,11 @@ class Q {
411
411
  if (i.connection.connectionState === "connected")
412
412
  try {
413
413
  const n = await i.connection.getStats();
414
- for (const s of ne(n.values()))
414
+ for (const s of ce(n.values()))
415
415
  e.push({ participantId: i.identity.participantId, level: s.level });
416
416
  } catch {
417
417
  }
418
- const t = ie(e, this.activeSpeaker);
418
+ const t = oe(e, this.activeSpeaker);
419
419
  t !== this.activeSpeaker && (this.activeSpeaker = t, this.options.onEvent("activespeaker", { participantId: t }));
420
420
  }
421
421
  async samplePeerQuality() {
@@ -428,9 +428,9 @@ class Q {
428
428
  } catch {
429
429
  continue;
430
430
  }
431
- const { sample: i, counters: n } = ce(t, e.counters);
431
+ const { sample: i, counters: n } = pe(t, e.counters);
432
432
  e.counters = n;
433
- const s = oe(e.quality, i, e.poorSamples, e.goodSamples, {
433
+ const s = me(e.quality, i, e.poorSamples, e.goodSamples, {
434
434
  samplesSeen: e.samplesSeen,
435
435
  ceiling: this.quality
436
436
  });
@@ -487,7 +487,7 @@ class Q {
487
487
  }
488
488
  send(e) {
489
489
  this.socket?.readyState === WebSocket.OPEN && this.socket.send(JSON.stringify({
490
- clientMessageId: e.clientMessageId ?? H(),
490
+ clientMessageId: e.clientMessageId ?? _(),
491
491
  ...e
492
492
  }));
493
493
  }
@@ -495,15 +495,15 @@ class Q {
495
495
  for (const t of e?.getTracks() ?? []) t.stop();
496
496
  }
497
497
  }
498
- function H() {
498
+ function _() {
499
499
  return globalThis.crypto?.randomUUID?.() ?? `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
500
500
  }
501
- const U = { active: !0, maxBitrate: 4e6, maxFramerate: 30, scaleResolutionDownBy: 1 }, _ = [
502
- { rid: "a", maxBitrate: M.high.maxBitrate, maxFramerate: M.high.maxFramerate },
503
- { rid: "b", maxBitrate: M.medium.maxBitrate, maxFramerate: M.medium.maxFramerate, scaleResolutionDownBy: 2 },
504
- { rid: "c", maxBitrate: M.low.maxBitrate, maxFramerate: M.low.maxFramerate, scaleResolutionDownBy: 4 }
505
- ], W = { high: "a", medium: "b", low: "c" };
506
- class he {
501
+ const z = { active: !0, maxBitrate: 4e6, maxFramerate: 30, scaleResolutionDownBy: 1 }, Y = [
502
+ { rid: "a", maxBitrate: N.high.maxBitrate, maxFramerate: N.high.maxFramerate },
503
+ { rid: "b", maxBitrate: N.medium.maxBitrate, maxFramerate: N.medium.maxFramerate, scaleResolutionDownBy: 2 },
504
+ { rid: "c", maxBitrate: N.low.maxBitrate, maxFramerate: N.low.maxFramerate, scaleResolutionDownBy: 4 }
505
+ ], G = { high: "a", medium: "b", low: "c" };
506
+ class ye {
507
507
  options;
508
508
  apiBase;
509
509
  socket = null;
@@ -594,13 +594,13 @@ class he {
594
594
  async setDevice(e) {
595
595
  const t = { deviceId: { exact: e.deviceId } }, i = await navigator.mediaDevices.getUserMedia(e.kind === "audioinput" ? { audio: t } : { video: { ...t, width: { ideal: 1920 }, height: { ideal: 1080 }, frameRate: { ideal: 30, max: 30 } } }), n = e.kind === "audioinput" ? i.getAudioTracks()[0] : i.getVideoTracks()[0];
596
596
  if (!n) throw new Error("Selected media device is unavailable");
597
- const s = e.kind === "audioinput" ? "audio" : "video", a = this.localStream?.getTracks().find((c) => c.kind === s);
598
- if (this.localStream || (this.localStream = new MediaStream()), a && (this.localStream.removeTrack(a), a.stop()), this.localStream.addTrack(n), s === "video") {
597
+ const s = e.kind === "audioinput" ? "audio" : "video", o = this.localStream?.getTracks().find((c) => c.kind === s);
598
+ if (this.localStream || (this.localStream = new MediaStream()), o && (this.localStream.removeTrack(o), o.stop()), this.localStream.addTrack(n), s === "video") {
599
599
  this.screenStream?.getVideoTracks().some((c) => c.readyState === "live") || await this.replaceVideoTrack(n);
600
600
  return;
601
601
  }
602
- const o = this.connection?.getSenders().find((c) => c.track?.kind === "audio");
603
- o && await o.replaceTrack(n);
602
+ const r = this.connection?.getSenders().find((c) => c.track?.kind === "audio");
603
+ r && await r.replaceTrack(n);
604
604
  }
605
605
  /** One uplink serves everyone, so the requested quality applies directly. */
606
606
  async setQuality(e) {
@@ -638,31 +638,31 @@ class he {
638
638
  async openSocket(e) {
639
639
  const { credential: t } = this.options, i = new URL(t.signalingUrl, globalThis.location?.href);
640
640
  i.protocol = i.protocol === "https:" ? "wss:" : "ws:";
641
- const n = new Promise((c, d) => {
642
- this.resolveJoin = c, this.rejectJoin = d;
643
- }), s = new WebSocket(i, ["rtc-client", t.signalingToken]), a = ++this.socketGeneration;
641
+ const n = new Promise((c, l) => {
642
+ this.resolveJoin = c, this.rejectJoin = l;
643
+ }), s = new WebSocket(i, ["rtc-client", t.signalingToken]), o = ++this.socketGeneration;
644
644
  this.socket = s;
645
- const o = setTimeout(() => this.rejectJoin?.(new Error("RTC signaling timed out")), 15e3);
645
+ const r = setTimeout(() => this.rejectJoin?.(new Error("RTC signaling timed out")), 15e3);
646
646
  s.addEventListener("open", () => {
647
647
  this.heartbeatDeadline = Date.now() + 45e3, this.startHeartbeat(s), this.send({ type: "rtc.room.join" });
648
648
  }), s.addEventListener("message", (c) => {
649
- const d = String(c.data);
649
+ const l = String(c.data);
650
650
  this.signalQueue = this.signalQueue.then(async () => {
651
- this.socket !== s || this.socketGeneration !== a || await this.handleSignal(d);
652
- }).catch((l) => {
653
- const h = l instanceof Error ? l.message : "RTC signaling processing failed";
654
- this.options.onEvent("signalingerror", { message: h }), this.joined || this.rejectJoin?.(new Error(h));
651
+ this.socket !== s || this.socketGeneration !== o || await this.handleSignal(l);
652
+ }).catch((d) => {
653
+ const u = d instanceof Error ? d.message : "RTC signaling processing failed";
654
+ this.options.onEvent("signalingerror", { message: u }), this.joined || this.rejectJoin?.(new Error(u));
655
655
  });
656
656
  }), s.addEventListener("error", () => this.rejectJoin?.(new Error("RTC signaling connection failed"))), s.addEventListener("close", (c) => {
657
657
  if (this.socket !== s) return;
658
658
  this.socket = null, this.stopHeartbeat(), !this.joined && !e && this.rejectJoin?.(new Error(c.reason || "RTC signaling connection closed"));
659
- const d = !this.stopped && (this.joined || e);
660
- this.joined = !1, d ? (this.options.onEvent("reconnecting", { reason: c.reason, code: c.code, attempt: this.reconnectAttempt + 1 }), this.scheduleReconnect()) : this.stopped || this.options.onEvent("disconnected", { reason: c.reason, code: c.code });
659
+ const l = !this.stopped && (this.joined || e);
660
+ this.joined = !1, l ? (this.options.onEvent("reconnecting", { reason: c.reason, code: c.code, attempt: this.reconnectAttempt + 1 }), this.scheduleReconnect()) : this.stopped || this.options.onEvent("disconnected", { reason: c.reason, code: c.code });
661
661
  });
662
662
  try {
663
663
  await n, this.joined = !0, this.options.onEvent("connected", { participantId: t.participantId, resumed: e });
664
664
  } finally {
665
- clearTimeout(o), this.resolveJoin = null, this.rejectJoin = null;
665
+ clearTimeout(r), this.resolveJoin = null, this.rejectJoin = null;
666
666
  }
667
667
  }
668
668
  async handleSignal(e) {
@@ -710,30 +710,30 @@ class he {
710
710
  iceServers: this.options.credential.iceServers ?? [],
711
711
  iceTransportPolicy: this.options.iceTransportPolicy ?? "all"
712
712
  });
713
- this.connection = t, t.addEventListener("track", (o) => this.handleRemoteTrack(o)), t.addEventListener("connectionstatechange", () => {
713
+ this.connection = t, t.addEventListener("track", (r) => this.handleRemoteTrack(r)), t.addEventListener("connectionstatechange", () => {
714
714
  this.options.onEvent("peerconnectionstate", { participant: this.selfIdentity(), state: t.connectionState });
715
715
  });
716
716
  const i = await this.sfuFetch("POST", "/v1/rtc/sfu/sessions");
717
717
  if (this.sessionId = i.sessionId, e.length === 0) return;
718
- const n = e.map((o) => t.addTransceiver(o, {
718
+ const n = e.map((r) => t.addTransceiver(r, {
719
719
  direction: "sendonly",
720
720
  streams: this.localStream ? [this.localStream] : [],
721
- ...o.kind === "video" ? { sendEncodings: _.map((c) => ({ ...c })) } : {}
721
+ ...r.kind === "video" ? { sendEncodings: Y.map((c) => ({ ...c })) } : {}
722
722
  })), s = await t.createOffer();
723
723
  await t.setLocalDescription(s);
724
- const a = await this.sfuFetch(
724
+ const o = await this.sfuFetch(
725
725
  "POST",
726
726
  `/v1/rtc/sfu/sessions/${encodeURIComponent(i.sessionId)}/tracks`,
727
727
  {
728
728
  sessionDescription: t.localDescription,
729
- tracks: n.map((o) => ({
729
+ tracks: n.map((r) => ({
730
730
  location: "local",
731
- mid: o.mid,
732
- trackName: this.trackName(o.sender.track)
731
+ mid: r.mid,
732
+ trackName: this.trackName(r.sender.track)
733
733
  }))
734
734
  }
735
735
  );
736
- a.sessionDescription && await t.setRemoteDescription(a.sessionDescription), this.videoSender = t.getSenders().find((o) => o.track?.kind === "video") ?? null, await this.applyVideoQuality();
736
+ o.sessionDescription && await t.setRemoteDescription(o.sessionDescription), this.videoSender = t.getSenders().find((r) => r.track?.kind === "video") ?? null, await this.applyVideoQuality();
737
737
  }
738
738
  trackName(e) {
739
739
  return `${this.options.credential.participantId}/${e.kind}`;
@@ -747,13 +747,13 @@ class he {
747
747
  e.length !== 0 && this.sendControl("sfu.tracks", { sessionId: this.sessionId, tracks: e });
748
748
  }
749
749
  async handleTrackAnnouncement(e, t) {
750
- const i = String(t?.sessionId ?? ""), n = (t?.tracks ?? []).map((o) => ({ trackName: String(o.trackName ?? ""), simulcast: o.simulcast === !0 })).filter((o) => !!o.trackName), s = n.map((o) => o.trackName);
750
+ const i = String(t?.sessionId ?? ""), n = (t?.tracks ?? []).map((r) => ({ trackName: String(r.trackName ?? ""), simulcast: r.simulcast === !0 })).filter((r) => !!r.trackName), s = n.map((r) => r.trackName);
751
751
  if (!i || s.length === 0 || e.participantId === this.options.credential.participantId) return;
752
752
  if (!this.connection || !this.sessionId) {
753
753
  this.pendingAnnouncements.set(e.participantId, { sender: e, detail: t ?? {} });
754
754
  return;
755
755
  }
756
- const a = this.remotes.get(e.participantId) ?? {
756
+ const o = this.remotes.get(e.participantId) ?? {
757
757
  identity: e,
758
758
  stream: new MediaStream(),
759
759
  sessionId: i,
@@ -761,14 +761,14 @@ class he {
761
761
  simulcastNames: /* @__PURE__ */ new Set(),
762
762
  mids: /* @__PURE__ */ new Map()
763
763
  };
764
- a.sessionId = i;
765
- for (const o of n)
766
- o.simulcast && a.simulcastNames.add(o.trackName);
767
- this.remotes.set(e.participantId, a), this.negotiationQueue = this.negotiationQueue.then(async () => {
768
- const o = s.filter((c) => !a.trackNames.includes(c));
769
- o.length !== 0 && await this.subscribe(a, o);
770
- }).catch((o) => {
771
- this.options.onEvent("signalingerror", { message: o instanceof Error ? o.message : "SFU subscribe failed" });
764
+ o.sessionId = i;
765
+ for (const r of n)
766
+ r.simulcast && o.simulcastNames.add(r.trackName);
767
+ this.remotes.set(e.participantId, o), this.negotiationQueue = this.negotiationQueue.then(async () => {
768
+ const r = s.filter((c) => !o.trackNames.includes(c));
769
+ r.length !== 0 && await this.subscribe(o, r);
770
+ }).catch((r) => {
771
+ this.options.onEvent("signalingerror", { message: r instanceof Error ? r.message : "SFU subscribe failed" });
772
772
  }), await this.negotiationQueue;
773
773
  }
774
774
  async subscribe(e, t) {
@@ -818,7 +818,7 @@ class he {
818
818
  n && await this.sfuFetch("PUT", `/v1/rtc/sfu/sessions/${encodeURIComponent(this.sessionId)}/tracks/update`, {
819
819
  tracks: [{
820
820
  mid: n,
821
- simulcast: { preferredRid: W[t], priorityOrdering: "asciibetical", ridNotAvailable: "asciibetical" }
821
+ simulcast: { preferredRid: G[t], priorityOrdering: "asciibetical", ridNotAvailable: "asciibetical" }
822
822
  }]
823
823
  });
824
824
  }
@@ -830,16 +830,16 @@ class he {
830
830
  if (!e) return;
831
831
  const t = this.screenStream?.getVideoTracks()[0]?.id, i = !!(t && e.track?.id === t), n = e.getParameters(), s = n.encodings?.length ? n.encodings : [{}];
832
832
  if (s.length > 1) {
833
- const a = this.quality === "audio" ? null : W[this.quality];
834
- n.encodings = s.map((o) => {
833
+ const o = this.quality === "audio" ? null : G[this.quality];
834
+ n.encodings = s.map((r) => {
835
835
  if (i)
836
- return { ...o, active: o.rid === "a", ...o.rid === "a" ? { maxBitrate: U.maxBitrate, maxFramerate: U.maxFramerate } : {} };
837
- const c = _.find((l) => l.rid === o.rid), d = a != null && String(o.rid ?? "") >= a;
838
- return { ...o, ...c ?? {}, active: d };
836
+ return { ...r, active: r.rid === "a", ...r.rid === "a" ? { maxBitrate: z.maxBitrate, maxFramerate: z.maxFramerate } : {} };
837
+ const c = Y.find((d) => d.rid === r.rid), l = o != null && String(r.rid ?? "") >= o;
838
+ return { ...r, ...c ?? {}, active: l };
839
839
  });
840
840
  } else {
841
- const a = this.quality === "audio" ? M.audio : M[this.quality], o = i ? U : a;
842
- n.encodings = s.map((c) => ({ ...c, ...o }));
841
+ const o = this.quality === "audio" ? N.audio : N[this.quality], r = i ? z : o;
842
+ n.encodings = s.map((c) => ({ ...c, ...r }));
843
843
  }
844
844
  n.degradationPreference = i ? "maintain-resolution" : "maintain-framerate", await e.setParameters(n);
845
845
  }
@@ -905,11 +905,11 @@ class he {
905
905
  return;
906
906
  }
907
907
  const i = [];
908
- for (const s of ne(t.values())) {
909
- const a = s.mid ? this.midToParticipant.get(s.mid) : void 0;
910
- a && i.push({ participantId: a, level: s.level });
908
+ for (const s of ce(t.values())) {
909
+ const o = s.mid ? this.midToParticipant.get(s.mid) : void 0;
910
+ o && i.push({ participantId: o, level: s.level });
911
911
  }
912
- const n = ie(i, this.activeSpeaker);
912
+ const n = oe(i, this.activeSpeaker);
913
913
  n !== this.activeSpeaker && (this.activeSpeaker = n, this.options.onEvent("activespeaker", { participantId: n }));
914
914
  }
915
915
  clearTimers() {
@@ -926,7 +926,7 @@ class he {
926
926
  }
927
927
  send(e) {
928
928
  this.socket?.readyState === WebSocket.OPEN && this.socket.send(JSON.stringify({
929
- clientMessageId: e.clientMessageId ?? ue(),
929
+ clientMessageId: e.clientMessageId ?? ve(),
930
930
  ...e
931
931
  }));
932
932
  }
@@ -934,28 +934,28 @@ class he {
934
934
  for (const t of e?.getTracks() ?? []) t.stop();
935
935
  }
936
936
  }
937
- function ue() {
937
+ function ve() {
938
938
  return globalThis.crypto?.randomUUID?.() ?? `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
939
939
  }
940
- function Ee(r, e = {}) {
940
+ function Ne(a, e = {}) {
941
941
  const t = e.document ?? globalThis.document, i = !!(t && t.pictureInPictureEnabled);
942
942
  let n = null;
943
943
  function s() {
944
944
  return n || (n = t.createElement("video"), n.muted = !0, n.playsInline = !0, n.style.position = "fixed", n.style.width = "1px", n.style.height = "1px", n.style.opacity = "0", n.style.pointerEvents = "none", n.addEventListener("leavepictureinpicture", () => e.onLeave?.()), t.body.appendChild(n), n);
945
945
  }
946
- function a(o) {
947
- const c = r.getRemoteStreams();
948
- return o ? c.find((d) => d.participant.participantId === o)?.stream ?? null : c[0]?.stream ?? r.getLocalStream();
946
+ function o(r) {
947
+ const c = a.getRemoteStreams();
948
+ return r ? c.find((l) => l.participant.participantId === r)?.stream ?? null : c[0]?.stream ?? a.getLocalStream();
949
949
  }
950
950
  return {
951
951
  supported: i,
952
- async enter(o) {
952
+ async enter(r) {
953
953
  if (!i) throw new Error("This browser does not support picture-in-picture");
954
- const c = a(o);
954
+ const c = o(r);
955
955
  if (!c) throw new Error("No video stream is available for picture-in-picture");
956
- const d = s();
957
- d.srcObject !== c && (d.srcObject = c), await d.play().catch(() => {
958
- }), await d.requestPictureInPicture();
956
+ const l = s();
957
+ l.srcObject !== c && (l.srcObject = c), await l.play().catch(() => {
958
+ }), await l.requestPictureInPicture();
959
959
  },
960
960
  async exit() {
961
961
  t.pictureInPictureElement && typeof t.exitPictureInPicture == "function" && await t.exitPictureInPicture();
@@ -965,196 +965,378 @@ function Ee(r, e = {}) {
965
965
  }
966
966
  };
967
967
  }
968
- function Re(r, e) {
969
- const t = e.document ?? globalThis.document, i = e.fetchImplementation ?? globalThis.fetch.bind(globalThis), n = new URL(e.credential.signalingUrl, globalThis.location?.href).origin, s = e.mimeType ?? "video/webm", a = e.height ?? 720, o = Math.round(a * 16 / 9);
970
- let c = null, d = null, l = null, h = null, v = 0, g = [], k = !1;
971
- async function p(C, S, y, T = "application/json") {
972
- const I = await i(`${n}${S}`, {
973
- method: C,
968
+ function je(a, e) {
969
+ const t = e.document ?? globalThis.document, i = e.fetchImplementation ?? globalThis.fetch.bind(globalThis), n = new URL(e.credential.signalingUrl, globalThis.location?.href).origin, s = e.mimeType ?? "video/webm", o = e.height ?? 720, r = Math.round(o * 16 / 9);
970
+ let c = null, l = null, d = null, u = null, S = 0, f = [], v = !1;
971
+ async function I(y, k, p, C = "application/json") {
972
+ const P = await i(`${n}${k}`, {
973
+ method: y,
974
974
  headers: {
975
975
  authorization: `Bearer ${e.credential.signalingToken}`,
976
- "content-type": T
976
+ "content-type": C
977
977
  },
978
- body: y === void 0 ? void 0 : T === "application/json" ? JSON.stringify(y) : y
978
+ body: p === void 0 ? void 0 : C === "application/json" ? JSON.stringify(p) : p
979
979
  });
980
- if (!I.ok) throw new Error(`Recording request failed with HTTP ${I.status}`);
981
- return I.json();
980
+ if (!P.ok) throw new Error(`Recording request failed with HTTP ${P.status}`);
981
+ return P.json();
982
982
  }
983
983
  return {
984
984
  get recording() {
985
- return k;
985
+ return v;
986
986
  },
987
987
  async start() {
988
- if (k) return;
989
- h = (await p("POST", "/v1/rtc/recordings")).recordingId;
990
- const S = t.createElement("canvas");
991
- S.width = o, S.height = a;
992
- const y = S.getContext("2d"), T = /* @__PURE__ */ new Map(), I = (b, P) => {
993
- let w = T.get(b);
994
- return w || (w = t.createElement("video"), w.muted = !0, w.playsInline = !0, w.srcObject = P, w.play().catch(() => {
995
- }), T.set(b, w)), w;
988
+ if (v) return;
989
+ u = (await I("POST", "/v1/rtc/recordings")).recordingId;
990
+ const k = t.createElement("canvas");
991
+ k.width = r, k.height = o;
992
+ const p = k.getContext("2d"), C = /* @__PURE__ */ new Map(), P = (T, R) => {
993
+ let b = C.get(T);
994
+ return b || (b = t.createElement("video"), b.muted = !0, b.playsInline = !0, b.srcObject = R, b.play().catch(() => {
995
+ }), C.set(T, b)), b;
996
996
  };
997
- d = setInterval(() => {
998
- const b = r.getRemoteStreams(), P = r.getLocalStream(), w = [
999
- ...b.map((D) => ({ key: D.participant.participantId, stream: D.stream, label: D.participant.displayName })),
1000
- ...P ? [{ key: "local", stream: P, label: "me" }] : []
997
+ l = setInterval(() => {
998
+ const T = a.getRemoteStreams(), R = a.getLocalStream(), b = [
999
+ ...T.map((j) => ({ key: j.participant.participantId, stream: j.stream, label: j.participant.displayName })),
1000
+ ...R ? [{ key: "local", stream: R, label: "me" }] : []
1001
1001
  ];
1002
- y.fillStyle = "#101418", y.fillRect(0, 0, o, a);
1003
- const N = Math.max(1, Math.ceil(Math.sqrt(w.length))), m = Math.max(1, Math.ceil(w.length / N)), A = o / N, V = a / m;
1004
- w.forEach((D, z) => {
1005
- const J = I(D.key, D.stream), j = z % N * A, B = Math.floor(z / N) * V;
1006
- J.videoWidth > 0 && y.drawImage(J, j + 2, B + 2, A - 4, V - 4), y.fillStyle = "rgba(0,0,0,.6)", y.fillRect(j + 8, B + V - 30, Math.min(A - 16, 12 + D.label.length * 9), 22), y.fillStyle = "#fff", y.font = "14px system-ui", y.fillText(D.label, j + 14, B + V - 14);
1002
+ p.fillStyle = "#101418", p.fillRect(0, 0, r, o);
1003
+ const O = Math.max(1, Math.ceil(Math.sqrt(b.length))), g = Math.max(1, Math.ceil(b.length / O)), q = r / O, x = o / g;
1004
+ b.forEach((j, J) => {
1005
+ const W = P(j.key, j.stream), B = J % O * q, F = Math.floor(J / O) * x;
1006
+ W.videoWidth > 0 && p.drawImage(W, B + 2, F + 2, q - 4, x - 4), p.fillStyle = "rgba(0,0,0,.6)", p.fillRect(B + 8, F + x - 30, Math.min(q - 16, 12 + j.label.length * 9), 22), p.fillStyle = "#fff", p.font = "14px system-ui", p.fillText(j.label, B + 14, F + x - 14);
1007
1007
  });
1008
1008
  }, 66);
1009
- const R = S.captureStream(15);
1010
- l = new AudioContext();
1011
- const E = l.createMediaStreamDestination(), f = /* @__PURE__ */ new Set(), u = () => {
1012
- const b = [
1013
- ...r.getRemoteStreams().map((P) => P.stream),
1014
- ...r.getLocalStream() ? [r.getLocalStream()] : []
1009
+ const L = k.captureStream(15);
1010
+ d = new AudioContext();
1011
+ const M = d.createMediaStreamDestination(), A = /* @__PURE__ */ new Set(), w = () => {
1012
+ const T = [
1013
+ ...a.getRemoteStreams().map((R) => R.stream),
1014
+ ...a.getLocalStream() ? [a.getLocalStream()] : []
1015
1015
  ];
1016
- for (const P of b)
1017
- for (const w of P.getAudioTracks())
1018
- f.has(w.id) || (f.add(w.id), l.createMediaStreamSource(new MediaStream([w])).connect(E));
1016
+ for (const R of T)
1017
+ for (const b of R.getAudioTracks())
1018
+ A.has(b.id) || (A.add(b.id), d.createMediaStreamSource(new MediaStream([b])).connect(M));
1019
1019
  };
1020
- u();
1021
- const q = setInterval(u, 1e3), L = () => clearInterval(q);
1022
- for (const b of E.stream.getAudioTracks()) R.addTrack(b);
1023
- c = (e.mediaRecorderFactory ?? ((b, P) => new MediaRecorder(b, P)))(R, { mimeType: s }), g = [], c.addEventListener("dataavailable", (b) => {
1024
- b.data && b.data.size > 0 && g.push(b.data);
1025
- }), c.addEventListener("stop", L), c.start(1e3), v = Date.now(), k = !0;
1020
+ w();
1021
+ const E = setInterval(w, 1e3), m = () => clearInterval(E);
1022
+ for (const T of M.stream.getAudioTracks()) L.addTrack(T);
1023
+ c = (e.mediaRecorderFactory ?? ((T, R) => new MediaRecorder(T, R)))(L, { mimeType: s }), f = [], c.addEventListener("dataavailable", (T) => {
1024
+ T.data && T.data.size > 0 && f.push(T.data);
1025
+ }), c.addEventListener("stop", m), c.start(1e3), S = Date.now(), v = !0;
1026
1026
  },
1027
1027
  async stop() {
1028
- if (!k || !c || !h) throw new Error("The recorder is not running");
1029
- k = !1;
1030
- const C = c, S = new Promise((E) => C.addEventListener("stop", () => E(), { once: !0 }));
1031
- C.stop(), await S, d && clearInterval(d), d = null, await l?.close().catch(() => {
1032
- }), l = null;
1033
- const y = new Blob(g, { type: s });
1034
- g = [];
1035
- const T = Math.max(1, Math.round((Date.now() - v) / 1e3)), I = await p(
1028
+ if (!v || !c || !u) throw new Error("The recorder is not running");
1029
+ v = !1;
1030
+ const y = c, k = new Promise((M) => y.addEventListener("stop", () => M(), { once: !0 }));
1031
+ y.stop(), await k, l && clearInterval(l), l = null, await d?.close().catch(() => {
1032
+ }), d = null;
1033
+ const p = new Blob(f, { type: s });
1034
+ f = [];
1035
+ const C = Math.max(1, Math.round((Date.now() - S) / 1e3)), P = await I(
1036
1036
  "PUT",
1037
- `/v1/rtc/recordings/${encodeURIComponent(h)}/media`,
1038
- y,
1037
+ `/v1/rtc/recordings/${encodeURIComponent(u)}/media`,
1038
+ p,
1039
1039
  s
1040
1040
  );
1041
- await p("POST", `/v1/rtc/recordings/${encodeURIComponent(h)}/complete`, { durationSeconds: T });
1042
- const R = { recordingId: h, sizeBytes: I.sizeBytes, durationSeconds: T };
1043
- return h = null, c = null, R;
1041
+ await I("POST", `/v1/rtc/recordings/${encodeURIComponent(u)}/complete`, { durationSeconds: C });
1042
+ const L = { recordingId: u, sizeBytes: P.sizeBytes, durationSeconds: C };
1043
+ return u = null, c = null, L;
1044
1044
  },
1045
1045
  dispose() {
1046
- d && clearInterval(d), d = null;
1046
+ l && clearInterval(l), l = null;
1047
1047
  try {
1048
1048
  c?.stop();
1049
1049
  } catch {
1050
1050
  }
1051
- c = null, l?.close().catch(() => {
1052
- }), l = null, k = !1;
1051
+ c = null, d?.close().catch(() => {
1052
+ }), d = null, v = !1;
1053
1053
  }
1054
1054
  };
1055
1055
  }
1056
- const me = ["none", "blur", "aurora", "cybergrid", "bokeh", "sunset", "image"], pe = ["none", "snow", "confetti", "vignette"], G = {
1057
- aurora(r, e, t, i) {
1058
- const n = r.createLinearGradient(0, 0, 0, t);
1059
- n.addColorStop(0, "#050b1e"), n.addColorStop(1, "#0b2540"), r.fillStyle = n, r.fillRect(0, 0, e, t);
1056
+ const Se = ["none", "blur", "aurora", "cybergrid", "bokeh", "sunset", "image"], ke = ["none", "snow", "confetti", "vignette"], K = {
1057
+ aurora(a, e, t, i) {
1058
+ const n = a.createLinearGradient(0, 0, 0, t);
1059
+ n.addColorStop(0, "#050b1e"), n.addColorStop(1, "#0b2540"), a.fillStyle = n, a.fillRect(0, 0, e, t);
1060
1060
  for (let s = 0; s < 3; s += 1) {
1061
- r.beginPath();
1062
- for (let a = 0; a <= e; a += 16) {
1063
- const o = t * 0.35 + s * 40 + Math.sin(a / 140 + i / 900 + s) * 60;
1064
- a === 0 ? r.moveTo(a, o) : r.lineTo(a, o);
1061
+ a.beginPath();
1062
+ for (let o = 0; o <= e; o += 16) {
1063
+ const r = t * 0.35 + s * 40 + Math.sin(o / 140 + i / 900 + s) * 60;
1064
+ o === 0 ? a.moveTo(o, r) : a.lineTo(o, r);
1065
1065
  }
1066
- r.lineTo(e, 0), r.lineTo(0, 0), r.closePath(), r.fillStyle = `hsla(${140 + s * 40 + Math.sin(i / 1300) * 20}, 80%, 55%, 0.14)`, r.fill();
1066
+ a.lineTo(e, 0), a.lineTo(0, 0), a.closePath(), a.fillStyle = `hsla(${140 + s * 40 + Math.sin(i / 1300) * 20}, 80%, 55%, 0.14)`, a.fill();
1067
1067
  }
1068
1068
  },
1069
- cybergrid(r, e, t, i) {
1070
- r.fillStyle = "#0a0118", r.fillRect(0, 0, e, t), r.strokeStyle = "rgba(255,0,180,0.5)", r.lineWidth = 1.5;
1069
+ cybergrid(a, e, t, i) {
1070
+ a.fillStyle = "#0a0118", a.fillRect(0, 0, e, t), a.strokeStyle = "rgba(255,0,180,0.5)", a.lineWidth = 1.5;
1071
1071
  const n = t * 0.45;
1072
- for (let a = 0; a < 12; a += 1) {
1073
- const o = n + (a * 34 + i / 18) % (t - n);
1074
- r.beginPath(), r.moveTo(0, o), r.lineTo(e, o), r.stroke();
1072
+ for (let o = 0; o < 12; o += 1) {
1073
+ const r = n + (o * 34 + i / 18) % (t - n);
1074
+ a.beginPath(), a.moveTo(0, r), a.lineTo(e, r), a.stroke();
1075
1075
  }
1076
- for (let a = -8; a <= 8; a += 1)
1077
- r.beginPath(), r.moveTo(e / 2 + a * 24, n), r.lineTo(e / 2 + a * e * 0.12, t), r.stroke();
1078
- const s = r.createLinearGradient(0, t * 0.1, 0, n);
1079
- s.addColorStop(0, "#ffd34d"), s.addColorStop(1, "#ff2d78"), r.fillStyle = s, r.beginPath(), r.arc(e / 2, n, t * 0.22, Math.PI, 0), r.fill();
1076
+ for (let o = -8; o <= 8; o += 1)
1077
+ a.beginPath(), a.moveTo(e / 2 + o * 24, n), a.lineTo(e / 2 + o * e * 0.12, t), a.stroke();
1078
+ const s = a.createLinearGradient(0, t * 0.1, 0, n);
1079
+ s.addColorStop(0, "#ffd34d"), s.addColorStop(1, "#ff2d78"), a.fillStyle = s, a.beginPath(), a.arc(e / 2, n, t * 0.22, Math.PI, 0), a.fill();
1080
1080
  },
1081
- bokeh(r, e, t, i) {
1082
- r.fillStyle = "#14100c", r.fillRect(0, 0, e, t);
1081
+ bokeh(a, e, t, i) {
1082
+ a.fillStyle = "#14100c", a.fillRect(0, 0, e, t);
1083
1083
  for (let n = 0; n < 26; n += 1) {
1084
- const s = (Math.sin(n * 12.9898) * 0.5 + 0.5) * e, a = (Math.sin(n * 78.233) * 0.5 + 0.5) * t + Math.sin(i / 1500 + n) * 12, o = 14 + n % 5 * 12;
1085
- r.fillStyle = `hsla(${35 + n % 4 * 12}, 90%, 60%, ${0.1 + n % 3 * 0.05})`, r.beginPath(), r.arc(s, a, o, 0, Math.PI * 2), r.fill();
1084
+ const s = (Math.sin(n * 12.9898) * 0.5 + 0.5) * e, o = (Math.sin(n * 78.233) * 0.5 + 0.5) * t + Math.sin(i / 1500 + n) * 12, r = 14 + n % 5 * 12;
1085
+ a.fillStyle = `hsla(${35 + n % 4 * 12}, 90%, 60%, ${0.1 + n % 3 * 0.05})`, a.beginPath(), a.arc(s, o, r, 0, Math.PI * 2), a.fill();
1086
1086
  }
1087
1087
  },
1088
- sunset(r, e, t, i) {
1089
- const n = r.createLinearGradient(0, 0, 0, t);
1090
- n.addColorStop(0, "#2b1055"), n.addColorStop(0.55, "#d1548a"), n.addColorStop(1, "#ffb36b"), r.fillStyle = n, r.fillRect(0, 0, e, t), r.fillStyle = "#ffdf8e", r.beginPath(), r.arc(e * 0.5, t * 0.62 + Math.sin(i / 4e3) * 4, t * 0.13, 0, Math.PI * 2), r.fill(), r.fillStyle = "rgba(20,8,40,0.85)", r.fillRect(0, t * 0.78, e, t * 0.22);
1088
+ sunset(a, e, t, i) {
1089
+ const n = a.createLinearGradient(0, 0, 0, t);
1090
+ n.addColorStop(0, "#2b1055"), n.addColorStop(0.55, "#d1548a"), n.addColorStop(1, "#ffb36b"), a.fillStyle = n, a.fillRect(0, 0, e, t), a.fillStyle = "#ffdf8e", a.beginPath(), a.arc(e * 0.5, t * 0.62 + Math.sin(i / 4e3) * 4, t * 0.13, 0, Math.PI * 2), a.fill(), a.fillStyle = "rgba(20,8,40,0.85)", a.fillRect(0, t * 0.78, e, t * 0.22);
1091
1091
  }
1092
1092
  };
1093
- function Pe(r, e = {}) {
1094
- const t = e.document ?? globalThis.document, i = e.width ?? 1280, n = e.height ?? 720, s = e.frameRate ?? 24, a = r instanceof MediaStreamTrack ? r : r.getVideoTracks()[0];
1095
- if (!a) throw new Error("A video track is required");
1096
- let o = e.background ?? "none", c = e.overlay ?? "none", d = e.backgroundImage ?? null, l = e.segmenter ?? null, h = null, v = !1;
1097
- const g = t.createElement("video");
1098
- g.muted = !0, g.playsInline = !0, g.srcObject = new MediaStream([a]), g.play().catch(() => {
1093
+ function Ve(a, e = {}) {
1094
+ const t = e.document ?? globalThis.document;
1095
+ let i = e.width ?? 1280, n = e.height ?? 720;
1096
+ const s = !!(e.width || e.height), o = e.frameRate ?? 24, r = a instanceof MediaStreamTrack ? a : a.getVideoTracks()[0];
1097
+ if (!r) throw new Error("A video track is required");
1098
+ let c = e.background ?? "none", l = e.overlay ?? "none", d = e.backgroundImage ?? null, u = e.segmenter ?? null, S = null, f = !1;
1099
+ const v = t.createElement("video");
1100
+ v.muted = !0, v.playsInline = !0, v.srcObject = new MediaStream([r]), v.play().catch(() => {
1099
1101
  });
1100
- const k = t.createElement("canvas");
1102
+ const I = t.createElement("canvas");
1103
+ I.width = i, I.height = n;
1104
+ const y = I.getContext("2d"), k = t.createElement("canvas");
1101
1105
  k.width = i, k.height = n;
1102
- const p = k.getContext("2d"), C = t.createElement("canvas");
1103
- C.width = i, C.height = n;
1104
- const S = C.getContext("2d"), y = Array.from({ length: 80 }, (f, u) => ({
1105
- x: (Math.sin(u * 12.9898) * 0.5 + 0.5) * i,
1106
- y: (Math.sin(u * 78.233) * 0.5 + 0.5) * n,
1107
- speed: 0.6 + u % 5 * 0.5,
1108
- drift: Math.sin(u) * 0.6,
1109
- size: 2 + u % 4,
1110
- hue: u * 47 % 360
1106
+ const p = k.getContext("2d"), C = () => Array.from({ length: 80 }, (m, h) => ({
1107
+ x: (Math.sin(h * 12.9898) * 0.5 + 0.5) * i,
1108
+ y: (Math.sin(h * 78.233) * 0.5 + 0.5) * n,
1109
+ speed: 0.6 + h % 5 * 0.5,
1110
+ drift: Math.sin(h) * 0.6,
1111
+ size: 2 + h % 4,
1112
+ hue: h * 47 % 360
1111
1113
  }));
1112
- function T() {
1113
- if (h) {
1114
- S.clearRect(0, 0, i, n), S.drawImage(h, 0, 0, i, n), S.globalCompositeOperation = "source-in", S.drawImage(g, 0, 0, i, n), S.globalCompositeOperation = "source-over", p.drawImage(C, 0, 0);
1114
+ let P = C();
1115
+ const L = () => {
1116
+ if (s) return;
1117
+ const m = v.videoWidth, h = v.videoHeight;
1118
+ if (!m || !h) return;
1119
+ const T = Math.min(1, 1280 / Math.max(m, h)), R = Math.round(m * T), b = Math.round(h * T);
1120
+ R === i && b === n || (i = R, n = b, I.width = i, I.height = n, k.width = i, k.height = n, P = C());
1121
+ };
1122
+ v.addEventListener?.("loadedmetadata", L), v.addEventListener?.("resize", L);
1123
+ function M() {
1124
+ if (S) {
1125
+ p.clearRect(0, 0, i, n), p.drawImage(S, 0, 0, i, n), p.globalCompositeOperation = "source-in", p.drawImage(v, 0, 0, i, n), p.globalCompositeOperation = "source-over", y.drawImage(k, 0, 0);
1115
1126
  return;
1116
1127
  }
1117
- const f = i * 0.42, u = f * 9 / 16, q = i - f - 24, L = n - u - 24;
1118
- p.save(), p.shadowColor = "rgba(0,0,0,.5)", p.shadowBlur = 24, p.beginPath(), p.roundRect(q, L, f, u, 14), p.clip(), p.drawImage(g, q, L, f, u), p.restore();
1119
- }
1120
- function I(f) {
1121
- if (o === "none" ? p.drawImage(g, 0, 0, i, n) : o === "blur" ? (p.filter = "blur(14px)", p.drawImage(g, -16, -16, i + 32, n + 32), p.filter = "none", h && T()) : o === "image" && d ? (p.drawImage(d, 0, 0, i, n), T()) : ((G[o] ?? G.aurora)(p, i, n, f), T()), c === "snow" || c === "confetti")
1122
- for (const u of y)
1123
- u.y += u.speed * (c === "confetti" ? 1.6 : 1), u.x += u.drift, u.y > n && (u.y = -6, u.x = Math.random() * i), p.fillStyle = c === "snow" ? "rgba(255,255,255,.85)" : `hsl(${u.hue}, 90%, 60%)`, c === "confetti" ? p.fillRect(u.x, u.y, u.size + 2, u.size) : (p.beginPath(), p.arc(u.x, u.y, u.size / 2, 0, Math.PI * 2), p.fill());
1124
- else if (c === "vignette") {
1125
- const u = p.createRadialGradient(i / 2, n / 2, n * 0.4, i / 2, n / 2, n * 0.85);
1126
- u.addColorStop(0, "rgba(0,0,0,0)"), u.addColorStop(1, "rgba(0,0,0,0.55)"), p.fillStyle = u, p.fillRect(0, 0, i, n);
1128
+ const m = i * 0.42, h = m * 9 / 16, T = i - m - 24, R = n - h - 24;
1129
+ y.save(), y.shadowColor = "rgba(0,0,0,.5)", y.shadowBlur = 24, y.beginPath(), y.roundRect(T, R, m, h, 14), y.clip(), y.drawImage(v, T, R, m, h), y.restore();
1130
+ }
1131
+ function A(m) {
1132
+ if (c === "none" ? y.drawImage(v, 0, 0, i, n) : c === "blur" ? (y.filter = "blur(14px)", y.drawImage(v, -16, -16, i + 32, n + 32), y.filter = "none", S && M()) : c === "image" && d ? (y.drawImage(d, 0, 0, i, n), M()) : ((K[c] ?? K.aurora)(y, i, n, m), M()), l === "snow" || l === "confetti")
1133
+ for (const h of P)
1134
+ h.y += h.speed * (l === "confetti" ? 1.6 : 1), h.x += h.drift, h.y > n && (h.y = -6, h.x = Math.random() * i), y.fillStyle = l === "snow" ? "rgba(255,255,255,.85)" : `hsl(${h.hue}, 90%, 60%)`, l === "confetti" ? y.fillRect(h.x, h.y, h.size + 2, h.size) : (y.beginPath(), y.arc(h.x, h.y, h.size / 2, 0, Math.PI * 2), y.fill());
1135
+ else if (l === "vignette") {
1136
+ const h = y.createRadialGradient(i / 2, n / 2, n * 0.4, i / 2, n / 2, n * 0.85);
1137
+ h.addColorStop(0, "rgba(0,0,0,0)"), h.addColorStop(1, "rgba(0,0,0,0.55)"), y.fillStyle = h, y.fillRect(0, 0, i, n);
1127
1138
  }
1128
1139
  }
1129
- const R = setInterval(() => {
1130
- I(Date.now()), l && !v && o !== "none" && (v = !0, l.segment(g).then((f) => {
1131
- h = f;
1140
+ const w = setInterval(() => {
1141
+ A(Date.now()), u && !f && c !== "none" && (f = !0, u.segment(v).then((m) => {
1142
+ S = m;
1132
1143
  }).catch(() => {
1133
- h = null;
1144
+ S = null;
1134
1145
  }).finally(() => {
1135
- v = !1;
1146
+ f = !1;
1136
1147
  }));
1137
- }, Math.round(1e3 / s)), E = k.captureStream(s);
1148
+ }, Math.round(1e3 / o)), E = I.captureStream(o);
1138
1149
  return {
1139
1150
  track: E.getVideoTracks()[0],
1140
1151
  stream: E,
1141
- setBackground(f, u) {
1142
- o = me.includes(f) ? f : "none", u !== void 0 && (d = u), o === "none" && (h = null);
1152
+ setBackground(m, h) {
1153
+ c = Se.includes(m) ? m : "none", h !== void 0 && (d = h), c === "none" && (S = null);
1143
1154
  },
1144
- setOverlay(f) {
1145
- c = pe.includes(f) ? f : "none";
1155
+ setOverlay(m) {
1156
+ l = ke.includes(m) ? m : "none";
1146
1157
  },
1147
- setSegmenter(f) {
1148
- l = f, f || (h = null);
1158
+ setSegmenter(m) {
1159
+ u = m, m || (S = null);
1160
+ },
1161
+ dispose() {
1162
+ clearInterval(w), u?.close?.();
1163
+ for (const m of E.getTracks()) m.stop();
1164
+ v.srcObject = null;
1165
+ }
1166
+ };
1167
+ }
1168
+ function le() {
1169
+ return {
1170
+ blinkLeft: 0,
1171
+ blinkRight: 0,
1172
+ jawOpen: 0,
1173
+ smile: 0,
1174
+ browUp: 0,
1175
+ pupilX: 0,
1176
+ pupilY: 0,
1177
+ headYaw: 0,
1178
+ headPitch: 0,
1179
+ headRoll: 0,
1180
+ tracked: !1
1181
+ };
1182
+ }
1183
+ function D(a, e) {
1184
+ const t = a.find((i) => i.categoryName === e);
1185
+ return t ? Math.max(0, Math.min(1, t.score)) : 0;
1186
+ }
1187
+ function we(a) {
1188
+ if (!a || a.length < 16) return { yaw: 0, pitch: 0, roll: 0 };
1189
+ a[0], a[4];
1190
+ const e = a[8], t = a[1], i = a[5], n = a[9], s = a[10], o = Math.atan2(e, s), r = Math.asin(Math.max(-1, Math.min(1, -n))), c = Math.atan2(t, i);
1191
+ return { yaw: o, pitch: r, roll: c };
1192
+ }
1193
+ function Te(a, e, t = 1) {
1194
+ if (!a || a.length === 0) return le();
1195
+ const i = we(e), n = (D(a, "eyeLookOutRight") + D(a, "eyeLookInLeft")) / 2, s = (D(a, "eyeLookOutLeft") + D(a, "eyeLookInRight")) / 2, o = (D(a, "eyeLookUpLeft") + D(a, "eyeLookUpRight")) / 2, r = (D(a, "eyeLookDownLeft") + D(a, "eyeLookDownRight")) / 2, c = (D(a, "mouthSmileLeft") + D(a, "mouthSmileRight")) / 2, l = (D(a, "mouthFrownLeft") + D(a, "mouthFrownRight")) / 2, d = D(a, "eyeBlinkLeft"), u = D(a, "eyeBlinkRight"), S = Math.min(1, Math.abs(i.yaw) / 0.35) * Math.max(0, Math.min(1, t)), f = (d + u) / 2;
1196
+ return {
1197
+ blinkLeft: d + (f - d) * S,
1198
+ blinkRight: u + (f - u) * S,
1199
+ jawOpen: D(a, "jawOpen"),
1200
+ smile: c - l,
1201
+ browUp: (D(a, "browInnerUp") + D(a, "browOuterUpLeft") + D(a, "browOuterUpRight")) / 3,
1202
+ pupilX: n - s,
1203
+ pupilY: r - o,
1204
+ headYaw: i.yaw,
1205
+ headPitch: i.pitch,
1206
+ headRoll: i.roll,
1207
+ tracked: !0
1208
+ };
1209
+ }
1210
+ function X(a, e, t = 0.4) {
1211
+ const i = (n, s) => n + (s - n) * t;
1212
+ return {
1213
+ blinkLeft: i(a.blinkLeft, e.blinkLeft),
1214
+ blinkRight: i(a.blinkRight, e.blinkRight),
1215
+ jawOpen: i(a.jawOpen, e.jawOpen),
1216
+ smile: i(a.smile, e.smile),
1217
+ browUp: i(a.browUp, e.browUp),
1218
+ pupilX: i(a.pupilX, e.pupilX),
1219
+ pupilY: i(a.pupilY, e.pupilY),
1220
+ headYaw: i(a.headYaw, e.headYaw),
1221
+ headPitch: i(a.headPitch, e.headPitch),
1222
+ headRoll: i(a.headRoll, e.headRoll),
1223
+ tracked: e.tracked
1224
+ };
1225
+ }
1226
+ function be(a, e, t, i, n) {
1227
+ n || (a.fillStyle = "#101822", a.fillRect(0, 0, t, i));
1228
+ const s = n ? n.x + e.headYaw * n.radius * 0.1 : t / 2 + e.headYaw * t * 0.18, o = n ? n.y + e.headPitch * n.radius * 0.1 : i / 2 + e.headPitch * i * 0.18, r = n ? n.radius : Math.min(t, i) * 0.28;
1229
+ a.save(), a.translate(s, o), a.rotate(e.headRoll * 0.6);
1230
+ const c = Math.max(-1.35, Math.min(1.35, e.headYaw)), l = Math.max(-0.9, Math.min(0.9, e.headPitch)), d = Math.sin(c), u = (A) => Math.sin(A + c) * r, S = (A) => Math.cos(A + c), f = Math.sin(l) * r * 0.22;
1231
+ a.fillStyle = "#3a2a1c", a.beginPath(), a.ellipse(-d * r * 0.35, -r * 0.25, r * 0.98, r * 0.95, 0, 0, Math.PI * 2), a.fill(), a.fillStyle = e.tracked ? "#f2c9a0" : "#5a6470", a.beginPath(), a.ellipse(0, 0, r * (1 - Math.abs(d) * 0.12), r * 1.12, 0, 0, Math.PI * 2), a.fill(), a.fillStyle = "#3a2a1c", a.beginPath(), a.ellipse(-d * r * 0.28, -r * 0.72 + f * 0.4, r * 0.92, r * 0.5, 0, Math.PI, Math.PI * 2), a.fill();
1232
+ const v = 0;
1233
+ a.fillStyle = "#e5b58a", a.beginPath();
1234
+ const I = u(v) * 0.9, y = r * 0.12 + f;
1235
+ a.ellipse(I + d * r * 0.14, y, r * 0.11, r * 0.14, 0, 0, Math.PI * 2), a.fill();
1236
+ for (const A of [-1, 1]) {
1237
+ const w = A * 0.42, E = S(w);
1238
+ if (E <= 0.12) continue;
1239
+ const m = u(w) * 0.82, h = Math.max(0.2, E), T = A < 0 ? e.blinkLeft : e.blinkRight, R = Math.max(0.06, 1 - T), b = e.browUp * r * 0.14;
1240
+ a.strokeStyle = "#4a2e18", a.lineWidth = r * 0.07, a.lineCap = "round", a.beginPath(), a.moveTo(m - r * 0.2 * h, -r * 0.42 - b + f), a.quadraticCurveTo(m, -r * 0.52 - b + f, m + r * 0.2 * h, -r * 0.44 - b + f), a.stroke();
1241
+ const O = -r * 0.18 + f;
1242
+ a.fillStyle = "#ffffff", a.beginPath(), a.ellipse(m, O, r * 0.16 * h, r * 0.12 * R, 0, 0, Math.PI * 2), a.fill(), a.fillStyle = "#22303c", a.beginPath(), a.ellipse(
1243
+ m + e.pupilX * r * 0.07 * h,
1244
+ O + e.pupilY * r * 0.05,
1245
+ r * 0.06 * h,
1246
+ r * 0.06 * R,
1247
+ 0,
1248
+ 0,
1249
+ Math.PI * 2
1250
+ ), a.fill();
1251
+ }
1252
+ const k = Math.max(0.25, S(0)), p = u(0) * 0.7, C = r * 0.55 * k, P = r * (0.04 + e.jawOpen * 0.34), L = e.smile * r * 0.12, M = r * 0.42 + f;
1253
+ a.fillStyle = "#8c2f2f", a.beginPath(), a.moveTo(p - C / 2, M - L), a.quadraticCurveTo(p, M + P, p + C / 2, M - L), a.quadraticCurveTo(p, M - P * 0.25, p - C / 2, M - L), a.closePath(), a.fill(), a.restore(), !e.tracked && !n && (a.fillStyle = "#9fb0bd", a.font = `${Math.round(i * 0.045)}px system-ui`, a.textAlign = "center", a.fillText("No face detected", t / 2, i * 0.9), a.textAlign = "left");
1254
+ }
1255
+ const Z = {
1256
+ mouthGain: 1.7,
1257
+ blinkGain: 1,
1258
+ eyeSync: 1,
1259
+ smoothing: 0.4,
1260
+ avatarScale: 1,
1261
+ avatarLift: 0,
1262
+ lostHoldMs: 400
1263
+ };
1264
+ function ee() {
1265
+ return {
1266
+ render(a, e, t, i, n) {
1267
+ be(a, e, t, i, n ?? void 0);
1268
+ }
1269
+ };
1270
+ }
1271
+ const $ = (a) => Math.max(0, Math.min(1, a));
1272
+ function xe(a, e = {}) {
1273
+ const t = e.document ?? globalThis.document;
1274
+ let i = e.width ?? 1280, n = e.height ?? 720;
1275
+ const s = !!(e.width || e.height), o = e.frameRate ?? 24, r = a instanceof MediaStreamTrack ? a : a.getVideoTracks()[0];
1276
+ if (!r) throw new Error("A video track is required");
1277
+ let c = e.tracker ?? null, l = e.renderer ?? ee(), d = e.mode ?? "replace";
1278
+ const u = { ...Z, ...e.tuning }, S = e.backdrop ?? "#101822", f = t.createElement("video");
1279
+ f.muted = !0, f.playsInline = !0, f.srcObject = new MediaStream([r]), f.play().catch(() => {
1280
+ });
1281
+ const v = t.createElement("canvas");
1282
+ v.width = i, v.height = n;
1283
+ const I = v.getContext("2d"), y = () => {
1284
+ if (s) return;
1285
+ const w = f.videoWidth, E = f.videoHeight;
1286
+ if (!w || !E) return;
1287
+ const m = Math.min(1, 1280 / Math.max(w, E)), h = Math.round(w * m), T = Math.round(E * m);
1288
+ (!(h === i || T === n) || h !== i || T !== n) && (i = h, n = T, v.width = i, v.height = n);
1289
+ };
1290
+ f.addEventListener?.("loadedmetadata", y), f.addEventListener?.("resize", y);
1291
+ let k = le(), p = null, C = 0;
1292
+ function P(w) {
1293
+ if (w.length <= 454) return;
1294
+ const E = w[10], m = w[152], h = w[234], T = w[454], R = (h.x + T.x) / 2 * i, b = (h.y + T.y) / 2 * n, O = Math.hypot((m.x - E.x) * i, (m.y - E.y) * n), g = Math.max(0.6, Math.cos(k.headPitch)), q = { x: R, y: b, radius: Math.max(24, O / g * 0.8) };
1295
+ p = p ? { x: p.x + (q.x - p.x) * 0.4, y: p.y + (q.y - p.y) * 0.4, radius: p.radius + (q.radius - p.radius) * 0.15 } : q;
1296
+ }
1297
+ function L(w) {
1298
+ if (c && f.readyState >= 2) {
1299
+ let m = null;
1300
+ try {
1301
+ m = c.detect(f, w);
1302
+ } catch {
1303
+ }
1304
+ const h = Te(m?.categories ?? null, m?.matrix ?? null, u.eyeSync);
1305
+ h.jawOpen = $(h.jawOpen * u.mouthGain), h.blinkLeft = $(h.blinkLeft * u.blinkGain), h.blinkRight = $(h.blinkRight * u.blinkGain), h.tracked ? (C = w, k = X(k, h, u.smoothing), m?.landmarks && P(m.landmarks)) : k.tracked && w - C > u.lostHoldMs && (k = X(k, { ...k, jawOpen: 0, browUp: 0, smile: k.smile * 0.5, blinkLeft: 0.1, blinkRight: 0.1 }, 0.05));
1306
+ }
1307
+ d === "overlay" && f.readyState >= 2 ? I.drawImage(f, 0, 0, i, n) : (I.fillStyle = S, I.fillRect(0, 0, i, n));
1308
+ const E = d === "overlay" && p ? { x: p.x, y: p.y + u.avatarLift * p.radius, radius: p.radius * u.avatarScale } : null;
1309
+ l?.render(I, k, i, n, E);
1310
+ }
1311
+ const M = setInterval(() => L(Date.now()), Math.round(1e3 / o)), A = v.captureStream(o);
1312
+ return {
1313
+ track: A.getVideoTracks()[0],
1314
+ stream: A,
1315
+ getRig: () => ({ ...k }),
1316
+ setMode(w) {
1317
+ d = w === "overlay" ? "overlay" : "replace";
1318
+ },
1319
+ setTracker(w) {
1320
+ c?.close?.(), c = w;
1321
+ },
1322
+ setRenderer(w) {
1323
+ l?.dispose?.(), l = w ?? ee();
1324
+ },
1325
+ setTuning(w) {
1326
+ for (const E of Object.keys(Z)) {
1327
+ const m = w[E];
1328
+ typeof m == "number" && Number.isFinite(m) && (u[E] = m);
1329
+ }
1149
1330
  },
1331
+ getTuning: () => ({ ...u }),
1150
1332
  dispose() {
1151
- clearInterval(R), l?.close?.();
1152
- for (const f of E.getTracks()) f.stop();
1153
- g.srcObject = null;
1333
+ clearInterval(M), c?.close?.(), l?.dispose?.();
1334
+ for (const w of A.getTracks()) w.stop();
1335
+ f.srcObject = null;
1154
1336
  }
1155
1337
  };
1156
1338
  }
1157
- class fe extends EventTarget {
1339
+ class Ce extends EventTarget {
1158
1340
  mode;
1159
1341
  options;
1160
1342
  client = null;
@@ -1188,7 +1370,7 @@ class fe extends EventTarget {
1188
1370
  async initialize() {
1189
1371
  if (this.client) return this;
1190
1372
  try {
1191
- const t = (this.options.transport === "auto" ? this.mode === "meeting" ? "sfu" : "mesh" : this.options.transport ?? "mesh") === "sfu" ? he : Q;
1373
+ const t = (this.options.transport === "auto" ? this.mode === "meeting" ? "sfu" : "mesh" : this.options.transport ?? "mesh") === "sfu" ? ye : Q;
1192
1374
  return this.client = new t({
1193
1375
  credential: {
1194
1376
  signalingToken: this.options.credential.signalingToken,
@@ -1207,19 +1389,19 @@ class fe extends EventTarget {
1207
1389
  iceTransportPolicy: this.options.iceTransportPolicy ?? "all",
1208
1390
  localStream: this.options.localStream,
1209
1391
  onEvent: (i, n) => this.handleNativeEvent(i, n)
1210
- }), await this.client.initialize(), this.startStats(), this.emit("initialized", { mode: this.mode, sdkVersion: be }), this.options.autoJoin && await this.join(), this;
1392
+ }), await this.client.initialize(), this.startStats(), this.emit("initialized", { mode: this.mode, sdkVersion: De }), this.options.autoJoin && await this.join(), this;
1211
1393
  } catch (e) {
1212
1394
  this.state = "failed";
1213
- const t = te(e, "RTC initialization failed");
1214
- throw this.emit("error", x(t)), t;
1395
+ const t = re(e, "RTC initialization failed");
1396
+ throw this.emit("error", V(t)), t;
1215
1397
  }
1216
1398
  }
1217
1399
  async join() {
1218
1400
  const e = this.requireClient();
1219
1401
  return this.joinPromise ? this.joinPromise : (this.state = "joining", this.emit("joining", {}), this.joinPromise = e.join().catch((t) => {
1220
1402
  this.state = "failed";
1221
- const i = te(t, "RTC connection failed");
1222
- throw this.emit("error", x(i)), i;
1403
+ const i = re(t, "RTC connection failed");
1404
+ throw this.emit("error", V(i)), i;
1223
1405
  }).finally(() => {
1224
1406
  this.joinPromise = null;
1225
1407
  }), this.joinPromise);
@@ -1257,14 +1439,14 @@ class fe extends EventTarget {
1257
1439
  return navigator.mediaDevices?.enumerateDevices ? navigator.mediaDevices.enumerateDevices() : [];
1258
1440
  }
1259
1441
  getParticipants() {
1260
- return this.requireClient().getParticipants().map(X);
1442
+ return this.requireClient().getParticipants().map(ne);
1261
1443
  }
1262
1444
  getLocalStream() {
1263
1445
  return this.client?.getLocalStream() ?? null;
1264
1446
  }
1265
1447
  getRemoteStreams() {
1266
1448
  return (this.client?.getRemoteStreams() ?? []).map((e) => ({
1267
- participant: X(e.participant),
1449
+ participant: ne(e.participant),
1268
1450
  stream: e.stream
1269
1451
  }));
1270
1452
  }
@@ -1333,45 +1515,45 @@ class fe extends EventTarget {
1333
1515
  let n = "unavailable";
1334
1516
  if (e.requestMedia !== !1 && navigator.mediaDevices?.getUserMedia)
1335
1517
  try {
1336
- (await navigator.mediaDevices.getUserMedia({ audio: this.options.audio, video: this.options.video })).getTracks().forEach((v) => v.stop()), n = "granted";
1518
+ (await navigator.mediaDevices.getUserMedia({ audio: this.options.audio, video: this.options.video })).getTracks().forEach((S) => S.stop()), n = "granted";
1337
1519
  } catch {
1338
1520
  n = "denied", t.push("media_permission_denied");
1339
1521
  }
1340
- const s = navigator.mediaDevices?.enumerateDevices ? await navigator.mediaDevices.enumerateDevices().catch(() => []) : [], a = s.filter((h) => h.kind === "audioinput").length, o = s.filter((h) => h.kind === "videoinput").length;
1341
- this.options.audio && a === 0 && t.push("microphone_unavailable"), this.options.video && o === 0 && t.push("camera_unavailable");
1522
+ const s = navigator.mediaDevices?.enumerateDevices ? await navigator.mediaDevices.enumerateDevices().catch(() => []) : [], o = s.filter((u) => u.kind === "audioinput").length, r = s.filter((u) => u.kind === "videoinput").length;
1523
+ this.options.audio && o === 0 && t.push("microphone_unavailable"), this.options.video && r === 0 && t.push("camera_unavailable");
1342
1524
  const c = e.probeUrl ?? this.options.preflightProbeUrl;
1343
- let d = null;
1525
+ let l = null;
1344
1526
  if (c) {
1345
- const h = performance.now();
1527
+ const u = performance.now();
1346
1528
  try {
1347
- const v = await fetch(c, { method: "HEAD", cache: "no-store", credentials: "omit" });
1348
- d = Math.round(performance.now() - h), v.ok || t.push("network_probe_failed");
1529
+ const S = await fetch(c, { method: "HEAD", cache: "no-store", credentials: "omit" });
1530
+ l = Math.round(performance.now() - u), S.ok || t.push("network_probe_failed");
1349
1531
  } catch {
1350
1532
  t.push("network_probe_failed");
1351
1533
  }
1352
1534
  }
1353
- const l = {
1535
+ const d = {
1354
1536
  passed: t.length === 0,
1355
1537
  sampledAt: (/* @__PURE__ */ new Date()).toISOString(),
1356
1538
  secureContext: i,
1357
1539
  mediaPermission: n,
1358
- microphoneCount: a,
1359
- cameraCount: o,
1360
- probeLatencyMs: d,
1361
- effectiveConnectionType: Z()?.effectiveType ?? null,
1540
+ microphoneCount: o,
1541
+ cameraCount: r,
1542
+ probeLatencyMs: l,
1543
+ effectiveConnectionType: ae()?.effectiveType ?? null,
1362
1544
  issues: t
1363
1545
  };
1364
- return this.emit("preflight", l), this.options.autoReportStats && await this.reportQuality({
1546
+ return this.emit("preflight", d), this.options.autoReportStats && await this.reportQuality({
1365
1547
  sampleType: "preflight",
1366
- sampledAt: l.sampledAt,
1367
- connectionState: l.passed ? "ready" : "failed",
1368
- rttMs: l.probeLatencyMs,
1369
- effectiveConnectionType: l.effectiveConnectionType
1548
+ sampledAt: d.sampledAt,
1549
+ connectionState: d.passed ? "ready" : "failed",
1550
+ rttMs: d.probeLatencyMs,
1551
+ effectiveConnectionType: d.effectiveConnectionType
1370
1552
  }).catch(() => {
1371
- }), l;
1553
+ }), d;
1372
1554
  }
1373
1555
  async collectQualitySample(e = "runtime") {
1374
- const t = this.options.statsProvider ? await this.options.statsProvider() : {}, i = this.peerConnection ? await ke(this.peerConnection, this.previousCounters) : null;
1556
+ const t = this.options.statsProvider ? await this.options.statsProvider() : {}, i = this.peerConnection ? await Pe(this.peerConnection, this.previousCounters) : null;
1375
1557
  i?.counters && (this.previousCounters = i.counters);
1376
1558
  const s = { ...{
1377
1559
  sampleType: e,
@@ -1400,10 +1582,10 @@ class fe extends EventTarget {
1400
1582
  transportProtocol: null,
1401
1583
  candidateType: null,
1402
1584
  qualityLimitationReason: null,
1403
- deviceMemoryGb: we(),
1404
- effectiveConnectionType: Z()?.effectiveType ?? null
1405
- }, ...i?.sample ?? {}, ...t, sampleType: e, sampledAt: (/* @__PURE__ */ new Date()).toISOString() }, a = Te(s);
1406
- return s.networkScore = a.networkScore, s.estimatedMos = a.estimatedMos, this.latestQuality = s, s;
1585
+ deviceMemoryGb: Le(),
1586
+ effectiveConnectionType: ae()?.effectiveType ?? null
1587
+ }, ...i?.sample ?? {}, ...t, sampleType: e, sampledAt: (/* @__PURE__ */ new Date()).toISOString() }, o = Ae(s);
1588
+ return s.networkScore = o.networkScore, s.estimatedMos = o.estimatedMos, this.latestQuality = s, s;
1407
1589
  }
1408
1590
  async reportQuality(e) {
1409
1591
  const t = this.options.credential.telemetryToken, i = this.options.telemetryEndpoint ?? this.options.credential.telemetryEndpoint;
@@ -1453,7 +1635,7 @@ class fe extends EventTarget {
1453
1635
  const i = Date.now(), n = e !== "runtime" || i - this.lastTelemetryAt >= this.options.telemetryInterval;
1454
1636
  this.options.autoReportStats && n && (await this.reportQuality(t), this.lastTelemetryAt = i);
1455
1637
  } catch (t) {
1456
- this.emit("telemetryerror", x(t));
1638
+ this.emit("telemetryerror", V(t));
1457
1639
  } finally {
1458
1640
  this.statsRunning = !1;
1459
1641
  }
@@ -1470,8 +1652,8 @@ class fe extends EventTarget {
1470
1652
  }
1471
1653
  const t = String(e.qualityLimitationReason ?? "").toLowerCase(), i = (e.packetLossPct ?? 0) >= 8 || (e.rttMs ?? 0) >= 800 || t === "cpu" || t === "bandwidth", n = i || (e.packetLossPct ?? 0) >= 3 || (e.rttMs ?? 0) >= 350 || e.availableOutgoingBitrateBps != null && e.availableOutgoingBitrateBps < 35e4, s = (e.packetLossPct ?? 100) < 1 && (e.rttMs ?? 1e3) < 180 && !t && (e.availableOutgoingBitrateBps == null || e.availableOutgoingBitrateBps > 1e6);
1472
1654
  this.poorQualitySamples = n ? this.poorQualitySamples + 1 : 0, this.goodQualitySamples = s ? this.goodQualitySamples + 1 : 0;
1473
- const a = ["low", "medium", "high"], o = a.indexOf(this.effectiveQuality);
1474
- i && o > 0 ? (this.poorQualitySamples = 0, await this.applyEffectiveQuality("low", "network")) : this.poorQualitySamples >= 2 && o > 0 ? (this.poorQualitySamples = 0, await this.applyEffectiveQuality(a[o - 1], "network")) : this.goodQualitySamples >= 6 && o >= 0 && o < a.length - 1 && (this.goodQualitySamples = 0, await this.applyEffectiveQuality(a[o + 1], "network"));
1655
+ const o = ["low", "medium", "high"], r = o.indexOf(this.effectiveQuality);
1656
+ i && r > 0 ? (this.poorQualitySamples = 0, await this.applyEffectiveQuality("low", "network")) : this.poorQualitySamples >= 2 && r > 0 ? (this.poorQualitySamples = 0, await this.applyEffectiveQuality(o[r - 1], "network")) : this.goodQualitySamples >= 6 && r >= 0 && r < o.length - 1 && (this.goodQualitySamples = 0, await this.applyEffectiveQuality(o[r + 1], "network"));
1475
1657
  }
1476
1658
  async applyEffectiveQuality(e, t) {
1477
1659
  if (this.effectiveQuality === e && this.client) return;
@@ -1487,21 +1669,21 @@ class fe extends EventTarget {
1487
1669
  this.dispatchEvent(new CustomEvent(e, { detail: i })), this.options.onEvent?.(i);
1488
1670
  }
1489
1671
  }
1490
- async function Me(r) {
1491
- return new fe(r).initialize();
1672
+ async function Ue(a) {
1673
+ return new Ce(a).initialize();
1492
1674
  }
1493
- let O = null;
1494
- function se() {
1495
- if (O) return O;
1496
- const r = globalThis, e = r.AudioContext ?? r.webkitAudioContext;
1497
- return e ? (O = new e(), O) : null;
1675
+ let U = null;
1676
+ function de() {
1677
+ if (U) return U;
1678
+ const a = globalThis, e = a.AudioContext ?? a.webkitAudioContext;
1679
+ return e ? (U = new e(), U) : null;
1498
1680
  }
1499
- async function ge() {
1500
- const r = se();
1501
- return r ? (r.state === "suspended" && await r.resume().catch(() => {
1502
- }), r.state === "running") : !1;
1681
+ async function Re() {
1682
+ const a = de();
1683
+ return a ? (a.state === "suspended" && await a.resume().catch(() => {
1684
+ }), a.state === "running") : !1;
1503
1685
  }
1504
- class ve {
1686
+ class Ie {
1505
1687
  options;
1506
1688
  media = null;
1507
1689
  cadenceTimer = null;
@@ -1510,7 +1692,7 @@ class ve {
1510
1692
  this.options = e;
1511
1693
  }
1512
1694
  unlock() {
1513
- return ge();
1695
+ return Re();
1514
1696
  }
1515
1697
  startIncoming() {
1516
1698
  this.start("incoming", this.options.ringtoneUrl);
@@ -1538,17 +1720,17 @@ class ve {
1538
1720
  i(), this.cadenceTimer = setInterval(i, e === "incoming" ? 3e3 : 3500);
1539
1721
  }
1540
1722
  playBuiltInTone(e) {
1541
- const t = se();
1723
+ const t = de();
1542
1724
  if (!t || t.state !== "running") return;
1543
- const i = e === "incoming" ? 1.25 : 1, n = e === "incoming" ? [440, 480] : [425], s = t.createGain(), a = t.currentTime;
1544
- s.gain.setValueAtTime(1e-4, a), s.gain.exponentialRampToValueAtTime(0.045, a + 0.025), s.gain.setValueAtTime(0.045, a + i - 0.06), s.gain.exponentialRampToValueAtTime(1e-4, a + i), s.connect(t.destination);
1545
- for (const o of n) {
1725
+ const i = e === "incoming" ? 1.25 : 1, n = e === "incoming" ? [440, 480] : [425], s = t.createGain(), o = t.currentTime;
1726
+ s.gain.setValueAtTime(1e-4, o), s.gain.exponentialRampToValueAtTime(0.045, o + 0.025), s.gain.setValueAtTime(0.045, o + i - 0.06), s.gain.exponentialRampToValueAtTime(1e-4, o + i), s.connect(t.destination);
1727
+ for (const r of n) {
1546
1728
  const c = t.createOscillator();
1547
- c.type = "sine", c.frequency.value = o, c.connect(s), this.oscillators.add(c), c.onended = () => this.oscillators.delete(c), c.start(a), c.stop(a + i + 0.02);
1729
+ c.type = "sine", c.frequency.value = r, c.connect(s), this.oscillators.add(c), c.onended = () => this.oscillators.delete(c), c.start(o), c.stop(o + i + 0.02);
1548
1730
  }
1549
1731
  }
1550
1732
  }
1551
- class ye extends EventTarget {
1733
+ class Me extends EventTarget {
1552
1734
  options;
1553
1735
  socket = null;
1554
1736
  stopped = !1;
@@ -1575,14 +1757,14 @@ class ye extends EventTarget {
1575
1757
  if (e.token && !e.token.startsWith("rtcc_")) throw new Error("A valid RTC client token is required");
1576
1758
  if (!e.signalingUrl && !e.apiBaseUrl && !globalThis.location?.origin)
1577
1759
  throw new Error("signalingUrl or apiBaseUrl is required outside a browser");
1578
- this.options = e, this.currentToken = e.token ?? null, this.currentTokenExpiresAt = e.tokenExpiresAt, this.callAudio = new ve(e), this.mount = K(e.mount), this.scheduleTokenRefresh();
1760
+ this.options = e, this.currentToken = e.token ?? null, this.currentTokenExpiresAt = e.tokenExpiresAt, this.callAudio = new Ie(e), this.mount = te(e.mount), this.scheduleTokenRefresh();
1579
1761
  }
1580
1762
  connect() {
1581
1763
  if (!this.options.signalingUrl) throw new Error("signalingUrl is required to connect signaling");
1582
1764
  return this.stopped = !1, this.scheduleTokenRefresh(), this.openSocket("initial"), this;
1583
1765
  }
1584
1766
  setMount(e) {
1585
- this.clearUi(), this.mount = K(e), this.currentCall && this.renderIncoming(this.currentCall);
1767
+ this.clearUi(), this.mount = te(e), this.currentCall && this.renderIncoming(this.currentCall);
1586
1768
  }
1587
1769
  /** Binds media lifetime to a call. Terminal call states automatically leave and destroy it. */
1588
1770
  attachMediaClient(e, t) {
@@ -1610,8 +1792,8 @@ class ye extends EventTarget {
1610
1792
  headers: e.idempotencyKey ? { "idempotency-key": e.idempotencyKey } : void 0,
1611
1793
  body: JSON.stringify(i)
1612
1794
  });
1613
- } catch (a) {
1614
- throw this.callAudio.stop(), a;
1795
+ } catch (o) {
1796
+ throw this.callAudio.stop(), o;
1615
1797
  }
1616
1798
  return !s.credential && s.callerCredential && (s.credential = s.callerCredential), this.outgoingCallId = String(s.call?.id ?? "") || null, this.outgoingCallId && this.activeCallIds.add(this.outgoingCallId), this.emit("outgoingcall", s), s;
1617
1799
  })();
@@ -1668,7 +1850,7 @@ class ye extends EventTarget {
1668
1850
  /** Applies a call state received through the application's own push or signaling channel. */
1669
1851
  handleCallUpdate(e) {
1670
1852
  const t = e.id === this.currentCall?.id || e.id === this.outgoingCallId;
1671
- t && e.status === "accepted" ? (this.activeCallIds.add(e.id), this.callAudio.stop(), this.dismiss(e.id)) : t && ["rejected", "busy", "cancelled", "ended", "failed", "missed"].includes(e.status) && this.showTerminal(e.id, Se(e.status)), ["rejected", "busy", "cancelled", "ended", "failed", "missed"].includes(e.status) && this.releaseMedia(e.id), this.emit("callupdated", e);
1853
+ t && e.status === "accepted" ? (this.activeCallIds.add(e.id), this.callAudio.stop(), this.dismiss(e.id)) : t && ["rejected", "busy", "cancelled", "ended", "failed", "missed"].includes(e.status) && this.showTerminal(e.id, Ee(e.status)), ["rejected", "busy", "cancelled", "ended", "failed", "missed"].includes(e.status) && this.releaseMedia(e.id), this.emit("callupdated", e);
1672
1854
  }
1673
1855
  disconnect() {
1674
1856
  this.stopped = !0, this.reconnectTimer && clearTimeout(this.reconnectTimer), this.heartbeatTimer && clearInterval(this.heartbeatTimer), this.tokenRefreshTimer && clearTimeout(this.tokenRefreshTimer), this.reconnectTimer = null, this.heartbeatTimer = null, this.tokenRefreshTimer = null, this.socket?.close(1e3, "Client disconnected"), this.socket = null, this.callAudio.stop(), this.dismiss();
@@ -1684,7 +1866,7 @@ class ye extends EventTarget {
1684
1866
  try {
1685
1867
  t = await this.resolveToken(e, e === "reconnect");
1686
1868
  } catch (s) {
1687
- this.emit("tokenerror", x(s)), !this.stopped && this.options.reconnect !== !1 && this.scheduleReconnect();
1869
+ this.emit("tokenerror", V(s)), !this.stopped && this.options.reconnect !== !1 && this.scheduleReconnect();
1688
1870
  return;
1689
1871
  }
1690
1872
  if (this.stopped || this.socket) return;
@@ -1692,8 +1874,8 @@ class ye extends EventTarget {
1692
1874
  i.protocol = i.protocol === "https:" ? "wss:" : "ws:";
1693
1875
  const n = new WebSocket(i, ["rtc-client", t]);
1694
1876
  this.socket = n, n.addEventListener("open", () => {
1695
- const s = this.reconnectAttempt > 0, a = this.lastDisconnect;
1696
- this.reconnectAttempt = 0, this.heartbeatDeadline = Date.now() + 45e3, this.emit("signalingconnected", { resumed: s }), a && (this.reportSignalingDiagnostic({ event: "recovered", code: a.code, reason: a.reason, wasClean: a.wasClean, outageMs: Date.now() - a.at }), this.lastDisconnect = null), this.heartbeatTimer && clearInterval(this.heartbeatTimer), this.heartbeatTimer = setInterval(() => {
1877
+ const s = this.reconnectAttempt > 0, o = this.lastDisconnect;
1878
+ this.reconnectAttempt = 0, this.heartbeatDeadline = Date.now() + 45e3, this.emit("signalingconnected", { resumed: s }), o && (this.reportSignalingDiagnostic({ event: "recovered", code: o.code, reason: o.reason, wasClean: o.wasClean, outageMs: Date.now() - o.at }), this.lastDisconnect = null), this.heartbeatTimer && clearInterval(this.heartbeatTimer), this.heartbeatTimer = setInterval(() => {
1697
1879
  if (n.readyState === WebSocket.OPEN) {
1698
1880
  if (Date.now() > this.heartbeatDeadline) {
1699
1881
  n.close(4e3, "Signaling heartbeat timed out");
@@ -1707,14 +1889,14 @@ class ye extends EventTarget {
1707
1889
  }), n.addEventListener("close", (s) => {
1708
1890
  if (this.socket !== n) return;
1709
1891
  this.socket = null, this.heartbeatTimer && clearInterval(this.heartbeatTimer), this.heartbeatTimer = null;
1710
- const a = !this.stopped && this.options.reconnect !== !1, o = {
1892
+ const o = !this.stopped && this.options.reconnect !== !1, r = {
1711
1893
  code: s.code,
1712
1894
  reason: s.reason,
1713
1895
  wasClean: s.wasClean,
1714
- willReconnect: a,
1896
+ willReconnect: o,
1715
1897
  attempt: this.reconnectAttempt + 1
1716
1898
  };
1717
- a && (this.lastDisconnect = { code: s.code, reason: s.reason, wasClean: s.wasClean, at: Date.now() }), this.emit("signalingdisconnected", o), this.reportSignalingDiagnostic({ event: a ? "reconnecting" : "disconnected", ...o }), a && this.scheduleReconnect();
1899
+ o && (this.lastDisconnect = { code: s.code, reason: s.reason, wasClean: s.wasClean, at: Date.now() }), this.emit("signalingdisconnected", r), this.reportSignalingDiagnostic({ event: o ? "reconnecting" : "disconnected", ...r }), o && this.scheduleReconnect();
1718
1900
  }), n.addEventListener("error", () => {
1719
1901
  const s = { message: "RTC signaling connection failed", reconnecting: !this.stopped && this.options.reconnect !== !1, attempt: this.reconnectAttempt + 1 };
1720
1902
  this.emit("signalingerror", s), this.reportSignalingDiagnostic({ event: "error", attempt: s.attempt });
@@ -1748,16 +1930,16 @@ class ye extends EventTarget {
1748
1930
  async request(e, t, i = !1) {
1749
1931
  const n = this.options.apiBaseUrl ?? this.options.signalingUrl ?? globalThis.location?.origin;
1750
1932
  if (!n) throw new Error("apiBaseUrl is required for call actions");
1751
- const s = new URL(n, globalThis.location?.href), a = await this.resolveToken("initial"), o = new Headers(t.headers);
1752
- o.set("authorization", `Bearer ${a}`), o.set("content-type", "application/json");
1753
- const c = await fetch(new URL(e, s.origin), { ...t, headers: o }), d = await c.json().catch(() => ({}));
1933
+ const s = new URL(n, globalThis.location?.href), o = await this.resolveToken("initial"), r = new Headers(t.headers);
1934
+ r.set("authorization", `Bearer ${o}`), r.set("content-type", "application/json");
1935
+ const c = await fetch(new URL(e, s.origin), { ...t, headers: r }), l = await c.json().catch(() => ({}));
1754
1936
  if (c.status === 401 && this.options.tokenProvider && !i)
1755
1937
  return await this.resolveToken("unauthorized", !0), this.request(e, t, !0);
1756
1938
  if (!c.ok) {
1757
- const l = d.error;
1758
- throw new Error(typeof l == "string" ? l : l?.message || `RTC API failed with HTTP ${c.status}`);
1939
+ const d = l.error;
1940
+ throw new Error(typeof d == "string" ? d : d?.message || `RTC API failed with HTTP ${c.status}`);
1759
1941
  }
1760
- return d;
1942
+ return l;
1761
1943
  }
1762
1944
  renderIncoming(e) {
1763
1945
  if (!this.mount) return;
@@ -1780,12 +1962,12 @@ class ye extends EventTarget {
1780
1962
  n.textContent = e.caller.name, n.style.fontSize = "20px";
1781
1963
  const s = document.createElement("div");
1782
1964
  Object.assign(s.style, { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "10px" });
1783
- const a = Y("Decline", "#fff", "#a32920", "#d9a09b"), o = Y("Answer", "#fff", "#08745f", "#08745f");
1784
- a.addEventListener("click", () => {
1785
- this.reject(e.id).catch((c) => this.emit("error", x(c)));
1786
- }), o.addEventListener("click", () => {
1787
- this.accept(e.id).catch((c) => this.emit("error", x(c)));
1788
- }), s.appendChild(a), s.appendChild(o), t.appendChild(i), t.appendChild(n), t.appendChild(s), this.mount.replaceChildren(t);
1965
+ const o = ie("Decline", "#fff", "#a32920", "#d9a09b"), r = ie("Answer", "#fff", "#08745f", "#08745f");
1966
+ o.addEventListener("click", () => {
1967
+ this.reject(e.id).catch((c) => this.emit("error", V(c)));
1968
+ }), r.addEventListener("click", () => {
1969
+ this.accept(e.id).catch((c) => this.emit("error", V(c)));
1970
+ }), s.appendChild(o), s.appendChild(r), t.appendChild(i), t.appendChild(n), t.appendChild(s), this.mount.replaceChildren(t);
1789
1971
  }
1790
1972
  dismiss(e) {
1791
1973
  e && this.currentCall?.id !== e && this.outgoingCallId !== e || (this.currentCall = null, (!e || this.outgoingCallId === e) && (this.outgoingCallId = null), this.callAudio.stop(), this.clearUi());
@@ -1852,7 +2034,7 @@ class ye extends EventTarget {
1852
2034
  const t = Math.max(0, e - this.refreshSkew(e));
1853
2035
  this.tokenRefreshTimer = setTimeout(() => {
1854
2036
  this.tokenRefreshTimer = null, this.resolveToken("expiring", !0).catch((i) => {
1855
- this.emit("tokenerror", x(i)), this.stopped || (this.tokenRefreshTimer = setTimeout(() => this.scheduleTokenRefresh(), 3e4));
2037
+ this.emit("tokenerror", V(i)), this.stopped || (this.tokenRefreshTimer = setTimeout(() => this.scheduleTokenRefresh(), 3e4));
1856
2038
  });
1857
2039
  }, Math.min(t, 2147e6));
1858
2040
  }
@@ -1866,23 +2048,23 @@ class ye extends EventTarget {
1866
2048
  this.options.reportSignalingDiagnostics === !1 || this.stopped || this.request("/v1/rtc/client/signaling-events", {
1867
2049
  method: "POST",
1868
2050
  body: JSON.stringify({ ...e, occurredAt: (/* @__PURE__ */ new Date()).toISOString() })
1869
- }).catch((t) => this.emit("diagnosticserror", x(t)));
2051
+ }).catch((t) => this.emit("diagnosticserror", V(t)));
1870
2052
  }
1871
2053
  emit(e, t) {
1872
2054
  const i = { type: e, detail: t, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
1873
2055
  this.dispatchEvent(new CustomEvent(e, { detail: i })), this.options.onEvent?.(i);
1874
2056
  }
1875
2057
  }
1876
- function Ae(r) {
1877
- const e = new ye(r);
1878
- return r.autoConnect !== !1 && r.signalingUrl && e.connect(), e;
2058
+ function Qe(a) {
2059
+ const e = new Me(a);
2060
+ return a.autoConnect !== !1 && a.signalingUrl && e.connect(), e;
1879
2061
  }
1880
- function K(r) {
1881
- return r ? typeof r == "string" ? document.querySelector(r) : r : null;
2062
+ function te(a) {
2063
+ return a ? typeof a == "string" ? document.querySelector(a) : a : null;
1882
2064
  }
1883
- function Y(r, e, t, i) {
2065
+ function ie(a, e, t, i) {
1884
2066
  const n = document.createElement("button");
1885
- return n.type = "button", n.textContent = r, Object.assign(n.style, {
2067
+ return n.type = "button", n.textContent = a, Object.assign(n.style, {
1886
2068
  minHeight: "44px",
1887
2069
  border: `1px solid ${i}`,
1888
2070
  borderRadius: "6px",
@@ -1893,7 +2075,7 @@ function Y(r, e, t, i) {
1893
2075
  fontWeight: "700"
1894
2076
  }), n;
1895
2077
  }
1896
- function Se(r) {
2078
+ function Ee(a) {
1897
2079
  return {
1898
2080
  rejected: "Call declined",
1899
2081
  busy: "Line busy",
@@ -1901,96 +2083,104 @@ function Se(r) {
1901
2083
  ended: "Call ended",
1902
2084
  failed: "Call failed",
1903
2085
  missed: "No answer"
1904
- }[r] ?? "Call ended";
2086
+ }[a] ?? "Call ended";
1905
2087
  }
1906
- function X(r) {
2088
+ function ne(a) {
1907
2089
  return {
1908
- id: r.participantId,
1909
- name: r.displayName,
2090
+ id: a.participantId,
2091
+ name: a.displayName,
1910
2092
  role: "participant"
1911
2093
  };
1912
2094
  }
1913
- async function ke(r, e) {
1914
- const t = await r.getStats(), i = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Map();
1915
- let s = 0, a = 0, o = 0, c = 0, d = 0, l = 0, h = 0, v = 0, g = 0, k = 0, p = 0, C = 0, S = 0, y = null, T = null, I = null, R = null, E = null, f = null;
1916
- t.forEach((m) => {
1917
- n.set(m.id, m), m.type === "codec" && i.set(m.id, String(m.mimeType ?? ""));
1918
- }), t.forEach((m) => {
1919
- if (m.type === "inbound-rtp" && !m.isRemote && (s += Number(m.bytesReceived ?? 0), o += Math.max(0, Number(m.packetsLost ?? 0)), c += Math.max(0, Number(m.packetsReceived ?? 0)), Number.isFinite(m.jitter) && (d += Number(m.jitter), l += 1), h = Math.max(h, Number(m.framesPerSecond ?? 0)), v = Math.max(v, Number(m.frameWidth ?? 0)), g = Math.max(g, Number(m.frameHeight ?? 0)), k += Number(m.framesDropped ?? 0), p += Number(m.freezeCount ?? 0), C += Number(m.totalFreezesDuration ?? 0), S += Number(m.concealedSamples ?? 0), E ||= i.get(String(m.codecId ?? "")) ?? null), m.type === "outbound-rtp" && !m.isRemote && (a += Number(m.bytesSent ?? 0), f ||= m.qualityLimitationReason ? String(m.qualityLimitationReason) : null, E ||= i.get(String(m.codecId ?? "")) ?? null), m.type === "candidate-pair" && (m.selected || m.nominated) && m.state === "succeeded") {
1920
- y = Number.isFinite(m.currentRoundTripTime) ? Number(m.currentRoundTripTime) : y, T = Number.isFinite(m.availableOutgoingBitrate) ? Number(m.availableOutgoingBitrate) : T;
1921
- const A = n.get(m.localCandidateId);
1922
- I = A?.candidateType ? String(A.candidateType) : I, R = A?.protocol ? String(A.protocol) : R;
2095
+ async function Pe(a, e) {
2096
+ const t = await a.getStats(), i = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Map();
2097
+ let s = 0, o = 0, r = 0, c = 0, l = 0, d = 0, u = 0, S = 0, f = 0, v = 0, I = 0, y = 0, k = 0, p = null, C = null, P = null, L = null, M = null, A = null;
2098
+ t.forEach((g) => {
2099
+ n.set(g.id, g), g.type === "codec" && i.set(g.id, String(g.mimeType ?? ""));
2100
+ }), t.forEach((g) => {
2101
+ if (g.type === "inbound-rtp" && !g.isRemote && (s += Number(g.bytesReceived ?? 0), r += Math.max(0, Number(g.packetsLost ?? 0)), c += Math.max(0, Number(g.packetsReceived ?? 0)), Number.isFinite(g.jitter) && (l += Number(g.jitter), d += 1), u = Math.max(u, Number(g.framesPerSecond ?? 0)), S = Math.max(S, Number(g.frameWidth ?? 0)), f = Math.max(f, Number(g.frameHeight ?? 0)), v += Number(g.framesDropped ?? 0), I += Number(g.freezeCount ?? 0), y += Number(g.totalFreezesDuration ?? 0), k += Number(g.concealedSamples ?? 0), M ||= i.get(String(g.codecId ?? "")) ?? null), g.type === "outbound-rtp" && !g.isRemote && (o += Number(g.bytesSent ?? 0), A ||= g.qualityLimitationReason ? String(g.qualityLimitationReason) : null, M ||= i.get(String(g.codecId ?? "")) ?? null), g.type === "candidate-pair" && (g.selected || g.nominated) && g.state === "succeeded") {
2102
+ p = Number.isFinite(g.currentRoundTripTime) ? Number(g.currentRoundTripTime) : p, C = Number.isFinite(g.availableOutgoingBitrate) ? Number(g.availableOutgoingBitrate) : C;
2103
+ const q = n.get(g.localCandidateId);
2104
+ P = q?.candidateType ? String(q.candidateType) : P, L = q?.protocol ? String(q.protocol) : L;
1923
2105
  }
1924
2106
  });
1925
- const u = Date.now(), q = e ? Math.max(1e-3, (u - e.timestamp) / 1e3) : 0, L = e ? Math.max(0, Math.round((s - e.inboundBytes) * 8 / q)) : null, $ = e ? Math.max(0, Math.round((a - e.outboundBytes) * 8 / q)) : null, b = o + c, P = e ? Math.max(0, o - e.packetsLost) : o, w = e ? Math.max(0, c - e.packetsReceived) : c, N = P + w;
2107
+ const w = Date.now(), E = e ? Math.max(1e-3, (w - e.timestamp) / 1e3) : 0, m = e ? Math.max(0, Math.round((s - e.inboundBytes) * 8 / E)) : null, h = e ? Math.max(0, Math.round((o - e.outboundBytes) * 8 / E)) : null, T = r + c, R = e ? Math.max(0, r - e.packetsLost) : r, b = e ? Math.max(0, c - e.packetsReceived) : c, O = R + b;
1926
2108
  return {
1927
- counters: { timestamp: u, inboundBytes: s, outboundBytes: a, packetsLost: o, packetsReceived: c },
2109
+ counters: { timestamp: w, inboundBytes: s, outboundBytes: o, packetsLost: r, packetsReceived: c },
1928
2110
  sample: {
1929
- rttMs: y === null ? null : y * 1e3,
1930
- jitterMs: l ? d / l * 1e3 : null,
1931
- packetLossPct: N ? P / N * 100 : null,
1932
- packetLossCumulativePct: b ? o / b * 100 : null,
1933
- packetsLost: o,
2111
+ rttMs: p === null ? null : p * 1e3,
2112
+ jitterMs: d ? l / d * 1e3 : null,
2113
+ packetLossPct: O ? R / O * 100 : null,
2114
+ packetLossCumulativePct: T ? r / T * 100 : null,
2115
+ packetsLost: r,
1934
2116
  packetsReceived: c,
1935
- inboundBitrateBps: L,
1936
- outboundBitrateBps: $,
1937
- availableOutgoingBitrateBps: T,
1938
- framesPerSecond: h || null,
1939
- frameWidth: v || null,
1940
- frameHeight: g || null,
1941
- framesDropped: k,
1942
- freezeCount: p,
1943
- freezeDurationMs: Math.round(C * 1e3),
1944
- concealedSamples: S,
1945
- codec: E,
1946
- transportProtocol: R,
1947
- candidateType: I,
1948
- qualityLimitationReason: f
2117
+ inboundBitrateBps: m,
2118
+ outboundBitrateBps: h,
2119
+ availableOutgoingBitrateBps: C,
2120
+ framesPerSecond: u || null,
2121
+ frameWidth: S || null,
2122
+ frameHeight: f || null,
2123
+ framesDropped: v,
2124
+ freezeCount: I,
2125
+ freezeDurationMs: Math.round(y * 1e3),
2126
+ concealedSamples: k,
2127
+ codec: M,
2128
+ transportProtocol: L,
2129
+ candidateType: P,
2130
+ qualityLimitationReason: A
1949
2131
  }
1950
2132
  };
1951
2133
  }
1952
- function Te(r) {
1953
- let e = r.mediaScore == null ? 5 : Math.round(ee(Number(r.mediaScore), 0, 5));
1954
- const t = F(r.packetLossPct) ?? 0, i = F(r.rttMs) ?? 0, n = F(r.jitterMs) ?? 0, s = String(r.connectionState ?? "unknown").toLowerCase();
2134
+ function Ae(a) {
2135
+ let e = a.mediaScore == null ? 5 : Math.round(se(Number(a.mediaScore), 0, 5));
2136
+ const t = H(a.packetLossPct) ?? 0, i = H(a.rttMs) ?? 0, n = H(a.jitterMs) ?? 0, s = String(a.connectionState ?? "unknown").toLowerCase();
1955
2137
  ["failed", "closed", "disconnected"].includes(s) ? e = 0 : ["reconnecting", "checking"].includes(s) && (e = Math.min(e, 2)), t >= 15 ? e = Math.min(e, 0) : t >= 8 ? e = Math.min(e, 1) : t >= 3 ? e = Math.min(e, 2) : t >= 1 && (e = Math.min(e, 3)), i >= 1200 ? e = Math.min(e, 0) : i >= 800 ? e = Math.min(e, 1) : i >= 400 ? e = Math.min(e, 2) : i >= 250 && (e = Math.min(e, 3)), n >= 150 ? e = Math.min(e, 1) : n >= 80 ? e = Math.min(e, 2) : n >= 40 && (e = Math.min(e, 3));
1956
- const a = Number(ee(4.5 - t * 0.09 - i * 11e-4 - n * 6e-3, 1, 4.5).toFixed(2));
1957
- return { networkScore: e, estimatedMos: a };
2138
+ const o = Number(se(4.5 - t * 0.09 - i * 11e-4 - n * 6e-3, 1, 4.5).toFixed(2));
2139
+ return { networkScore: e, estimatedMos: o };
1958
2140
  }
1959
- function Z() {
2141
+ function ae() {
1960
2142
  return navigator.connection ?? null;
1961
2143
  }
1962
- function we() {
1963
- const r = Number(navigator.deviceMemory);
1964
- return Number.isFinite(r) ? r : null;
2144
+ function Le() {
2145
+ const a = Number(navigator.deviceMemory);
2146
+ return Number.isFinite(a) ? a : null;
1965
2147
  }
1966
- function F(r) {
1967
- const e = Number(r);
2148
+ function H(a) {
2149
+ const e = Number(a);
1968
2150
  return Number.isFinite(e) ? e : null;
1969
2151
  }
1970
- function ee(r, e, t) {
1971
- return Math.min(t, Math.max(e, r));
2152
+ function se(a, e, t) {
2153
+ return Math.min(t, Math.max(e, a));
1972
2154
  }
1973
- function x(r) {
1974
- return r instanceof Error ? { name: r.name, message: r.message } : { name: "ServiceError", message: String(r ?? "Service request failed") };
2155
+ function V(a) {
2156
+ return a instanceof Error ? { name: a.name, message: a.message } : { name: "ServiceError", message: String(a ?? "Service request failed") };
1975
2157
  }
1976
- function te(r, e) {
1977
- return r instanceof Error ? r : new Error(e);
2158
+ function re(a, e) {
2159
+ return a instanceof Error ? a : new Error(e);
1978
2160
  }
1979
- const be = "1.3.15";
2161
+ const De = "1.3.17";
1980
2162
  export {
1981
- me as BACKGROUND_PRESETS,
1982
- pe as OVERLAY_EFFECTS,
1983
- fe as RtcClient,
1984
- ye as RtcIncomingClient,
1985
- Ce as SPEAKER_NOISE_FLOOR,
1986
- Ie as SPEAKER_TAKEOVER_RATIO,
1987
- Re as createCallRecorder,
1988
- Ee as createPictureInPictureController,
1989
- Me as createRtcClient,
1990
- Ae as createRtcIncomingClient,
1991
- Pe as createVideoEffectsPipeline,
1992
- ie as pickActiveSpeaker,
1993
- G as scenePainters,
1994
- ge as unlockRtcCallAudio,
1995
- be as version
2163
+ Z as AVATAR_TUNING_DEFAULTS,
2164
+ Se as BACKGROUND_PRESETS,
2165
+ ke as OVERLAY_EFFECTS,
2166
+ Ce as RtcClient,
2167
+ Me as RtcIncomingClient,
2168
+ qe as SPEAKER_NOISE_FLOOR,
2169
+ Oe as SPEAKER_TAKEOVER_RATIO,
2170
+ xe as createAvatarPipeline,
2171
+ je as createCallRecorder,
2172
+ Ne as createPictureInPictureController,
2173
+ Ue as createRtcClient,
2174
+ Qe as createRtcIncomingClient,
2175
+ Ve as createVideoEffectsPipeline,
2176
+ be as drawAvatar,
2177
+ we as headPoseFromMatrix,
2178
+ le as neutralRig,
2179
+ oe as pickActiveSpeaker,
2180
+ Te as rigFromBlendshapes,
2181
+ K as scenePainters,
2182
+ X as smoothRig,
2183
+ ee as stylizedAvatarRenderer,
2184
+ Re as unlockRtcCallAudio,
2185
+ De as version
1996
2186
  };