@streaming-cdn/rtc-web 1.3.22 → 1.3.24
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.
- package/README.md +8 -1
- package/dist/index.d.ts +17 -1
- package/dist/rtc-web.es.js +108 -75
- package/dist/rtc-web.umd.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# RTC Web SDK 1.3.
|
|
1
|
+
# RTC Web SDK 1.3.24
|
|
2
2
|
|
|
3
3
|
The customer package contains compiled ESM/UMD JavaScript and TypeScript declarations. It intentionally excludes implementation source and source maps. Example source remains available under `examples/`.
|
|
4
4
|
|
|
@@ -357,6 +357,13 @@ operational diagnostics. Tokens, SDP, media, and chat content are never part of
|
|
|
357
357
|
that report. Set `reportSignalingDiagnostics: false` only when replacing this
|
|
358
358
|
with an application-owned diagnostics pipeline.
|
|
359
359
|
|
|
360
|
+
## 1.3.24 notes
|
|
361
|
+
|
|
362
|
+
- `callupdated` now carries `actorExternalId`, matching the React Native SDK.
|
|
363
|
+
For `recipient_rejected` and `recipient_busy` this names the recipient the
|
|
364
|
+
update is about, so a caller ringing several people can tell who declined.
|
|
365
|
+
It is `null` for updates that are about the call as a whole.
|
|
366
|
+
|
|
360
367
|
## 1.3.21 notes
|
|
361
368
|
|
|
362
369
|
- Per-peer quality: a `bandwidth` limitation reason counts against a link only
|
package/dist/index.d.ts
CHANGED
|
@@ -320,6 +320,7 @@ export declare class RtcIncomingClient extends EventTarget {
|
|
|
320
320
|
private readonly mediaClients;
|
|
321
321
|
private readonly activeCallIds;
|
|
322
322
|
private readonly seenInvitationIds;
|
|
323
|
+
private readonly invitationExpiryTimers;
|
|
323
324
|
private startCallRequest;
|
|
324
325
|
private lastDisconnect;
|
|
325
326
|
constructor(options: RtcIncomingClientOptions);
|
|
@@ -352,10 +353,25 @@ export declare class RtcIncomingClient extends EventTarget {
|
|
|
352
353
|
cancel(callId: string): Promise<RtcCallActionResult>;
|
|
353
354
|
/** Injects an incoming call received through the application's own push or signaling channel. */
|
|
354
355
|
handleIncomingCall(call: RtcIncomingCall): void;
|
|
356
|
+
/**
|
|
357
|
+
* B20 (PingSafe): a recipient who correctly does NOTHING with an invitation
|
|
358
|
+
* must not lose the ability to place calls for the life of the process. The
|
|
359
|
+
* invitation self-releases shortly after its expiry unless it was accepted.
|
|
360
|
+
*/
|
|
361
|
+
private scheduleInvitationExpiry;
|
|
362
|
+
private clearInvitationExpiry;
|
|
363
|
+
/**
|
|
364
|
+
* Locally forgets a call WITHOUT any server action (B20): clears it from the
|
|
365
|
+
* active set, stops ringing UI if it is the current call, and tears down any
|
|
366
|
+
* attached media client. For declining, use reject()/busy() — this is the
|
|
367
|
+
* escape hatch for calls that ended in ways this device never heard about.
|
|
368
|
+
*/
|
|
369
|
+
release(callId: string): void;
|
|
355
370
|
/** Applies a call state received through the application's own push or signaling channel. */
|
|
356
371
|
handleCallUpdate(call: {
|
|
357
372
|
id: string;
|
|
358
373
|
status: string;
|
|
374
|
+
actorExternalId?: string | null;
|
|
359
375
|
}): void;
|
|
360
376
|
disconnect(): void;
|
|
361
377
|
destroy(): void;
|
|
@@ -377,4 +393,4 @@ export declare class RtcIncomingClient extends EventTarget {
|
|
|
377
393
|
private emit;
|
|
378
394
|
}
|
|
379
395
|
export declare function createRtcIncomingClient(options: RtcIncomingClientOptions): RtcIncomingClient;
|
|
380
|
-
export declare const version = "1.3.
|
|
396
|
+
export declare const version = "1.3.24";
|
package/dist/rtc-web.es.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const Ne = 0.02,
|
|
1
|
+
const Ne = 0.02, Oe = 1.4;
|
|
2
2
|
function le(a, e, t = 0.02, i = 1.4) {
|
|
3
3
|
const n = a.filter((r) => r.level >= t);
|
|
4
4
|
if (n.length === 0) return null;
|
|
@@ -14,7 +14,7 @@ function de(a) {
|
|
|
14
14
|
}
|
|
15
15
|
return e;
|
|
16
16
|
}
|
|
17
|
-
const
|
|
17
|
+
const O = {
|
|
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 },
|
|
@@ -37,8 +37,8 @@ function pe(a) {
|
|
|
37
37
|
return !e || e === "none" ? "" : e === "cpu" || e === "bandwidth" ? e : "other";
|
|
38
38
|
}
|
|
39
39
|
function fe(a, e, t, i, n = {}) {
|
|
40
|
-
const s = { ...me, ...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, k = pe(e.qualityLimitationReason), f = e.packetLossPct ?? 0, v = e.rttMs ?? 0, C = e.availableOutgoingBitrateBps, y =
|
|
41
|
-
return l > c ? { quality: o[c], poorSamples: 0, goodSamples: 0, samplesSeen: d, changed: !0 } : u ? S : w && l > 0 ? { quality: o[l - 1], poorSamples: 0, goodSamples: 0, samplesSeen: d, changed: !0 } :
|
|
40
|
+
const s = { ...me, ...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, k = pe(e.qualityLimitationReason), f = e.packetLossPct ?? 0, v = e.rttMs ?? 0, C = e.availableOutgoingBitrateBps, y = O[a].maxBitrate, w = f >= s.criticalLossPct || v >= s.criticalRttMs || k === "cpu", p = k === "bandwidth" && (C == null || C < y * s.bandwidthHeadroomRatio), R = w || f >= s.poorLossPct || v >= s.poorRttMs || p, P = C == null || C >= y * s.sustainRatio, L = f < s.goodLossPct && v < s.goodRttMs && !k && P, E = R ? t + 1 : 0, A = L ? i + 1 : 0, S = { quality: a, poorSamples: E, goodSamples: A, samplesSeen: d, changed: !1 };
|
|
41
|
+
return l > c ? { quality: o[c], poorSamples: 0, goodSamples: 0, samplesSeen: d, changed: !0 } : u ? S : w && l > 0 ? { quality: o[l - 1], poorSamples: 0, goodSamples: 0, samplesSeen: d, changed: !0 } : E >= s.poorSamplesToStepDown && l > 0 ? { quality: o[l - 1], poorSamples: 0, goodSamples: 0, samplesSeen: d, changed: !0 } : A >= s.goodSamplesToStepUp && l < c ? { quality: o[l + 1], poorSamples: 0, goodSamples: 0, samplesSeen: d, changed: !0 } : S;
|
|
42
42
|
}
|
|
43
43
|
function ge(a, e) {
|
|
44
44
|
const t = a instanceof Map ? a : new Map(a);
|
|
@@ -398,7 +398,7 @@ class Q {
|
|
|
398
398
|
async applyPeerVideoQuality(e) {
|
|
399
399
|
const t = this.screenStream?.getVideoTracks()[0]?.id;
|
|
400
400
|
for (const i of e.connection.getSenders().filter((n) => n.track?.kind === "video")) {
|
|
401
|
-
const n = !!(t && i.track?.id === t), s = n ? ve : this.quality === "audio" ? ye.audio :
|
|
401
|
+
const n = !!(t && i.track?.id === t), s = n ? ve : this.quality === "audio" ? ye.audio : O[e.quality], o = i.getParameters(), r = o.encodings?.length ? o.encodings : [{}];
|
|
402
402
|
o.encodings = r.map((c) => ({ ...c, ...s })), o.degradationPreference = n ? "maintain-resolution" : "maintain-framerate", await i.setParameters(o);
|
|
403
403
|
}
|
|
404
404
|
}
|
|
@@ -506,9 +506,9 @@ function G() {
|
|
|
506
506
|
return globalThis.crypto?.randomUUID?.() ?? `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
507
507
|
}
|
|
508
508
|
const H = { active: !0, maxBitrate: 4e6, maxFramerate: 30, scaleResolutionDownBy: 1 }, K = [
|
|
509
|
-
{ rid: "a", maxBitrate:
|
|
510
|
-
{ rid: "b", maxBitrate:
|
|
511
|
-
{ rid: "c", maxBitrate:
|
|
509
|
+
{ rid: "a", maxBitrate: O.high.maxBitrate, maxFramerate: O.high.maxFramerate },
|
|
510
|
+
{ rid: "b", maxBitrate: O.medium.maxBitrate, maxFramerate: O.medium.maxFramerate, scaleResolutionDownBy: 2 },
|
|
511
|
+
{ rid: "c", maxBitrate: O.low.maxBitrate, maxFramerate: O.low.maxFramerate, scaleResolutionDownBy: 4 }
|
|
512
512
|
], X = { high: "a", medium: "b", low: "c" };
|
|
513
513
|
class Se {
|
|
514
514
|
options;
|
|
@@ -849,7 +849,7 @@ class Se {
|
|
|
849
849
|
return { ...r, ...c ?? {}, active: l };
|
|
850
850
|
});
|
|
851
851
|
} else {
|
|
852
|
-
const o = this.quality === "audio" ?
|
|
852
|
+
const o = this.quality === "audio" ? O.audio : O[this.quality], r = i ? H : o;
|
|
853
853
|
n.encodings = s.map((c) => ({ ...c, ...r }));
|
|
854
854
|
}
|
|
855
855
|
n.degradationPreference = i ? "maintain-resolution" : "maintain-framerate", await e.setParameters(n);
|
|
@@ -948,7 +948,7 @@ class Se {
|
|
|
948
948
|
function ke() {
|
|
949
949
|
return globalThis.crypto?.randomUUID?.() ?? `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
950
950
|
}
|
|
951
|
-
function
|
|
951
|
+
function je(a, e = {}) {
|
|
952
952
|
const t = e.document ?? globalThis.document, i = !!(t && t.pictureInPictureEnabled);
|
|
953
953
|
let n = null;
|
|
954
954
|
function s() {
|
|
@@ -1011,26 +1011,26 @@ function Ve(a, e) {
|
|
|
1011
1011
|
...M ? [{ key: "local", stream: M, label: "me" }] : []
|
|
1012
1012
|
];
|
|
1013
1013
|
p.fillStyle = "#101418", p.fillRect(0, 0, r, o);
|
|
1014
|
-
const
|
|
1014
|
+
const x = Math.max(1, Math.ceil(Math.sqrt(b.length))), V = Math.max(1, Math.ceil(b.length / x)), N = r / x, g = o / V;
|
|
1015
1015
|
b.forEach((q, _) => {
|
|
1016
|
-
const Y = P(q.key, q.stream), B = _ %
|
|
1016
|
+
const Y = P(q.key, q.stream), B = _ % x * N, F = Math.floor(_ / x) * g;
|
|
1017
1017
|
Y.videoWidth > 0 && p.drawImage(Y, B + 2, F + 2, N - 4, g - 4), p.fillStyle = "rgba(0,0,0,.6)", p.fillRect(B + 8, F + g - 30, Math.min(N - 16, 12 + q.label.length * 9), 22), p.fillStyle = "#fff", p.font = "14px system-ui", p.fillText(q.label, B + 14, F + g - 14);
|
|
1018
1018
|
});
|
|
1019
1019
|
}, 66);
|
|
1020
1020
|
const L = w.captureStream(15);
|
|
1021
1021
|
d = new AudioContext();
|
|
1022
|
-
const
|
|
1022
|
+
const E = d.createMediaStreamDestination(), A = /* @__PURE__ */ new Set(), S = () => {
|
|
1023
1023
|
const T = [
|
|
1024
1024
|
...a.getRemoteStreams().map((M) => M.stream),
|
|
1025
1025
|
...a.getLocalStream() ? [a.getLocalStream()] : []
|
|
1026
1026
|
];
|
|
1027
1027
|
for (const M of T)
|
|
1028
1028
|
for (const b of M.getAudioTracks())
|
|
1029
|
-
A.has(b.id) || (A.add(b.id), d.createMediaStreamSource(new MediaStream([b])).connect(
|
|
1029
|
+
A.has(b.id) || (A.add(b.id), d.createMediaStreamSource(new MediaStream([b])).connect(E));
|
|
1030
1030
|
};
|
|
1031
1031
|
S();
|
|
1032
|
-
const
|
|
1033
|
-
for (const T of
|
|
1032
|
+
const I = setInterval(S, 1e3), m = () => clearInterval(I);
|
|
1033
|
+
for (const T of E.stream.getAudioTracks()) L.addTrack(T);
|
|
1034
1034
|
c = (e.mediaRecorderFactory ?? ((T, M) => new MediaRecorder(T, M)))(L, { mimeType: s }), f = [], c.addEventListener("dataavailable", (T) => {
|
|
1035
1035
|
T.data && T.data.size > 0 && f.push(T.data);
|
|
1036
1036
|
}), c.addEventListener("stop", m), c.start(1e3), k = Date.now(), v = !0;
|
|
@@ -1038,7 +1038,7 @@ function Ve(a, e) {
|
|
|
1038
1038
|
async stop() {
|
|
1039
1039
|
if (!v || !c || !u) throw new Error("The recorder is not running");
|
|
1040
1040
|
v = !1;
|
|
1041
|
-
const y = c, w = new Promise((
|
|
1041
|
+
const y = c, w = new Promise((E) => y.addEventListener("stop", () => E(), { once: !0 }));
|
|
1042
1042
|
y.stop(), await w, l && clearInterval(l), l = null, await d?.close().catch(() => {
|
|
1043
1043
|
}), d = null;
|
|
1044
1044
|
const p = new Blob(f, { type: s });
|
|
@@ -1131,7 +1131,7 @@ function Ue(a, e = {}) {
|
|
|
1131
1131
|
M === i && b === n || (i = M, n = b, C.width = i, C.height = n, w.width = i, w.height = n, P = R());
|
|
1132
1132
|
};
|
|
1133
1133
|
v.addEventListener?.("loadedmetadata", L), v.addEventListener?.("resize", L);
|
|
1134
|
-
function
|
|
1134
|
+
function E() {
|
|
1135
1135
|
if (k) {
|
|
1136
1136
|
p.clearRect(0, 0, i, n), p.drawImage(k, 0, 0, i, n), p.globalCompositeOperation = "source-in", p.drawImage(v, 0, 0, i, n), p.globalCompositeOperation = "source-over", y.drawImage(w, 0, 0);
|
|
1137
1137
|
return;
|
|
@@ -1140,7 +1140,7 @@ function Ue(a, e = {}) {
|
|
|
1140
1140
|
y.save(), y.shadowColor = "rgba(0,0,0,.5)", y.shadowBlur = 24, y.beginPath(), y.roundRect(T, M, m, h, 14), y.clip(), y.drawImage(v, T, M, m, h), y.restore();
|
|
1141
1141
|
}
|
|
1142
1142
|
function A(m) {
|
|
1143
|
-
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", k &&
|
|
1143
|
+
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", k && E()) : c === "image" && d ? (y.drawImage(d, 0, 0, i, n), E()) : ((Z[c] ?? Z.aurora)(y, i, n, m), E()), l === "snow" || l === "confetti")
|
|
1144
1144
|
for (const h of P)
|
|
1145
1145
|
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());
|
|
1146
1146
|
else if (l === "vignette") {
|
|
@@ -1156,10 +1156,10 @@ function Ue(a, e = {}) {
|
|
|
1156
1156
|
}).finally(() => {
|
|
1157
1157
|
f = !1;
|
|
1158
1158
|
}));
|
|
1159
|
-
}, Math.round(1e3 / o)),
|
|
1159
|
+
}, Math.round(1e3 / o)), I = C.captureStream(o);
|
|
1160
1160
|
return {
|
|
1161
|
-
track:
|
|
1162
|
-
stream:
|
|
1161
|
+
track: I.getVideoTracks()[0],
|
|
1162
|
+
stream: I,
|
|
1163
1163
|
setBackground(m, h) {
|
|
1164
1164
|
c = we.includes(m) ? m : "none", h !== void 0 && (d = h), c === "none" && (k = null);
|
|
1165
1165
|
},
|
|
@@ -1171,7 +1171,7 @@ function Ue(a, e = {}) {
|
|
|
1171
1171
|
},
|
|
1172
1172
|
dispose() {
|
|
1173
1173
|
clearInterval(S), u?.close?.();
|
|
1174
|
-
for (const m of
|
|
1174
|
+
for (const m of I.getTracks()) m.stop();
|
|
1175
1175
|
v.srcObject = null;
|
|
1176
1176
|
}
|
|
1177
1177
|
};
|
|
@@ -1245,14 +1245,14 @@ function Re(a, e, t, i, n) {
|
|
|
1245
1245
|
const C = u(v) * 0.9, y = r * 0.12 + f;
|
|
1246
1246
|
a.ellipse(C + d * r * 0.14, y, r * 0.11, r * 0.14, 0, 0, Math.PI * 2), a.fill();
|
|
1247
1247
|
for (const A of [-1, 1]) {
|
|
1248
|
-
const S = A * 0.42,
|
|
1249
|
-
if (
|
|
1250
|
-
const m = u(S) * 0.82, h = Math.max(0.2,
|
|
1248
|
+
const S = A * 0.42, I = k(S);
|
|
1249
|
+
if (I <= 0.12) continue;
|
|
1250
|
+
const m = u(S) * 0.82, h = Math.max(0.2, I), T = A < 0 ? e.blinkLeft : e.blinkRight, M = Math.max(0.06, 1 - T), b = e.browUp * r * 0.14;
|
|
1251
1251
|
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();
|
|
1252
|
-
const
|
|
1253
|
-
a.fillStyle = "#ffffff", a.beginPath(), a.ellipse(m,
|
|
1252
|
+
const x = -r * 0.18 + f;
|
|
1253
|
+
a.fillStyle = "#ffffff", a.beginPath(), a.ellipse(m, x, r * 0.16 * h, r * 0.12 * M, 0, 0, Math.PI * 2), a.fill(), a.fillStyle = "#22303c", a.beginPath(), a.ellipse(
|
|
1254
1254
|
m + e.pupilX * r * 0.07 * h,
|
|
1255
|
-
|
|
1255
|
+
x + e.pupilY * r * 0.05,
|
|
1256
1256
|
r * 0.06 * h,
|
|
1257
1257
|
r * 0.06 * M,
|
|
1258
1258
|
0,
|
|
@@ -1260,8 +1260,8 @@ function Re(a, e, t, i, n) {
|
|
|
1260
1260
|
Math.PI * 2
|
|
1261
1261
|
), a.fill();
|
|
1262
1262
|
}
|
|
1263
|
-
const w = Math.max(0.25, k(0)), p = u(0) * 0.7, R = r * 0.55 * w, P = r * (0.04 + e.jawOpen * 0.34), L = e.smile * r * 0.12,
|
|
1264
|
-
a.fillStyle = "#8c2f2f", a.beginPath(), a.moveTo(p - R / 2,
|
|
1263
|
+
const w = Math.max(0.25, k(0)), p = u(0) * 0.7, R = r * 0.55 * w, P = r * (0.04 + e.jawOpen * 0.34), L = e.smile * r * 0.12, E = r * 0.42 + f;
|
|
1264
|
+
a.fillStyle = "#8c2f2f", a.beginPath(), a.moveTo(p - R / 2, E - L), a.quadraticCurveTo(p, E + P, p + R / 2, E - L), a.quadraticCurveTo(p, E - P * 0.25, p - R / 2, E - 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");
|
|
1265
1265
|
}
|
|
1266
1266
|
const te = {
|
|
1267
1267
|
mouthGain: 1.7,
|
|
@@ -1293,16 +1293,16 @@ function Qe(a, e = {}) {
|
|
|
1293
1293
|
v.width = i, v.height = n;
|
|
1294
1294
|
const C = v.getContext("2d"), y = () => {
|
|
1295
1295
|
if (s) return;
|
|
1296
|
-
const S = f.videoWidth,
|
|
1297
|
-
if (!S || !
|
|
1298
|
-
const m = Math.min(1, 1280 / Math.max(S,
|
|
1296
|
+
const S = f.videoWidth, I = f.videoHeight;
|
|
1297
|
+
if (!S || !I) return;
|
|
1298
|
+
const m = Math.min(1, 1280 / Math.max(S, I)), h = Math.round(S * m), T = Math.round(I * m);
|
|
1299
1299
|
(!(h === i || T === n) || h !== i || T !== n) && (i = h, n = T, v.width = i, v.height = n);
|
|
1300
1300
|
};
|
|
1301
1301
|
f.addEventListener?.("loadedmetadata", y), f.addEventListener?.("resize", y);
|
|
1302
1302
|
let w = he(), p = null, R = 0;
|
|
1303
1303
|
function P(S) {
|
|
1304
1304
|
if (S.length <= 454) return;
|
|
1305
|
-
const
|
|
1305
|
+
const I = S[10], m = S[152], h = S[234], T = S[454], M = (h.x + T.x) / 2 * i, b = (h.y + T.y) / 2 * n, x = Math.hypot((m.x - I.x) * i, (m.y - I.y) * n), V = Math.max(0.6, Math.cos(w.headPitch)), N = { x: M, y: b, radius: Math.max(24, x / V * 0.8) };
|
|
1306
1306
|
p = p ? { x: p.x + (N.x - p.x) * 0.4, y: p.y + (N.y - p.y) * 0.4, radius: p.radius + (N.radius - p.radius) * 0.15 } : N;
|
|
1307
1307
|
}
|
|
1308
1308
|
function L(S) {
|
|
@@ -1316,10 +1316,10 @@ function Qe(a, e = {}) {
|
|
|
1316
1316
|
h.jawOpen = z(h.jawOpen * u.mouthGain), h.blinkLeft = z(h.blinkLeft * u.blinkGain), h.blinkRight = z(h.blinkRight * u.blinkGain), h.tracked ? (R = S, w = ee(w, h, u.smoothing), m?.landmarks && P(m.landmarks)) : w.tracked && S - R > u.lostHoldMs && (w = ee(w, { ...w, jawOpen: 0, browUp: 0, smile: w.smile * 0.5, blinkLeft: 0.1, blinkRight: 0.1 }, 0.05));
|
|
1317
1317
|
}
|
|
1318
1318
|
d === "overlay" && f.readyState >= 2 ? C.drawImage(f, 0, 0, i, n) : (C.fillStyle = k, C.fillRect(0, 0, i, n));
|
|
1319
|
-
const
|
|
1320
|
-
l?.render(C, w, i, n,
|
|
1319
|
+
const I = p ? { x: p.x, y: p.y + u.avatarLift * p.radius, radius: p.radius * u.avatarScale } : null;
|
|
1320
|
+
l?.render(C, w, i, n, I);
|
|
1321
1321
|
}
|
|
1322
|
-
const
|
|
1322
|
+
const E = setInterval(() => L(Date.now()), Math.round(1e3 / o)), A = v.captureStream(o);
|
|
1323
1323
|
return {
|
|
1324
1324
|
track: A.getVideoTracks()[0],
|
|
1325
1325
|
stream: A,
|
|
@@ -1334,20 +1334,20 @@ function Qe(a, e = {}) {
|
|
|
1334
1334
|
l?.dispose?.(), l = S ?? ie();
|
|
1335
1335
|
},
|
|
1336
1336
|
setTuning(S) {
|
|
1337
|
-
for (const
|
|
1338
|
-
const m = S[
|
|
1339
|
-
typeof m == "number" && Number.isFinite(m) && (u[
|
|
1337
|
+
for (const I of Object.keys(te)) {
|
|
1338
|
+
const m = S[I];
|
|
1339
|
+
typeof m == "number" && Number.isFinite(m) && (u[I] = m);
|
|
1340
1340
|
}
|
|
1341
1341
|
},
|
|
1342
1342
|
getTuning: () => ({ ...u }),
|
|
1343
1343
|
dispose() {
|
|
1344
|
-
clearInterval(
|
|
1344
|
+
clearInterval(E), c?.close?.(), l?.dispose?.();
|
|
1345
1345
|
for (const S of A.getTracks()) S.stop();
|
|
1346
1346
|
f.srcObject = null;
|
|
1347
1347
|
}
|
|
1348
1348
|
};
|
|
1349
1349
|
}
|
|
1350
|
-
class
|
|
1350
|
+
class Ee extends EventTarget {
|
|
1351
1351
|
mode;
|
|
1352
1352
|
options;
|
|
1353
1353
|
client = null;
|
|
@@ -1401,11 +1401,11 @@ class Ie extends EventTarget {
|
|
|
1401
1401
|
localStream: this.options.localStream,
|
|
1402
1402
|
simulcast: this.options.simulcast ?? !0,
|
|
1403
1403
|
onEvent: (i, n) => this.handleNativeEvent(i, n)
|
|
1404
|
-
}), await this.client.initialize(), this.startStats(), this.emit("initialized", { mode: this.mode, sdkVersion:
|
|
1404
|
+
}), await this.client.initialize(), this.startStats(), this.emit("initialized", { mode: this.mode, sdkVersion: xe }), this.options.autoJoin && await this.join(), this;
|
|
1405
1405
|
} catch (e) {
|
|
1406
1406
|
this.state = "failed";
|
|
1407
1407
|
const t = ce(e, "RTC initialization failed");
|
|
1408
|
-
throw this.emit("error",
|
|
1408
|
+
throw this.emit("error", j(t)), t;
|
|
1409
1409
|
}
|
|
1410
1410
|
}
|
|
1411
1411
|
async join() {
|
|
@@ -1413,7 +1413,7 @@ class Ie extends EventTarget {
|
|
|
1413
1413
|
return this.joinPromise ? this.joinPromise : (this.state = "joining", this.emit("joining", {}), this.joinPromise = e.join().catch((t) => {
|
|
1414
1414
|
this.state = "failed";
|
|
1415
1415
|
const i = ce(t, "RTC connection failed");
|
|
1416
|
-
throw this.emit("error",
|
|
1416
|
+
throw this.emit("error", j(i)), i;
|
|
1417
1417
|
}).finally(() => {
|
|
1418
1418
|
this.joinPromise = null;
|
|
1419
1419
|
}), this.joinPromise);
|
|
@@ -1651,7 +1651,7 @@ class Ie extends EventTarget {
|
|
|
1651
1651
|
const i = Date.now(), n = e !== "runtime" || i - this.lastTelemetryAt >= this.options.telemetryInterval;
|
|
1652
1652
|
this.options.autoReportStats && n && (await this.reportQuality(t), this.lastTelemetryAt = i);
|
|
1653
1653
|
} catch (t) {
|
|
1654
|
-
this.emit("telemetryerror",
|
|
1654
|
+
this.emit("telemetryerror", j(t));
|
|
1655
1655
|
} finally {
|
|
1656
1656
|
this.statsRunning = !1;
|
|
1657
1657
|
}
|
|
@@ -1686,7 +1686,7 @@ class Ie extends EventTarget {
|
|
|
1686
1686
|
}
|
|
1687
1687
|
}
|
|
1688
1688
|
async function Be(a) {
|
|
1689
|
-
return new
|
|
1689
|
+
return new Ee(a).initialize();
|
|
1690
1690
|
}
|
|
1691
1691
|
let U = null;
|
|
1692
1692
|
function ue() {
|
|
@@ -1694,7 +1694,7 @@ function ue() {
|
|
|
1694
1694
|
const a = globalThis, e = a.AudioContext ?? a.webkitAudioContext;
|
|
1695
1695
|
return e ? (U = new e(), U) : null;
|
|
1696
1696
|
}
|
|
1697
|
-
async function
|
|
1697
|
+
async function Ie() {
|
|
1698
1698
|
const a = ue();
|
|
1699
1699
|
return a ? (a.state === "suspended" && await a.resume().catch(() => {
|
|
1700
1700
|
}), a.state === "running") : !1;
|
|
@@ -1708,7 +1708,7 @@ class Me {
|
|
|
1708
1708
|
this.options = e;
|
|
1709
1709
|
}
|
|
1710
1710
|
unlock() {
|
|
1711
|
-
return
|
|
1711
|
+
return Ie();
|
|
1712
1712
|
}
|
|
1713
1713
|
startIncoming() {
|
|
1714
1714
|
this.start("incoming", this.options.ringtoneUrl);
|
|
@@ -1766,6 +1766,7 @@ class Pe extends EventTarget {
|
|
|
1766
1766
|
mediaClients = /* @__PURE__ */ new Map();
|
|
1767
1767
|
activeCallIds = /* @__PURE__ */ new Set();
|
|
1768
1768
|
seenInvitationIds = /* @__PURE__ */ new Set();
|
|
1769
|
+
invitationExpiryTimers = /* @__PURE__ */ new Map();
|
|
1769
1770
|
startCallRequest = null;
|
|
1770
1771
|
lastDisconnect = null;
|
|
1771
1772
|
constructor(e) {
|
|
@@ -1823,7 +1824,7 @@ class Pe extends EventTarget {
|
|
|
1823
1824
|
async accept(e = this.currentCall?.id) {
|
|
1824
1825
|
if (!e) throw new Error("No incoming call is available");
|
|
1825
1826
|
const t = await this.callAction(e, "accept");
|
|
1826
|
-
return this.activeCallIds.add(e), this.callAudio.stop(), this.dismiss(e), this.emit("callaccepted", t), t;
|
|
1827
|
+
return this.activeCallIds.add(e), this.clearInvitationExpiry(e), this.callAudio.stop(), this.dismiss(e), this.emit("callaccepted", t), t;
|
|
1827
1828
|
}
|
|
1828
1829
|
async reject(e = this.currentCall?.id) {
|
|
1829
1830
|
if (!e) throw new Error("No incoming call is available");
|
|
@@ -1861,15 +1862,42 @@ class Pe extends EventTarget {
|
|
|
1861
1862
|
this.emit("callrestored", e);
|
|
1862
1863
|
return;
|
|
1863
1864
|
}
|
|
1864
|
-
this.seenInvitationIds.add(e.id), this.activeCallIds.add(e.id), this.currentCall = e, this.callAudio.startIncoming(), this.renderIncoming(e), this.emit("incomingcall", e);
|
|
1865
|
+
this.seenInvitationIds.add(e.id), this.activeCallIds.add(e.id), this.scheduleInvitationExpiry(e.id, e.expiresAt), this.currentCall = e, this.callAudio.startIncoming(), this.renderIncoming(e), this.emit("incomingcall", e);
|
|
1866
|
+
}
|
|
1867
|
+
/**
|
|
1868
|
+
* B20 (PingSafe): a recipient who correctly does NOTHING with an invitation
|
|
1869
|
+
* must not lose the ability to place calls for the life of the process. The
|
|
1870
|
+
* invitation self-releases shortly after its expiry unless it was accepted.
|
|
1871
|
+
*/
|
|
1872
|
+
scheduleInvitationExpiry(e, t) {
|
|
1873
|
+
const i = t ? Date.parse(t) : Date.now() + 45e3, n = Math.min(18e4, Math.max(5e3, (Number.isFinite(i) ? i : Date.now() + 45e3) - Date.now() + 1e4));
|
|
1874
|
+
this.clearInvitationExpiry(e), this.invitationExpiryTimers.set(e, setTimeout(() => {
|
|
1875
|
+
this.invitationExpiryTimers.delete(e), !(!this.activeCallIds.has(e) || this.mediaClients.has(e)) && (this.emit("callexpired", { id: e, expiresAt: t ?? null }), this.release(e));
|
|
1876
|
+
}, n));
|
|
1877
|
+
}
|
|
1878
|
+
clearInvitationExpiry(e) {
|
|
1879
|
+
const t = this.invitationExpiryTimers.get(e);
|
|
1880
|
+
t && clearTimeout(t), this.invitationExpiryTimers.delete(e);
|
|
1881
|
+
}
|
|
1882
|
+
/**
|
|
1883
|
+
* Locally forgets a call WITHOUT any server action (B20): clears it from the
|
|
1884
|
+
* active set, stops ringing UI if it is the current call, and tears down any
|
|
1885
|
+
* attached media client. For declining, use reject()/busy() — this is the
|
|
1886
|
+
* escape hatch for calls that ended in ways this device never heard about.
|
|
1887
|
+
*/
|
|
1888
|
+
release(e) {
|
|
1889
|
+
if (!e.trim()) throw new Error("callId is required");
|
|
1890
|
+
this.clearInvitationExpiry(e), this.currentCall?.id === e && (this.callAudio.stop(), this.dismiss(e)), this.outgoingCallId === e && (this.outgoingCallId = null), this.releaseMedia(e), this.emit("callreleased", { id: e });
|
|
1865
1891
|
}
|
|
1866
1892
|
/** Applies a call state received through the application's own push or signaling channel. */
|
|
1867
1893
|
handleCallUpdate(e) {
|
|
1868
|
-
const t = e.id === this.currentCall?.id || e.id === this.outgoingCallId;
|
|
1869
|
-
t && e.status === "accepted" ? (this.activeCallIds.add(e.id), this.callAudio.stop(), this.dismiss(e.id)) : t &&
|
|
1894
|
+
const t = e.id === this.currentCall?.id || e.id === this.outgoingCallId, i = ["answered_elsewhere", "rejected", "busy", "cancelled", "ended", "failed", "missed"];
|
|
1895
|
+
(e.status === "accepted" || i.includes(e.status)) && this.clearInvitationExpiry(e.id), t && e.status === "accepted" ? (this.activeCallIds.add(e.id), this.callAudio.stop(), this.dismiss(e.id)) : t && i.includes(e.status) && this.showTerminal(e.id, Ae(e.status)), i.includes(e.status) && this.releaseMedia(e.id), this.emit("callupdated", e);
|
|
1870
1896
|
}
|
|
1871
1897
|
disconnect() {
|
|
1872
1898
|
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();
|
|
1899
|
+
for (const e of this.invitationExpiryTimers.values()) clearTimeout(e);
|
|
1900
|
+
this.invitationExpiryTimers.clear();
|
|
1873
1901
|
for (const e of this.mediaClients.keys()) this.releaseMedia(e);
|
|
1874
1902
|
this.activeCallIds.clear(), this.seenInvitationIds.clear();
|
|
1875
1903
|
}
|
|
@@ -1882,7 +1910,7 @@ class Pe extends EventTarget {
|
|
|
1882
1910
|
try {
|
|
1883
1911
|
t = await this.resolveToken(e, e === "reconnect");
|
|
1884
1912
|
} catch (s) {
|
|
1885
|
-
this.emit("tokenerror",
|
|
1913
|
+
this.emit("tokenerror", j(s)), !this.stopped && this.options.reconnect !== !1 && this.scheduleReconnect();
|
|
1886
1914
|
return;
|
|
1887
1915
|
}
|
|
1888
1916
|
if (this.stopped || this.socket) return;
|
|
@@ -1930,7 +1958,11 @@ class Pe extends EventTarget {
|
|
|
1930
1958
|
}
|
|
1931
1959
|
if (i === "call.updated") {
|
|
1932
1960
|
const n = t.call;
|
|
1933
|
-
n?.id && n.status && this.handleCallUpdate({
|
|
1961
|
+
n?.id && n.status && this.handleCallUpdate({
|
|
1962
|
+
id: n.id,
|
|
1963
|
+
status: n.status,
|
|
1964
|
+
actorExternalId: typeof n.actorExternalId == "string" ? n.actorExternalId : null
|
|
1965
|
+
});
|
|
1934
1966
|
return;
|
|
1935
1967
|
}
|
|
1936
1968
|
i === "presence" ? this.emit("presence", { users: t.users ?? [] }) : i !== "pong" && this.emit(i || "signal", t);
|
|
@@ -1978,9 +2010,9 @@ class Pe extends EventTarget {
|
|
|
1978
2010
|
Object.assign(s.style, { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "10px" });
|
|
1979
2011
|
const o = ae("Decline", "#fff", "#a32920", "#d9a09b"), r = ae("Answer", "#fff", "#08745f", "#08745f");
|
|
1980
2012
|
o.addEventListener("click", () => {
|
|
1981
|
-
this.reject(e.id).catch((c) => this.emit("error",
|
|
2013
|
+
this.reject(e.id).catch((c) => this.emit("error", j(c)));
|
|
1982
2014
|
}), r.addEventListener("click", () => {
|
|
1983
|
-
this.accept(e.id).catch((c) => this.emit("error",
|
|
2015
|
+
this.accept(e.id).catch((c) => this.emit("error", j(c)));
|
|
1984
2016
|
}), s.appendChild(o), s.appendChild(r), t.appendChild(i), t.appendChild(n), t.appendChild(s), this.mount.replaceChildren(t);
|
|
1985
2017
|
}
|
|
1986
2018
|
dismiss(e) {
|
|
@@ -2048,7 +2080,7 @@ class Pe extends EventTarget {
|
|
|
2048
2080
|
const t = Math.max(0, e - this.refreshSkew(e));
|
|
2049
2081
|
this.tokenRefreshTimer = setTimeout(() => {
|
|
2050
2082
|
this.tokenRefreshTimer = null, this.resolveToken("expiring", !0).catch((i) => {
|
|
2051
|
-
this.emit("tokenerror",
|
|
2083
|
+
this.emit("tokenerror", j(i)), this.stopped || (this.tokenRefreshTimer = setTimeout(() => this.scheduleTokenRefresh(), 3e4));
|
|
2052
2084
|
});
|
|
2053
2085
|
}, Math.min(t, 2147e6));
|
|
2054
2086
|
}
|
|
@@ -2062,7 +2094,7 @@ class Pe extends EventTarget {
|
|
|
2062
2094
|
this.options.reportSignalingDiagnostics === !1 || this.stopped || this.request("/v1/rtc/client/signaling-events", {
|
|
2063
2095
|
method: "POST",
|
|
2064
2096
|
body: JSON.stringify({ ...e, occurredAt: (/* @__PURE__ */ new Date()).toISOString() })
|
|
2065
|
-
}).catch((t) => this.emit("diagnosticserror",
|
|
2097
|
+
}).catch((t) => this.emit("diagnosticserror", j(t)));
|
|
2066
2098
|
}
|
|
2067
2099
|
emit(e, t) {
|
|
2068
2100
|
const i = { type: e, detail: t, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
|
|
@@ -2096,7 +2128,8 @@ function Ae(a) {
|
|
|
2096
2128
|
cancelled: "Call cancelled",
|
|
2097
2129
|
ended: "Call ended",
|
|
2098
2130
|
failed: "Call failed",
|
|
2099
|
-
missed: "No answer"
|
|
2131
|
+
missed: "No answer",
|
|
2132
|
+
answered_elsewhere: "Answered on another device"
|
|
2100
2133
|
}[a] ?? "Call ended";
|
|
2101
2134
|
}
|
|
2102
2135
|
function se(a) {
|
|
@@ -2108,23 +2141,23 @@ function se(a) {
|
|
|
2108
2141
|
}
|
|
2109
2142
|
async function Le(a, e) {
|
|
2110
2143
|
const t = await a.getStats(), i = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Map();
|
|
2111
|
-
let s = 0, o = 0, r = 0, c = 0, l = 0, d = 0, u = 0, k = 0, f = 0, v = 0, C = 0, y = 0, w = 0, p = 0, R = 0, P = null, L = null,
|
|
2144
|
+
let s = 0, o = 0, r = 0, c = 0, l = 0, d = 0, u = 0, k = 0, f = 0, v = 0, C = 0, y = 0, w = 0, p = 0, R = 0, P = null, L = null, E = null, A = null, S = null, I = null;
|
|
2112
2145
|
t.forEach((g) => {
|
|
2113
2146
|
n.set(g.id, g), g.type === "codec" && i.set(g.id, String(g.mimeType ?? ""));
|
|
2114
2147
|
}), t.forEach((g) => {
|
|
2115
|
-
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)), k = Math.max(k, Number(g.frameWidth ?? 0)), C = Math.max(C, Number(g.frameHeight ?? 0)), y += Number(g.framesDropped ?? 0), w += Number(g.freezeCount ?? 0), p += Number(g.totalFreezesDuration ?? 0), R += Number(g.concealedSamples ?? 0), S ||= i.get(String(g.codecId ?? "")) ?? null), g.type === "outbound-rtp" && !g.isRemote && (f = Math.max(f, Number(g.frameWidth ?? 0)), v = Math.max(v, Number(g.frameHeight ?? 0)), o += Number(g.bytesSent ?? 0),
|
|
2148
|
+
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)), k = Math.max(k, Number(g.frameWidth ?? 0)), C = Math.max(C, Number(g.frameHeight ?? 0)), y += Number(g.framesDropped ?? 0), w += Number(g.freezeCount ?? 0), p += Number(g.totalFreezesDuration ?? 0), R += Number(g.concealedSamples ?? 0), S ||= i.get(String(g.codecId ?? "")) ?? null), g.type === "outbound-rtp" && !g.isRemote && (f = Math.max(f, Number(g.frameWidth ?? 0)), v = Math.max(v, Number(g.frameHeight ?? 0)), o += Number(g.bytesSent ?? 0), I ||= g.qualityLimitationReason ? String(g.qualityLimitationReason) : null, S ||= i.get(String(g.codecId ?? "")) ?? null), g.type === "candidate-pair" && (g.selected || g.nominated) && g.state === "succeeded") {
|
|
2116
2149
|
P = Number.isFinite(g.currentRoundTripTime) ? Number(g.currentRoundTripTime) : P, L = Number.isFinite(g.availableOutgoingBitrate) ? Number(g.availableOutgoingBitrate) : L;
|
|
2117
2150
|
const q = n.get(g.localCandidateId);
|
|
2118
|
-
|
|
2151
|
+
E = q?.candidateType ? String(q.candidateType) : E, A = q?.protocol ? String(q.protocol) : A;
|
|
2119
2152
|
}
|
|
2120
2153
|
});
|
|
2121
|
-
const m = Date.now(), h = e ? Math.max(1e-3, (m - e.timestamp) / 1e3) : 0, T = e ? Math.max(0, Math.round((s - e.inboundBytes) * 8 / h)) : null, M = e ? Math.max(0, Math.round((o - e.outboundBytes) * 8 / h)) : null, b = r + c,
|
|
2154
|
+
const m = Date.now(), h = e ? Math.max(1e-3, (m - e.timestamp) / 1e3) : 0, T = e ? Math.max(0, Math.round((s - e.inboundBytes) * 8 / h)) : null, M = e ? Math.max(0, Math.round((o - e.outboundBytes) * 8 / h)) : null, b = r + c, x = e ? Math.max(0, r - e.packetsLost) : r, V = e ? Math.max(0, c - e.packetsReceived) : c, N = x + V;
|
|
2122
2155
|
return {
|
|
2123
2156
|
counters: { timestamp: m, inboundBytes: s, outboundBytes: o, packetsLost: r, packetsReceived: c },
|
|
2124
2157
|
sample: {
|
|
2125
2158
|
rttMs: P === null ? null : P * 1e3,
|
|
2126
2159
|
jitterMs: d ? l / d * 1e3 : null,
|
|
2127
|
-
packetLossPct: N ?
|
|
2160
|
+
packetLossPct: N ? x / N * 100 : null,
|
|
2128
2161
|
packetLossCumulativePct: b ? r / b * 100 : null,
|
|
2129
2162
|
packetsLost: r,
|
|
2130
2163
|
packetsReceived: c,
|
|
@@ -2144,8 +2177,8 @@ async function Le(a, e) {
|
|
|
2144
2177
|
concealedSamples: R,
|
|
2145
2178
|
codec: S,
|
|
2146
2179
|
transportProtocol: A,
|
|
2147
|
-
candidateType:
|
|
2148
|
-
qualityLimitationReason:
|
|
2180
|
+
candidateType: E,
|
|
2181
|
+
qualityLimitationReason: I
|
|
2149
2182
|
}
|
|
2150
2183
|
};
|
|
2151
2184
|
}
|
|
@@ -2170,24 +2203,24 @@ function W(a) {
|
|
|
2170
2203
|
function oe(a, e, t) {
|
|
2171
2204
|
return Math.min(t, Math.max(e, a));
|
|
2172
2205
|
}
|
|
2173
|
-
function
|
|
2206
|
+
function j(a) {
|
|
2174
2207
|
return a instanceof Error ? { name: a.name, message: a.message } : { name: "ServiceError", message: String(a ?? "Service request failed") };
|
|
2175
2208
|
}
|
|
2176
2209
|
function ce(a, e) {
|
|
2177
2210
|
return a instanceof Error ? a : new Error(e);
|
|
2178
2211
|
}
|
|
2179
|
-
const
|
|
2212
|
+
const xe = "1.3.24";
|
|
2180
2213
|
export {
|
|
2181
2214
|
te as AVATAR_TUNING_DEFAULTS,
|
|
2182
2215
|
we as BACKGROUND_PRESETS,
|
|
2183
2216
|
Te as OVERLAY_EFFECTS,
|
|
2184
|
-
|
|
2217
|
+
Ee as RtcClient,
|
|
2185
2218
|
Pe as RtcIncomingClient,
|
|
2186
2219
|
Ne as SPEAKER_NOISE_FLOOR,
|
|
2187
|
-
|
|
2220
|
+
Oe as SPEAKER_TAKEOVER_RATIO,
|
|
2188
2221
|
Qe as createAvatarPipeline,
|
|
2189
2222
|
Ve as createCallRecorder,
|
|
2190
|
-
|
|
2223
|
+
je as createPictureInPictureController,
|
|
2191
2224
|
Be as createRtcClient,
|
|
2192
2225
|
Fe as createRtcIncomingClient,
|
|
2193
2226
|
Ue as createVideoEffectsPipeline,
|
|
@@ -2199,6 +2232,6 @@ export {
|
|
|
2199
2232
|
Z as scenePainters,
|
|
2200
2233
|
ee as smoothRig,
|
|
2201
2234
|
ie as stylizedAvatarRenderer,
|
|
2202
|
-
|
|
2203
|
-
|
|
2235
|
+
Ie as unlockRtcCallAudio,
|
|
2236
|
+
xe as version
|
|
2204
2237
|
};
|
package/dist/rtc-web.umd.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(b,B){typeof exports=="object"&&typeof module<"u"?B(exports):typeof define=="function"&&define.amd?define(["exports"],B):(b=typeof globalThis<"u"?globalThis:b||self,B(b.StreamingCdnRtc={}))})(this,(function(b){"use strict";function H(a,e,t=.02,i=1.4){const n=a.filter(r=>r.level>=t);if(n.length===0)return null;const s=n.reduce((r,c)=>c.level>r.level?c:r),o=n.find(r=>r.participantId===e);return o?s.participantId===e?e:s.level>=o.level*i?s.participantId:e:s.participantId}function ie(a){const e=[];for(const t of a){if(String(t.type??"")!=="inbound-rtp"||String(t.kind??t.mediaType??"")!=="audio")continue;const i=Number(t.audioLevel??NaN);Number.isFinite(i)&&e.push({mid:typeof t.mid=="string"?t.mid:null,level:i})}return e}const V={audio:{active:!1,maxBitrate:0,maxFramerate:0,scaleResolutionDownBy:4},low:{active:!0,maxBitrate:35e4,maxFramerate:15,scaleResolutionDownBy:4},medium:{active:!0,maxBitrate:11e5,maxFramerate:24,scaleResolutionDownBy:2},high:{active:!0,maxBitrate:28e5,maxFramerate:30,scaleResolutionDownBy:1}},Ce={warmupSamples:3,poorSamplesToStepDown:2,goodSamplesToStepUp:6,criticalLossPct:8,criticalRttMs:800,poorLossPct:3,poorRttMs:350,goodLossPct:1,goodRttMs:250,sustainRatio:.8,bandwidthHeadroomRatio:1.25};function Re(a){const e=String(a??"").trim().toLowerCase();return!e||e==="none"?"":e==="cpu"||e==="bandwidth"?e:"other"}function Ie(a,e,t,i,n={}){const s={...Ce,...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,k=Re(e.qualityLimitationReason),f=e.packetLossPct??0,v=e.rttMs??0,R=e.availableOutgoingBitrateBps,y=V[a].maxBitrate,w=f>=s.criticalLossPct||v>=s.criticalRttMs||k==="cpu",p=k==="bandwidth"&&(R==null||R<y*s.bandwidthHeadroomRatio),I=w||f>=s.poorLossPct||v>=s.poorRttMs||p,A=R==null||R>=y*s.sustainRatio,D=f<s.goodLossPct&&v<s.goodRttMs&&!k&&A,E=I?t+1:0,L=D?i+1:0,S={quality:a,poorSamples:E,goodSamples:L,samplesSeen:d,changed:!1};return l>c?{quality:o[c],poorSamples:0,goodSamples:0,samplesSeen:d,changed:!0}:u?S:w&&l>0?{quality:o[l-1],poorSamples:0,goodSamples:0,samplesSeen:d,changed:!0}:E>=s.poorSamplesToStepDown&&l>0?{quality:o[l-1],poorSamples:0,goodSamples:0,samplesSeen:d,changed:!0}:L>=s.goodSamplesToStepUp&&l<c?{quality:o[l+1],poorSamples:0,goodSamples:0,samplesSeen:d,changed:!0}:S}function Ee(a,e){const t=a instanceof Map?a:new Map(a);let i=0,n=0,s=null,o=null,r=null;for(const d of t.values()){const u=String(d.type??"");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"){n+=Number(d.packetsLost??0);const k=Number(d.roundTripTime??NaN);Number.isFinite(k)&&(o=k*1e3)}if(u==="candidate-pair"&&(d.nominated===!0||String(d.state??"")==="succeeded")){const k=Number(d.currentRoundTripTime??NaN);o==null&&Number.isFinite(k)&&(o=k*1e3);const f=Number(d.availableOutgoingBitrate??NaN);Number.isFinite(f)&&(r=f)}}const c={packetsSent:i,packetsLost:n,timestamp:Date.now()};let l=null;if(e){const d=i-e.packetsSent,u=n-e.packetsLost;d>0&&u>=0&&(l=u/(d+u)*100)}return{sample:{packetLossPct:l,rttMs:o,availableOutgoingBitrateBps:r,qualityLimitationReason:s},counters:c}}function z(a,e){const t=new URL(String(a),e);return t.protocol==="https:"?t.protocol="wss:":t.protocol==="http:"&&(t.protocol="ws:"),t}function _(a,e){const t=new URL(String(a),e);return t.protocol==="wss:"?t.protocol="https:":t.protocol==="ws:"&&(t.protocol="http:"),t}const Me={audio:{active:!1,maxBitrate:0,maxFramerate:0,scaleResolutionDownBy:4}},Pe={active:!0,maxBitrate:4e6,maxFramerate:30,scaleResolutionDownBy:1};class x{options;socket=null;localStream=null;screenStream=null;peers=new Map;joinPromise=null;resolveJoin=null;rejectJoin=null;joined=!1;stopped=!1;reconnectAttempt=0;reconnectTimer=null;heartbeatTimer=null;qualityTimer=null;speakerTimer=null;activeSpeaker=null;heartbeatDeadline=0;quality;preferredVideoCodec;signalQueue=Promise.resolve();socketGeneration=0;constructor(e){this.options=e,this.localStream=e.localStream??null,this.quality=e.quality??"high",this.preferredVideoCodec=e.preferredVideoCodec??"auto"}async initialize(){if(!globalThis.RTCPeerConnection||!globalThis.WebSocket)throw new Error("This browser does not support WebRTC")}async join(){if(!this.joined){if(this.joinPromise)return this.joinPromise;this.joinPromise=this.connect();try{await this.joinPromise}finally{this.joinPromise=null}}}async leave(){this.stopped=!0,this.clearConnectionTimers(),this.send({type:"rtc.leave"}),this.joined=!1,this.socket?.close(1e3,"Participant left"),this.socket=null;for(const e of this.peers.values())e.connection.close();this.peers.clear(),this.stopStream(this.localStream),this.stopStream(this.screenStream),this.localStream=null,this.screenStream=null}setMuted(e){for(const t of this.localStream?.getAudioTracks()??[])t.enabled=!e}async setCameraEnabled(e){if(e&&!this.localStream?.getVideoTracks().length){const i=(await navigator.mediaDevices.getUserMedia({video:{width:{ideal:1920},height:{ideal:1080},frameRate:{ideal:30,max:30}}})).getVideoTracks()[0];if(!i)throw new Error("Camera is unavailable");this.localStream||(this.localStream=new MediaStream),this.localStream.addTrack(i),this.screenStream?.getVideoTracks().some(n=>n.readyState==="live")||await this.replaceVideoTrack(i);return}for(const t of this.localStream?.getVideoTracks()??[])t.enabled=e}async setScreenShareEnabled(e){if(!e){const n=this.screenStream;this.screenStream=null,this.stopStream(n);const s=this.localStream?.getVideoTracks()[0]??null;await this.replaceVideoTrack(s),this.options.onEvent("screensharechange",{enabled:!1,stream:null});return}if(this.screenStream?.getVideoTracks()[0]?.readyState==="live")return;const t=await navigator.mediaDevices.getDisplayMedia({video:{width:{ideal:1920},height:{ideal:1080},frameRate:{ideal:30,max:30}},audio:!1}),i=t.getVideoTracks()[0];if(!i)throw new Error("Screen sharing is unavailable");i.contentHint="detail",this.screenStream=t,i.addEventListener("ended",()=>{this.screenStream?.getVideoTracks()[0]?.id===i.id&&this.setScreenShareEnabled(!1)});try{await this.replaceVideoTrack(i),this.options.onEvent("screensharechange",{enabled:!0,stream:t})}catch(n){throw this.screenStream=null,this.stopStream(t),n}}async setDevice(e){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];if(!n)throw new Error("Selected media device is unavailable");const s=e.kind==="audioinput"?"audio":"video",o=this.localStream?.getTracks().find(r=>r.kind===s);if(this.localStream||(this.localStream=new MediaStream),o&&(this.localStream.removeTrack(o),o.stop()),this.localStream.addTrack(n),s==="video"){this.screenStream?.getVideoTracks().some(r=>r.readyState==="live")||await this.replaceVideoTrack(n);return}for(const r of this.peers.values()){const c=r.connection.getSenders().find(l=>l.track?.kind===s);c?await c.replaceTrack(n):r.connection.addTrack(n,this.localStream)}await this.applyVideoQuality()}async setQuality(e){this.quality=e;for(const t of this.localStream?.getVideoTracks()??[])t.enabled=e!=="audio";for(const t of this.peers.values())t.quality=x.clampToCeiling(t.quality,e),t.poorSamples=0,t.goodSamples=0;await this.applyVideoQuality()}static clampToCeiling(e,t){if(t==="audio")return e;const i=["low","medium","high"];return i.indexOf(e)>i.indexOf(t)?t:e}async setPreferredVideoCodec(e){this.preferredVideoCodec=e;for(const t of this.peers.values())this.applyCodecPreference(t.connection);await this.renegotiateAll()}getParticipants(){return[...this.peers.values()].map(e=>e.identity)}getLocalStream(){return this.localStream}async setVideoTrack(e){if(e.kind!=="video")throw new Error("A video track is required");this.localStream||(this.localStream=new MediaStream);const t=this.localStream.getVideoTracks()[0];t?.id!==e.id&&(t&&this.localStream.removeTrack(t),this.localStream.addTrack(e)),await this.replaceVideoTrack(e),this.options.onEvent("localstream",{stream:this.localStream,track:e})}getRemoteStreams(){return[...this.peers.values()].map(e=>({participant:e.identity,stream:e.stream}))}getPrimaryPeerConnection(){return this.peers.values().next().value?.connection??null}sendChat(e,t){this.send({type:"rtc.chat",message:e,participantIds:t??[]})}sendControl(e,t={}){this.send({type:"rtc.control",action:e,detail:t})}async connect(){const{credential:e}=this.options;if(!e.signalingToken||!e.signalingUrl||!e.roomId||!e.participantId)throw new Error("A room signaling credential is required");this.stopped=!1,!this.localStream&&(this.options.audio||this.options.video)&&(this.localStream=await navigator.mediaDevices.getUserMedia({audio:this.options.audio,video:this.options.video})),this.localStream&&this.options.onEvent("localstream",{stream:this.localStream}),await this.openSocket(!1)}async openSocket(e){const{credential:t}=this.options,i=z(t.signalingUrl,globalThis.location?.href),n=new Promise((c,l)=>{this.resolveJoin=c,this.rejectJoin=l}),s=new WebSocket(i,["rtc-client",t.signalingToken]),o=++this.socketGeneration;this.socket=s;const r=setTimeout(()=>this.rejectJoin?.(new Error("RTC signaling timed out")),15e3);s.addEventListener("open",()=>{this.heartbeatDeadline=Date.now()+45e3,this.startHeartbeat(s),this.startQualitySampling(),this.send({type:"rtc.room.join"})}),s.addEventListener("message",c=>{const l=String(c.data);this.signalQueue=this.signalQueue.then(async()=>{this.socket!==s||this.socketGeneration!==o||await this.handleSignal(l)}).catch(d=>{const u=d instanceof Error?d.message:"RTC signaling processing failed";this.options.onEvent("signalingerror",{message:u}),this.joined||this.rejectJoin?.(new Error(u))})}),s.addEventListener("error",()=>this.rejectJoin?.(new Error("RTC signaling connection failed"))),s.addEventListener("close",c=>{if(this.socket!==s)return;this.socket=null,this.stopHeartbeat(),this.stopQualitySampling(),!this.joined&&!e&&this.rejectJoin?.(new Error(c.reason||"RTC signaling connection closed"));const l=!this.stopped&&(this.joined||e);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})});try{await n,this.joined=!0,this.options.onEvent("connected",{participantId:t.participantId,resumed:e})}finally{clearTimeout(r),this.resolveJoin=null,this.rejectJoin=null}}async handleSignal(e){let t;try{t=JSON.parse(e)}catch{return}const i=String(t.type??"");if(i!=="ready"){if(i==="pong"){this.heartbeatDeadline=Date.now()+45e3;return}if(i==="rtc.room.peers"){this.reconnectAttempt=0;const n=Array.isArray(t.peers)?t.peers:[];for(const s of n)this.ensurePeer(s);this.resolveJoin?.();for(const s of n)this.shouldInitiateOffer(s)&&await this.createOffer(s);return}if(i==="rtc.room.join"&&t.sender){this.options.onEvent("participantjoined",t.sender),this.ensurePeer(t.sender),this.shouldInitiateOffer(t.sender)&&await this.createOffer(t.sender);return}if(i==="rtc.offer"&&t.sender&&t.description){const n=this.ensurePeer(t.sender);n.offerTask&&await n.offerTask;const s=n.connection.signalingState!=="stable",o=!this.shouldInitiateOffer(t.sender);if(s&&!o)return;s&&await n.connection.setLocalDescription({type:"rollback"}),await n.connection.setRemoteDescription(t.description);const r=await n.connection.createAnswer();await n.connection.setLocalDescription(r),this.send({type:"rtc.answer",targetParticipantId:t.sender.participantId,negotiationId:t.negotiationId,description:n.connection.localDescription});return}if(i==="rtc.answer"&&t.sender&&t.description){const n=this.peers.get(t.sender.participantId);if(!n||n.connection.signalingState!=="have-local-offer"||t.negotiationId&&n.activeNegotiationId&&t.negotiationId!==n.activeNegotiationId)return;await n.connection.setRemoteDescription(t.description),n.activeNegotiationId=null;return}if(i==="rtc.ice"&&t.sender&&t.candidate){await this.ensurePeer(t.sender).connection.addIceCandidate(t.candidate).catch(()=>{});return}if((i==="rtc.peer.left"||i==="rtc.leave")&&t.sender){this.removePeer(t.sender.participantId);return}i==="rtc.chat"?this.options.onEvent("chatupdate",t):i==="rtc.control"&&this.options.onEvent("control",t)}}ensurePeer(e){const t=this.peers.get(e.participantId);if(t)return t;const i=new RTCPeerConnection({iceServers:this.options.credential.iceServers??[],iceTransportPolicy:this.options.iceTransportPolicy??"all"}),n=new MediaStream,s={identity:e,connection:i,stream:n,makingOffer:!1,offerTask:null,activeNegotiationId:null,quality:x.startingQuality(this.quality),poorSamples:0,goodSamples:0,samplesSeen:0,counters:null};this.peers.set(e.participantId,s);for(const c of this.localStream?.getAudioTracks()??[])i.addTrack(c,this.localStream);const o=this.screenStream?.getVideoTracks().some(c=>c.readyState==="live")?this.screenStream:this.localStream,r=o?.getVideoTracks()[0];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=>{c.candidate&&this.send({type:"rtc.ice",targetParticipantId:e.participantId,candidate:c.candidate.toJSON()})}),i.addEventListener("track",c=>{n.getTracks().some(l=>l.id===c.track.id)||n.addTrack(c.track),this.options.onEvent("remotestream",{participant:e,stream:n,track:c.track})}),i.addEventListener("connectionstatechange",()=>{["failed","closed"].includes(i.connectionState)&&this.removePeer(e.participantId),this.options.onEvent("peerconnectionstate",{participant:e,state:i.connectionState})}),s}async createOffer(e){const t=this.ensurePeer(e);if(t.offerTask)return t.offerTask;if(t.connection.signalingState!=="stable")return;const i=this.performOffer(t);t.offerTask=i;try{await i}finally{t.offerTask===i&&(t.offerTask=null)}}async performOffer(e){e.makingOffer=!0;try{const t=await e.connection.createOffer({offerToReceiveAudio:this.options.receiveAudio,offerToReceiveVideo:this.options.receiveVideo});if(e.connection.signalingState!=="stable")return;await e.connection.setLocalDescription(t);const i=ne();e.activeNegotiationId=i,this.send({type:"rtc.offer",targetParticipantId:e.identity.participantId,negotiationId:i,description:e.connection.localDescription})}finally{e.makingOffer=!1}}shouldInitiateOffer(e){return this.options.credential.participantId.localeCompare(e.participantId)<0}async renegotiateAll(){for(const e of this.peers.values())await this.createOffer(e.identity)}async replaceVideoTrack(e){let t=!1;for(const i of this.peers.values()){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);if(n){const o=e?this.options.receiveVideo?"sendrecv":"sendonly":this.options.receiveVideo?"recvonly":"inactive";s&&s.direction!==o&&(s.direction=o,t=!0),await n.replaceTrack(e),i.videoSender=n,s&&(i.videoTransceiver=s)}else e&&(i.videoSender=i.connection.addTrack(e,this.screenStream??this.localStream??new MediaStream([e])),t=!0)}t&&await this.renegotiateAll(),await this.applyVideoQuality()}async applyVideoQuality(e){const t=e?[...this.peers.values()].filter(i=>i.connection===e):[...this.peers.values()];for(const i of t)await this.applyPeerVideoQuality(i)}async applyPeerVideoQuality(e){const t=this.screenStream?.getVideoTracks()[0]?.id;for(const i of e.connection.getSenders().filter(n=>n.track?.kind==="video")){const n=!!(t&&i.track?.id===t),s=n?Pe:this.quality==="audio"?Me.audio:V[e.quality],o=i.getParameters(),r=o.encodings?.length?o.encodings:[{}];o.encodings=r.map(c=>({...c,...s})),o.degradationPreference=n?"maintain-resolution":"maintain-framerate",await i.setParameters(o)}}startQualitySampling(){this.stopQualitySampling(),this.qualityTimer=setInterval(()=>{this.samplePeerQuality()},5e3),this.speakerTimer=setInterval(()=>{this.sampleActiveSpeaker()},1e3)}stopQualitySampling(){this.qualityTimer&&clearInterval(this.qualityTimer),this.qualityTimer=null,this.speakerTimer&&clearInterval(this.speakerTimer),this.speakerTimer=null}async sampleActiveSpeaker(){const e=[];for(const i of[...this.peers.values()])if(i.connection.connectionState==="connected")try{const n=await i.connection.getStats();for(const s of ie(n.values()))e.push({participantId:i.identity.participantId,level:s.level})}catch{}const t=H(e,this.activeSpeaker);t!==this.activeSpeaker&&(this.activeSpeaker=t,this.options.onEvent("activespeaker",{participantId:t}))}async samplePeerQuality(){if(this.quality!=="audio")for(const e of[...this.peers.values()]){if(e.connection.connectionState!=="connected")continue;let t;try{t=await e.connection.getStats()}catch{continue}const{sample:i,counters:n}=Ee(t,e.counters);e.counters=n;const s=Ie(e.quality,i,e.poorSamples,e.goodSamples,{samplesSeen:e.samplesSeen,ceiling:this.quality});e.poorSamples=s.poorSamples,e.goodSamples=s.goodSamples,e.samplesSeen=s.samplesSeen,s.changed&&(e.quality=s.quality,await this.applyPeerVideoQuality(e).catch(()=>{}),this.options.onEvent("peerquality",{participantId:e.identity.participantId,quality:s.quality}))}}static startingQuality(e){return e==="low"?"low":"medium"}applyCodecPreference(e){if(this.preferredVideoCodec==="auto"||!globalThis.RTCRtpReceiver?.getCapabilities)return;const t=RTCRtpReceiver.getCapabilities("video")?.codecs??[],i=`video/${this.preferredVideoCodec}`,n=[...t.filter(s=>s.mimeType.toLowerCase()===i),...t.filter(s=>s.mimeType.toLowerCase()!==i)];for(const s of e.getTransceivers())s.receiver.track.kind==="video"&&typeof s.setCodecPreferences=="function"&&s.setCodecPreferences(n)}removePeer(e){const t=this.peers.get(e);t&&(t.connection.close(),this.peers.delete(e),this.options.onEvent("participantleft",t.identity))}resetPeers(){for(const e of this.peers.values())e.connection.close();this.peers.clear()}startHeartbeat(e){this.stopHeartbeat(),this.stopQualitySampling(),this.heartbeatTimer=setInterval(()=>{if(!(this.socket!==e||e.readyState!==WebSocket.OPEN)){if(Date.now()>this.heartbeatDeadline){e.close(4e3,"Signaling heartbeat timed out");return}e.send("ping")}},2e4)}stopHeartbeat(){this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.heartbeatTimer=null}clearConnectionTimers(){this.stopHeartbeat(),this.stopQualitySampling(),this.reconnectTimer&&clearTimeout(this.reconnectTimer),this.reconnectTimer=null}scheduleReconnect(){if(this.reconnectTimer||this.stopped)return;const e=Math.min(3e4,1e3*2**Math.min(this.reconnectAttempt,5))+Math.round(Math.random()*300);this.reconnectAttempt+=1,this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.openSocket(!0).catch(t=>{this.options.onEvent("signalingerror",{message:t instanceof Error?t.message:"RTC signaling connection failed"}),this.scheduleReconnect()})},e)}send(e){this.socket?.readyState===WebSocket.OPEN&&this.socket.send(JSON.stringify({clientMessageId:e.clientMessageId??ne(),...e}))}stopStream(e){for(const t of e?.getTracks()??[])t.stop()}}function ne(){return globalThis.crypto?.randomUUID?.()??`${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}const W={active:!0,maxBitrate:4e6,maxFramerate:30,scaleResolutionDownBy:1},ae=[{rid:"a",maxBitrate:V.high.maxBitrate,maxFramerate:V.high.maxFramerate},{rid:"b",maxBitrate:V.medium.maxBitrate,maxFramerate:V.medium.maxFramerate,scaleResolutionDownBy:2},{rid:"c",maxBitrate:V.low.maxBitrate,maxFramerate:V.low.maxFramerate,scaleResolutionDownBy:4}],se={high:"a",medium:"b",low:"c"};class Ae{options;apiBase;socket=null;connection=null;sessionId=null;localStream=null;screenStream=null;remotes=new Map;midToParticipant=new Map;pendingAnnouncements=new Map;videoSender=null;joined=!1;stopped=!1;reconnectAttempt=0;reconnectTimer=null;heartbeatTimer=null;heartbeatDeadline=0;speakerTimer=null;activeSpeaker=null;quality;resolveJoin=null;rejectJoin=null;signalQueue=Promise.resolve();negotiationQueue=Promise.resolve();socketGeneration=0;constructor(e){this.options=e,this.localStream=e.localStream??null,this.quality=e.quality??"high",this.apiBase=_(e.credential.signalingUrl,globalThis.location?.href).origin}async initialize(){if(!globalThis.RTCPeerConnection||!globalThis.WebSocket)throw new Error("This browser does not support WebRTC")}async join(){if(this.joined)return;const{credential:e}=this.options;if(!e.signalingToken||!e.signalingUrl||!e.roomId||!e.participantId)throw new Error("A room signaling credential is required");this.stopped=!1,!this.localStream&&(this.options.audio||this.options.video)&&(this.localStream=await navigator.mediaDevices.getUserMedia({audio:this.options.audio,video:this.options.video})),this.localStream&&this.options.onEvent("localstream",{stream:this.localStream}),await this.openSocket(!1),await this.publish(),this.startSpeakerSampling(),this.announceTracks();const t=[...this.pendingAnnouncements.values()];this.pendingAnnouncements.clear();for(const i of t)await this.handleTrackAnnouncement(i.sender,i.detail)}async leave(){this.stopped=!0,this.clearTimers(),this.send({type:"rtc.leave"}),this.joined=!1,this.socket?.close(1e3,"Participant left"),this.socket=null,await this.closePublishedTracks().catch(()=>{}),this.connection?.close(),this.connection=null,this.sessionId=null,this.remotes.clear(),this.midToParticipant.clear(),this.stopStream(this.localStream),this.stopStream(this.screenStream),this.localStream=null,this.screenStream=null}setMuted(e){for(const t of this.localStream?.getAudioTracks()??[])t.enabled=!e}async setCameraEnabled(e){if(e&&!this.localStream?.getVideoTracks().length){const i=(await navigator.mediaDevices.getUserMedia({video:{width:{ideal:1920},height:{ideal:1080},frameRate:{ideal:30,max:30}}})).getVideoTracks()[0];if(!i)throw new Error("Camera is unavailable");this.localStream||(this.localStream=new MediaStream),this.localStream.addTrack(i),this.screenStream?.getVideoTracks().some(n=>n.readyState==="live")||await this.replaceVideoTrack(i);return}for(const t of this.localStream?.getVideoTracks()??[])t.enabled=e}async setScreenShareEnabled(e){if(!e){const n=this.screenStream;this.screenStream=null,this.stopStream(n);const s=this.localStream?.getVideoTracks()[0]??null;await this.replaceVideoTrack(s),this.options.onEvent("screensharechange",{enabled:!1,stream:null});return}if(this.screenStream?.getVideoTracks()[0]?.readyState==="live")return;const t=await navigator.mediaDevices.getDisplayMedia({video:{width:{ideal:1920},height:{ideal:1080},frameRate:{ideal:30,max:30}},audio:!1}),i=t.getVideoTracks()[0];if(!i)throw new Error("Screen sharing is unavailable");i.contentHint="detail",this.screenStream=t,i.addEventListener("ended",()=>{this.screenStream?.getVideoTracks()[0]?.id===i.id&&this.setScreenShareEnabled(!1)});try{await this.replaceVideoTrack(i),this.options.onEvent("screensharechange",{enabled:!0,stream:t})}catch(n){throw this.screenStream=null,this.stopStream(t),n}}async setDevice(e){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];if(!n)throw new Error("Selected media device is unavailable");const s=e.kind==="audioinput"?"audio":"video",o=this.localStream?.getTracks().find(c=>c.kind===s);if(this.localStream||(this.localStream=new MediaStream),o&&(this.localStream.removeTrack(o),o.stop()),this.localStream.addTrack(n),s==="video"){this.screenStream?.getVideoTracks().some(c=>c.readyState==="live")||await this.replaceVideoTrack(n);return}const r=this.connection?.getSenders().find(c=>c.track?.kind==="audio");r&&await r.replaceTrack(n)}async setQuality(e){this.quality=e;for(const t of this.localStream?.getVideoTracks()??[])t.enabled=e!=="audio";await this.applyVideoQuality()}async setPreferredVideoCodec(e){}async setVideoTrack(e){if(e.kind!=="video")throw new Error("A video track is required");this.localStream||(this.localStream=new MediaStream);const t=this.localStream.getVideoTracks()[0];t?.id!==e.id&&(t&&this.localStream.removeTrack(t),this.localStream.addTrack(e)),await this.replaceVideoTrack(e),this.options.onEvent("localstream",{stream:this.localStream,track:e})}getParticipants(){return[...this.remotes.values()].map(e=>e.identity)}getLocalStream(){return this.localStream}getRemoteStreams(){return[...this.remotes.values()].map(e=>({participant:e.identity,stream:e.stream}))}getPrimaryPeerConnection(){return this.connection}sendChat(e,t){this.send({type:"rtc.chat",message:e,participantIds:t??[]})}sendControl(e,t={}){this.send({type:"rtc.control",action:e,detail:t})}async openSocket(e){const{credential:t}=this.options,i=z(t.signalingUrl,globalThis.location?.href),n=new Promise((c,l)=>{this.resolveJoin=c,this.rejectJoin=l}),s=new WebSocket(i,["rtc-client",t.signalingToken]),o=++this.socketGeneration;this.socket=s;const r=setTimeout(()=>this.rejectJoin?.(new Error("RTC signaling timed out")),15e3);s.addEventListener("open",()=>{this.heartbeatDeadline=Date.now()+45e3,this.startHeartbeat(s),this.send({type:"rtc.room.join"})}),s.addEventListener("message",c=>{const l=String(c.data);this.signalQueue=this.signalQueue.then(async()=>{this.socket!==s||this.socketGeneration!==o||await this.handleSignal(l)}).catch(d=>{const u=d instanceof Error?d.message:"RTC signaling processing failed";this.options.onEvent("signalingerror",{message:u}),this.joined||this.rejectJoin?.(new Error(u))})}),s.addEventListener("error",()=>this.rejectJoin?.(new Error("RTC signaling connection failed"))),s.addEventListener("close",c=>{if(this.socket!==s)return;this.socket=null,this.stopHeartbeat(),!this.joined&&!e&&this.rejectJoin?.(new Error(c.reason||"RTC signaling connection closed"));const l=!this.stopped&&(this.joined||e);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})});try{await n,this.joined=!0,this.options.onEvent("connected",{participantId:t.participantId,resumed:e})}finally{clearTimeout(r),this.resolveJoin=null,this.rejectJoin=null}}async handleSignal(e){let t;try{t=JSON.parse(e)}catch{return}const i=String(t.type??"");if(i!=="ready"){if(i==="pong"){this.heartbeatDeadline=Date.now()+45e3;return}if(i==="rtc.room.peers"){this.reconnectAttempt=0,this.resolveJoin?.();return}if(i==="rtc.room.join"&&t.sender){this.options.onEvent("participantjoined",t.sender),this.sessionId&&this.announceTracks();return}if((i==="rtc.peer.left"||i==="rtc.leave")&&t.sender){this.removeRemote(t.sender.participantId);return}if(i==="rtc.control"){if(String(t.action??"")==="sfu.tracks"&&t.sender){await this.handleTrackAnnouncement(t.sender,t.detail);return}this.options.onEvent("control",t);return}i==="rtc.chat"&&this.options.onEvent("chatupdate",t)}}async publish(){const e=[...this.localStream?.getAudioTracks()??[],...this.localStream?.getVideoTracks()??[]],t=new RTCPeerConnection({iceServers:this.options.credential.iceServers??[],iceTransportPolicy:this.options.iceTransportPolicy??"all"});this.connection=t,t.addEventListener("track",r=>this.handleRemoteTrack(r)),t.addEventListener("connectionstatechange",()=>{this.options.onEvent("peerconnectionstate",{participant:this.selfIdentity(),state:t.connectionState})});const i=await this.sfuFetch("POST","/v1/rtc/sfu/sessions");if(this.sessionId=i.sessionId,e.length===0)return;const n=e.map(r=>t.addTransceiver(r,{direction:"sendonly",streams:this.localStream?[this.localStream]:[],...r.kind==="video"&&this.simulcastEnabled()?{sendEncodings:ae.map(c=>({...c}))}:{}})),s=await t.createOffer();await t.setLocalDescription(s);const o=await this.sfuFetch("POST",`/v1/rtc/sfu/sessions/${encodeURIComponent(i.sessionId)}/tracks`,{sessionDescription:t.localDescription,tracks:n.map(r=>({location:"local",mid:r.mid,trackName:this.trackName(r.sender.track)}))});o.sessionDescription&&await t.setRemoteDescription(o.sessionDescription),this.videoSender=t.getSenders().find(r=>r.track?.kind==="video")??null,await this.applyVideoQuality()}trackName(e){return`${this.options.credential.participantId}/${e.kind}`}announceTracks(){if(!this.sessionId)return;const e=[...(this.localStream?.getAudioTracks()??[]).map(t=>({trackName:this.trackName(t),kind:t.kind,simulcast:!1})),...(this.localStream?.getVideoTracks()??[]).map(t=>({trackName:this.trackName(t),kind:t.kind,simulcast:this.simulcastEnabled()}))];e.length!==0&&this.sendControl("sfu.tracks",{sessionId:this.sessionId,tracks:e})}async handleTrackAnnouncement(e,t){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);if(!i||s.length===0||e.participantId===this.options.credential.participantId)return;if(!this.connection||!this.sessionId){this.pendingAnnouncements.set(e.participantId,{sender:e,detail:t??{}});return}const o=this.remotes.get(e.participantId)??{identity:e,stream:new MediaStream,sessionId:i,trackNames:[],simulcastNames:new Set,mids:new Map};o.sessionId=i;for(const r of n)r.simulcast&&o.simulcastNames.add(r.trackName);this.remotes.set(e.participantId,o),this.negotiationQueue=this.negotiationQueue.then(async()=>{const r=s.filter(c=>!o.trackNames.includes(c));r.length!==0&&await this.subscribe(o,r)}).catch(r=>{this.options.onEvent("signalingerror",{message:r instanceof Error?r.message:"SFU subscribe failed"})}),await this.negotiationQueue}async subscribe(e,t){const i=this.connection;if(!i||!this.sessionId||t.length===0)return;const n=await this.sfuFetch("POST",`/v1/rtc/sfu/sessions/${encodeURIComponent(this.sessionId)}/tracks`,{tracks:t.map(s=>({location:"remote",sessionId:e.sessionId,trackName:s,...e.simulcastNames.has(s)?{simulcast:{preferredRid:"a",priorityOrdering:"asciibetical",ridNotAvailable:"asciibetical"}}:{}}))});for(const s of n.tracks??[])s.mid&&(e.mids.set(s.mid,String(s.trackName??"")),this.midToParticipant.set(s.mid,e.identity.participantId));if(e.trackNames=[...new Set([...e.trackNames,...t])],n.requiresImmediateRenegotiation&&n.sessionDescription){await i.setRemoteDescription(n.sessionDescription);const s=await i.createAnswer();await i.setLocalDescription(s),await this.sfuFetch("PUT",`/v1/rtc/sfu/sessions/${encodeURIComponent(this.sessionId)}/renegotiate`,{sessionDescription:i.localDescription})}}handleRemoteTrack(e){const t=e.transceiver?.mid??null,i=t?this.midToParticipant.get(t):void 0,n=i?this.remotes.get(i):void 0;n&&(n.stream.getTracks().some(s=>s.id===e.track.id)||n.stream.addTrack(e.track),this.options.onEvent("remotestream",{participant:n.identity,stream:n.stream,track:e.track}))}removeRemote(e){const t=this.remotes.get(e);if(t){for(const i of t.mids.keys())this.midToParticipant.delete(i);this.remotes.delete(e),this.options.onEvent("participantleft",t.identity)}}async setRemoteQuality(e,t){const i=this.remotes.get(e);if(!i||!this.sessionId)return;const n=[...i.mids.entries()].find(([,s])=>i.simulcastNames.has(s))?.[0];n&&await this.sfuFetch("PUT",`/v1/rtc/sfu/sessions/${encodeURIComponent(this.sessionId)}/tracks/update`,{tracks:[{mid:n,simulcast:{preferredRid:se[t],priorityOrdering:"asciibetical",ridNotAvailable:"asciibetical"}}]})}async replaceVideoTrack(e){this.videoSender&&(await this.videoSender.replaceTrack(e),await this.applyVideoQuality())}simulcastEnabled(){return this.options.simulcast!==!1}async applyVideoQuality(){const e=this.videoSender;if(!e)return;const t=this.screenStream?.getVideoTracks()[0]?.id,i=!!(t&&e.track?.id===t),n=e.getParameters(),s=n.encodings?.length?n.encodings:[{}];if(s.length>1){const o=this.quality==="audio"?null:se[this.quality];n.encodings=s.map(r=>{if(i)return{...r,active:r.rid==="a",...r.rid==="a"?{maxBitrate:W.maxBitrate,maxFramerate:W.maxFramerate}:{}};const c=ae.find(d=>d.rid===r.rid),l=o!=null&&String(r.rid??"")>=o;return{...r,...c??{},active:l}})}else{const o=this.quality==="audio"?V.audio:V[this.quality],r=i?W:o;n.encodings=s.map(c=>({...c,...r}))}n.degradationPreference=i?"maintain-resolution":"maintain-framerate",await e.setParameters(n)}async closePublishedTracks(){const e=this.connection;if(!e||!this.sessionId)return;const t=e.getTransceivers().filter(i=>i.sender.track&&i.mid).map(i=>({mid:i.mid}));t.length!==0&&await this.sfuFetch("PUT",`/v1/rtc/sfu/sessions/${encodeURIComponent(this.sessionId)}/tracks/close`,{tracks:t,force:!0})}selfIdentity(){return{participantId:this.options.credential.participantId,externalUserId:this.options.credential.externalUserId??this.options.credential.participantId,displayName:this.options.credential.externalUserId??this.options.credential.participantId,platform:"web"}}async sfuFetch(e,t,i){const n=await fetch(`${this.apiBase}${t}`,{method:e,headers:{authorization:`Bearer ${this.options.credential.signalingToken}`,"content-type":"application/json"},body:i===void 0?void 0:JSON.stringify(i)});if(!n.ok)throw new Error(`SFU request failed with HTTP ${n.status}`);return n.json()}startHeartbeat(e){this.stopHeartbeat(),this.heartbeatTimer=setInterval(()=>{if(!(this.socket!==e||e.readyState!==WebSocket.OPEN)){if(Date.now()>this.heartbeatDeadline){e.close(4e3,"Signaling heartbeat timed out");return}e.send("ping")}},2e4)}stopHeartbeat(){this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.heartbeatTimer=null}startSpeakerSampling(){this.stopSpeakerSampling(),this.speakerTimer=setInterval(()=>{this.sampleActiveSpeaker()},1e3)}stopSpeakerSampling(){this.speakerTimer&&clearInterval(this.speakerTimer),this.speakerTimer=null}async sampleActiveSpeaker(){const e=this.connection;if(!e||e.connectionState!=="connected")return;let t;try{t=await e.getStats()}catch{return}const i=[];for(const s of ie(t.values())){const o=s.mid?this.midToParticipant.get(s.mid):void 0;o&&i.push({participantId:o,level:s.level})}const n=H(i,this.activeSpeaker);n!==this.activeSpeaker&&(this.activeSpeaker=n,this.options.onEvent("activespeaker",{participantId:n}))}clearTimers(){this.stopHeartbeat(),this.stopSpeakerSampling(),this.reconnectTimer&&clearTimeout(this.reconnectTimer),this.reconnectTimer=null}scheduleReconnect(){if(this.reconnectTimer||this.stopped)return;const e=Math.min(3e4,1e3*2**Math.min(this.reconnectAttempt,5))+Math.round(Math.random()*300);this.reconnectAttempt+=1,this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.openSocket(!0).then(()=>this.announceTracks()).catch(t=>{this.options.onEvent("signalingerror",{message:t instanceof Error?t.message:"RTC signaling connection failed"}),this.scheduleReconnect()})},e)}send(e){this.socket?.readyState===WebSocket.OPEN&&this.socket.send(JSON.stringify({clientMessageId:e.clientMessageId??Le(),...e}))}stopStream(e){for(const t of e?.getTracks()??[])t.stop()}}function Le(){return globalThis.crypto?.randomUUID?.()??`${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}function De(a,e={}){const t=e.document??globalThis.document,i=!!(t&&t.pictureInPictureEnabled);let n=null;function s(){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)}function o(r){const c=a.getRemoteStreams();return r?c.find(l=>l.participant.participantId===r)?.stream??null:c[0]?.stream??a.getLocalStream()}return{supported:i,async enter(r){if(!i)throw new Error("This browser does not support picture-in-picture");const c=o(r);if(!c)throw new Error("No video stream is available for picture-in-picture");const l=s();l.srcObject!==c&&(l.srcObject=c),await l.play().catch(()=>{}),await l.requestPictureInPicture()},async exit(){t.pictureInPictureElement&&typeof t.exitPictureInPicture=="function"&&await t.exitPictureInPicture()},dispose(){n?.remove(),n=null}}}function Oe(a,e){const t=e.document??globalThis.document,i=e.fetchImplementation??globalThis.fetch.bind(globalThis),n=_(e.credential.signalingUrl,globalThis.location?.href).origin,s=e.mimeType??"video/webm",o=e.height??720,r=Math.round(o*16/9);let c=null,l=null,d=null,u=null,k=0,f=[],v=!1;async function R(y,w,p,I="application/json"){const A=await i(`${n}${w}`,{method:y,headers:{authorization:`Bearer ${e.credential.signalingToken}`,"content-type":I},body:p===void 0?void 0:I==="application/json"?JSON.stringify(p):p});if(!A.ok)throw new Error(`Recording request failed with HTTP ${A.status}`);return A.json()}return{get recording(){return v},async start(){if(v)return;u=(await R("POST","/v1/rtc/recordings")).recordingId;const w=t.createElement("canvas");w.width=r,w.height=o;const p=w.getContext("2d"),I=new Map,A=(T,P)=>{let C=I.get(T);return C||(C=t.createElement("video"),C.muted=!0,C.playsInline=!0,C.srcObject=P,C.play().catch(()=>{}),I.set(T,C)),C};l=setInterval(()=>{const T=a.getRemoteStreams(),P=a.getLocalStream(),C=[...T.map(q=>({key:q.participant.participantId,stream:q.stream,label:q.participant.displayName})),...P?[{key:"local",stream:P,label:"me"}]:[]];p.fillStyle="#101418",p.fillRect(0,0,r,o);const N=Math.max(1,Math.ceil(Math.sqrt(C.length))),Q=Math.max(1,Math.ceil(C.length/N)),j=r/N,g=o/Q;C.forEach((q,Te)=>{const be=A(q.key,q.stream),ee=Te%N*j,te=Math.floor(Te/N)*g;be.videoWidth>0&&p.drawImage(be,ee+2,te+2,j-4,g-4),p.fillStyle="rgba(0,0,0,.6)",p.fillRect(ee+8,te+g-30,Math.min(j-16,12+q.label.length*9),22),p.fillStyle="#fff",p.font="14px system-ui",p.fillText(q.label,ee+14,te+g-14)})},66);const D=w.captureStream(15);d=new AudioContext;const E=d.createMediaStreamDestination(),L=new Set,S=()=>{const T=[...a.getRemoteStreams().map(P=>P.stream),...a.getLocalStream()?[a.getLocalStream()]:[]];for(const P of T)for(const C of P.getAudioTracks())L.has(C.id)||(L.add(C.id),d.createMediaStreamSource(new MediaStream([C])).connect(E))};S();const M=setInterval(S,1e3),m=()=>clearInterval(M);for(const T of E.stream.getAudioTracks())D.addTrack(T);c=(e.mediaRecorderFactory??((T,P)=>new MediaRecorder(T,P)))(D,{mimeType:s}),f=[],c.addEventListener("dataavailable",T=>{T.data&&T.data.size>0&&f.push(T.data)}),c.addEventListener("stop",m),c.start(1e3),k=Date.now(),v=!0},async stop(){if(!v||!c||!u)throw new Error("The recorder is not running");v=!1;const y=c,w=new Promise(E=>y.addEventListener("stop",()=>E(),{once:!0}));y.stop(),await w,l&&clearInterval(l),l=null,await d?.close().catch(()=>{}),d=null;const p=new Blob(f,{type:s});f=[];const I=Math.max(1,Math.round((Date.now()-k)/1e3)),A=await R("PUT",`/v1/rtc/recordings/${encodeURIComponent(u)}/media`,p,s);await R("POST",`/v1/rtc/recordings/${encodeURIComponent(u)}/complete`,{durationSeconds:I});const D={recordingId:u,sizeBytes:A.sizeBytes,durationSeconds:I};return u=null,c=null,D},dispose(){l&&clearInterval(l),l=null;try{c?.stop()}catch{}c=null,d?.close().catch(()=>{}),d=null,v=!1}}}const re=["none","blur","aurora","cybergrid","bokeh","sunset","image"],oe=["none","snow","confetti","vignette"],$={aurora(a,e,t,i){const n=a.createLinearGradient(0,0,0,t);n.addColorStop(0,"#050b1e"),n.addColorStop(1,"#0b2540"),a.fillStyle=n,a.fillRect(0,0,e,t);for(let s=0;s<3;s+=1){a.beginPath();for(let o=0;o<=e;o+=16){const r=t*.35+s*40+Math.sin(o/140+i/900+s)*60;o===0?a.moveTo(o,r):a.lineTo(o,r)}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()}},cybergrid(a,e,t,i){a.fillStyle="#0a0118",a.fillRect(0,0,e,t),a.strokeStyle="rgba(255,0,180,0.5)",a.lineWidth=1.5;const n=t*.45;for(let o=0;o<12;o+=1){const r=n+(o*34+i/18)%(t-n);a.beginPath(),a.moveTo(0,r),a.lineTo(e,r),a.stroke()}for(let o=-8;o<=8;o+=1)a.beginPath(),a.moveTo(e/2+o*24,n),a.lineTo(e/2+o*e*.12,t),a.stroke();const s=a.createLinearGradient(0,t*.1,0,n);s.addColorStop(0,"#ffd34d"),s.addColorStop(1,"#ff2d78"),a.fillStyle=s,a.beginPath(),a.arc(e/2,n,t*.22,Math.PI,0),a.fill()},bokeh(a,e,t,i){a.fillStyle="#14100c",a.fillRect(0,0,e,t);for(let n=0;n<26;n+=1){const s=(Math.sin(n*12.9898)*.5+.5)*e,o=(Math.sin(n*78.233)*.5+.5)*t+Math.sin(i/1500+n)*12,r=14+n%5*12;a.fillStyle=`hsla(${35+n%4*12}, 90%, 60%, ${.1+n%3*.05})`,a.beginPath(),a.arc(s,o,r,0,Math.PI*2),a.fill()}},sunset(a,e,t,i){const n=a.createLinearGradient(0,0,0,t);n.addColorStop(0,"#2b1055"),n.addColorStop(.55,"#d1548a"),n.addColorStop(1,"#ffb36b"),a.fillStyle=n,a.fillRect(0,0,e,t),a.fillStyle="#ffdf8e",a.beginPath(),a.arc(e*.5,t*.62+Math.sin(i/4e3)*4,t*.13,0,Math.PI*2),a.fill(),a.fillStyle="rgba(20,8,40,0.85)",a.fillRect(0,t*.78,e,t*.22)}};function qe(a,e={}){const t=e.document??globalThis.document;let i=e.width??1280,n=e.height??720;const s=!!(e.width||e.height),o=e.frameRate??24,r=a instanceof MediaStreamTrack?a:a.getVideoTracks()[0];if(!r)throw new Error("A video track is required");let c=e.background??"none",l=e.overlay??"none",d=e.backgroundImage??null,u=e.segmenter??null,k=null,f=!1;const v=t.createElement("video");v.muted=!0,v.playsInline=!0,v.srcObject=new MediaStream([r]),v.play().catch(()=>{});const R=t.createElement("canvas");R.width=i,R.height=n;const y=R.getContext("2d"),w=t.createElement("canvas");w.width=i,w.height=n;const p=w.getContext("2d"),I=()=>Array.from({length:80},(m,h)=>({x:(Math.sin(h*12.9898)*.5+.5)*i,y:(Math.sin(h*78.233)*.5+.5)*n,speed:.6+h%5*.5,drift:Math.sin(h)*.6,size:2+h%4,hue:h*47%360}));let A=I();const D=()=>{if(s)return;const m=v.videoWidth,h=v.videoHeight;if(!m||!h)return;const T=Math.min(1,1280/Math.max(m,h)),P=Math.round(m*T),C=Math.round(h*T);P===i&&C===n||(i=P,n=C,R.width=i,R.height=n,w.width=i,w.height=n,A=I())};v.addEventListener?.("loadedmetadata",D),v.addEventListener?.("resize",D);function E(){if(k){p.clearRect(0,0,i,n),p.drawImage(k,0,0,i,n),p.globalCompositeOperation="source-in",p.drawImage(v,0,0,i,n),p.globalCompositeOperation="source-over",y.drawImage(w,0,0);return}const m=i*.42,h=m*9/16,T=i-m-24,P=n-h-24;y.save(),y.shadowColor="rgba(0,0,0,.5)",y.shadowBlur=24,y.beginPath(),y.roundRect(T,P,m,h,14),y.clip(),y.drawImage(v,T,P,m,h),y.restore()}function L(m){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",k&&E()):c==="image"&&d?(y.drawImage(d,0,0,i,n),E()):(($[c]??$.aurora)(y,i,n,m),E()),l==="snow"||l==="confetti")for(const h of A)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());else if(l==="vignette"){const h=y.createRadialGradient(i/2,n/2,n*.4,i/2,n/2,n*.85);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)}}const S=setInterval(()=>{L(Date.now()),u&&!f&&c!=="none"&&(f=!0,u.segment(v).then(m=>{k=m}).catch(()=>{k=null}).finally(()=>{f=!1}))},Math.round(1e3/o)),M=R.captureStream(o);return{track:M.getVideoTracks()[0],stream:M,setBackground(m,h){c=re.includes(m)?m:"none",h!==void 0&&(d=h),c==="none"&&(k=null)},setOverlay(m){l=oe.includes(m)?m:"none"},setSegmenter(m){u=m,m||(k=null)},dispose(){clearInterval(S),u?.close?.();for(const m of M.getTracks())m.stop();v.srcObject=null}}}function J(){return{blinkLeft:0,blinkRight:0,jawOpen:0,smile:0,browUp:0,pupilX:0,pupilY:0,headYaw:0,headPitch:0,headRoll:0,tracked:!1}}function O(a,e){const t=a.find(i=>i.categoryName===e);return t?Math.max(0,Math.min(1,t.score)):0}function ce(a){if(!a||a.length<16)return{yaw:0,pitch:0,roll:0};a[0],a[4];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);return{yaw:o,pitch:r,roll:c}}function le(a,e,t=1){if(!a||a.length===0)return J();const i=ce(e),n=(O(a,"eyeLookOutRight")+O(a,"eyeLookInLeft"))/2,s=(O(a,"eyeLookOutLeft")+O(a,"eyeLookInRight"))/2,o=(O(a,"eyeLookUpLeft")+O(a,"eyeLookUpRight"))/2,r=(O(a,"eyeLookDownLeft")+O(a,"eyeLookDownRight"))/2,c=(O(a,"mouthSmileLeft")+O(a,"mouthSmileRight"))/2,l=(O(a,"mouthFrownLeft")+O(a,"mouthFrownRight"))/2,d=O(a,"eyeBlinkLeft"),u=O(a,"eyeBlinkRight"),k=Math.min(1,Math.abs(i.yaw)/.35)*Math.max(0,Math.min(1,t)),f=(d+u)/2;return{blinkLeft:d+(f-d)*k,blinkRight:u+(f-u)*k,jawOpen:O(a,"jawOpen"),smile:c-l,browUp:(O(a,"browInnerUp")+O(a,"browOuterUpLeft")+O(a,"browOuterUpRight"))/3,pupilX:n-s,pupilY:r-o,headYaw:i.yaw,headPitch:i.pitch,headRoll:i.roll,tracked:!0}}function G(a,e,t=.4){const i=(n,s)=>n+(s-n)*t;return{blinkLeft:i(a.blinkLeft,e.blinkLeft),blinkRight:i(a.blinkRight,e.blinkRight),jawOpen:i(a.jawOpen,e.jawOpen),smile:i(a.smile,e.smile),browUp:i(a.browUp,e.browUp),pupilX:i(a.pupilX,e.pupilX),pupilY:i(a.pupilY,e.pupilY),headYaw:i(a.headYaw,e.headYaw),headPitch:i(a.headPitch,e.headPitch),headRoll:i(a.headRoll,e.headRoll),tracked:e.tracked}}function de(a,e,t,i,n){n||(a.fillStyle="#101822",a.fillRect(0,0,t,i));const s=n?n.x+e.headYaw*n.radius*.1:t/2+e.headYaw*t*.18,o=n?n.y+e.headPitch*n.radius*.1:i/2+e.headPitch*i*.18,r=n?n.radius:Math.min(t,i)*.28;a.save(),a.translate(s,o),a.rotate(e.headRoll*.6);const c=Math.max(-1.35,Math.min(1.35,e.headYaw)),l=Math.max(-.9,Math.min(.9,e.headPitch)),d=Math.sin(c),u=L=>Math.sin(L+c)*r,k=L=>Math.cos(L+c),f=Math.sin(l)*r*.22;a.fillStyle="#3a2a1c",a.beginPath(),a.ellipse(-d*r*.35,-r*.25,r*.98,r*.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)*.12),r*1.12,0,0,Math.PI*2),a.fill(),a.fillStyle="#3a2a1c",a.beginPath(),a.ellipse(-d*r*.28,-r*.72+f*.4,r*.92,r*.5,0,Math.PI,Math.PI*2),a.fill();const v=0;a.fillStyle="#e5b58a",a.beginPath();const R=u(v)*.9,y=r*.12+f;a.ellipse(R+d*r*.14,y,r*.11,r*.14,0,0,Math.PI*2),a.fill();for(const L of[-1,1]){const S=L*.42,M=k(S);if(M<=.12)continue;const m=u(S)*.82,h=Math.max(.2,M),T=L<0?e.blinkLeft:e.blinkRight,P=Math.max(.06,1-T),C=e.browUp*r*.14;a.strokeStyle="#4a2e18",a.lineWidth=r*.07,a.lineCap="round",a.beginPath(),a.moveTo(m-r*.2*h,-r*.42-C+f),a.quadraticCurveTo(m,-r*.52-C+f,m+r*.2*h,-r*.44-C+f),a.stroke();const N=-r*.18+f;a.fillStyle="#ffffff",a.beginPath(),a.ellipse(m,N,r*.16*h,r*.12*P,0,0,Math.PI*2),a.fill(),a.fillStyle="#22303c",a.beginPath(),a.ellipse(m+e.pupilX*r*.07*h,N+e.pupilY*r*.05,r*.06*h,r*.06*P,0,0,Math.PI*2),a.fill()}const w=Math.max(.25,k(0)),p=u(0)*.7,I=r*.55*w,A=r*(.04+e.jawOpen*.34),D=e.smile*r*.12,E=r*.42+f;a.fillStyle="#8c2f2f",a.beginPath(),a.moveTo(p-I/2,E-D),a.quadraticCurveTo(p,E+A,p+I/2,E-D),a.quadraticCurveTo(p,E-A*.25,p-I/2,E-D),a.closePath(),a.fill(),a.restore(),!e.tracked&&!n&&(a.fillStyle="#9fb0bd",a.font=`${Math.round(i*.045)}px system-ui`,a.textAlign="center",a.fillText("No face detected",t/2,i*.9),a.textAlign="left")}const Y={mouthGain:1.7,blinkGain:1,eyeSync:1,smoothing:.4,avatarScale:1,avatarLift:0,lostHoldMs:400};function K(){return{render(a,e,t,i,n){de(a,e,t,i,n??void 0)}}}const X=a=>Math.max(0,Math.min(1,a));function Ne(a,e={}){const t=e.document??globalThis.document;let i=e.width??1280,n=e.height??720;const s=!!(e.width||e.height),o=e.frameRate??24,r=a instanceof MediaStreamTrack?a:a.getVideoTracks()[0];if(!r)throw new Error("A video track is required");let c=e.tracker??null,l=e.renderer??K(),d=e.mode??"replace";const u={...Y,...e.tuning},k=e.backdrop??"#101822",f=t.createElement("video");f.muted=!0,f.playsInline=!0,f.srcObject=new MediaStream([r]),f.play().catch(()=>{});const v=t.createElement("canvas");v.width=i,v.height=n;const R=v.getContext("2d"),y=()=>{if(s)return;const S=f.videoWidth,M=f.videoHeight;if(!S||!M)return;const m=Math.min(1,1280/Math.max(S,M)),h=Math.round(S*m),T=Math.round(M*m);(!(h===i||T===n)||h!==i||T!==n)&&(i=h,n=T,v.width=i,v.height=n)};f.addEventListener?.("loadedmetadata",y),f.addEventListener?.("resize",y);let w=J(),p=null,I=0;function A(S){if(S.length<=454)return;const M=S[10],m=S[152],h=S[234],T=S[454],P=(h.x+T.x)/2*i,C=(h.y+T.y)/2*n,N=Math.hypot((m.x-M.x)*i,(m.y-M.y)*n),Q=Math.max(.6,Math.cos(w.headPitch)),j={x:P,y:C,radius:Math.max(24,N/Q*.8)};p=p?{x:p.x+(j.x-p.x)*.4,y:p.y+(j.y-p.y)*.4,radius:p.radius+(j.radius-p.radius)*.15}:j}function D(S){if(c&&f.readyState>=2){let m=null;try{m=c.detect(f,S)}catch{}const h=le(m?.categories??null,m?.matrix??null,u.eyeSync);h.jawOpen=X(h.jawOpen*u.mouthGain),h.blinkLeft=X(h.blinkLeft*u.blinkGain),h.blinkRight=X(h.blinkRight*u.blinkGain),h.tracked?(I=S,w=G(w,h,u.smoothing),m?.landmarks&&A(m.landmarks)):w.tracked&&S-I>u.lostHoldMs&&(w=G(w,{...w,jawOpen:0,browUp:0,smile:w.smile*.5,blinkLeft:.1,blinkRight:.1},.05))}d==="overlay"&&f.readyState>=2?R.drawImage(f,0,0,i,n):(R.fillStyle=k,R.fillRect(0,0,i,n));const M=p?{x:p.x,y:p.y+u.avatarLift*p.radius,radius:p.radius*u.avatarScale}:null;l?.render(R,w,i,n,M)}const E=setInterval(()=>D(Date.now()),Math.round(1e3/o)),L=v.captureStream(o);return{track:L.getVideoTracks()[0],stream:L,getRig:()=>({...w}),setMode(S){d=S==="overlay"?"overlay":"replace"},setTracker(S){c?.close?.(),c=S},setRenderer(S){l?.dispose?.(),l=S??K()},setTuning(S){for(const M of Object.keys(Y)){const m=S[M];typeof m=="number"&&Number.isFinite(m)&&(u[M]=m)}},getTuning:()=>({...u}),dispose(){clearInterval(E),c?.close?.(),l?.dispose?.();for(const S of L.getTracks())S.stop();f.srcObject=null}}}class he extends EventTarget{mode;options;client=null;peerConnection;state="initialized";latestMediaScore=null;latestQuality=null;statsTimer=null;statsRunning=!1;previousCounters=null;lastTelemetryAt=0;joinPromise=null;requestedQuality;effectiveQuality;poorQualitySamples=0;goodQualitySamples=0;constructor(e){if(super(),!e.credential?.signalingToken||!e.credential.signalingUrl||!e.credential.roomId||!e.credential.participantId)throw new Error("A valid room signaling credential is required");this.mode=e.mode,this.requestedQuality=e.mode==="voice"?"audio":e.quality??"auto",this.effectiveQuality=this.requestedQuality==="auto"?"medium":this.requestedQuality,this.peerConnection=e.peerConnection??null,this.options={...e,audio:e.audio??!0,video:e.mode==="voice"?!1:e.video??!0,receiveAudio:e.receiveAudio??!0,receiveVideo:e.mode==="voice"?!1:e.receiveVideo??!0,statsInterval:Math.max(1e3,e.statsInterval??5e3),telemetryInterval:Math.max(1e4,e.telemetryInterval??3e4),autoReportStats:e.autoReportStats??!0}}async initialize(){if(this.client)return this;try{const t=(this.options.transport==="auto"?this.mode==="meeting"?"sfu":"mesh":this.options.transport??"mesh")==="sfu"?Ae:x;return this.client=new t({credential:{signalingToken:this.options.credential.signalingToken,signalingUrl:this.options.credential.signalingUrl,roomId:this.options.credential.roomId,participantId:this.options.credential.participantId,externalUserId:this.options.credential.externalUserId,iceServers:this.options.credential.iceServers},audio:this.options.audio,video:this.options.video,receiveAudio:this.options.receiveAudio,receiveVideo:this.options.receiveVideo,quality:this.effectiveQuality,preferredVideoCodec:this.options.preferredVideoCodec??"auto",iceTransportPolicy:this.options.iceTransportPolicy??"all",localStream:this.options.localStream,simulcast:this.options.simulcast??!0,onEvent:(i,n)=>this.handleNativeEvent(i,n)}),await this.client.initialize(),this.startStats(),this.emit("initialized",{mode:this.mode,sdkVersion:we}),this.options.autoJoin&&await this.join(),this}catch(e){this.state="failed";const t=ke(e,"RTC initialization failed");throw this.emit("error",U(t)),t}}async join(){const e=this.requireClient();return this.joinPromise?this.joinPromise:(this.state="joining",this.emit("joining",{}),this.joinPromise=e.join().catch(t=>{this.state="failed";const i=ke(t,"RTC connection failed");throw this.emit("error",U(i)),i}).finally(()=>{this.joinPromise=null}),this.joinPromise)}async leave(){this.client&&(await this.joinPromise?.catch(()=>{}),await this.sampleAndReport("final").catch(()=>{}),await this.client.leave(),this.state="disconnected",this.stopStats())}async setMuted(e){this.requireClient().setMuted(e),this.emit("mediachange",{kind:"audio",enabled:!e})}async setCameraEnabled(e){if(this.mode==="voice"&&e)throw new Error("Camera is unavailable in voice mode");await this.requireClient().setCameraEnabled(e),this.emit("mediachange",{kind:"video",enabled:e})}async setVideoTrack(e){if(this.mode==="voice")throw new Error("Camera is unavailable in voice mode");await this.requireClient().setVideoTrack(e),this.emit("mediachange",{kind:"video",enabled:e.enabled,trackId:e.id})}async setScreenShareEnabled(e){await this.requireClient().setScreenShareEnabled(e),this.emit("mediachange",{kind:"screenshare",enabled:e})}async setDevice(e){await this.requireClient().setDevice(e),this.emit("devicechange",{deviceId:e.deviceId,kind:e.kind})}async setQuality(e){if(this.mode==="voice"&&e!=="audio")throw new Error("Voice calls only support audio quality");this.requestedQuality=e,this.poorQualitySamples=0,this.goodQualitySamples=0,await this.applyEffectiveQuality(e==="auto"?"medium":e,"manual")}async setPreferredVideoCodec(e){await this.requireClient().setPreferredVideoCodec(e),this.emit("codecchange",{preferredVideoCodec:e})}async getDevices(){return navigator.mediaDevices?.enumerateDevices?navigator.mediaDevices.enumerateDevices():[]}getParticipants(){return this.requireClient().getParticipants().map(ye)}getLocalStream(){return this.client?.getLocalStream()??null}getRemoteStreams(){return(this.client?.getRemoteStreams()??[]).map(e=>({participant:ye(e.participant),stream:e.stream}))}setPeerConnection(e){this.peerConnection=e,this.previousCounters=null}async sendChatMessage(e,t){const i=e.trim();if(!i)throw new Error("message is required");this.requireClient().sendChat(i,t)}async sendControlMessage(e,t={}){const i=e.trim();if(!i)throw new Error("action is required");this.requireClient().sendControl(i,t)}async createPoll(e,t,i={}){const n=t.map(s=>s.trim()).filter(Boolean);if(!e.trim()||n.length<2)throw new Error("A poll requires a question and at least two options");this.requireClient().sendControl("poll.create",{poll:{id:`poll-${crypto.randomUUID()}`,question:e.trim(),options:n,votes:{},...i}})}async votePoll(e,t){this.requireClient().sendControl("poll.vote",{pollId:e,optionIndex:t})}async requestStageAccess(){this.requireClient().sendControl("stage.request")}async cancelStageAccess(){this.requireClient().sendControl("stage.cancel")}async joinStage(){this.requireClient().sendControl("stage.join")}async leaveStage(){this.requireClient().sendControl("stage.leave")}async grantStageAccess(e){this.requireClient().sendControl("stage.grant",{participantIds:e})}async denyStageAccess(e){this.requireClient().sendControl("stage.deny",{participantIds:e})}async startRecording(e=!1){this.requireClient().sendControl("recording.start",{allowMultiple:e})}async stopRecording(e){this.requireClient().sendControl("recording.stop",{recordingId:e})}async pauseRecording(e){this.requireClient().sendControl("recording.pause",{recordingId:e})}async resumeRecording(e){this.requireClient().sendControl("recording.resume",{recordingId:e})}async runPreflight(e={}){const t=[],i=globalThis.isSecureContext===!0;i||t.push("secure_context_required");let n="unavailable";if(e.requestMedia!==!1&&navigator.mediaDevices?.getUserMedia)try{(await navigator.mediaDevices.getUserMedia({audio:this.options.audio,video:this.options.video})).getTracks().forEach(k=>k.stop()),n="granted"}catch{n="denied",t.push("media_permission_denied")}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;this.options.audio&&o===0&&t.push("microphone_unavailable"),this.options.video&&r===0&&t.push("camera_unavailable");const c=e.probeUrl??this.options.preflightProbeUrl;let l=null;if(c){const u=performance.now();try{const k=await fetch(c,{method:"HEAD",cache:"no-store",credentials:"omit"});l=Math.round(performance.now()-u),k.ok||t.push("network_probe_failed")}catch{t.push("network_probe_failed")}}const d={passed:t.length===0,sampledAt:new Date().toISOString(),secureContext:i,mediaPermission:n,microphoneCount:o,cameraCount:r,probeLatencyMs:l,effectiveConnectionType:ve()?.effectiveType??null,issues:t};return this.emit("preflight",d),this.options.autoReportStats&&await this.reportQuality({sampleType:"preflight",sampledAt:d.sampledAt,connectionState:d.passed?"ready":"failed",rttMs:d.probeLatencyMs,effectiveConnectionType:d.effectiveConnectionType}).catch(()=>{}),d}async collectQualitySample(e="runtime"){const t=this.options.statsProvider?await this.options.statsProvider():{},i=this.peerConnection?await Be(this.peerConnection,this.previousCounters):null;i?.counters&&(this.previousCounters=i.counters);const s={...{sampleType:e,sampledAt:new Date().toISOString(),connectionState:this.peerConnection?.connectionState??this.state,mediaScore:this.latestMediaScore,networkScore:this.latestMediaScore==null?5:Math.max(0,Math.min(5,Math.round(this.latestMediaScore))),estimatedMos:4.5,rttMs:null,jitterMs:null,packetLossPct:null,packetLossCumulativePct:null,packetsLost:null,packetsReceived:null,inboundBitrateBps:null,outboundBitrateBps:null,availableOutgoingBitrateBps:null,framesPerSecond:null,frameWidth:null,frameHeight:null,inboundFrameWidth:null,inboundFrameHeight:null,outboundFrameWidth:null,outboundFrameHeight:null,framesDropped:null,freezeCount:null,freezeDurationMs:null,concealedSamples:null,codec:null,transportProtocol:null,candidateType:null,qualityLimitationReason:null,deviceMemoryGb:Fe(),effectiveConnectionType:ve()?.effectiveType??null},...i?.sample??{},...t,sampleType:e,sampledAt:new Date().toISOString()},o=xe(s);return s.networkScore=o.networkScore,s.estimatedMos=o.estimatedMos,this.latestQuality=s,s}async reportQuality(e){const t=this.options.credential.telemetryToken,i=this.options.telemetryEndpoint??this.options.credential.telemetryEndpoint;if(!t||!i)return;const n=await fetch(i,{method:"POST",headers:{authorization:`Bearer ${t}`,"content-type":"application/json"},body:JSON.stringify(e),keepalive:e.sampleType==="final"});if(!n.ok)throw new Error(`RTC telemetry failed with HTTP ${n.status}`)}getStats(){const e=this.client,t=e?.getLocalStream();return{state:this.state,participantCount:e?e.getParticipants().length+1:0,audioEnabled:!!t?.getAudioTracks().some(i=>i.enabled),videoEnabled:!!t?.getVideoTracks().some(i=>i.enabled),screenShareEnabled:!1,latestMediaScore:this.latestMediaScore,latestQuality:this.latestQuality,sampledAt:new Date().toISOString()}}getNativeClient(){return this.client}destroy(){this.stopStats(),this.client&&this.client.leave(),this.client=null,this.joinPromise=null,this.peerConnection=null,this.state="disconnected"}handleNativeEvent(e,t){e==="connected"?(this.state="connected",this.peerConnection=this.client?.getPrimaryPeerConnection()??null):e==="disconnected"?this.state="disconnected":e==="peerconnectionstate"&&(this.peerConnection=this.client?.getPrimaryPeerConnection()??null),this.emit(e,t)}startStats(){this.stopStats(),this.statsTimer=setInterval(()=>{this.sampleAndReport("runtime")},this.options.statsInterval)}async sampleAndReport(e){if(!this.statsRunning){this.statsRunning=!0;try{const t=await this.collectQualitySample(e);await this.adaptQuality(t),this.emit("stats",{...this.getStats(),latestQuality:t});const i=Date.now(),n=e!=="runtime"||i-this.lastTelemetryAt>=this.options.telemetryInterval;this.options.autoReportStats&&n&&(await this.reportQuality(t),this.lastTelemetryAt=i)}catch(t){this.emit("telemetryerror",U(t))}finally{this.statsRunning=!1}}}stopStats(){this.statsTimer!==null&&clearInterval(this.statsTimer),this.statsTimer=null}async adaptQuality(e){if(this.requestedQuality!=="auto"||this.options.adaptiveQuality===!1||this.mode==="voice")return;if((this.client?.getParticipants().length??0)>1){this.poorQualitySamples=0,this.goodQualitySamples=0,this.effectiveQuality!=="high"&&await this.applyEffectiveQuality("high","network");return}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);this.poorQualitySamples=n?this.poorQualitySamples+1:0,this.goodQualitySamples=s?this.goodQualitySamples+1:0;const o=["low","medium","high"],r=o.indexOf(this.effectiveQuality);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"))}async applyEffectiveQuality(e,t){if(this.effectiveQuality===e&&this.client)return;const i=this.effectiveQuality;this.effectiveQuality=e,this.client&&await this.client.setQuality(e),this.emit("qualitychange",{requestedQuality:this.requestedQuality,quality:e,previous:i,reason:t})}requireClient(){if(!this.client)throw new Error("Call initialize() before using the RTC client");return this.client}emit(e,t){const i={type:e,detail:t,timestamp:new Date().toISOString()};this.dispatchEvent(new CustomEvent(e,{detail:i})),this.options.onEvent?.(i)}}async function je(a){return new he(a).initialize()}let F=null;function ue(){if(F)return F;const a=globalThis,e=a.AudioContext??a.webkitAudioContext;return e?(F=new e,F):null}async function me(){const a=ue();return a?(a.state==="suspended"&&await a.resume().catch(()=>{}),a.state==="running"):!1}class Ve{options;media=null;cadenceTimer=null;oscillators=new Set;constructor(e){this.options=e}unlock(){return me()}startIncoming(){this.start("incoming",this.options.ringtoneUrl)}startRingback(){this.start("ringback",this.options.ringbackUrl)}stop(){this.cadenceTimer&&clearInterval(this.cadenceTimer),this.cadenceTimer=null,this.media?.pause(),this.media&&(this.media.currentTime=0),this.media=null;for(const e of this.oscillators)try{e.stop()}catch{}this.oscillators.clear()}start(e,t){if(this.stop(),this.options.sounds===!1)return;if(t&&typeof Audio<"u"){this.media=new Audio(t),this.media.loop=!0,this.media.play().catch(()=>{});return}const i=()=>this.playBuiltInTone(e);i(),this.cadenceTimer=setInterval(i,e==="incoming"?3e3:3500)}playBuiltInTone(e){const t=ue();if(!t||t.state!=="running")return;const i=e==="incoming"?1.25:1,n=e==="incoming"?[440,480]:[425],s=t.createGain(),o=t.currentTime;s.gain.setValueAtTime(1e-4,o),s.gain.exponentialRampToValueAtTime(.045,o+.025),s.gain.setValueAtTime(.045,o+i-.06),s.gain.exponentialRampToValueAtTime(1e-4,o+i),s.connect(t.destination);for(const r of n){const c=t.createOscillator();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+.02)}}}class pe extends EventTarget{options;socket=null;stopped=!1;reconnectAttempt=0;reconnectTimer=null;heartbeatTimer=null;heartbeatDeadline=0;currentCall=null;outgoingCallId=null;mount=null;terminalTimer=null;tokenRefreshTimer=null;tokenRequest=null;currentToken;currentTokenExpiresAt;callAudio;mediaClients=new Map;activeCallIds=new Set;seenInvitationIds=new Set;startCallRequest=null;lastDisconnect=null;constructor(e){if(super(),!e.token&&!e.tokenProvider)throw new Error("A client token or tokenProvider is required");if(e.token&&!e.token.startsWith("rtcc_"))throw new Error("A valid RTC client token is required");if(!e.signalingUrl&&!e.callActionsUrl&&!e.apiBaseUrl&&!globalThis.location?.origin)throw new Error("signalingUrl or callActionsUrl is required outside a browser");this.options=e,this.currentToken=e.token??null,this.currentTokenExpiresAt=e.tokenExpiresAt,this.callAudio=new Ve(e),this.mount=fe(e.mount),this.scheduleTokenRefresh()}connect(){if(!this.options.signalingUrl)throw new Error("signalingUrl is required to connect signaling");return this.stopped=!1,this.scheduleTokenRefresh(),this.openSocket("initial"),this}setMount(e){this.clearUi(),this.mount=fe(e),this.currentCall&&this.renderIncoming(this.currentCall)}attachMediaClient(e,t){if(!e.trim())throw new Error("callId is required");return this.mediaClients.set(e,t),()=>{this.mediaClients.get(e)===t&&this.mediaClients.delete(e)}}async startCall(e){if(this.startCallRequest)return this.startCallRequest;if(this.activeCallIds.size>0)throw new Error("Another call is already active");const t=e.targets?.length?e.targets:e.target?[e.target]:[];if(t.length===0)throw new Error("startCall requires at least one target");const i={mode:e.mode,targets:t,metadata:e.metadata,idempotencyKey:e.idempotencyKey},n=(async()=>{await this.callAudio.unlock(),this.callAudio.startRingback();let s;try{s=await this.request("/v1/rtc/client/calls",{method:"POST",headers:e.idempotencyKey?{"idempotency-key":e.idempotencyKey}:void 0,body:JSON.stringify(i)})}catch(o){throw this.callAudio.stop(),o}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})();this.startCallRequest=n;try{return await n}finally{this.startCallRequest===n&&(this.startCallRequest=null)}}async accept(e=this.currentCall?.id){if(!e)throw new Error("No incoming call is available");const t=await this.callAction(e,"accept");return this.activeCallIds.add(e),this.callAudio.stop(),this.dismiss(e),this.emit("callaccepted",t),t}async reject(e=this.currentCall?.id){if(!e)throw new Error("No incoming call is available");const t=await this.callAction(e,"reject");return this.releaseMedia(e),this.showTerminal(e,"Call declined"),this.emit("callrejected",t),t}async busy(e=this.currentCall?.id){if(!e)throw new Error("No incoming call is available");const t=await this.callAction(e,"busy");return this.releaseMedia(e),this.showTerminal(e,"Line busy"),t}async end(e){this.callAudio.stop();const t=await this.callAction(e,"end");return this.releaseMedia(e),this.showTerminal(e,"Call ended"),t}async cancel(e){this.callAudio.stop();const t=await this.callAction(e,"cancel");return this.releaseMedia(e),this.showTerminal(e,"Call cancelled"),t}handleIncomingCall(e){if(!e?.id||!e.caller?.id||!e.caller.name||e.mode!=="voice"&&e.mode!=="video")throw new Error("Incoming call payload is invalid");if(e.expiresAt&&Date.parse(e.expiresAt)<=Date.now()){this.emit("callexpired",{id:e.id,expiresAt:e.expiresAt});return}if(this.currentCall?.id===e.id){this.currentCall=e,this.emit("callrestored",e);return}if(this.seenInvitationIds.has(e.id)){this.emit("callrestored",e);return}this.seenInvitationIds.add(e.id),this.activeCallIds.add(e.id),this.currentCall=e,this.callAudio.startIncoming(),this.renderIncoming(e),this.emit("incomingcall",e)}handleCallUpdate(e){const t=e.id===this.currentCall?.id||e.id===this.outgoingCallId;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,Qe(e.status)),["rejected","busy","cancelled","ended","failed","missed"].includes(e.status)&&this.releaseMedia(e.id),this.emit("callupdated",e)}disconnect(){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();for(const e of this.mediaClients.keys())this.releaseMedia(e);this.activeCallIds.clear(),this.seenInvitationIds.clear()}destroy(){this.disconnect(),this.mount=null}async openSocket(e){if(!this.options.signalingUrl||this.stopped||this.socket?.readyState===WebSocket.OPEN||this.socket?.readyState===WebSocket.CONNECTING)return;let t;try{t=await this.resolveToken(e,e==="reconnect")}catch(s){this.emit("tokenerror",U(s)),!this.stopped&&this.options.reconnect!==!1&&this.scheduleReconnect();return}if(this.stopped||this.socket)return;const i=z(this.options.signalingUrl,globalThis.location?.href),n=new WebSocket(i,["rtc-client",t]);this.socket=n,n.addEventListener("open",()=>{const s=this.reconnectAttempt>0,o=this.lastDisconnect;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(()=>{if(n.readyState===WebSocket.OPEN){if(Date.now()>this.heartbeatDeadline){n.close(4e3,"Signaling heartbeat timed out");return}n.send("ping")}},2e4)}),n.addEventListener("message",s=>{this.heartbeatDeadline=Date.now()+45e3,this.handleSignal(String(s.data))}),n.addEventListener("close",s=>{if(this.socket!==n)return;this.socket=null,this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.heartbeatTimer=null;const o=!this.stopped&&this.options.reconnect!==!1,r={code:s.code,reason:s.reason,wasClean:s.wasClean,willReconnect:o,attempt:this.reconnectAttempt+1};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()}),n.addEventListener("error",()=>{const s={message:"RTC signaling connection failed",reconnecting:!this.stopped&&this.options.reconnect!==!1,attempt:this.reconnectAttempt+1};this.emit("signalingerror",s),this.reportSignalingDiagnostic({event:"error",attempt:s.attempt})})}handleSignal(e){let t;try{t=JSON.parse(e)}catch{return}const i=String(t.type??"");if(i==="call.incoming"&&t.call&&typeof t.call=="object"){this.handleIncomingCall(t.call);return}if(i==="call.updated"){const n=t.call;n?.id&&n.status&&this.handleCallUpdate({id:n.id,status:n.status});return}i==="presence"?this.emit("presence",{users:t.users??[]}):i!=="pong"&&this.emit(i||"signal",t)}async callAction(e,t){return this.request(`/v1/rtc/client/calls/${encodeURIComponent(e)}/${t}`,{method:"POST",body:"{}"})}async request(e,t,i=!1){const n=this.options.callActionsUrl??this.options.apiBaseUrl??this.options.signalingUrl??globalThis.location?.origin;if(!n)throw new Error("callActionsUrl is required for call actions");const s=_(n,globalThis.location?.href),o=await this.resolveToken("initial"),r=new Headers(t.headers);r.set("authorization",`Bearer ${o}`),r.set("content-type","application/json");const c=await fetch(new URL(e,s.origin),{...t,headers:r}),l=await c.json().catch(()=>({}));if(c.status===401&&this.options.tokenProvider&&!i)return await this.resolveToken("unauthorized",!0),this.request(e,t,!0);if(!c.ok){const d=l.error;throw new Error(typeof d=="string"?d:d?.message||`RTC API failed with HTTP ${c.status}`)}return l}renderIncoming(e){if(!this.mount)return;this.clearUi();const t=document.createElement("section");t.dataset.rtcIncomingCall=e.id,t.setAttribute("role","dialog"),t.setAttribute("aria-label",`Incoming ${e.mode} call from ${e.caller.name}`),Object.assign(t.style,{display:"grid",gap:"14px",padding:"20px",border:"1px solid #cbd8dc",borderRadius:"8px",background:"#fff",color:"#102027",boxShadow:"0 14px 38px #07101429",fontFamily:"system-ui, sans-serif"});const i=document.createElement("small");i.textContent=e.mode==="video"?"Incoming video call":"Incoming voice call",i.style.color="#5d7079";const n=document.createElement("strong");n.textContent=e.caller.name,n.style.fontSize="20px";const s=document.createElement("div");Object.assign(s.style,{display:"grid",gridTemplateColumns:"1fr 1fr",gap:"10px"});const o=ge("Decline","#fff","#a32920","#d9a09b"),r=ge("Answer","#fff","#08745f","#08745f");o.addEventListener("click",()=>{this.reject(e.id).catch(c=>this.emit("error",U(c)))}),r.addEventListener("click",()=>{this.accept(e.id).catch(c=>this.emit("error",U(c)))}),s.appendChild(o),s.appendChild(r),t.appendChild(i),t.appendChild(n),t.appendChild(s),this.mount.replaceChildren(t)}dismiss(e){e&&this.currentCall?.id!==e&&this.outgoingCallId!==e||(this.currentCall=null,(!e||this.outgoingCallId===e)&&(this.outgoingCallId=null),this.callAudio.stop(),this.clearUi())}showTerminal(e,t){if(this.callAudio.stop(),this.currentCall?.id===e&&(this.currentCall=null),this.outgoingCallId===e&&(this.outgoingCallId=null),!this.mount)return;this.clearUi();const i=document.createElement("section");i.dataset.rtcCallTerminal=e,i.setAttribute("role","status"),Object.assign(i.style,{display:"grid",placeItems:"center",gap:"10px",minHeight:"150px",padding:"20px",border:"1px solid #cbd8dc",borderRadius:"8px",background:"#fff",color:"#102027",boxShadow:"0 14px 38px #07101429",fontFamily:"system-ui, sans-serif"});const n=document.createElement("strong");n.textContent=t,n.style.fontSize="18px",i.appendChild(n),this.mount.replaceChildren(i);const s=Math.max(0,this.options.terminalStateDurationMs??1600);this.terminalTimer=setTimeout(()=>this.clearUi(),s)}clearUi(){this.terminalTimer&&clearTimeout(this.terminalTimer),this.terminalTimer=null,this.mount&&this.mount.replaceChildren()}scheduleReconnect(){this.reconnectTimer&&clearTimeout(this.reconnectTimer);const e=Math.min(3e4,1e3*2**Math.min(this.reconnectAttempt,5))+Math.round(Math.random()*300);this.reconnectAttempt+=1,this.reconnectTimer=setTimeout(()=>{this.openSocket("reconnect")},e)}async resolveToken(e,t=!1){if(!t&&this.currentToken&&!this.tokenNeedsRefresh())return this.currentToken;if(!this.options.tokenProvider){if(!this.currentToken)throw new Error("A valid RTC client token is required");return this.currentToken}return this.tokenRequest?this.tokenRequest:(this.tokenRequest=(async()=>{const i=await this.options.tokenProvider?.({reason:e,previousExpiresAt:this.currentTokenExpiresAt}),n=typeof i=="string"?{token:i}:i;if(!n?.token?.startsWith("rtcc_"))throw new Error("tokenProvider returned an invalid RTC client token");return this.currentToken=n.token,this.currentTokenExpiresAt=n.expiresAt,this.scheduleTokenRefresh(),this.emit("tokenrefreshed",{reason:e,expiresAt:n.expiresAt}),n.token})().finally(()=>{this.tokenRequest=null}),this.tokenRequest)}tokenNeedsRefresh(){if(!this.currentTokenExpiresAt)return!1;const e=Date.parse(this.currentTokenExpiresAt)-Date.now();return Number.isFinite(e)?e<=this.refreshSkew(e):!0}refreshSkew(e){const t=Math.max(5e3,this.options.tokenRefreshSkewMs??3e5);return Math.min(t,Math.max(5e3,e*.2))}scheduleTokenRefresh(){if(this.tokenRefreshTimer&&clearTimeout(this.tokenRefreshTimer),this.tokenRefreshTimer=null,this.stopped||!this.options.tokenProvider||!this.currentTokenExpiresAt)return;const e=Date.parse(this.currentTokenExpiresAt)-Date.now();if(!Number.isFinite(e))return;const t=Math.max(0,e-this.refreshSkew(e));this.tokenRefreshTimer=setTimeout(()=>{this.tokenRefreshTimer=null,this.resolveToken("expiring",!0).catch(i=>{this.emit("tokenerror",U(i)),this.stopped||(this.tokenRefreshTimer=setTimeout(()=>this.scheduleTokenRefresh(),3e4))})},Math.min(t,2147e6))}releaseMedia(e){this.activeCallIds.delete(e),this.seenInvitationIds.delete(e);const t=this.mediaClients.get(e);t&&(this.mediaClients.delete(e),t.leave().catch(()=>{}).finally(()=>t.destroy()))}reportSignalingDiagnostic(e){this.options.reportSignalingDiagnostics===!1||this.stopped||this.request("/v1/rtc/client/signaling-events",{method:"POST",body:JSON.stringify({...e,occurredAt:new Date().toISOString()})}).catch(t=>this.emit("diagnosticserror",U(t)))}emit(e,t){const i={type:e,detail:t,timestamp:new Date().toISOString()};this.dispatchEvent(new CustomEvent(e,{detail:i})),this.options.onEvent?.(i)}}function Ue(a){const e=new pe(a);return a.autoConnect!==!1&&a.signalingUrl&&e.connect(),e}function fe(a){return a?typeof a=="string"?document.querySelector(a):a:null}function ge(a,e,t,i){const n=document.createElement("button");return n.type="button",n.textContent=a,Object.assign(n.style,{minHeight:"44px",border:`1px solid ${i}`,borderRadius:"6px",background:t,color:e,cursor:"pointer",font:"inherit",fontWeight:"700"}),n}function Qe(a){return{rejected:"Call declined",busy:"Line busy",cancelled:"Call cancelled",ended:"Call ended",failed:"Call failed",missed:"No answer"}[a]??"Call ended"}function ye(a){return{id:a.participantId,name:a.displayName,role:"participant"}}async function Be(a,e){const t=await a.getStats(),i=new Map,n=new Map;let s=0,o=0,r=0,c=0,l=0,d=0,u=0,k=0,f=0,v=0,R=0,y=0,w=0,p=0,I=0,A=null,D=null,E=null,L=null,S=null,M=null;t.forEach(g=>{n.set(g.id,g),g.type==="codec"&&i.set(g.id,String(g.mimeType??""))}),t.forEach(g=>{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)),k=Math.max(k,Number(g.frameWidth??0)),R=Math.max(R,Number(g.frameHeight??0)),y+=Number(g.framesDropped??0),w+=Number(g.freezeCount??0),p+=Number(g.totalFreezesDuration??0),I+=Number(g.concealedSamples??0),S||=i.get(String(g.codecId??""))??null),g.type==="outbound-rtp"&&!g.isRemote&&(f=Math.max(f,Number(g.frameWidth??0)),v=Math.max(v,Number(g.frameHeight??0)),o+=Number(g.bytesSent??0),M||=g.qualityLimitationReason?String(g.qualityLimitationReason):null,S||=i.get(String(g.codecId??""))??null),g.type==="candidate-pair"&&(g.selected||g.nominated)&&g.state==="succeeded"){A=Number.isFinite(g.currentRoundTripTime)?Number(g.currentRoundTripTime):A,D=Number.isFinite(g.availableOutgoingBitrate)?Number(g.availableOutgoingBitrate):D;const q=n.get(g.localCandidateId);E=q?.candidateType?String(q.candidateType):E,L=q?.protocol?String(q.protocol):L}});const m=Date.now(),h=e?Math.max(.001,(m-e.timestamp)/1e3):0,T=e?Math.max(0,Math.round((s-e.inboundBytes)*8/h)):null,P=e?Math.max(0,Math.round((o-e.outboundBytes)*8/h)):null,C=r+c,N=e?Math.max(0,r-e.packetsLost):r,Q=e?Math.max(0,c-e.packetsReceived):c,j=N+Q;return{counters:{timestamp:m,inboundBytes:s,outboundBytes:o,packetsLost:r,packetsReceived:c},sample:{rttMs:A===null?null:A*1e3,jitterMs:d?l/d*1e3:null,packetLossPct:j?N/j*100:null,packetLossCumulativePct:C?r/C*100:null,packetsLost:r,packetsReceived:c,inboundBitrateBps:T,outboundBitrateBps:P,availableOutgoingBitrateBps:D,framesPerSecond:u||null,frameWidth:k||null,frameHeight:R||null,inboundFrameWidth:k||null,inboundFrameHeight:R||null,outboundFrameWidth:f||null,outboundFrameHeight:v||null,framesDropped:y,freezeCount:w,freezeDurationMs:Math.round(p*1e3),concealedSamples:I,codec:S,transportProtocol:L,candidateType:E,qualityLimitationReason:M}}}function xe(a){let e=a.mediaScore==null?5:Math.round(Se(Number(a.mediaScore),0,5));const t=Z(a.packetLossPct)??0,i=Z(a.rttMs)??0,n=Z(a.jitterMs)??0,s=String(a.connectionState??"unknown").toLowerCase();["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));const o=Number(Se(4.5-t*.09-i*.0011-n*.006,1,4.5).toFixed(2));return{networkScore:e,estimatedMos:o}}function ve(){return navigator.connection??null}function Fe(){const a=Number(navigator.deviceMemory);return Number.isFinite(a)?a:null}function Z(a){const e=Number(a);return Number.isFinite(e)?e:null}function Se(a,e,t){return Math.min(t,Math.max(e,a))}function U(a){return a instanceof Error?{name:a.name,message:a.message}:{name:"ServiceError",message:String(a??"Service request failed")}}function ke(a,e){return a instanceof Error?a:new Error(e)}const we="1.3.22";b.AVATAR_TUNING_DEFAULTS=Y,b.BACKGROUND_PRESETS=re,b.OVERLAY_EFFECTS=oe,b.RtcClient=he,b.RtcIncomingClient=pe,b.SPEAKER_NOISE_FLOOR=.02,b.SPEAKER_TAKEOVER_RATIO=1.4,b.createAvatarPipeline=Ne,b.createCallRecorder=Oe,b.createPictureInPictureController=De,b.createRtcClient=je,b.createRtcIncomingClient=Ue,b.createVideoEffectsPipeline=qe,b.drawAvatar=de,b.headPoseFromMatrix=ce,b.neutralRig=J,b.pickActiveSpeaker=H,b.rigFromBlendshapes=le,b.scenePainters=$,b.smoothRig=G,b.stylizedAvatarRenderer=K,b.unlockRtcCallAudio=me,b.version=we,Object.defineProperty(b,Symbol.toStringTag,{value:"Module"})}));
|
|
1
|
+
(function(b,Q){typeof exports=="object"&&typeof module<"u"?Q(exports):typeof define=="function"&&define.amd?define(["exports"],Q):(b=typeof globalThis<"u"?globalThis:b||self,Q(b.StreamingCdnRtc={}))})(this,(function(b){"use strict";function H(a,e,t=.02,i=1.4){const n=a.filter(r=>r.level>=t);if(n.length===0)return null;const s=n.reduce((r,c)=>c.level>r.level?c:r),o=n.find(r=>r.participantId===e);return o?s.participantId===e?e:s.level>=o.level*i?s.participantId:e:s.participantId}function ie(a){const e=[];for(const t of a){if(String(t.type??"")!=="inbound-rtp"||String(t.kind??t.mediaType??"")!=="audio")continue;const i=Number(t.audioLevel??NaN);Number.isFinite(i)&&e.push({mid:typeof t.mid=="string"?t.mid:null,level:i})}return e}const V={audio:{active:!1,maxBitrate:0,maxFramerate:0,scaleResolutionDownBy:4},low:{active:!0,maxBitrate:35e4,maxFramerate:15,scaleResolutionDownBy:4},medium:{active:!0,maxBitrate:11e5,maxFramerate:24,scaleResolutionDownBy:2},high:{active:!0,maxBitrate:28e5,maxFramerate:30,scaleResolutionDownBy:1}},Ce={warmupSamples:3,poorSamplesToStepDown:2,goodSamplesToStepUp:6,criticalLossPct:8,criticalRttMs:800,poorLossPct:3,poorRttMs:350,goodLossPct:1,goodRttMs:250,sustainRatio:.8,bandwidthHeadroomRatio:1.25};function Re(a){const e=String(a??"").trim().toLowerCase();return!e||e==="none"?"":e==="cpu"||e==="bandwidth"?e:"other"}function Ee(a,e,t,i,n={}){const s={...Ce,...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,k=Re(e.qualityLimitationReason),f=e.packetLossPct??0,v=e.rttMs??0,R=e.availableOutgoingBitrateBps,y=V[a].maxBitrate,w=f>=s.criticalLossPct||v>=s.criticalRttMs||k==="cpu",p=k==="bandwidth"&&(R==null||R<y*s.bandwidthHeadroomRatio),E=w||f>=s.poorLossPct||v>=s.poorRttMs||p,A=R==null||R>=y*s.sustainRatio,D=f<s.goodLossPct&&v<s.goodRttMs&&!k&&A,I=E?t+1:0,L=D?i+1:0,S={quality:a,poorSamples:I,goodSamples:L,samplesSeen:d,changed:!1};return l>c?{quality:o[c],poorSamples:0,goodSamples:0,samplesSeen:d,changed:!0}:u?S:w&&l>0?{quality:o[l-1],poorSamples:0,goodSamples:0,samplesSeen:d,changed:!0}:I>=s.poorSamplesToStepDown&&l>0?{quality:o[l-1],poorSamples:0,goodSamples:0,samplesSeen:d,changed:!0}:L>=s.goodSamplesToStepUp&&l<c?{quality:o[l+1],poorSamples:0,goodSamples:0,samplesSeen:d,changed:!0}:S}function Ie(a,e){const t=a instanceof Map?a:new Map(a);let i=0,n=0,s=null,o=null,r=null;for(const d of t.values()){const u=String(d.type??"");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"){n+=Number(d.packetsLost??0);const k=Number(d.roundTripTime??NaN);Number.isFinite(k)&&(o=k*1e3)}if(u==="candidate-pair"&&(d.nominated===!0||String(d.state??"")==="succeeded")){const k=Number(d.currentRoundTripTime??NaN);o==null&&Number.isFinite(k)&&(o=k*1e3);const f=Number(d.availableOutgoingBitrate??NaN);Number.isFinite(f)&&(r=f)}}const c={packetsSent:i,packetsLost:n,timestamp:Date.now()};let l=null;if(e){const d=i-e.packetsSent,u=n-e.packetsLost;d>0&&u>=0&&(l=u/(d+u)*100)}return{sample:{packetLossPct:l,rttMs:o,availableOutgoingBitrateBps:r,qualityLimitationReason:s},counters:c}}function z(a,e){const t=new URL(String(a),e);return t.protocol==="https:"?t.protocol="wss:":t.protocol==="http:"&&(t.protocol="ws:"),t}function _(a,e){const t=new URL(String(a),e);return t.protocol==="wss:"?t.protocol="https:":t.protocol==="ws:"&&(t.protocol="http:"),t}const Me={audio:{active:!1,maxBitrate:0,maxFramerate:0,scaleResolutionDownBy:4}},Pe={active:!0,maxBitrate:4e6,maxFramerate:30,scaleResolutionDownBy:1};class B{options;socket=null;localStream=null;screenStream=null;peers=new Map;joinPromise=null;resolveJoin=null;rejectJoin=null;joined=!1;stopped=!1;reconnectAttempt=0;reconnectTimer=null;heartbeatTimer=null;qualityTimer=null;speakerTimer=null;activeSpeaker=null;heartbeatDeadline=0;quality;preferredVideoCodec;signalQueue=Promise.resolve();socketGeneration=0;constructor(e){this.options=e,this.localStream=e.localStream??null,this.quality=e.quality??"high",this.preferredVideoCodec=e.preferredVideoCodec??"auto"}async initialize(){if(!globalThis.RTCPeerConnection||!globalThis.WebSocket)throw new Error("This browser does not support WebRTC")}async join(){if(!this.joined){if(this.joinPromise)return this.joinPromise;this.joinPromise=this.connect();try{await this.joinPromise}finally{this.joinPromise=null}}}async leave(){this.stopped=!0,this.clearConnectionTimers(),this.send({type:"rtc.leave"}),this.joined=!1,this.socket?.close(1e3,"Participant left"),this.socket=null;for(const e of this.peers.values())e.connection.close();this.peers.clear(),this.stopStream(this.localStream),this.stopStream(this.screenStream),this.localStream=null,this.screenStream=null}setMuted(e){for(const t of this.localStream?.getAudioTracks()??[])t.enabled=!e}async setCameraEnabled(e){if(e&&!this.localStream?.getVideoTracks().length){const i=(await navigator.mediaDevices.getUserMedia({video:{width:{ideal:1920},height:{ideal:1080},frameRate:{ideal:30,max:30}}})).getVideoTracks()[0];if(!i)throw new Error("Camera is unavailable");this.localStream||(this.localStream=new MediaStream),this.localStream.addTrack(i),this.screenStream?.getVideoTracks().some(n=>n.readyState==="live")||await this.replaceVideoTrack(i);return}for(const t of this.localStream?.getVideoTracks()??[])t.enabled=e}async setScreenShareEnabled(e){if(!e){const n=this.screenStream;this.screenStream=null,this.stopStream(n);const s=this.localStream?.getVideoTracks()[0]??null;await this.replaceVideoTrack(s),this.options.onEvent("screensharechange",{enabled:!1,stream:null});return}if(this.screenStream?.getVideoTracks()[0]?.readyState==="live")return;const t=await navigator.mediaDevices.getDisplayMedia({video:{width:{ideal:1920},height:{ideal:1080},frameRate:{ideal:30,max:30}},audio:!1}),i=t.getVideoTracks()[0];if(!i)throw new Error("Screen sharing is unavailable");i.contentHint="detail",this.screenStream=t,i.addEventListener("ended",()=>{this.screenStream?.getVideoTracks()[0]?.id===i.id&&this.setScreenShareEnabled(!1)});try{await this.replaceVideoTrack(i),this.options.onEvent("screensharechange",{enabled:!0,stream:t})}catch(n){throw this.screenStream=null,this.stopStream(t),n}}async setDevice(e){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];if(!n)throw new Error("Selected media device is unavailable");const s=e.kind==="audioinput"?"audio":"video",o=this.localStream?.getTracks().find(r=>r.kind===s);if(this.localStream||(this.localStream=new MediaStream),o&&(this.localStream.removeTrack(o),o.stop()),this.localStream.addTrack(n),s==="video"){this.screenStream?.getVideoTracks().some(r=>r.readyState==="live")||await this.replaceVideoTrack(n);return}for(const r of this.peers.values()){const c=r.connection.getSenders().find(l=>l.track?.kind===s);c?await c.replaceTrack(n):r.connection.addTrack(n,this.localStream)}await this.applyVideoQuality()}async setQuality(e){this.quality=e;for(const t of this.localStream?.getVideoTracks()??[])t.enabled=e!=="audio";for(const t of this.peers.values())t.quality=B.clampToCeiling(t.quality,e),t.poorSamples=0,t.goodSamples=0;await this.applyVideoQuality()}static clampToCeiling(e,t){if(t==="audio")return e;const i=["low","medium","high"];return i.indexOf(e)>i.indexOf(t)?t:e}async setPreferredVideoCodec(e){this.preferredVideoCodec=e;for(const t of this.peers.values())this.applyCodecPreference(t.connection);await this.renegotiateAll()}getParticipants(){return[...this.peers.values()].map(e=>e.identity)}getLocalStream(){return this.localStream}async setVideoTrack(e){if(e.kind!=="video")throw new Error("A video track is required");this.localStream||(this.localStream=new MediaStream);const t=this.localStream.getVideoTracks()[0];t?.id!==e.id&&(t&&this.localStream.removeTrack(t),this.localStream.addTrack(e)),await this.replaceVideoTrack(e),this.options.onEvent("localstream",{stream:this.localStream,track:e})}getRemoteStreams(){return[...this.peers.values()].map(e=>({participant:e.identity,stream:e.stream}))}getPrimaryPeerConnection(){return this.peers.values().next().value?.connection??null}sendChat(e,t){this.send({type:"rtc.chat",message:e,participantIds:t??[]})}sendControl(e,t={}){this.send({type:"rtc.control",action:e,detail:t})}async connect(){const{credential:e}=this.options;if(!e.signalingToken||!e.signalingUrl||!e.roomId||!e.participantId)throw new Error("A room signaling credential is required");this.stopped=!1,!this.localStream&&(this.options.audio||this.options.video)&&(this.localStream=await navigator.mediaDevices.getUserMedia({audio:this.options.audio,video:this.options.video})),this.localStream&&this.options.onEvent("localstream",{stream:this.localStream}),await this.openSocket(!1)}async openSocket(e){const{credential:t}=this.options,i=z(t.signalingUrl,globalThis.location?.href),n=new Promise((c,l)=>{this.resolveJoin=c,this.rejectJoin=l}),s=new WebSocket(i,["rtc-client",t.signalingToken]),o=++this.socketGeneration;this.socket=s;const r=setTimeout(()=>this.rejectJoin?.(new Error("RTC signaling timed out")),15e3);s.addEventListener("open",()=>{this.heartbeatDeadline=Date.now()+45e3,this.startHeartbeat(s),this.startQualitySampling(),this.send({type:"rtc.room.join"})}),s.addEventListener("message",c=>{const l=String(c.data);this.signalQueue=this.signalQueue.then(async()=>{this.socket!==s||this.socketGeneration!==o||await this.handleSignal(l)}).catch(d=>{const u=d instanceof Error?d.message:"RTC signaling processing failed";this.options.onEvent("signalingerror",{message:u}),this.joined||this.rejectJoin?.(new Error(u))})}),s.addEventListener("error",()=>this.rejectJoin?.(new Error("RTC signaling connection failed"))),s.addEventListener("close",c=>{if(this.socket!==s)return;this.socket=null,this.stopHeartbeat(),this.stopQualitySampling(),!this.joined&&!e&&this.rejectJoin?.(new Error(c.reason||"RTC signaling connection closed"));const l=!this.stopped&&(this.joined||e);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})});try{await n,this.joined=!0,this.options.onEvent("connected",{participantId:t.participantId,resumed:e})}finally{clearTimeout(r),this.resolveJoin=null,this.rejectJoin=null}}async handleSignal(e){let t;try{t=JSON.parse(e)}catch{return}const i=String(t.type??"");if(i!=="ready"){if(i==="pong"){this.heartbeatDeadline=Date.now()+45e3;return}if(i==="rtc.room.peers"){this.reconnectAttempt=0;const n=Array.isArray(t.peers)?t.peers:[];for(const s of n)this.ensurePeer(s);this.resolveJoin?.();for(const s of n)this.shouldInitiateOffer(s)&&await this.createOffer(s);return}if(i==="rtc.room.join"&&t.sender){this.options.onEvent("participantjoined",t.sender),this.ensurePeer(t.sender),this.shouldInitiateOffer(t.sender)&&await this.createOffer(t.sender);return}if(i==="rtc.offer"&&t.sender&&t.description){const n=this.ensurePeer(t.sender);n.offerTask&&await n.offerTask;const s=n.connection.signalingState!=="stable",o=!this.shouldInitiateOffer(t.sender);if(s&&!o)return;s&&await n.connection.setLocalDescription({type:"rollback"}),await n.connection.setRemoteDescription(t.description);const r=await n.connection.createAnswer();await n.connection.setLocalDescription(r),this.send({type:"rtc.answer",targetParticipantId:t.sender.participantId,negotiationId:t.negotiationId,description:n.connection.localDescription});return}if(i==="rtc.answer"&&t.sender&&t.description){const n=this.peers.get(t.sender.participantId);if(!n||n.connection.signalingState!=="have-local-offer"||t.negotiationId&&n.activeNegotiationId&&t.negotiationId!==n.activeNegotiationId)return;await n.connection.setRemoteDescription(t.description),n.activeNegotiationId=null;return}if(i==="rtc.ice"&&t.sender&&t.candidate){await this.ensurePeer(t.sender).connection.addIceCandidate(t.candidate).catch(()=>{});return}if((i==="rtc.peer.left"||i==="rtc.leave")&&t.sender){this.removePeer(t.sender.participantId);return}i==="rtc.chat"?this.options.onEvent("chatupdate",t):i==="rtc.control"&&this.options.onEvent("control",t)}}ensurePeer(e){const t=this.peers.get(e.participantId);if(t)return t;const i=new RTCPeerConnection({iceServers:this.options.credential.iceServers??[],iceTransportPolicy:this.options.iceTransportPolicy??"all"}),n=new MediaStream,s={identity:e,connection:i,stream:n,makingOffer:!1,offerTask:null,activeNegotiationId:null,quality:B.startingQuality(this.quality),poorSamples:0,goodSamples:0,samplesSeen:0,counters:null};this.peers.set(e.participantId,s);for(const c of this.localStream?.getAudioTracks()??[])i.addTrack(c,this.localStream);const o=this.screenStream?.getVideoTracks().some(c=>c.readyState==="live")?this.screenStream:this.localStream,r=o?.getVideoTracks()[0];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=>{c.candidate&&this.send({type:"rtc.ice",targetParticipantId:e.participantId,candidate:c.candidate.toJSON()})}),i.addEventListener("track",c=>{n.getTracks().some(l=>l.id===c.track.id)||n.addTrack(c.track),this.options.onEvent("remotestream",{participant:e,stream:n,track:c.track})}),i.addEventListener("connectionstatechange",()=>{["failed","closed"].includes(i.connectionState)&&this.removePeer(e.participantId),this.options.onEvent("peerconnectionstate",{participant:e,state:i.connectionState})}),s}async createOffer(e){const t=this.ensurePeer(e);if(t.offerTask)return t.offerTask;if(t.connection.signalingState!=="stable")return;const i=this.performOffer(t);t.offerTask=i;try{await i}finally{t.offerTask===i&&(t.offerTask=null)}}async performOffer(e){e.makingOffer=!0;try{const t=await e.connection.createOffer({offerToReceiveAudio:this.options.receiveAudio,offerToReceiveVideo:this.options.receiveVideo});if(e.connection.signalingState!=="stable")return;await e.connection.setLocalDescription(t);const i=ne();e.activeNegotiationId=i,this.send({type:"rtc.offer",targetParticipantId:e.identity.participantId,negotiationId:i,description:e.connection.localDescription})}finally{e.makingOffer=!1}}shouldInitiateOffer(e){return this.options.credential.participantId.localeCompare(e.participantId)<0}async renegotiateAll(){for(const e of this.peers.values())await this.createOffer(e.identity)}async replaceVideoTrack(e){let t=!1;for(const i of this.peers.values()){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);if(n){const o=e?this.options.receiveVideo?"sendrecv":"sendonly":this.options.receiveVideo?"recvonly":"inactive";s&&s.direction!==o&&(s.direction=o,t=!0),await n.replaceTrack(e),i.videoSender=n,s&&(i.videoTransceiver=s)}else e&&(i.videoSender=i.connection.addTrack(e,this.screenStream??this.localStream??new MediaStream([e])),t=!0)}t&&await this.renegotiateAll(),await this.applyVideoQuality()}async applyVideoQuality(e){const t=e?[...this.peers.values()].filter(i=>i.connection===e):[...this.peers.values()];for(const i of t)await this.applyPeerVideoQuality(i)}async applyPeerVideoQuality(e){const t=this.screenStream?.getVideoTracks()[0]?.id;for(const i of e.connection.getSenders().filter(n=>n.track?.kind==="video")){const n=!!(t&&i.track?.id===t),s=n?Pe:this.quality==="audio"?Me.audio:V[e.quality],o=i.getParameters(),r=o.encodings?.length?o.encodings:[{}];o.encodings=r.map(c=>({...c,...s})),o.degradationPreference=n?"maintain-resolution":"maintain-framerate",await i.setParameters(o)}}startQualitySampling(){this.stopQualitySampling(),this.qualityTimer=setInterval(()=>{this.samplePeerQuality()},5e3),this.speakerTimer=setInterval(()=>{this.sampleActiveSpeaker()},1e3)}stopQualitySampling(){this.qualityTimer&&clearInterval(this.qualityTimer),this.qualityTimer=null,this.speakerTimer&&clearInterval(this.speakerTimer),this.speakerTimer=null}async sampleActiveSpeaker(){const e=[];for(const i of[...this.peers.values()])if(i.connection.connectionState==="connected")try{const n=await i.connection.getStats();for(const s of ie(n.values()))e.push({participantId:i.identity.participantId,level:s.level})}catch{}const t=H(e,this.activeSpeaker);t!==this.activeSpeaker&&(this.activeSpeaker=t,this.options.onEvent("activespeaker",{participantId:t}))}async samplePeerQuality(){if(this.quality!=="audio")for(const e of[...this.peers.values()]){if(e.connection.connectionState!=="connected")continue;let t;try{t=await e.connection.getStats()}catch{continue}const{sample:i,counters:n}=Ie(t,e.counters);e.counters=n;const s=Ee(e.quality,i,e.poorSamples,e.goodSamples,{samplesSeen:e.samplesSeen,ceiling:this.quality});e.poorSamples=s.poorSamples,e.goodSamples=s.goodSamples,e.samplesSeen=s.samplesSeen,s.changed&&(e.quality=s.quality,await this.applyPeerVideoQuality(e).catch(()=>{}),this.options.onEvent("peerquality",{participantId:e.identity.participantId,quality:s.quality}))}}static startingQuality(e){return e==="low"?"low":"medium"}applyCodecPreference(e){if(this.preferredVideoCodec==="auto"||!globalThis.RTCRtpReceiver?.getCapabilities)return;const t=RTCRtpReceiver.getCapabilities("video")?.codecs??[],i=`video/${this.preferredVideoCodec}`,n=[...t.filter(s=>s.mimeType.toLowerCase()===i),...t.filter(s=>s.mimeType.toLowerCase()!==i)];for(const s of e.getTransceivers())s.receiver.track.kind==="video"&&typeof s.setCodecPreferences=="function"&&s.setCodecPreferences(n)}removePeer(e){const t=this.peers.get(e);t&&(t.connection.close(),this.peers.delete(e),this.options.onEvent("participantleft",t.identity))}resetPeers(){for(const e of this.peers.values())e.connection.close();this.peers.clear()}startHeartbeat(e){this.stopHeartbeat(),this.stopQualitySampling(),this.heartbeatTimer=setInterval(()=>{if(!(this.socket!==e||e.readyState!==WebSocket.OPEN)){if(Date.now()>this.heartbeatDeadline){e.close(4e3,"Signaling heartbeat timed out");return}e.send("ping")}},2e4)}stopHeartbeat(){this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.heartbeatTimer=null}clearConnectionTimers(){this.stopHeartbeat(),this.stopQualitySampling(),this.reconnectTimer&&clearTimeout(this.reconnectTimer),this.reconnectTimer=null}scheduleReconnect(){if(this.reconnectTimer||this.stopped)return;const e=Math.min(3e4,1e3*2**Math.min(this.reconnectAttempt,5))+Math.round(Math.random()*300);this.reconnectAttempt+=1,this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.openSocket(!0).catch(t=>{this.options.onEvent("signalingerror",{message:t instanceof Error?t.message:"RTC signaling connection failed"}),this.scheduleReconnect()})},e)}send(e){this.socket?.readyState===WebSocket.OPEN&&this.socket.send(JSON.stringify({clientMessageId:e.clientMessageId??ne(),...e}))}stopStream(e){for(const t of e?.getTracks()??[])t.stop()}}function ne(){return globalThis.crypto?.randomUUID?.()??`${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}const W={active:!0,maxBitrate:4e6,maxFramerate:30,scaleResolutionDownBy:1},ae=[{rid:"a",maxBitrate:V.high.maxBitrate,maxFramerate:V.high.maxFramerate},{rid:"b",maxBitrate:V.medium.maxBitrate,maxFramerate:V.medium.maxFramerate,scaleResolutionDownBy:2},{rid:"c",maxBitrate:V.low.maxBitrate,maxFramerate:V.low.maxFramerate,scaleResolutionDownBy:4}],se={high:"a",medium:"b",low:"c"};class Ae{options;apiBase;socket=null;connection=null;sessionId=null;localStream=null;screenStream=null;remotes=new Map;midToParticipant=new Map;pendingAnnouncements=new Map;videoSender=null;joined=!1;stopped=!1;reconnectAttempt=0;reconnectTimer=null;heartbeatTimer=null;heartbeatDeadline=0;speakerTimer=null;activeSpeaker=null;quality;resolveJoin=null;rejectJoin=null;signalQueue=Promise.resolve();negotiationQueue=Promise.resolve();socketGeneration=0;constructor(e){this.options=e,this.localStream=e.localStream??null,this.quality=e.quality??"high",this.apiBase=_(e.credential.signalingUrl,globalThis.location?.href).origin}async initialize(){if(!globalThis.RTCPeerConnection||!globalThis.WebSocket)throw new Error("This browser does not support WebRTC")}async join(){if(this.joined)return;const{credential:e}=this.options;if(!e.signalingToken||!e.signalingUrl||!e.roomId||!e.participantId)throw new Error("A room signaling credential is required");this.stopped=!1,!this.localStream&&(this.options.audio||this.options.video)&&(this.localStream=await navigator.mediaDevices.getUserMedia({audio:this.options.audio,video:this.options.video})),this.localStream&&this.options.onEvent("localstream",{stream:this.localStream}),await this.openSocket(!1),await this.publish(),this.startSpeakerSampling(),this.announceTracks();const t=[...this.pendingAnnouncements.values()];this.pendingAnnouncements.clear();for(const i of t)await this.handleTrackAnnouncement(i.sender,i.detail)}async leave(){this.stopped=!0,this.clearTimers(),this.send({type:"rtc.leave"}),this.joined=!1,this.socket?.close(1e3,"Participant left"),this.socket=null,await this.closePublishedTracks().catch(()=>{}),this.connection?.close(),this.connection=null,this.sessionId=null,this.remotes.clear(),this.midToParticipant.clear(),this.stopStream(this.localStream),this.stopStream(this.screenStream),this.localStream=null,this.screenStream=null}setMuted(e){for(const t of this.localStream?.getAudioTracks()??[])t.enabled=!e}async setCameraEnabled(e){if(e&&!this.localStream?.getVideoTracks().length){const i=(await navigator.mediaDevices.getUserMedia({video:{width:{ideal:1920},height:{ideal:1080},frameRate:{ideal:30,max:30}}})).getVideoTracks()[0];if(!i)throw new Error("Camera is unavailable");this.localStream||(this.localStream=new MediaStream),this.localStream.addTrack(i),this.screenStream?.getVideoTracks().some(n=>n.readyState==="live")||await this.replaceVideoTrack(i);return}for(const t of this.localStream?.getVideoTracks()??[])t.enabled=e}async setScreenShareEnabled(e){if(!e){const n=this.screenStream;this.screenStream=null,this.stopStream(n);const s=this.localStream?.getVideoTracks()[0]??null;await this.replaceVideoTrack(s),this.options.onEvent("screensharechange",{enabled:!1,stream:null});return}if(this.screenStream?.getVideoTracks()[0]?.readyState==="live")return;const t=await navigator.mediaDevices.getDisplayMedia({video:{width:{ideal:1920},height:{ideal:1080},frameRate:{ideal:30,max:30}},audio:!1}),i=t.getVideoTracks()[0];if(!i)throw new Error("Screen sharing is unavailable");i.contentHint="detail",this.screenStream=t,i.addEventListener("ended",()=>{this.screenStream?.getVideoTracks()[0]?.id===i.id&&this.setScreenShareEnabled(!1)});try{await this.replaceVideoTrack(i),this.options.onEvent("screensharechange",{enabled:!0,stream:t})}catch(n){throw this.screenStream=null,this.stopStream(t),n}}async setDevice(e){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];if(!n)throw new Error("Selected media device is unavailable");const s=e.kind==="audioinput"?"audio":"video",o=this.localStream?.getTracks().find(c=>c.kind===s);if(this.localStream||(this.localStream=new MediaStream),o&&(this.localStream.removeTrack(o),o.stop()),this.localStream.addTrack(n),s==="video"){this.screenStream?.getVideoTracks().some(c=>c.readyState==="live")||await this.replaceVideoTrack(n);return}const r=this.connection?.getSenders().find(c=>c.track?.kind==="audio");r&&await r.replaceTrack(n)}async setQuality(e){this.quality=e;for(const t of this.localStream?.getVideoTracks()??[])t.enabled=e!=="audio";await this.applyVideoQuality()}async setPreferredVideoCodec(e){}async setVideoTrack(e){if(e.kind!=="video")throw new Error("A video track is required");this.localStream||(this.localStream=new MediaStream);const t=this.localStream.getVideoTracks()[0];t?.id!==e.id&&(t&&this.localStream.removeTrack(t),this.localStream.addTrack(e)),await this.replaceVideoTrack(e),this.options.onEvent("localstream",{stream:this.localStream,track:e})}getParticipants(){return[...this.remotes.values()].map(e=>e.identity)}getLocalStream(){return this.localStream}getRemoteStreams(){return[...this.remotes.values()].map(e=>({participant:e.identity,stream:e.stream}))}getPrimaryPeerConnection(){return this.connection}sendChat(e,t){this.send({type:"rtc.chat",message:e,participantIds:t??[]})}sendControl(e,t={}){this.send({type:"rtc.control",action:e,detail:t})}async openSocket(e){const{credential:t}=this.options,i=z(t.signalingUrl,globalThis.location?.href),n=new Promise((c,l)=>{this.resolveJoin=c,this.rejectJoin=l}),s=new WebSocket(i,["rtc-client",t.signalingToken]),o=++this.socketGeneration;this.socket=s;const r=setTimeout(()=>this.rejectJoin?.(new Error("RTC signaling timed out")),15e3);s.addEventListener("open",()=>{this.heartbeatDeadline=Date.now()+45e3,this.startHeartbeat(s),this.send({type:"rtc.room.join"})}),s.addEventListener("message",c=>{const l=String(c.data);this.signalQueue=this.signalQueue.then(async()=>{this.socket!==s||this.socketGeneration!==o||await this.handleSignal(l)}).catch(d=>{const u=d instanceof Error?d.message:"RTC signaling processing failed";this.options.onEvent("signalingerror",{message:u}),this.joined||this.rejectJoin?.(new Error(u))})}),s.addEventListener("error",()=>this.rejectJoin?.(new Error("RTC signaling connection failed"))),s.addEventListener("close",c=>{if(this.socket!==s)return;this.socket=null,this.stopHeartbeat(),!this.joined&&!e&&this.rejectJoin?.(new Error(c.reason||"RTC signaling connection closed"));const l=!this.stopped&&(this.joined||e);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})});try{await n,this.joined=!0,this.options.onEvent("connected",{participantId:t.participantId,resumed:e})}finally{clearTimeout(r),this.resolveJoin=null,this.rejectJoin=null}}async handleSignal(e){let t;try{t=JSON.parse(e)}catch{return}const i=String(t.type??"");if(i!=="ready"){if(i==="pong"){this.heartbeatDeadline=Date.now()+45e3;return}if(i==="rtc.room.peers"){this.reconnectAttempt=0,this.resolveJoin?.();return}if(i==="rtc.room.join"&&t.sender){this.options.onEvent("participantjoined",t.sender),this.sessionId&&this.announceTracks();return}if((i==="rtc.peer.left"||i==="rtc.leave")&&t.sender){this.removeRemote(t.sender.participantId);return}if(i==="rtc.control"){if(String(t.action??"")==="sfu.tracks"&&t.sender){await this.handleTrackAnnouncement(t.sender,t.detail);return}this.options.onEvent("control",t);return}i==="rtc.chat"&&this.options.onEvent("chatupdate",t)}}async publish(){const e=[...this.localStream?.getAudioTracks()??[],...this.localStream?.getVideoTracks()??[]],t=new RTCPeerConnection({iceServers:this.options.credential.iceServers??[],iceTransportPolicy:this.options.iceTransportPolicy??"all"});this.connection=t,t.addEventListener("track",r=>this.handleRemoteTrack(r)),t.addEventListener("connectionstatechange",()=>{this.options.onEvent("peerconnectionstate",{participant:this.selfIdentity(),state:t.connectionState})});const i=await this.sfuFetch("POST","/v1/rtc/sfu/sessions");if(this.sessionId=i.sessionId,e.length===0)return;const n=e.map(r=>t.addTransceiver(r,{direction:"sendonly",streams:this.localStream?[this.localStream]:[],...r.kind==="video"&&this.simulcastEnabled()?{sendEncodings:ae.map(c=>({...c}))}:{}})),s=await t.createOffer();await t.setLocalDescription(s);const o=await this.sfuFetch("POST",`/v1/rtc/sfu/sessions/${encodeURIComponent(i.sessionId)}/tracks`,{sessionDescription:t.localDescription,tracks:n.map(r=>({location:"local",mid:r.mid,trackName:this.trackName(r.sender.track)}))});o.sessionDescription&&await t.setRemoteDescription(o.sessionDescription),this.videoSender=t.getSenders().find(r=>r.track?.kind==="video")??null,await this.applyVideoQuality()}trackName(e){return`${this.options.credential.participantId}/${e.kind}`}announceTracks(){if(!this.sessionId)return;const e=[...(this.localStream?.getAudioTracks()??[]).map(t=>({trackName:this.trackName(t),kind:t.kind,simulcast:!1})),...(this.localStream?.getVideoTracks()??[]).map(t=>({trackName:this.trackName(t),kind:t.kind,simulcast:this.simulcastEnabled()}))];e.length!==0&&this.sendControl("sfu.tracks",{sessionId:this.sessionId,tracks:e})}async handleTrackAnnouncement(e,t){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);if(!i||s.length===0||e.participantId===this.options.credential.participantId)return;if(!this.connection||!this.sessionId){this.pendingAnnouncements.set(e.participantId,{sender:e,detail:t??{}});return}const o=this.remotes.get(e.participantId)??{identity:e,stream:new MediaStream,sessionId:i,trackNames:[],simulcastNames:new Set,mids:new Map};o.sessionId=i;for(const r of n)r.simulcast&&o.simulcastNames.add(r.trackName);this.remotes.set(e.participantId,o),this.negotiationQueue=this.negotiationQueue.then(async()=>{const r=s.filter(c=>!o.trackNames.includes(c));r.length!==0&&await this.subscribe(o,r)}).catch(r=>{this.options.onEvent("signalingerror",{message:r instanceof Error?r.message:"SFU subscribe failed"})}),await this.negotiationQueue}async subscribe(e,t){const i=this.connection;if(!i||!this.sessionId||t.length===0)return;const n=await this.sfuFetch("POST",`/v1/rtc/sfu/sessions/${encodeURIComponent(this.sessionId)}/tracks`,{tracks:t.map(s=>({location:"remote",sessionId:e.sessionId,trackName:s,...e.simulcastNames.has(s)?{simulcast:{preferredRid:"a",priorityOrdering:"asciibetical",ridNotAvailable:"asciibetical"}}:{}}))});for(const s of n.tracks??[])s.mid&&(e.mids.set(s.mid,String(s.trackName??"")),this.midToParticipant.set(s.mid,e.identity.participantId));if(e.trackNames=[...new Set([...e.trackNames,...t])],n.requiresImmediateRenegotiation&&n.sessionDescription){await i.setRemoteDescription(n.sessionDescription);const s=await i.createAnswer();await i.setLocalDescription(s),await this.sfuFetch("PUT",`/v1/rtc/sfu/sessions/${encodeURIComponent(this.sessionId)}/renegotiate`,{sessionDescription:i.localDescription})}}handleRemoteTrack(e){const t=e.transceiver?.mid??null,i=t?this.midToParticipant.get(t):void 0,n=i?this.remotes.get(i):void 0;n&&(n.stream.getTracks().some(s=>s.id===e.track.id)||n.stream.addTrack(e.track),this.options.onEvent("remotestream",{participant:n.identity,stream:n.stream,track:e.track}))}removeRemote(e){const t=this.remotes.get(e);if(t){for(const i of t.mids.keys())this.midToParticipant.delete(i);this.remotes.delete(e),this.options.onEvent("participantleft",t.identity)}}async setRemoteQuality(e,t){const i=this.remotes.get(e);if(!i||!this.sessionId)return;const n=[...i.mids.entries()].find(([,s])=>i.simulcastNames.has(s))?.[0];n&&await this.sfuFetch("PUT",`/v1/rtc/sfu/sessions/${encodeURIComponent(this.sessionId)}/tracks/update`,{tracks:[{mid:n,simulcast:{preferredRid:se[t],priorityOrdering:"asciibetical",ridNotAvailable:"asciibetical"}}]})}async replaceVideoTrack(e){this.videoSender&&(await this.videoSender.replaceTrack(e),await this.applyVideoQuality())}simulcastEnabled(){return this.options.simulcast!==!1}async applyVideoQuality(){const e=this.videoSender;if(!e)return;const t=this.screenStream?.getVideoTracks()[0]?.id,i=!!(t&&e.track?.id===t),n=e.getParameters(),s=n.encodings?.length?n.encodings:[{}];if(s.length>1){const o=this.quality==="audio"?null:se[this.quality];n.encodings=s.map(r=>{if(i)return{...r,active:r.rid==="a",...r.rid==="a"?{maxBitrate:W.maxBitrate,maxFramerate:W.maxFramerate}:{}};const c=ae.find(d=>d.rid===r.rid),l=o!=null&&String(r.rid??"")>=o;return{...r,...c??{},active:l}})}else{const o=this.quality==="audio"?V.audio:V[this.quality],r=i?W:o;n.encodings=s.map(c=>({...c,...r}))}n.degradationPreference=i?"maintain-resolution":"maintain-framerate",await e.setParameters(n)}async closePublishedTracks(){const e=this.connection;if(!e||!this.sessionId)return;const t=e.getTransceivers().filter(i=>i.sender.track&&i.mid).map(i=>({mid:i.mid}));t.length!==0&&await this.sfuFetch("PUT",`/v1/rtc/sfu/sessions/${encodeURIComponent(this.sessionId)}/tracks/close`,{tracks:t,force:!0})}selfIdentity(){return{participantId:this.options.credential.participantId,externalUserId:this.options.credential.externalUserId??this.options.credential.participantId,displayName:this.options.credential.externalUserId??this.options.credential.participantId,platform:"web"}}async sfuFetch(e,t,i){const n=await fetch(`${this.apiBase}${t}`,{method:e,headers:{authorization:`Bearer ${this.options.credential.signalingToken}`,"content-type":"application/json"},body:i===void 0?void 0:JSON.stringify(i)});if(!n.ok)throw new Error(`SFU request failed with HTTP ${n.status}`);return n.json()}startHeartbeat(e){this.stopHeartbeat(),this.heartbeatTimer=setInterval(()=>{if(!(this.socket!==e||e.readyState!==WebSocket.OPEN)){if(Date.now()>this.heartbeatDeadline){e.close(4e3,"Signaling heartbeat timed out");return}e.send("ping")}},2e4)}stopHeartbeat(){this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.heartbeatTimer=null}startSpeakerSampling(){this.stopSpeakerSampling(),this.speakerTimer=setInterval(()=>{this.sampleActiveSpeaker()},1e3)}stopSpeakerSampling(){this.speakerTimer&&clearInterval(this.speakerTimer),this.speakerTimer=null}async sampleActiveSpeaker(){const e=this.connection;if(!e||e.connectionState!=="connected")return;let t;try{t=await e.getStats()}catch{return}const i=[];for(const s of ie(t.values())){const o=s.mid?this.midToParticipant.get(s.mid):void 0;o&&i.push({participantId:o,level:s.level})}const n=H(i,this.activeSpeaker);n!==this.activeSpeaker&&(this.activeSpeaker=n,this.options.onEvent("activespeaker",{participantId:n}))}clearTimers(){this.stopHeartbeat(),this.stopSpeakerSampling(),this.reconnectTimer&&clearTimeout(this.reconnectTimer),this.reconnectTimer=null}scheduleReconnect(){if(this.reconnectTimer||this.stopped)return;const e=Math.min(3e4,1e3*2**Math.min(this.reconnectAttempt,5))+Math.round(Math.random()*300);this.reconnectAttempt+=1,this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.openSocket(!0).then(()=>this.announceTracks()).catch(t=>{this.options.onEvent("signalingerror",{message:t instanceof Error?t.message:"RTC signaling connection failed"}),this.scheduleReconnect()})},e)}send(e){this.socket?.readyState===WebSocket.OPEN&&this.socket.send(JSON.stringify({clientMessageId:e.clientMessageId??Le(),...e}))}stopStream(e){for(const t of e?.getTracks()??[])t.stop()}}function Le(){return globalThis.crypto?.randomUUID?.()??`${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}function De(a,e={}){const t=e.document??globalThis.document,i=!!(t&&t.pictureInPictureEnabled);let n=null;function s(){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)}function o(r){const c=a.getRemoteStreams();return r?c.find(l=>l.participant.participantId===r)?.stream??null:c[0]?.stream??a.getLocalStream()}return{supported:i,async enter(r){if(!i)throw new Error("This browser does not support picture-in-picture");const c=o(r);if(!c)throw new Error("No video stream is available for picture-in-picture");const l=s();l.srcObject!==c&&(l.srcObject=c),await l.play().catch(()=>{}),await l.requestPictureInPicture()},async exit(){t.pictureInPictureElement&&typeof t.exitPictureInPicture=="function"&&await t.exitPictureInPicture()},dispose(){n?.remove(),n=null}}}function Oe(a,e){const t=e.document??globalThis.document,i=e.fetchImplementation??globalThis.fetch.bind(globalThis),n=_(e.credential.signalingUrl,globalThis.location?.href).origin,s=e.mimeType??"video/webm",o=e.height??720,r=Math.round(o*16/9);let c=null,l=null,d=null,u=null,k=0,f=[],v=!1;async function R(y,w,p,E="application/json"){const A=await i(`${n}${w}`,{method:y,headers:{authorization:`Bearer ${e.credential.signalingToken}`,"content-type":E},body:p===void 0?void 0:E==="application/json"?JSON.stringify(p):p});if(!A.ok)throw new Error(`Recording request failed with HTTP ${A.status}`);return A.json()}return{get recording(){return v},async start(){if(v)return;u=(await R("POST","/v1/rtc/recordings")).recordingId;const w=t.createElement("canvas");w.width=r,w.height=o;const p=w.getContext("2d"),E=new Map,A=(T,P)=>{let C=E.get(T);return C||(C=t.createElement("video"),C.muted=!0,C.playsInline=!0,C.srcObject=P,C.play().catch(()=>{}),E.set(T,C)),C};l=setInterval(()=>{const T=a.getRemoteStreams(),P=a.getLocalStream(),C=[...T.map(q=>({key:q.participant.participantId,stream:q.stream,label:q.participant.displayName})),...P?[{key:"local",stream:P,label:"me"}]:[]];p.fillStyle="#101418",p.fillRect(0,0,r,o);const N=Math.max(1,Math.ceil(Math.sqrt(C.length))),U=Math.max(1,Math.ceil(C.length/N)),j=r/N,g=o/U;C.forEach((q,Te)=>{const be=A(q.key,q.stream),ee=Te%N*j,te=Math.floor(Te/N)*g;be.videoWidth>0&&p.drawImage(be,ee+2,te+2,j-4,g-4),p.fillStyle="rgba(0,0,0,.6)",p.fillRect(ee+8,te+g-30,Math.min(j-16,12+q.label.length*9),22),p.fillStyle="#fff",p.font="14px system-ui",p.fillText(q.label,ee+14,te+g-14)})},66);const D=w.captureStream(15);d=new AudioContext;const I=d.createMediaStreamDestination(),L=new Set,S=()=>{const T=[...a.getRemoteStreams().map(P=>P.stream),...a.getLocalStream()?[a.getLocalStream()]:[]];for(const P of T)for(const C of P.getAudioTracks())L.has(C.id)||(L.add(C.id),d.createMediaStreamSource(new MediaStream([C])).connect(I))};S();const M=setInterval(S,1e3),m=()=>clearInterval(M);for(const T of I.stream.getAudioTracks())D.addTrack(T);c=(e.mediaRecorderFactory??((T,P)=>new MediaRecorder(T,P)))(D,{mimeType:s}),f=[],c.addEventListener("dataavailable",T=>{T.data&&T.data.size>0&&f.push(T.data)}),c.addEventListener("stop",m),c.start(1e3),k=Date.now(),v=!0},async stop(){if(!v||!c||!u)throw new Error("The recorder is not running");v=!1;const y=c,w=new Promise(I=>y.addEventListener("stop",()=>I(),{once:!0}));y.stop(),await w,l&&clearInterval(l),l=null,await d?.close().catch(()=>{}),d=null;const p=new Blob(f,{type:s});f=[];const E=Math.max(1,Math.round((Date.now()-k)/1e3)),A=await R("PUT",`/v1/rtc/recordings/${encodeURIComponent(u)}/media`,p,s);await R("POST",`/v1/rtc/recordings/${encodeURIComponent(u)}/complete`,{durationSeconds:E});const D={recordingId:u,sizeBytes:A.sizeBytes,durationSeconds:E};return u=null,c=null,D},dispose(){l&&clearInterval(l),l=null;try{c?.stop()}catch{}c=null,d?.close().catch(()=>{}),d=null,v=!1}}}const re=["none","blur","aurora","cybergrid","bokeh","sunset","image"],oe=["none","snow","confetti","vignette"],$={aurora(a,e,t,i){const n=a.createLinearGradient(0,0,0,t);n.addColorStop(0,"#050b1e"),n.addColorStop(1,"#0b2540"),a.fillStyle=n,a.fillRect(0,0,e,t);for(let s=0;s<3;s+=1){a.beginPath();for(let o=0;o<=e;o+=16){const r=t*.35+s*40+Math.sin(o/140+i/900+s)*60;o===0?a.moveTo(o,r):a.lineTo(o,r)}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()}},cybergrid(a,e,t,i){a.fillStyle="#0a0118",a.fillRect(0,0,e,t),a.strokeStyle="rgba(255,0,180,0.5)",a.lineWidth=1.5;const n=t*.45;for(let o=0;o<12;o+=1){const r=n+(o*34+i/18)%(t-n);a.beginPath(),a.moveTo(0,r),a.lineTo(e,r),a.stroke()}for(let o=-8;o<=8;o+=1)a.beginPath(),a.moveTo(e/2+o*24,n),a.lineTo(e/2+o*e*.12,t),a.stroke();const s=a.createLinearGradient(0,t*.1,0,n);s.addColorStop(0,"#ffd34d"),s.addColorStop(1,"#ff2d78"),a.fillStyle=s,a.beginPath(),a.arc(e/2,n,t*.22,Math.PI,0),a.fill()},bokeh(a,e,t,i){a.fillStyle="#14100c",a.fillRect(0,0,e,t);for(let n=0;n<26;n+=1){const s=(Math.sin(n*12.9898)*.5+.5)*e,o=(Math.sin(n*78.233)*.5+.5)*t+Math.sin(i/1500+n)*12,r=14+n%5*12;a.fillStyle=`hsla(${35+n%4*12}, 90%, 60%, ${.1+n%3*.05})`,a.beginPath(),a.arc(s,o,r,0,Math.PI*2),a.fill()}},sunset(a,e,t,i){const n=a.createLinearGradient(0,0,0,t);n.addColorStop(0,"#2b1055"),n.addColorStop(.55,"#d1548a"),n.addColorStop(1,"#ffb36b"),a.fillStyle=n,a.fillRect(0,0,e,t),a.fillStyle="#ffdf8e",a.beginPath(),a.arc(e*.5,t*.62+Math.sin(i/4e3)*4,t*.13,0,Math.PI*2),a.fill(),a.fillStyle="rgba(20,8,40,0.85)",a.fillRect(0,t*.78,e,t*.22)}};function qe(a,e={}){const t=e.document??globalThis.document;let i=e.width??1280,n=e.height??720;const s=!!(e.width||e.height),o=e.frameRate??24,r=a instanceof MediaStreamTrack?a:a.getVideoTracks()[0];if(!r)throw new Error("A video track is required");let c=e.background??"none",l=e.overlay??"none",d=e.backgroundImage??null,u=e.segmenter??null,k=null,f=!1;const v=t.createElement("video");v.muted=!0,v.playsInline=!0,v.srcObject=new MediaStream([r]),v.play().catch(()=>{});const R=t.createElement("canvas");R.width=i,R.height=n;const y=R.getContext("2d"),w=t.createElement("canvas");w.width=i,w.height=n;const p=w.getContext("2d"),E=()=>Array.from({length:80},(m,h)=>({x:(Math.sin(h*12.9898)*.5+.5)*i,y:(Math.sin(h*78.233)*.5+.5)*n,speed:.6+h%5*.5,drift:Math.sin(h)*.6,size:2+h%4,hue:h*47%360}));let A=E();const D=()=>{if(s)return;const m=v.videoWidth,h=v.videoHeight;if(!m||!h)return;const T=Math.min(1,1280/Math.max(m,h)),P=Math.round(m*T),C=Math.round(h*T);P===i&&C===n||(i=P,n=C,R.width=i,R.height=n,w.width=i,w.height=n,A=E())};v.addEventListener?.("loadedmetadata",D),v.addEventListener?.("resize",D);function I(){if(k){p.clearRect(0,0,i,n),p.drawImage(k,0,0,i,n),p.globalCompositeOperation="source-in",p.drawImage(v,0,0,i,n),p.globalCompositeOperation="source-over",y.drawImage(w,0,0);return}const m=i*.42,h=m*9/16,T=i-m-24,P=n-h-24;y.save(),y.shadowColor="rgba(0,0,0,.5)",y.shadowBlur=24,y.beginPath(),y.roundRect(T,P,m,h,14),y.clip(),y.drawImage(v,T,P,m,h),y.restore()}function L(m){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",k&&I()):c==="image"&&d?(y.drawImage(d,0,0,i,n),I()):(($[c]??$.aurora)(y,i,n,m),I()),l==="snow"||l==="confetti")for(const h of A)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());else if(l==="vignette"){const h=y.createRadialGradient(i/2,n/2,n*.4,i/2,n/2,n*.85);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)}}const S=setInterval(()=>{L(Date.now()),u&&!f&&c!=="none"&&(f=!0,u.segment(v).then(m=>{k=m}).catch(()=>{k=null}).finally(()=>{f=!1}))},Math.round(1e3/o)),M=R.captureStream(o);return{track:M.getVideoTracks()[0],stream:M,setBackground(m,h){c=re.includes(m)?m:"none",h!==void 0&&(d=h),c==="none"&&(k=null)},setOverlay(m){l=oe.includes(m)?m:"none"},setSegmenter(m){u=m,m||(k=null)},dispose(){clearInterval(S),u?.close?.();for(const m of M.getTracks())m.stop();v.srcObject=null}}}function J(){return{blinkLeft:0,blinkRight:0,jawOpen:0,smile:0,browUp:0,pupilX:0,pupilY:0,headYaw:0,headPitch:0,headRoll:0,tracked:!1}}function O(a,e){const t=a.find(i=>i.categoryName===e);return t?Math.max(0,Math.min(1,t.score)):0}function ce(a){if(!a||a.length<16)return{yaw:0,pitch:0,roll:0};a[0],a[4];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);return{yaw:o,pitch:r,roll:c}}function le(a,e,t=1){if(!a||a.length===0)return J();const i=ce(e),n=(O(a,"eyeLookOutRight")+O(a,"eyeLookInLeft"))/2,s=(O(a,"eyeLookOutLeft")+O(a,"eyeLookInRight"))/2,o=(O(a,"eyeLookUpLeft")+O(a,"eyeLookUpRight"))/2,r=(O(a,"eyeLookDownLeft")+O(a,"eyeLookDownRight"))/2,c=(O(a,"mouthSmileLeft")+O(a,"mouthSmileRight"))/2,l=(O(a,"mouthFrownLeft")+O(a,"mouthFrownRight"))/2,d=O(a,"eyeBlinkLeft"),u=O(a,"eyeBlinkRight"),k=Math.min(1,Math.abs(i.yaw)/.35)*Math.max(0,Math.min(1,t)),f=(d+u)/2;return{blinkLeft:d+(f-d)*k,blinkRight:u+(f-u)*k,jawOpen:O(a,"jawOpen"),smile:c-l,browUp:(O(a,"browInnerUp")+O(a,"browOuterUpLeft")+O(a,"browOuterUpRight"))/3,pupilX:n-s,pupilY:r-o,headYaw:i.yaw,headPitch:i.pitch,headRoll:i.roll,tracked:!0}}function G(a,e,t=.4){const i=(n,s)=>n+(s-n)*t;return{blinkLeft:i(a.blinkLeft,e.blinkLeft),blinkRight:i(a.blinkRight,e.blinkRight),jawOpen:i(a.jawOpen,e.jawOpen),smile:i(a.smile,e.smile),browUp:i(a.browUp,e.browUp),pupilX:i(a.pupilX,e.pupilX),pupilY:i(a.pupilY,e.pupilY),headYaw:i(a.headYaw,e.headYaw),headPitch:i(a.headPitch,e.headPitch),headRoll:i(a.headRoll,e.headRoll),tracked:e.tracked}}function de(a,e,t,i,n){n||(a.fillStyle="#101822",a.fillRect(0,0,t,i));const s=n?n.x+e.headYaw*n.radius*.1:t/2+e.headYaw*t*.18,o=n?n.y+e.headPitch*n.radius*.1:i/2+e.headPitch*i*.18,r=n?n.radius:Math.min(t,i)*.28;a.save(),a.translate(s,o),a.rotate(e.headRoll*.6);const c=Math.max(-1.35,Math.min(1.35,e.headYaw)),l=Math.max(-.9,Math.min(.9,e.headPitch)),d=Math.sin(c),u=L=>Math.sin(L+c)*r,k=L=>Math.cos(L+c),f=Math.sin(l)*r*.22;a.fillStyle="#3a2a1c",a.beginPath(),a.ellipse(-d*r*.35,-r*.25,r*.98,r*.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)*.12),r*1.12,0,0,Math.PI*2),a.fill(),a.fillStyle="#3a2a1c",a.beginPath(),a.ellipse(-d*r*.28,-r*.72+f*.4,r*.92,r*.5,0,Math.PI,Math.PI*2),a.fill();const v=0;a.fillStyle="#e5b58a",a.beginPath();const R=u(v)*.9,y=r*.12+f;a.ellipse(R+d*r*.14,y,r*.11,r*.14,0,0,Math.PI*2),a.fill();for(const L of[-1,1]){const S=L*.42,M=k(S);if(M<=.12)continue;const m=u(S)*.82,h=Math.max(.2,M),T=L<0?e.blinkLeft:e.blinkRight,P=Math.max(.06,1-T),C=e.browUp*r*.14;a.strokeStyle="#4a2e18",a.lineWidth=r*.07,a.lineCap="round",a.beginPath(),a.moveTo(m-r*.2*h,-r*.42-C+f),a.quadraticCurveTo(m,-r*.52-C+f,m+r*.2*h,-r*.44-C+f),a.stroke();const N=-r*.18+f;a.fillStyle="#ffffff",a.beginPath(),a.ellipse(m,N,r*.16*h,r*.12*P,0,0,Math.PI*2),a.fill(),a.fillStyle="#22303c",a.beginPath(),a.ellipse(m+e.pupilX*r*.07*h,N+e.pupilY*r*.05,r*.06*h,r*.06*P,0,0,Math.PI*2),a.fill()}const w=Math.max(.25,k(0)),p=u(0)*.7,E=r*.55*w,A=r*(.04+e.jawOpen*.34),D=e.smile*r*.12,I=r*.42+f;a.fillStyle="#8c2f2f",a.beginPath(),a.moveTo(p-E/2,I-D),a.quadraticCurveTo(p,I+A,p+E/2,I-D),a.quadraticCurveTo(p,I-A*.25,p-E/2,I-D),a.closePath(),a.fill(),a.restore(),!e.tracked&&!n&&(a.fillStyle="#9fb0bd",a.font=`${Math.round(i*.045)}px system-ui`,a.textAlign="center",a.fillText("No face detected",t/2,i*.9),a.textAlign="left")}const Y={mouthGain:1.7,blinkGain:1,eyeSync:1,smoothing:.4,avatarScale:1,avatarLift:0,lostHoldMs:400};function K(){return{render(a,e,t,i,n){de(a,e,t,i,n??void 0)}}}const X=a=>Math.max(0,Math.min(1,a));function Ne(a,e={}){const t=e.document??globalThis.document;let i=e.width??1280,n=e.height??720;const s=!!(e.width||e.height),o=e.frameRate??24,r=a instanceof MediaStreamTrack?a:a.getVideoTracks()[0];if(!r)throw new Error("A video track is required");let c=e.tracker??null,l=e.renderer??K(),d=e.mode??"replace";const u={...Y,...e.tuning},k=e.backdrop??"#101822",f=t.createElement("video");f.muted=!0,f.playsInline=!0,f.srcObject=new MediaStream([r]),f.play().catch(()=>{});const v=t.createElement("canvas");v.width=i,v.height=n;const R=v.getContext("2d"),y=()=>{if(s)return;const S=f.videoWidth,M=f.videoHeight;if(!S||!M)return;const m=Math.min(1,1280/Math.max(S,M)),h=Math.round(S*m),T=Math.round(M*m);(!(h===i||T===n)||h!==i||T!==n)&&(i=h,n=T,v.width=i,v.height=n)};f.addEventListener?.("loadedmetadata",y),f.addEventListener?.("resize",y);let w=J(),p=null,E=0;function A(S){if(S.length<=454)return;const M=S[10],m=S[152],h=S[234],T=S[454],P=(h.x+T.x)/2*i,C=(h.y+T.y)/2*n,N=Math.hypot((m.x-M.x)*i,(m.y-M.y)*n),U=Math.max(.6,Math.cos(w.headPitch)),j={x:P,y:C,radius:Math.max(24,N/U*.8)};p=p?{x:p.x+(j.x-p.x)*.4,y:p.y+(j.y-p.y)*.4,radius:p.radius+(j.radius-p.radius)*.15}:j}function D(S){if(c&&f.readyState>=2){let m=null;try{m=c.detect(f,S)}catch{}const h=le(m?.categories??null,m?.matrix??null,u.eyeSync);h.jawOpen=X(h.jawOpen*u.mouthGain),h.blinkLeft=X(h.blinkLeft*u.blinkGain),h.blinkRight=X(h.blinkRight*u.blinkGain),h.tracked?(E=S,w=G(w,h,u.smoothing),m?.landmarks&&A(m.landmarks)):w.tracked&&S-E>u.lostHoldMs&&(w=G(w,{...w,jawOpen:0,browUp:0,smile:w.smile*.5,blinkLeft:.1,blinkRight:.1},.05))}d==="overlay"&&f.readyState>=2?R.drawImage(f,0,0,i,n):(R.fillStyle=k,R.fillRect(0,0,i,n));const M=p?{x:p.x,y:p.y+u.avatarLift*p.radius,radius:p.radius*u.avatarScale}:null;l?.render(R,w,i,n,M)}const I=setInterval(()=>D(Date.now()),Math.round(1e3/o)),L=v.captureStream(o);return{track:L.getVideoTracks()[0],stream:L,getRig:()=>({...w}),setMode(S){d=S==="overlay"?"overlay":"replace"},setTracker(S){c?.close?.(),c=S},setRenderer(S){l?.dispose?.(),l=S??K()},setTuning(S){for(const M of Object.keys(Y)){const m=S[M];typeof m=="number"&&Number.isFinite(m)&&(u[M]=m)}},getTuning:()=>({...u}),dispose(){clearInterval(I),c?.close?.(),l?.dispose?.();for(const S of L.getTracks())S.stop();f.srcObject=null}}}class he extends EventTarget{mode;options;client=null;peerConnection;state="initialized";latestMediaScore=null;latestQuality=null;statsTimer=null;statsRunning=!1;previousCounters=null;lastTelemetryAt=0;joinPromise=null;requestedQuality;effectiveQuality;poorQualitySamples=0;goodQualitySamples=0;constructor(e){if(super(),!e.credential?.signalingToken||!e.credential.signalingUrl||!e.credential.roomId||!e.credential.participantId)throw new Error("A valid room signaling credential is required");this.mode=e.mode,this.requestedQuality=e.mode==="voice"?"audio":e.quality??"auto",this.effectiveQuality=this.requestedQuality==="auto"?"medium":this.requestedQuality,this.peerConnection=e.peerConnection??null,this.options={...e,audio:e.audio??!0,video:e.mode==="voice"?!1:e.video??!0,receiveAudio:e.receiveAudio??!0,receiveVideo:e.mode==="voice"?!1:e.receiveVideo??!0,statsInterval:Math.max(1e3,e.statsInterval??5e3),telemetryInterval:Math.max(1e4,e.telemetryInterval??3e4),autoReportStats:e.autoReportStats??!0}}async initialize(){if(this.client)return this;try{const t=(this.options.transport==="auto"?this.mode==="meeting"?"sfu":"mesh":this.options.transport??"mesh")==="sfu"?Ae:B;return this.client=new t({credential:{signalingToken:this.options.credential.signalingToken,signalingUrl:this.options.credential.signalingUrl,roomId:this.options.credential.roomId,participantId:this.options.credential.participantId,externalUserId:this.options.credential.externalUserId,iceServers:this.options.credential.iceServers},audio:this.options.audio,video:this.options.video,receiveAudio:this.options.receiveAudio,receiveVideo:this.options.receiveVideo,quality:this.effectiveQuality,preferredVideoCodec:this.options.preferredVideoCodec??"auto",iceTransportPolicy:this.options.iceTransportPolicy??"all",localStream:this.options.localStream,simulcast:this.options.simulcast??!0,onEvent:(i,n)=>this.handleNativeEvent(i,n)}),await this.client.initialize(),this.startStats(),this.emit("initialized",{mode:this.mode,sdkVersion:we}),this.options.autoJoin&&await this.join(),this}catch(e){this.state="failed";const t=ke(e,"RTC initialization failed");throw this.emit("error",x(t)),t}}async join(){const e=this.requireClient();return this.joinPromise?this.joinPromise:(this.state="joining",this.emit("joining",{}),this.joinPromise=e.join().catch(t=>{this.state="failed";const i=ke(t,"RTC connection failed");throw this.emit("error",x(i)),i}).finally(()=>{this.joinPromise=null}),this.joinPromise)}async leave(){this.client&&(await this.joinPromise?.catch(()=>{}),await this.sampleAndReport("final").catch(()=>{}),await this.client.leave(),this.state="disconnected",this.stopStats())}async setMuted(e){this.requireClient().setMuted(e),this.emit("mediachange",{kind:"audio",enabled:!e})}async setCameraEnabled(e){if(this.mode==="voice"&&e)throw new Error("Camera is unavailable in voice mode");await this.requireClient().setCameraEnabled(e),this.emit("mediachange",{kind:"video",enabled:e})}async setVideoTrack(e){if(this.mode==="voice")throw new Error("Camera is unavailable in voice mode");await this.requireClient().setVideoTrack(e),this.emit("mediachange",{kind:"video",enabled:e.enabled,trackId:e.id})}async setScreenShareEnabled(e){await this.requireClient().setScreenShareEnabled(e),this.emit("mediachange",{kind:"screenshare",enabled:e})}async setDevice(e){await this.requireClient().setDevice(e),this.emit("devicechange",{deviceId:e.deviceId,kind:e.kind})}async setQuality(e){if(this.mode==="voice"&&e!=="audio")throw new Error("Voice calls only support audio quality");this.requestedQuality=e,this.poorQualitySamples=0,this.goodQualitySamples=0,await this.applyEffectiveQuality(e==="auto"?"medium":e,"manual")}async setPreferredVideoCodec(e){await this.requireClient().setPreferredVideoCodec(e),this.emit("codecchange",{preferredVideoCodec:e})}async getDevices(){return navigator.mediaDevices?.enumerateDevices?navigator.mediaDevices.enumerateDevices():[]}getParticipants(){return this.requireClient().getParticipants().map(ye)}getLocalStream(){return this.client?.getLocalStream()??null}getRemoteStreams(){return(this.client?.getRemoteStreams()??[]).map(e=>({participant:ye(e.participant),stream:e.stream}))}setPeerConnection(e){this.peerConnection=e,this.previousCounters=null}async sendChatMessage(e,t){const i=e.trim();if(!i)throw new Error("message is required");this.requireClient().sendChat(i,t)}async sendControlMessage(e,t={}){const i=e.trim();if(!i)throw new Error("action is required");this.requireClient().sendControl(i,t)}async createPoll(e,t,i={}){const n=t.map(s=>s.trim()).filter(Boolean);if(!e.trim()||n.length<2)throw new Error("A poll requires a question and at least two options");this.requireClient().sendControl("poll.create",{poll:{id:`poll-${crypto.randomUUID()}`,question:e.trim(),options:n,votes:{},...i}})}async votePoll(e,t){this.requireClient().sendControl("poll.vote",{pollId:e,optionIndex:t})}async requestStageAccess(){this.requireClient().sendControl("stage.request")}async cancelStageAccess(){this.requireClient().sendControl("stage.cancel")}async joinStage(){this.requireClient().sendControl("stage.join")}async leaveStage(){this.requireClient().sendControl("stage.leave")}async grantStageAccess(e){this.requireClient().sendControl("stage.grant",{participantIds:e})}async denyStageAccess(e){this.requireClient().sendControl("stage.deny",{participantIds:e})}async startRecording(e=!1){this.requireClient().sendControl("recording.start",{allowMultiple:e})}async stopRecording(e){this.requireClient().sendControl("recording.stop",{recordingId:e})}async pauseRecording(e){this.requireClient().sendControl("recording.pause",{recordingId:e})}async resumeRecording(e){this.requireClient().sendControl("recording.resume",{recordingId:e})}async runPreflight(e={}){const t=[],i=globalThis.isSecureContext===!0;i||t.push("secure_context_required");let n="unavailable";if(e.requestMedia!==!1&&navigator.mediaDevices?.getUserMedia)try{(await navigator.mediaDevices.getUserMedia({audio:this.options.audio,video:this.options.video})).getTracks().forEach(k=>k.stop()),n="granted"}catch{n="denied",t.push("media_permission_denied")}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;this.options.audio&&o===0&&t.push("microphone_unavailable"),this.options.video&&r===0&&t.push("camera_unavailable");const c=e.probeUrl??this.options.preflightProbeUrl;let l=null;if(c){const u=performance.now();try{const k=await fetch(c,{method:"HEAD",cache:"no-store",credentials:"omit"});l=Math.round(performance.now()-u),k.ok||t.push("network_probe_failed")}catch{t.push("network_probe_failed")}}const d={passed:t.length===0,sampledAt:new Date().toISOString(),secureContext:i,mediaPermission:n,microphoneCount:o,cameraCount:r,probeLatencyMs:l,effectiveConnectionType:ve()?.effectiveType??null,issues:t};return this.emit("preflight",d),this.options.autoReportStats&&await this.reportQuality({sampleType:"preflight",sampledAt:d.sampledAt,connectionState:d.passed?"ready":"failed",rttMs:d.probeLatencyMs,effectiveConnectionType:d.effectiveConnectionType}).catch(()=>{}),d}async collectQualitySample(e="runtime"){const t=this.options.statsProvider?await this.options.statsProvider():{},i=this.peerConnection?await Qe(this.peerConnection,this.previousCounters):null;i?.counters&&(this.previousCounters=i.counters);const s={...{sampleType:e,sampledAt:new Date().toISOString(),connectionState:this.peerConnection?.connectionState??this.state,mediaScore:this.latestMediaScore,networkScore:this.latestMediaScore==null?5:Math.max(0,Math.min(5,Math.round(this.latestMediaScore))),estimatedMos:4.5,rttMs:null,jitterMs:null,packetLossPct:null,packetLossCumulativePct:null,packetsLost:null,packetsReceived:null,inboundBitrateBps:null,outboundBitrateBps:null,availableOutgoingBitrateBps:null,framesPerSecond:null,frameWidth:null,frameHeight:null,inboundFrameWidth:null,inboundFrameHeight:null,outboundFrameWidth:null,outboundFrameHeight:null,framesDropped:null,freezeCount:null,freezeDurationMs:null,concealedSamples:null,codec:null,transportProtocol:null,candidateType:null,qualityLimitationReason:null,deviceMemoryGb:Fe(),effectiveConnectionType:ve()?.effectiveType??null},...i?.sample??{},...t,sampleType:e,sampledAt:new Date().toISOString()},o=Be(s);return s.networkScore=o.networkScore,s.estimatedMos=o.estimatedMos,this.latestQuality=s,s}async reportQuality(e){const t=this.options.credential.telemetryToken,i=this.options.telemetryEndpoint??this.options.credential.telemetryEndpoint;if(!t||!i)return;const n=await fetch(i,{method:"POST",headers:{authorization:`Bearer ${t}`,"content-type":"application/json"},body:JSON.stringify(e),keepalive:e.sampleType==="final"});if(!n.ok)throw new Error(`RTC telemetry failed with HTTP ${n.status}`)}getStats(){const e=this.client,t=e?.getLocalStream();return{state:this.state,participantCount:e?e.getParticipants().length+1:0,audioEnabled:!!t?.getAudioTracks().some(i=>i.enabled),videoEnabled:!!t?.getVideoTracks().some(i=>i.enabled),screenShareEnabled:!1,latestMediaScore:this.latestMediaScore,latestQuality:this.latestQuality,sampledAt:new Date().toISOString()}}getNativeClient(){return this.client}destroy(){this.stopStats(),this.client&&this.client.leave(),this.client=null,this.joinPromise=null,this.peerConnection=null,this.state="disconnected"}handleNativeEvent(e,t){e==="connected"?(this.state="connected",this.peerConnection=this.client?.getPrimaryPeerConnection()??null):e==="disconnected"?this.state="disconnected":e==="peerconnectionstate"&&(this.peerConnection=this.client?.getPrimaryPeerConnection()??null),this.emit(e,t)}startStats(){this.stopStats(),this.statsTimer=setInterval(()=>{this.sampleAndReport("runtime")},this.options.statsInterval)}async sampleAndReport(e){if(!this.statsRunning){this.statsRunning=!0;try{const t=await this.collectQualitySample(e);await this.adaptQuality(t),this.emit("stats",{...this.getStats(),latestQuality:t});const i=Date.now(),n=e!=="runtime"||i-this.lastTelemetryAt>=this.options.telemetryInterval;this.options.autoReportStats&&n&&(await this.reportQuality(t),this.lastTelemetryAt=i)}catch(t){this.emit("telemetryerror",x(t))}finally{this.statsRunning=!1}}}stopStats(){this.statsTimer!==null&&clearInterval(this.statsTimer),this.statsTimer=null}async adaptQuality(e){if(this.requestedQuality!=="auto"||this.options.adaptiveQuality===!1||this.mode==="voice")return;if((this.client?.getParticipants().length??0)>1){this.poorQualitySamples=0,this.goodQualitySamples=0,this.effectiveQuality!=="high"&&await this.applyEffectiveQuality("high","network");return}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);this.poorQualitySamples=n?this.poorQualitySamples+1:0,this.goodQualitySamples=s?this.goodQualitySamples+1:0;const o=["low","medium","high"],r=o.indexOf(this.effectiveQuality);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"))}async applyEffectiveQuality(e,t){if(this.effectiveQuality===e&&this.client)return;const i=this.effectiveQuality;this.effectiveQuality=e,this.client&&await this.client.setQuality(e),this.emit("qualitychange",{requestedQuality:this.requestedQuality,quality:e,previous:i,reason:t})}requireClient(){if(!this.client)throw new Error("Call initialize() before using the RTC client");return this.client}emit(e,t){const i={type:e,detail:t,timestamp:new Date().toISOString()};this.dispatchEvent(new CustomEvent(e,{detail:i})),this.options.onEvent?.(i)}}async function je(a){return new he(a).initialize()}let F=null;function ue(){if(F)return F;const a=globalThis,e=a.AudioContext??a.webkitAudioContext;return e?(F=new e,F):null}async function me(){const a=ue();return a?(a.state==="suspended"&&await a.resume().catch(()=>{}),a.state==="running"):!1}class Ve{options;media=null;cadenceTimer=null;oscillators=new Set;constructor(e){this.options=e}unlock(){return me()}startIncoming(){this.start("incoming",this.options.ringtoneUrl)}startRingback(){this.start("ringback",this.options.ringbackUrl)}stop(){this.cadenceTimer&&clearInterval(this.cadenceTimer),this.cadenceTimer=null,this.media?.pause(),this.media&&(this.media.currentTime=0),this.media=null;for(const e of this.oscillators)try{e.stop()}catch{}this.oscillators.clear()}start(e,t){if(this.stop(),this.options.sounds===!1)return;if(t&&typeof Audio<"u"){this.media=new Audio(t),this.media.loop=!0,this.media.play().catch(()=>{});return}const i=()=>this.playBuiltInTone(e);i(),this.cadenceTimer=setInterval(i,e==="incoming"?3e3:3500)}playBuiltInTone(e){const t=ue();if(!t||t.state!=="running")return;const i=e==="incoming"?1.25:1,n=e==="incoming"?[440,480]:[425],s=t.createGain(),o=t.currentTime;s.gain.setValueAtTime(1e-4,o),s.gain.exponentialRampToValueAtTime(.045,o+.025),s.gain.setValueAtTime(.045,o+i-.06),s.gain.exponentialRampToValueAtTime(1e-4,o+i),s.connect(t.destination);for(const r of n){const c=t.createOscillator();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+.02)}}}class pe extends EventTarget{options;socket=null;stopped=!1;reconnectAttempt=0;reconnectTimer=null;heartbeatTimer=null;heartbeatDeadline=0;currentCall=null;outgoingCallId=null;mount=null;terminalTimer=null;tokenRefreshTimer=null;tokenRequest=null;currentToken;currentTokenExpiresAt;callAudio;mediaClients=new Map;activeCallIds=new Set;seenInvitationIds=new Set;invitationExpiryTimers=new Map;startCallRequest=null;lastDisconnect=null;constructor(e){if(super(),!e.token&&!e.tokenProvider)throw new Error("A client token or tokenProvider is required");if(e.token&&!e.token.startsWith("rtcc_"))throw new Error("A valid RTC client token is required");if(!e.signalingUrl&&!e.callActionsUrl&&!e.apiBaseUrl&&!globalThis.location?.origin)throw new Error("signalingUrl or callActionsUrl is required outside a browser");this.options=e,this.currentToken=e.token??null,this.currentTokenExpiresAt=e.tokenExpiresAt,this.callAudio=new Ve(e),this.mount=fe(e.mount),this.scheduleTokenRefresh()}connect(){if(!this.options.signalingUrl)throw new Error("signalingUrl is required to connect signaling");return this.stopped=!1,this.scheduleTokenRefresh(),this.openSocket("initial"),this}setMount(e){this.clearUi(),this.mount=fe(e),this.currentCall&&this.renderIncoming(this.currentCall)}attachMediaClient(e,t){if(!e.trim())throw new Error("callId is required");return this.mediaClients.set(e,t),()=>{this.mediaClients.get(e)===t&&this.mediaClients.delete(e)}}async startCall(e){if(this.startCallRequest)return this.startCallRequest;if(this.activeCallIds.size>0)throw new Error("Another call is already active");const t=e.targets?.length?e.targets:e.target?[e.target]:[];if(t.length===0)throw new Error("startCall requires at least one target");const i={mode:e.mode,targets:t,metadata:e.metadata,idempotencyKey:e.idempotencyKey},n=(async()=>{await this.callAudio.unlock(),this.callAudio.startRingback();let s;try{s=await this.request("/v1/rtc/client/calls",{method:"POST",headers:e.idempotencyKey?{"idempotency-key":e.idempotencyKey}:void 0,body:JSON.stringify(i)})}catch(o){throw this.callAudio.stop(),o}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})();this.startCallRequest=n;try{return await n}finally{this.startCallRequest===n&&(this.startCallRequest=null)}}async accept(e=this.currentCall?.id){if(!e)throw new Error("No incoming call is available");const t=await this.callAction(e,"accept");return this.activeCallIds.add(e),this.clearInvitationExpiry(e),this.callAudio.stop(),this.dismiss(e),this.emit("callaccepted",t),t}async reject(e=this.currentCall?.id){if(!e)throw new Error("No incoming call is available");const t=await this.callAction(e,"reject");return this.releaseMedia(e),this.showTerminal(e,"Call declined"),this.emit("callrejected",t),t}async busy(e=this.currentCall?.id){if(!e)throw new Error("No incoming call is available");const t=await this.callAction(e,"busy");return this.releaseMedia(e),this.showTerminal(e,"Line busy"),t}async end(e){this.callAudio.stop();const t=await this.callAction(e,"end");return this.releaseMedia(e),this.showTerminal(e,"Call ended"),t}async cancel(e){this.callAudio.stop();const t=await this.callAction(e,"cancel");return this.releaseMedia(e),this.showTerminal(e,"Call cancelled"),t}handleIncomingCall(e){if(!e?.id||!e.caller?.id||!e.caller.name||e.mode!=="voice"&&e.mode!=="video")throw new Error("Incoming call payload is invalid");if(e.expiresAt&&Date.parse(e.expiresAt)<=Date.now()){this.emit("callexpired",{id:e.id,expiresAt:e.expiresAt});return}if(this.currentCall?.id===e.id){this.currentCall=e,this.emit("callrestored",e);return}if(this.seenInvitationIds.has(e.id)){this.emit("callrestored",e);return}this.seenInvitationIds.add(e.id),this.activeCallIds.add(e.id),this.scheduleInvitationExpiry(e.id,e.expiresAt),this.currentCall=e,this.callAudio.startIncoming(),this.renderIncoming(e),this.emit("incomingcall",e)}scheduleInvitationExpiry(e,t){const i=t?Date.parse(t):Date.now()+45e3,n=Math.min(18e4,Math.max(5e3,(Number.isFinite(i)?i:Date.now()+45e3)-Date.now()+1e4));this.clearInvitationExpiry(e),this.invitationExpiryTimers.set(e,setTimeout(()=>{this.invitationExpiryTimers.delete(e),!(!this.activeCallIds.has(e)||this.mediaClients.has(e))&&(this.emit("callexpired",{id:e,expiresAt:t??null}),this.release(e))},n))}clearInvitationExpiry(e){const t=this.invitationExpiryTimers.get(e);t&&clearTimeout(t),this.invitationExpiryTimers.delete(e)}release(e){if(!e.trim())throw new Error("callId is required");this.clearInvitationExpiry(e),this.currentCall?.id===e&&(this.callAudio.stop(),this.dismiss(e)),this.outgoingCallId===e&&(this.outgoingCallId=null),this.releaseMedia(e),this.emit("callreleased",{id:e})}handleCallUpdate(e){const t=e.id===this.currentCall?.id||e.id===this.outgoingCallId,i=["answered_elsewhere","rejected","busy","cancelled","ended","failed","missed"];(e.status==="accepted"||i.includes(e.status))&&this.clearInvitationExpiry(e.id),t&&e.status==="accepted"?(this.activeCallIds.add(e.id),this.callAudio.stop(),this.dismiss(e.id)):t&&i.includes(e.status)&&this.showTerminal(e.id,Ue(e.status)),i.includes(e.status)&&this.releaseMedia(e.id),this.emit("callupdated",e)}disconnect(){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();for(const e of this.invitationExpiryTimers.values())clearTimeout(e);this.invitationExpiryTimers.clear();for(const e of this.mediaClients.keys())this.releaseMedia(e);this.activeCallIds.clear(),this.seenInvitationIds.clear()}destroy(){this.disconnect(),this.mount=null}async openSocket(e){if(!this.options.signalingUrl||this.stopped||this.socket?.readyState===WebSocket.OPEN||this.socket?.readyState===WebSocket.CONNECTING)return;let t;try{t=await this.resolveToken(e,e==="reconnect")}catch(s){this.emit("tokenerror",x(s)),!this.stopped&&this.options.reconnect!==!1&&this.scheduleReconnect();return}if(this.stopped||this.socket)return;const i=z(this.options.signalingUrl,globalThis.location?.href),n=new WebSocket(i,["rtc-client",t]);this.socket=n,n.addEventListener("open",()=>{const s=this.reconnectAttempt>0,o=this.lastDisconnect;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(()=>{if(n.readyState===WebSocket.OPEN){if(Date.now()>this.heartbeatDeadline){n.close(4e3,"Signaling heartbeat timed out");return}n.send("ping")}},2e4)}),n.addEventListener("message",s=>{this.heartbeatDeadline=Date.now()+45e3,this.handleSignal(String(s.data))}),n.addEventListener("close",s=>{if(this.socket!==n)return;this.socket=null,this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.heartbeatTimer=null;const o=!this.stopped&&this.options.reconnect!==!1,r={code:s.code,reason:s.reason,wasClean:s.wasClean,willReconnect:o,attempt:this.reconnectAttempt+1};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()}),n.addEventListener("error",()=>{const s={message:"RTC signaling connection failed",reconnecting:!this.stopped&&this.options.reconnect!==!1,attempt:this.reconnectAttempt+1};this.emit("signalingerror",s),this.reportSignalingDiagnostic({event:"error",attempt:s.attempt})})}handleSignal(e){let t;try{t=JSON.parse(e)}catch{return}const i=String(t.type??"");if(i==="call.incoming"&&t.call&&typeof t.call=="object"){this.handleIncomingCall(t.call);return}if(i==="call.updated"){const n=t.call;n?.id&&n.status&&this.handleCallUpdate({id:n.id,status:n.status,actorExternalId:typeof n.actorExternalId=="string"?n.actorExternalId:null});return}i==="presence"?this.emit("presence",{users:t.users??[]}):i!=="pong"&&this.emit(i||"signal",t)}async callAction(e,t){return this.request(`/v1/rtc/client/calls/${encodeURIComponent(e)}/${t}`,{method:"POST",body:"{}"})}async request(e,t,i=!1){const n=this.options.callActionsUrl??this.options.apiBaseUrl??this.options.signalingUrl??globalThis.location?.origin;if(!n)throw new Error("callActionsUrl is required for call actions");const s=_(n,globalThis.location?.href),o=await this.resolveToken("initial"),r=new Headers(t.headers);r.set("authorization",`Bearer ${o}`),r.set("content-type","application/json");const c=await fetch(new URL(e,s.origin),{...t,headers:r}),l=await c.json().catch(()=>({}));if(c.status===401&&this.options.tokenProvider&&!i)return await this.resolveToken("unauthorized",!0),this.request(e,t,!0);if(!c.ok){const d=l.error;throw new Error(typeof d=="string"?d:d?.message||`RTC API failed with HTTP ${c.status}`)}return l}renderIncoming(e){if(!this.mount)return;this.clearUi();const t=document.createElement("section");t.dataset.rtcIncomingCall=e.id,t.setAttribute("role","dialog"),t.setAttribute("aria-label",`Incoming ${e.mode} call from ${e.caller.name}`),Object.assign(t.style,{display:"grid",gap:"14px",padding:"20px",border:"1px solid #cbd8dc",borderRadius:"8px",background:"#fff",color:"#102027",boxShadow:"0 14px 38px #07101429",fontFamily:"system-ui, sans-serif"});const i=document.createElement("small");i.textContent=e.mode==="video"?"Incoming video call":"Incoming voice call",i.style.color="#5d7079";const n=document.createElement("strong");n.textContent=e.caller.name,n.style.fontSize="20px";const s=document.createElement("div");Object.assign(s.style,{display:"grid",gridTemplateColumns:"1fr 1fr",gap:"10px"});const o=ge("Decline","#fff","#a32920","#d9a09b"),r=ge("Answer","#fff","#08745f","#08745f");o.addEventListener("click",()=>{this.reject(e.id).catch(c=>this.emit("error",x(c)))}),r.addEventListener("click",()=>{this.accept(e.id).catch(c=>this.emit("error",x(c)))}),s.appendChild(o),s.appendChild(r),t.appendChild(i),t.appendChild(n),t.appendChild(s),this.mount.replaceChildren(t)}dismiss(e){e&&this.currentCall?.id!==e&&this.outgoingCallId!==e||(this.currentCall=null,(!e||this.outgoingCallId===e)&&(this.outgoingCallId=null),this.callAudio.stop(),this.clearUi())}showTerminal(e,t){if(this.callAudio.stop(),this.currentCall?.id===e&&(this.currentCall=null),this.outgoingCallId===e&&(this.outgoingCallId=null),!this.mount)return;this.clearUi();const i=document.createElement("section");i.dataset.rtcCallTerminal=e,i.setAttribute("role","status"),Object.assign(i.style,{display:"grid",placeItems:"center",gap:"10px",minHeight:"150px",padding:"20px",border:"1px solid #cbd8dc",borderRadius:"8px",background:"#fff",color:"#102027",boxShadow:"0 14px 38px #07101429",fontFamily:"system-ui, sans-serif"});const n=document.createElement("strong");n.textContent=t,n.style.fontSize="18px",i.appendChild(n),this.mount.replaceChildren(i);const s=Math.max(0,this.options.terminalStateDurationMs??1600);this.terminalTimer=setTimeout(()=>this.clearUi(),s)}clearUi(){this.terminalTimer&&clearTimeout(this.terminalTimer),this.terminalTimer=null,this.mount&&this.mount.replaceChildren()}scheduleReconnect(){this.reconnectTimer&&clearTimeout(this.reconnectTimer);const e=Math.min(3e4,1e3*2**Math.min(this.reconnectAttempt,5))+Math.round(Math.random()*300);this.reconnectAttempt+=1,this.reconnectTimer=setTimeout(()=>{this.openSocket("reconnect")},e)}async resolveToken(e,t=!1){if(!t&&this.currentToken&&!this.tokenNeedsRefresh())return this.currentToken;if(!this.options.tokenProvider){if(!this.currentToken)throw new Error("A valid RTC client token is required");return this.currentToken}return this.tokenRequest?this.tokenRequest:(this.tokenRequest=(async()=>{const i=await this.options.tokenProvider?.({reason:e,previousExpiresAt:this.currentTokenExpiresAt}),n=typeof i=="string"?{token:i}:i;if(!n?.token?.startsWith("rtcc_"))throw new Error("tokenProvider returned an invalid RTC client token");return this.currentToken=n.token,this.currentTokenExpiresAt=n.expiresAt,this.scheduleTokenRefresh(),this.emit("tokenrefreshed",{reason:e,expiresAt:n.expiresAt}),n.token})().finally(()=>{this.tokenRequest=null}),this.tokenRequest)}tokenNeedsRefresh(){if(!this.currentTokenExpiresAt)return!1;const e=Date.parse(this.currentTokenExpiresAt)-Date.now();return Number.isFinite(e)?e<=this.refreshSkew(e):!0}refreshSkew(e){const t=Math.max(5e3,this.options.tokenRefreshSkewMs??3e5);return Math.min(t,Math.max(5e3,e*.2))}scheduleTokenRefresh(){if(this.tokenRefreshTimer&&clearTimeout(this.tokenRefreshTimer),this.tokenRefreshTimer=null,this.stopped||!this.options.tokenProvider||!this.currentTokenExpiresAt)return;const e=Date.parse(this.currentTokenExpiresAt)-Date.now();if(!Number.isFinite(e))return;const t=Math.max(0,e-this.refreshSkew(e));this.tokenRefreshTimer=setTimeout(()=>{this.tokenRefreshTimer=null,this.resolveToken("expiring",!0).catch(i=>{this.emit("tokenerror",x(i)),this.stopped||(this.tokenRefreshTimer=setTimeout(()=>this.scheduleTokenRefresh(),3e4))})},Math.min(t,2147e6))}releaseMedia(e){this.activeCallIds.delete(e),this.seenInvitationIds.delete(e);const t=this.mediaClients.get(e);t&&(this.mediaClients.delete(e),t.leave().catch(()=>{}).finally(()=>t.destroy()))}reportSignalingDiagnostic(e){this.options.reportSignalingDiagnostics===!1||this.stopped||this.request("/v1/rtc/client/signaling-events",{method:"POST",body:JSON.stringify({...e,occurredAt:new Date().toISOString()})}).catch(t=>this.emit("diagnosticserror",x(t)))}emit(e,t){const i={type:e,detail:t,timestamp:new Date().toISOString()};this.dispatchEvent(new CustomEvent(e,{detail:i})),this.options.onEvent?.(i)}}function xe(a){const e=new pe(a);return a.autoConnect!==!1&&a.signalingUrl&&e.connect(),e}function fe(a){return a?typeof a=="string"?document.querySelector(a):a:null}function ge(a,e,t,i){const n=document.createElement("button");return n.type="button",n.textContent=a,Object.assign(n.style,{minHeight:"44px",border:`1px solid ${i}`,borderRadius:"6px",background:t,color:e,cursor:"pointer",font:"inherit",fontWeight:"700"}),n}function Ue(a){return{rejected:"Call declined",busy:"Line busy",cancelled:"Call cancelled",ended:"Call ended",failed:"Call failed",missed:"No answer",answered_elsewhere:"Answered on another device"}[a]??"Call ended"}function ye(a){return{id:a.participantId,name:a.displayName,role:"participant"}}async function Qe(a,e){const t=await a.getStats(),i=new Map,n=new Map;let s=0,o=0,r=0,c=0,l=0,d=0,u=0,k=0,f=0,v=0,R=0,y=0,w=0,p=0,E=0,A=null,D=null,I=null,L=null,S=null,M=null;t.forEach(g=>{n.set(g.id,g),g.type==="codec"&&i.set(g.id,String(g.mimeType??""))}),t.forEach(g=>{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)),k=Math.max(k,Number(g.frameWidth??0)),R=Math.max(R,Number(g.frameHeight??0)),y+=Number(g.framesDropped??0),w+=Number(g.freezeCount??0),p+=Number(g.totalFreezesDuration??0),E+=Number(g.concealedSamples??0),S||=i.get(String(g.codecId??""))??null),g.type==="outbound-rtp"&&!g.isRemote&&(f=Math.max(f,Number(g.frameWidth??0)),v=Math.max(v,Number(g.frameHeight??0)),o+=Number(g.bytesSent??0),M||=g.qualityLimitationReason?String(g.qualityLimitationReason):null,S||=i.get(String(g.codecId??""))??null),g.type==="candidate-pair"&&(g.selected||g.nominated)&&g.state==="succeeded"){A=Number.isFinite(g.currentRoundTripTime)?Number(g.currentRoundTripTime):A,D=Number.isFinite(g.availableOutgoingBitrate)?Number(g.availableOutgoingBitrate):D;const q=n.get(g.localCandidateId);I=q?.candidateType?String(q.candidateType):I,L=q?.protocol?String(q.protocol):L}});const m=Date.now(),h=e?Math.max(.001,(m-e.timestamp)/1e3):0,T=e?Math.max(0,Math.round((s-e.inboundBytes)*8/h)):null,P=e?Math.max(0,Math.round((o-e.outboundBytes)*8/h)):null,C=r+c,N=e?Math.max(0,r-e.packetsLost):r,U=e?Math.max(0,c-e.packetsReceived):c,j=N+U;return{counters:{timestamp:m,inboundBytes:s,outboundBytes:o,packetsLost:r,packetsReceived:c},sample:{rttMs:A===null?null:A*1e3,jitterMs:d?l/d*1e3:null,packetLossPct:j?N/j*100:null,packetLossCumulativePct:C?r/C*100:null,packetsLost:r,packetsReceived:c,inboundBitrateBps:T,outboundBitrateBps:P,availableOutgoingBitrateBps:D,framesPerSecond:u||null,frameWidth:k||null,frameHeight:R||null,inboundFrameWidth:k||null,inboundFrameHeight:R||null,outboundFrameWidth:f||null,outboundFrameHeight:v||null,framesDropped:y,freezeCount:w,freezeDurationMs:Math.round(p*1e3),concealedSamples:E,codec:S,transportProtocol:L,candidateType:I,qualityLimitationReason:M}}}function Be(a){let e=a.mediaScore==null?5:Math.round(Se(Number(a.mediaScore),0,5));const t=Z(a.packetLossPct)??0,i=Z(a.rttMs)??0,n=Z(a.jitterMs)??0,s=String(a.connectionState??"unknown").toLowerCase();["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));const o=Number(Se(4.5-t*.09-i*.0011-n*.006,1,4.5).toFixed(2));return{networkScore:e,estimatedMos:o}}function ve(){return navigator.connection??null}function Fe(){const a=Number(navigator.deviceMemory);return Number.isFinite(a)?a:null}function Z(a){const e=Number(a);return Number.isFinite(e)?e:null}function Se(a,e,t){return Math.min(t,Math.max(e,a))}function x(a){return a instanceof Error?{name:a.name,message:a.message}:{name:"ServiceError",message:String(a??"Service request failed")}}function ke(a,e){return a instanceof Error?a:new Error(e)}const we="1.3.24";b.AVATAR_TUNING_DEFAULTS=Y,b.BACKGROUND_PRESETS=re,b.OVERLAY_EFFECTS=oe,b.RtcClient=he,b.RtcIncomingClient=pe,b.SPEAKER_NOISE_FLOOR=.02,b.SPEAKER_TAKEOVER_RATIO=1.4,b.createAvatarPipeline=Ne,b.createCallRecorder=Oe,b.createPictureInPictureController=De,b.createRtcClient=je,b.createRtcIncomingClient=xe,b.createVideoEffectsPipeline=qe,b.drawAvatar=de,b.headPoseFromMatrix=ce,b.neutralRig=J,b.pickActiveSpeaker=H,b.rigFromBlendshapes=le,b.scenePainters=$,b.smoothRig=G,b.stylizedAvatarRenderer=K,b.unlockRtcCallAudio=me,b.version=we,Object.defineProperty(b,Symbol.toStringTag,{value:"Module"})}));
|