@streaming-cdn/rtc-web 1.3.28 → 1.4.0
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/dist/index.d.ts +9 -1
- package/dist/rtc-web.es.js +139 -102
- package/dist/rtc-web.umd.js +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -324,12 +324,20 @@ export declare class RtcIncomingClient extends EventTarget {
|
|
|
324
324
|
private readonly seenInvitationIds;
|
|
325
325
|
private readonly invitationExpiryTimers;
|
|
326
326
|
private startCallRequest;
|
|
327
|
+
/** Bumped by disconnect() so a request still in flight cannot re-claim the call slot it just freed. */
|
|
328
|
+
private callGeneration;
|
|
327
329
|
private lastDisconnect;
|
|
328
330
|
constructor(options: RtcIncomingClientOptions);
|
|
329
331
|
connect(): this;
|
|
330
332
|
setMount(target: HTMLElement | string | null): void;
|
|
331
333
|
/** Binds media lifetime to a call. Terminal call states automatically leave and destroy it. */
|
|
332
334
|
attachMediaClient(callId: string, media: RtcClient): () => void;
|
|
335
|
+
/**
|
|
336
|
+
* Arms the self-release timer for a call that holds the single call slot but
|
|
337
|
+
* has no media attached. Every path that claims the slot arms this, so no
|
|
338
|
+
* failure can hold it for the life of the page.
|
|
339
|
+
*/
|
|
340
|
+
private armCallWatchdog;
|
|
333
341
|
/**
|
|
334
342
|
* Rings everyone in `targets` as a single call and connects whoever answers
|
|
335
343
|
* first; the rest stop ringing on their own. `target` still works and means a
|
|
@@ -395,4 +403,4 @@ export declare class RtcIncomingClient extends EventTarget {
|
|
|
395
403
|
private emit;
|
|
396
404
|
}
|
|
397
405
|
export declare function createRtcIncomingClient(options: RtcIncomingClientOptions): RtcIncomingClient;
|
|
398
|
-
export declare const version = "1.
|
|
406
|
+
export declare const version = "1.4.0";
|
package/dist/rtc-web.es.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const st = 0.02, rt = 1.4;
|
|
2
2
|
function ge(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;
|
|
@@ -937,7 +937,7 @@ class xe {
|
|
|
937
937
|
}
|
|
938
938
|
send(e) {
|
|
939
939
|
this.socket?.readyState === WebSocket.OPEN && this.socket.send(JSON.stringify({
|
|
940
|
-
clientMessageId: e.clientMessageId ??
|
|
940
|
+
clientMessageId: e.clientMessageId ?? Ne(),
|
|
941
941
|
...e
|
|
942
942
|
}));
|
|
943
943
|
}
|
|
@@ -945,10 +945,10 @@ class xe {
|
|
|
945
945
|
for (const t of e?.getTracks() ?? []) t.stop();
|
|
946
946
|
}
|
|
947
947
|
}
|
|
948
|
-
function
|
|
948
|
+
function Ne() {
|
|
949
949
|
return globalThis.crypto?.randomUUID?.() ?? `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
950
950
|
}
|
|
951
|
-
function
|
|
951
|
+
function ot(a, e = {}) {
|
|
952
952
|
const t = e.document ?? globalThis.document, i = !!(t && t.pictureInPictureEnabled);
|
|
953
953
|
let n = null;
|
|
954
954
|
function s() {
|
|
@@ -976,7 +976,7 @@ function rt(a, e = {}) {
|
|
|
976
976
|
}
|
|
977
977
|
};
|
|
978
978
|
}
|
|
979
|
-
function
|
|
979
|
+
function ct(a, e) {
|
|
980
980
|
const t = e.document ?? globalThis.document, i = e.fetchImplementation ?? globalThis.fetch.bind(globalThis), n = Z(e.credential.signalingUrl, globalThis.location?.href).origin, s = e.mimeType ?? "video/webm", o = e.height ?? 720, r = Math.round(o * 16 / 9);
|
|
981
981
|
let c = null, l = null, d = null, u = null, v = 0, p = [], S = !1;
|
|
982
982
|
async function T(y, k, f, M = "application/json") {
|
|
@@ -1007,14 +1007,14 @@ function ot(a, e) {
|
|
|
1007
1007
|
};
|
|
1008
1008
|
l = setInterval(() => {
|
|
1009
1009
|
const b = a.getRemoteStreams(), I = a.getLocalStream(), C = [
|
|
1010
|
-
...b.map((
|
|
1010
|
+
...b.map((N) => ({ key: N.participant.participantId, stream: N.stream, label: N.participant.displayName })),
|
|
1011
1011
|
...I ? [{ key: "local", stream: I, label: "me" }] : []
|
|
1012
1012
|
];
|
|
1013
1013
|
f.fillStyle = "#101418", f.fillRect(0, 0, r, o);
|
|
1014
|
-
const
|
|
1015
|
-
C.forEach((
|
|
1016
|
-
const te = R(
|
|
1017
|
-
te.videoWidth > 0 && f.drawImage(te,
|
|
1014
|
+
const U = Math.max(1, Math.ceil(Math.sqrt(C.length))), Q = Math.max(1, Math.ceil(C.length / U)), q = r / U, g = o / Q;
|
|
1015
|
+
C.forEach((N, ee) => {
|
|
1016
|
+
const te = R(N.key, N.stream), W = ee % U * q, $ = Math.floor(ee / U) * g;
|
|
1017
|
+
te.videoWidth > 0 && f.drawImage(te, W + 2, $ + 2, q - 4, g - 4), f.fillStyle = "rgba(0,0,0,.6)", f.fillRect(W + 8, $ + g - 30, Math.min(q - 16, 12 + N.label.length * 9), 22), f.fillStyle = "#fff", f.font = "14px system-ui", f.fillText(N.label, W + 14, $ + g - 14);
|
|
1018
1018
|
});
|
|
1019
1019
|
}, 66);
|
|
1020
1020
|
const L = k.captureStream(15);
|
|
@@ -1064,7 +1064,7 @@ function ot(a, e) {
|
|
|
1064
1064
|
}
|
|
1065
1065
|
};
|
|
1066
1066
|
}
|
|
1067
|
-
const
|
|
1067
|
+
const Ue = ["none", "blur", "aurora", "cybergrid", "bokeh", "sunset", "image"], Oe = ["none", "snow", "confetti", "vignette"], se = {
|
|
1068
1068
|
aurora(a, e, t, i) {
|
|
1069
1069
|
const n = a.createLinearGradient(0, 0, 0, t);
|
|
1070
1070
|
n.addColorStop(0, "#050b1e"), n.addColorStop(1, "#0b2540"), a.fillStyle = n, a.fillRect(0, 0, e, t);
|
|
@@ -1101,7 +1101,7 @@ const Ne = ["none", "blur", "aurora", "cybergrid", "bokeh", "sunset", "image"],
|
|
|
1101
1101
|
n.addColorStop(0, "#2b1055"), n.addColorStop(0.55, "#d1548a"), n.addColorStop(1, "#ffb36b"), a.fillStyle = n, a.fillRect(0, 0, e, t), a.fillStyle = "#ffdf8e", a.beginPath(), a.arc(e * 0.5, t * 0.62 + Math.sin(i / 4e3) * 4, t * 0.13, 0, Math.PI * 2), a.fill(), a.fillStyle = "rgba(20,8,40,0.85)", a.fillRect(0, t * 0.78, e, t * 0.22);
|
|
1102
1102
|
}
|
|
1103
1103
|
};
|
|
1104
|
-
function
|
|
1104
|
+
function lt(a, e = {}) {
|
|
1105
1105
|
const t = e.document ?? globalThis.document;
|
|
1106
1106
|
let i = e.width ?? 1280, n = e.height ?? 720;
|
|
1107
1107
|
const s = !!(e.width || e.height), o = e.frameRate ?? 24, r = a instanceof MediaStreamTrack ? a : a.getVideoTracks()[0];
|
|
@@ -1161,10 +1161,10 @@ function ct(a, e = {}) {
|
|
|
1161
1161
|
track: E.getVideoTracks()[0],
|
|
1162
1162
|
stream: E,
|
|
1163
1163
|
setBackground(m, h) {
|
|
1164
|
-
c =
|
|
1164
|
+
c = Ue.includes(m) ? m : "none", h !== void 0 && (d = h), c === "none" && (v = null);
|
|
1165
1165
|
},
|
|
1166
1166
|
setOverlay(m) {
|
|
1167
|
-
l =
|
|
1167
|
+
l = Oe.includes(m) ? m : "none";
|
|
1168
1168
|
},
|
|
1169
1169
|
setSegmenter(m) {
|
|
1170
1170
|
u = m, m || (v = null);
|
|
@@ -1195,7 +1195,7 @@ function D(a, e) {
|
|
|
1195
1195
|
const t = a.find((i) => i.categoryName === e);
|
|
1196
1196
|
return t ? Math.max(0, Math.min(1, t.score)) : 0;
|
|
1197
1197
|
}
|
|
1198
|
-
function
|
|
1198
|
+
function qe(a) {
|
|
1199
1199
|
if (!a || a.length < 16) return { yaw: 0, pitch: 0, roll: 0 };
|
|
1200
1200
|
a[0], a[4];
|
|
1201
1201
|
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);
|
|
@@ -1203,7 +1203,7 @@ function Oe(a) {
|
|
|
1203
1203
|
}
|
|
1204
1204
|
function Ve(a, e, t = 1) {
|
|
1205
1205
|
if (!a || a.length === 0) return ve();
|
|
1206
|
-
const i =
|
|
1206
|
+
const i = qe(e), n = (D(a, "eyeLookOutRight") + D(a, "eyeLookInLeft")) / 2, s = (D(a, "eyeLookOutLeft") + D(a, "eyeLookInRight")) / 2, o = (D(a, "eyeLookUpLeft") + D(a, "eyeLookUpRight")) / 2, r = (D(a, "eyeLookDownLeft") + D(a, "eyeLookDownRight")) / 2, c = (D(a, "mouthSmileLeft") + D(a, "mouthSmileRight")) / 2, l = (D(a, "mouthFrownLeft") + D(a, "mouthFrownRight")) / 2, d = D(a, "eyeBlinkLeft"), u = D(a, "eyeBlinkRight"), v = Math.min(1, Math.abs(i.yaw) / 0.35) * Math.max(0, Math.min(1, t)), p = (d + u) / 2;
|
|
1207
1207
|
return {
|
|
1208
1208
|
blinkLeft: d + (p - d) * v,
|
|
1209
1209
|
blinkRight: u + (p - u) * v,
|
|
@@ -1249,10 +1249,10 @@ function je(a, e, t, i, n) {
|
|
|
1249
1249
|
if (E <= 0.12) continue;
|
|
1250
1250
|
const m = u(w) * 0.82, h = Math.max(0.2, E), b = A < 0 ? e.blinkLeft : e.blinkRight, I = Math.max(0.06, 1 - b), C = 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 - C + p), a.quadraticCurveTo(m, -r * 0.52 - C + p, m + r * 0.2 * h, -r * 0.44 - C + p), a.stroke();
|
|
1252
|
-
const
|
|
1253
|
-
a.fillStyle = "#ffffff", a.beginPath(), a.ellipse(m,
|
|
1252
|
+
const U = -r * 0.18 + p;
|
|
1253
|
+
a.fillStyle = "#ffffff", a.beginPath(), a.ellipse(m, U, r * 0.16 * h, r * 0.12 * I, 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
|
+
U + e.pupilY * r * 0.05,
|
|
1256
1256
|
r * 0.06 * h,
|
|
1257
1257
|
r * 0.06 * I,
|
|
1258
1258
|
0,
|
|
@@ -1280,7 +1280,7 @@ function ce() {
|
|
|
1280
1280
|
};
|
|
1281
1281
|
}
|
|
1282
1282
|
const G = (a) => Math.max(0, Math.min(1, a));
|
|
1283
|
-
function
|
|
1283
|
+
function dt(a, e = {}) {
|
|
1284
1284
|
const t = e.document ?? globalThis.document;
|
|
1285
1285
|
let i = e.width ?? 1280, n = e.height ?? 720;
|
|
1286
1286
|
const s = !!(e.width || e.height), o = e.frameRate ?? 24, r = a instanceof MediaStreamTrack ? a : a.getVideoTracks()[0];
|
|
@@ -1302,8 +1302,8 @@ function lt(a, e = {}) {
|
|
|
1302
1302
|
let k = ve(), f = null, M = 0;
|
|
1303
1303
|
function R(w) {
|
|
1304
1304
|
if (w.length <= 454) return;
|
|
1305
|
-
const E = w[10], m = w[152], h = w[234], b = w[454], I = (h.x + b.x) / 2 * i, C = (h.y + b.y) / 2 * n,
|
|
1306
|
-
f = f ? { x: f.x + (
|
|
1305
|
+
const E = w[10], m = w[152], h = w[234], b = w[454], I = (h.x + b.x) / 2 * i, C = (h.y + b.y) / 2 * n, U = Math.hypot((m.x - E.x) * i, (m.y - E.y) * n), Q = Math.max(0.6, Math.cos(k.headPitch)), q = { x: I, y: C, radius: Math.max(24, U / Q * 0.8) };
|
|
1306
|
+
f = f ? { x: f.x + (q.x - f.x) * 0.4, y: f.y + (q.y - f.y) * 0.4, radius: f.radius + (q.radius - f.radius) * 0.15 } : q;
|
|
1307
1307
|
}
|
|
1308
1308
|
function L(w) {
|
|
1309
1309
|
if (c && p.readyState >= 2) {
|
|
@@ -1404,7 +1404,7 @@ async function Qe(a, e) {
|
|
|
1404
1404
|
const c = new Blob([r]).stream().pipeThrough(new DecompressionStream("deflate-raw"));
|
|
1405
1405
|
return new Response(c).arrayBuffer();
|
|
1406
1406
|
}
|
|
1407
|
-
async function
|
|
1407
|
+
async function ht(a) {
|
|
1408
1408
|
const e = typeof File < "u" && a instanceof File ? await a.arrayBuffer() : a;
|
|
1409
1409
|
if (!Te(e)) return [];
|
|
1410
1410
|
try {
|
|
@@ -1429,11 +1429,11 @@ class x extends Error {
|
|
|
1429
1429
|
function Ce(a) {
|
|
1430
1430
|
return a.filter((e) => /\.vrm$/i.test(e.path) && !e.path.endsWith("/") && !e.path.split("/").some((t) => t === "__MACOSX" || t.startsWith("._")));
|
|
1431
1431
|
}
|
|
1432
|
-
function
|
|
1432
|
+
function O(a) {
|
|
1433
1433
|
const e = typeof a == "string" ? a.trim() : "";
|
|
1434
1434
|
return e.length > 0 ? e : null;
|
|
1435
1435
|
}
|
|
1436
|
-
function
|
|
1436
|
+
function We(a) {
|
|
1437
1437
|
const e = new DataView(a);
|
|
1438
1438
|
if (a.byteLength < 20 || e.getUint32(0, !0) !== ze)
|
|
1439
1439
|
throw new x("model_not_glb", "The file is not a binary glTF (.vrm / .glb) model.");
|
|
@@ -1457,24 +1457,24 @@ function $e(a) {
|
|
|
1457
1457
|
if (o) {
|
|
1458
1458
|
const r = Array.isArray(o.authors) ? o.authors.filter((c) => typeof c == "string") : [];
|
|
1459
1459
|
return {
|
|
1460
|
-
title:
|
|
1460
|
+
title: O(o.name),
|
|
1461
1461
|
author: r.length ? r.join(", ") : null,
|
|
1462
|
-
allowedUser:
|
|
1463
|
-
commercialUse:
|
|
1464
|
-
licenseName:
|
|
1465
|
-
licenseUrl:
|
|
1462
|
+
allowedUser: O(o.avatarPermission),
|
|
1463
|
+
commercialUse: O(o.commercialUsage),
|
|
1464
|
+
licenseName: O(o.licenseUrl) ? "VRM 1.0" : null,
|
|
1465
|
+
licenseUrl: O(o.licenseUrl) ?? O(o.otherLicenseUrl)
|
|
1466
1466
|
};
|
|
1467
1467
|
}
|
|
1468
1468
|
return {
|
|
1469
|
-
title:
|
|
1470
|
-
author:
|
|
1471
|
-
allowedUser:
|
|
1472
|
-
commercialUse:
|
|
1473
|
-
licenseName:
|
|
1474
|
-
licenseUrl:
|
|
1469
|
+
title: O(s?.title),
|
|
1470
|
+
author: O(s?.author),
|
|
1471
|
+
allowedUser: O(s?.allowedUserName),
|
|
1472
|
+
commercialUse: O(s?.commercialUssageName),
|
|
1473
|
+
licenseName: O(s?.licenseName),
|
|
1474
|
+
licenseUrl: O(s?.otherLicenseUrl)
|
|
1475
1475
|
};
|
|
1476
1476
|
}
|
|
1477
|
-
function
|
|
1477
|
+
function $e() {
|
|
1478
1478
|
return new Promise((a, e) => {
|
|
1479
1479
|
if (typeof indexedDB > "u") {
|
|
1480
1480
|
e(new x("storage_unavailable", "This browser cannot store imported avatar models."));
|
|
@@ -1487,7 +1487,7 @@ function We() {
|
|
|
1487
1487
|
});
|
|
1488
1488
|
}
|
|
1489
1489
|
async function H(a, e) {
|
|
1490
|
-
const t = await
|
|
1490
|
+
const t = await $e();
|
|
1491
1491
|
try {
|
|
1492
1492
|
return await new Promise((i, n) => {
|
|
1493
1493
|
const s = e(t.transaction(z, a).objectStore(z));
|
|
@@ -1504,7 +1504,7 @@ const Ee = (a) => ({
|
|
|
1504
1504
|
importedAt: a.importedAt,
|
|
1505
1505
|
license: a.license
|
|
1506
1506
|
});
|
|
1507
|
-
async function
|
|
1507
|
+
async function ut(a, e = {}) {
|
|
1508
1508
|
const t = e.maxBytes ?? _e, i = typeof File < "u" && a instanceof File;
|
|
1509
1509
|
let n = i ? a.name : null, s = i ? await a.arrayBuffer() : a;
|
|
1510
1510
|
if (Te(s)) {
|
|
@@ -1516,7 +1516,7 @@ async function ht(a, e = {}) {
|
|
|
1516
1516
|
"model_too_large",
|
|
1517
1517
|
"The model is larger than the " + Math.round(t / 1024 / 1024) + " MB limit."
|
|
1518
1518
|
);
|
|
1519
|
-
const o =
|
|
1519
|
+
const o = We(s);
|
|
1520
1520
|
if (!o)
|
|
1521
1521
|
throw new x("model_not_vrm", "The file is a glTF model but carries no VRM character data.");
|
|
1522
1522
|
const r = o.title || (n ? n.replace(/\.(vrm|glb)$/i, "") : null), c = {
|
|
@@ -1559,13 +1559,13 @@ async function Je(a, e, t) {
|
|
|
1559
1559
|
function le(a) {
|
|
1560
1560
|
return a === "zip_encrypted" ? "archive_encrypted" : a === "zip_unsupported" ? "archive_unsupported" : "archive_unreadable";
|
|
1561
1561
|
}
|
|
1562
|
-
async function
|
|
1562
|
+
async function mt() {
|
|
1563
1563
|
return (await H("readonly", (e) => e.getAll()) ?? []).map(Ee).sort((e, t) => t.importedAt.localeCompare(e.importedAt));
|
|
1564
1564
|
}
|
|
1565
|
-
async function
|
|
1565
|
+
async function pt(a) {
|
|
1566
1566
|
return (await H("readonly", (t) => t.get(a)))?.data ?? null;
|
|
1567
1567
|
}
|
|
1568
|
-
async function
|
|
1568
|
+
async function ft(a) {
|
|
1569
1569
|
await H("readwrite", (e) => e.delete(a));
|
|
1570
1570
|
}
|
|
1571
1571
|
class Ge extends EventTarget {
|
|
@@ -1622,7 +1622,7 @@ class Ge extends EventTarget {
|
|
|
1622
1622
|
localStream: this.options.localStream,
|
|
1623
1623
|
simulcast: this.options.simulcast ?? !0,
|
|
1624
1624
|
onEvent: (i, n) => this.handleNativeEvent(i, n)
|
|
1625
|
-
}), await this.client.initialize(), this.startStats(), this.emit("initialized", { mode: this.mode, sdkVersion:
|
|
1625
|
+
}), await this.client.initialize(), this.startStats(), this.emit("initialized", { mode: this.mode, sdkVersion: at }), this.options.autoJoin && await this.join(), this;
|
|
1626
1626
|
} catch (e) {
|
|
1627
1627
|
this.state = "failed";
|
|
1628
1628
|
const t = fe(e, "RTC initialization failed");
|
|
@@ -1786,7 +1786,7 @@ class Ge extends EventTarget {
|
|
|
1786
1786
|
}), d;
|
|
1787
1787
|
}
|
|
1788
1788
|
async collectQualitySample(e = "runtime") {
|
|
1789
|
-
const t = this.options.statsProvider ? await this.options.statsProvider() : {}, i = this.peerConnection ? await
|
|
1789
|
+
const t = this.options.statsProvider ? await this.options.statsProvider() : {}, i = this.peerConnection ? await tt(this.peerConnection, this.previousCounters) : null;
|
|
1790
1790
|
i?.counters && (this.previousCounters = i.counters);
|
|
1791
1791
|
const s = { ...{
|
|
1792
1792
|
sampleType: e,
|
|
@@ -1819,9 +1819,9 @@ class Ge extends EventTarget {
|
|
|
1819
1819
|
transportProtocol: null,
|
|
1820
1820
|
candidateType: null,
|
|
1821
1821
|
qualityLimitationReason: null,
|
|
1822
|
-
deviceMemoryGb:
|
|
1822
|
+
deviceMemoryGb: nt(),
|
|
1823
1823
|
effectiveConnectionType: me()?.effectiveType ?? null
|
|
1824
|
-
}, ...i?.sample ?? {}, ...t, sampleType: e, sampledAt: (/* @__PURE__ */ new Date()).toISOString() }, o =
|
|
1824
|
+
}, ...i?.sample ?? {}, ...t, sampleType: e, sampledAt: (/* @__PURE__ */ new Date()).toISOString() }, o = it(s);
|
|
1825
1825
|
return s.networkScore = o.networkScore, s.estimatedMos = o.estimatedMos, this.latestQuality = s, s;
|
|
1826
1826
|
}
|
|
1827
1827
|
async reportQuality(e) {
|
|
@@ -1906,7 +1906,7 @@ class Ge extends EventTarget {
|
|
|
1906
1906
|
this.dispatchEvent(new CustomEvent(e, { detail: i })), this.options.onEvent?.(i);
|
|
1907
1907
|
}
|
|
1908
1908
|
}
|
|
1909
|
-
async function
|
|
1909
|
+
async function gt(a) {
|
|
1910
1910
|
return new Ge(a).initialize();
|
|
1911
1911
|
}
|
|
1912
1912
|
let _ = null;
|
|
@@ -1967,7 +1967,8 @@ class Ke {
|
|
|
1967
1967
|
}
|
|
1968
1968
|
}
|
|
1969
1969
|
}
|
|
1970
|
-
|
|
1970
|
+
const Xe = 6e4;
|
|
1971
|
+
class Ze extends EventTarget {
|
|
1971
1972
|
options;
|
|
1972
1973
|
socket = null;
|
|
1973
1974
|
stopped = !1;
|
|
@@ -1989,6 +1990,8 @@ class Xe extends EventTarget {
|
|
|
1989
1990
|
seenInvitationIds = /* @__PURE__ */ new Set();
|
|
1990
1991
|
invitationExpiryTimers = /* @__PURE__ */ new Map();
|
|
1991
1992
|
startCallRequest = null;
|
|
1993
|
+
/** Bumped by disconnect() so a request still in flight cannot re-claim the call slot it just freed. */
|
|
1994
|
+
callGeneration = 0;
|
|
1992
1995
|
lastDisconnect = null;
|
|
1993
1996
|
constructor(e) {
|
|
1994
1997
|
if (super(), !e.token && !e.tokenProvider) throw new Error("A client token or tokenProvider is required");
|
|
@@ -2007,10 +2010,18 @@ class Xe extends EventTarget {
|
|
|
2007
2010
|
/** Binds media lifetime to a call. Terminal call states automatically leave and destroy it. */
|
|
2008
2011
|
attachMediaClient(e, t) {
|
|
2009
2012
|
if (!e.trim()) throw new Error("callId is required");
|
|
2010
|
-
return this.mediaClients.set(e, t), () => {
|
|
2011
|
-
this.mediaClients.get(e) === t && this.mediaClients.delete(e);
|
|
2013
|
+
return this.mediaClients.set(e, t), this.clearInvitationExpiry(e), () => {
|
|
2014
|
+
this.mediaClients.get(e) === t && (this.mediaClients.delete(e), this.activeCallIds.has(e) && this.armCallWatchdog(e));
|
|
2012
2015
|
};
|
|
2013
2016
|
}
|
|
2017
|
+
/**
|
|
2018
|
+
* Arms the self-release timer for a call that holds the single call slot but
|
|
2019
|
+
* has no media attached. Every path that claims the slot arms this, so no
|
|
2020
|
+
* failure can hold it for the life of the page.
|
|
2021
|
+
*/
|
|
2022
|
+
armCallWatchdog(e) {
|
|
2023
|
+
this.scheduleInvitationExpiry(e, new Date(Date.now() + Xe).toISOString());
|
|
2024
|
+
}
|
|
2014
2025
|
/**
|
|
2015
2026
|
* Rings everyone in `targets` as a single call and connects whoever answers
|
|
2016
2027
|
* first; the rest stop ringing on their own. `target` still works and means a
|
|
@@ -2021,51 +2032,77 @@ class Xe extends EventTarget {
|
|
|
2021
2032
|
if (this.activeCallIds.size > 0) throw new Error("Another call is already active");
|
|
2022
2033
|
const t = e.targets?.length ? e.targets : e.target ? [e.target] : [];
|
|
2023
2034
|
if (t.length === 0) throw new Error("startCall requires at least one target");
|
|
2024
|
-
const i = { mode: e.mode, targets: t, metadata: e.metadata, idempotencyKey: e.idempotencyKey }, n = (async () => {
|
|
2035
|
+
const i = { mode: e.mode, targets: t, metadata: e.metadata, idempotencyKey: e.idempotencyKey }, n = this.callGeneration, s = (async () => {
|
|
2025
2036
|
await this.callAudio.unlock(), this.callAudio.startRingback();
|
|
2026
|
-
let
|
|
2037
|
+
let o;
|
|
2027
2038
|
try {
|
|
2028
|
-
|
|
2039
|
+
o = await this.request("/v1/rtc/client/calls", {
|
|
2029
2040
|
method: "POST",
|
|
2030
2041
|
headers: e.idempotencyKey ? { "idempotency-key": e.idempotencyKey } : void 0,
|
|
2031
2042
|
body: JSON.stringify(i)
|
|
2032
2043
|
});
|
|
2033
|
-
} catch (
|
|
2034
|
-
throw this.callAudio.stop(),
|
|
2044
|
+
} catch (c) {
|
|
2045
|
+
throw this.callAudio.stop(), c;
|
|
2035
2046
|
}
|
|
2036
|
-
|
|
2047
|
+
!o.credential && o.callerCredential && (o.credential = o.callerCredential);
|
|
2048
|
+
const r = String(o.call?.id ?? "") || null;
|
|
2049
|
+
if (r && n === this.callGeneration) {
|
|
2050
|
+
this.outgoingCallId = r, this.activeCallIds.add(r);
|
|
2051
|
+
const c = o.call?.expiresAt;
|
|
2052
|
+
this.scheduleInvitationExpiry(r, typeof c == "string" ? c : void 0);
|
|
2053
|
+
} else r && this.callAction(r, "cancel").catch(() => {
|
|
2054
|
+
});
|
|
2055
|
+
return this.emit("outgoingcall", o), o;
|
|
2037
2056
|
})();
|
|
2038
|
-
this.startCallRequest =
|
|
2057
|
+
this.startCallRequest = s;
|
|
2039
2058
|
try {
|
|
2040
|
-
return await
|
|
2059
|
+
return await s;
|
|
2041
2060
|
} finally {
|
|
2042
|
-
this.startCallRequest ===
|
|
2061
|
+
this.startCallRequest === s && (this.startCallRequest = null);
|
|
2043
2062
|
}
|
|
2044
2063
|
}
|
|
2045
2064
|
async accept(e = this.currentCall?.id) {
|
|
2046
2065
|
if (!e) throw new Error("No incoming call is available");
|
|
2047
2066
|
const t = await this.callAction(e, "accept");
|
|
2048
|
-
return this.activeCallIds.add(e), this.
|
|
2067
|
+
return this.activeCallIds.add(e), this.armCallWatchdog(e), this.callAudio.stop(), this.dismiss(e), this.emit("callaccepted", t), t;
|
|
2049
2068
|
}
|
|
2069
|
+
// Local release runs in a finally on all four: it must never depend on a
|
|
2070
|
+
// network round trip. A hang-up while offline, or after the server already
|
|
2071
|
+
// swept the call terminal (which answers 409), used to leave the call id in
|
|
2072
|
+
// the active set for the life of the page, and startCall() refuses while any
|
|
2073
|
+
// id is held.
|
|
2050
2074
|
async reject(e = this.currentCall?.id) {
|
|
2051
2075
|
if (!e) throw new Error("No incoming call is available");
|
|
2052
|
-
|
|
2053
|
-
|
|
2076
|
+
let t;
|
|
2077
|
+
try {
|
|
2078
|
+
return t = await this.callAction(e, "reject");
|
|
2079
|
+
} finally {
|
|
2080
|
+
this.releaseMedia(e), this.showTerminal(e, "Call declined"), this.emit("callrejected", t ?? { call: { id: e } });
|
|
2081
|
+
}
|
|
2054
2082
|
}
|
|
2055
2083
|
async busy(e = this.currentCall?.id) {
|
|
2056
2084
|
if (!e) throw new Error("No incoming call is available");
|
|
2057
|
-
|
|
2058
|
-
|
|
2085
|
+
try {
|
|
2086
|
+
return await this.callAction(e, "busy");
|
|
2087
|
+
} finally {
|
|
2088
|
+
this.releaseMedia(e), this.showTerminal(e, "Line busy");
|
|
2089
|
+
}
|
|
2059
2090
|
}
|
|
2060
2091
|
async end(e) {
|
|
2061
2092
|
this.callAudio.stop();
|
|
2062
|
-
|
|
2063
|
-
|
|
2093
|
+
try {
|
|
2094
|
+
return await this.callAction(e, "end");
|
|
2095
|
+
} finally {
|
|
2096
|
+
this.releaseMedia(e), this.showTerminal(e, "Call ended");
|
|
2097
|
+
}
|
|
2064
2098
|
}
|
|
2065
2099
|
async cancel(e) {
|
|
2066
2100
|
this.callAudio.stop();
|
|
2067
|
-
|
|
2068
|
-
|
|
2101
|
+
try {
|
|
2102
|
+
return await this.callAction(e, "cancel");
|
|
2103
|
+
} finally {
|
|
2104
|
+
this.releaseMedia(e), this.showTerminal(e, "Call cancelled");
|
|
2105
|
+
}
|
|
2069
2106
|
}
|
|
2070
2107
|
/** Injects an incoming call received through the application's own push or signaling channel. */
|
|
2071
2108
|
handleIncomingCall(e) {
|
|
@@ -2113,10 +2150,10 @@ class Xe extends EventTarget {
|
|
|
2113
2150
|
/** Applies a call state received through the application's own push or signaling channel. */
|
|
2114
2151
|
handleCallUpdate(e) {
|
|
2115
2152
|
const t = e.id === this.currentCall?.id || e.id === this.outgoingCallId, i = ["answered_elsewhere", "rejected", "busy", "cancelled", "ended", "failed", "missed"];
|
|
2116
|
-
(e.status === "accepted"
|
|
2153
|
+
i.includes(e.status) && this.clearInvitationExpiry(e.id), e.status === "accepted" && (this.mediaClients.has(e.id) ? this.clearInvitationExpiry(e.id) : this.armCallWatchdog(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, et(e.status)), i.includes(e.status) && this.releaseMedia(e.id), this.emit("callupdated", e);
|
|
2117
2154
|
}
|
|
2118
2155
|
disconnect() {
|
|
2119
|
-
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();
|
|
2156
|
+
this.stopped = !0, this.callGeneration += 1, 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();
|
|
2120
2157
|
for (const e of this.invitationExpiryTimers.values()) clearTimeout(e);
|
|
2121
2158
|
this.invitationExpiryTimers.clear();
|
|
2122
2159
|
for (const e of this.mediaClients.keys()) this.releaseMedia(e);
|
|
@@ -2306,7 +2343,7 @@ class Xe extends EventTarget {
|
|
|
2306
2343
|
}, Math.min(t, 2147e6));
|
|
2307
2344
|
}
|
|
2308
2345
|
releaseMedia(e) {
|
|
2309
|
-
this.activeCallIds.delete(e), this.seenInvitationIds.delete(e);
|
|
2346
|
+
this.clearInvitationExpiry(e), this.activeCallIds.delete(e), this.seenInvitationIds.delete(e);
|
|
2310
2347
|
const t = this.mediaClients.get(e);
|
|
2311
2348
|
t && (this.mediaClients.delete(e), t.leave().catch(() => {
|
|
2312
2349
|
}).finally(() => t.destroy()));
|
|
@@ -2322,8 +2359,8 @@ class Xe extends EventTarget {
|
|
|
2322
2359
|
this.dispatchEvent(new CustomEvent(e, { detail: i })), this.options.onEvent?.(i);
|
|
2323
2360
|
}
|
|
2324
2361
|
}
|
|
2325
|
-
function
|
|
2326
|
-
const e = new
|
|
2362
|
+
function yt(a) {
|
|
2363
|
+
const e = new Ze(a);
|
|
2327
2364
|
return a.autoConnect !== !1 && a.signalingUrl && e.connect(), e;
|
|
2328
2365
|
}
|
|
2329
2366
|
function de(a) {
|
|
@@ -2342,7 +2379,7 @@ function he(a, e, t, i) {
|
|
|
2342
2379
|
fontWeight: "700"
|
|
2343
2380
|
}), n;
|
|
2344
2381
|
}
|
|
2345
|
-
function
|
|
2382
|
+
function et(a) {
|
|
2346
2383
|
return {
|
|
2347
2384
|
rejected: "Call declined",
|
|
2348
2385
|
busy: "Line busy",
|
|
@@ -2360,7 +2397,7 @@ function ue(a) {
|
|
|
2360
2397
|
role: "participant"
|
|
2361
2398
|
};
|
|
2362
2399
|
}
|
|
2363
|
-
async function
|
|
2400
|
+
async function tt(a, e) {
|
|
2364
2401
|
const t = await a.getStats(), i = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Map();
|
|
2365
2402
|
let s = 0, o = 0, r = 0, c = 0, l = 0, d = 0, u = 0, v = 0, p = 0, S = 0, T = 0, y = 0, k = 0, f = 0, M = 0, R = null, L = null, P = null, A = null, w = null, E = null;
|
|
2366
2403
|
t.forEach((g) => {
|
|
@@ -2368,17 +2405,17 @@ async function et(a, e) {
|
|
|
2368
2405
|
}), t.forEach((g) => {
|
|
2369
2406
|
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)), v = Math.max(v, Number(g.frameWidth ?? 0)), T = Math.max(T, Number(g.frameHeight ?? 0)), y += Number(g.framesDropped ?? 0), k += Number(g.freezeCount ?? 0), f += Number(g.totalFreezesDuration ?? 0), M += Number(g.concealedSamples ?? 0), w ||= i.get(String(g.codecId ?? "")) ?? null), g.type === "outbound-rtp" && !g.isRemote && (p = Math.max(p, Number(g.frameWidth ?? 0)), S = Math.max(S, Number(g.frameHeight ?? 0)), o += Number(g.bytesSent ?? 0), E ||= g.qualityLimitationReason ? String(g.qualityLimitationReason) : null, w ||= i.get(String(g.codecId ?? "")) ?? null), g.type === "candidate-pair" && (g.selected || g.nominated) && g.state === "succeeded") {
|
|
2370
2407
|
R = Number.isFinite(g.currentRoundTripTime) ? Number(g.currentRoundTripTime) : R, L = Number.isFinite(g.availableOutgoingBitrate) ? Number(g.availableOutgoingBitrate) : L;
|
|
2371
|
-
const
|
|
2372
|
-
P =
|
|
2408
|
+
const N = n.get(g.localCandidateId);
|
|
2409
|
+
P = N?.candidateType ? String(N.candidateType) : P, A = N?.protocol ? String(N.protocol) : A;
|
|
2373
2410
|
}
|
|
2374
2411
|
});
|
|
2375
|
-
const m = Date.now(), h = e ? Math.max(1e-3, (m - e.timestamp) / 1e3) : 0, b = e ? Math.max(0, Math.round((s - e.inboundBytes) * 8 / h)) : null, I = e ? Math.max(0, Math.round((o - e.outboundBytes) * 8 / h)) : null, C = r + c,
|
|
2412
|
+
const m = Date.now(), h = e ? Math.max(1e-3, (m - e.timestamp) / 1e3) : 0, b = e ? Math.max(0, Math.round((s - e.inboundBytes) * 8 / h)) : null, I = e ? Math.max(0, Math.round((o - e.outboundBytes) * 8 / h)) : null, C = r + c, U = e ? Math.max(0, r - e.packetsLost) : r, Q = e ? Math.max(0, c - e.packetsReceived) : c, q = U + Q;
|
|
2376
2413
|
return {
|
|
2377
2414
|
counters: { timestamp: m, inboundBytes: s, outboundBytes: o, packetsLost: r, packetsReceived: c },
|
|
2378
2415
|
sample: {
|
|
2379
2416
|
rttMs: R === null ? null : R * 1e3,
|
|
2380
2417
|
jitterMs: d ? l / d * 1e3 : null,
|
|
2381
|
-
packetLossPct:
|
|
2418
|
+
packetLossPct: q ? U / q * 100 : null,
|
|
2382
2419
|
packetLossCumulativePct: C ? r / C * 100 : null,
|
|
2383
2420
|
packetsLost: r,
|
|
2384
2421
|
packetsReceived: c,
|
|
@@ -2403,7 +2440,7 @@ async function et(a, e) {
|
|
|
2403
2440
|
}
|
|
2404
2441
|
};
|
|
2405
2442
|
}
|
|
2406
|
-
function
|
|
2443
|
+
function it(a) {
|
|
2407
2444
|
let e = a.mediaScore == null ? 5 : Math.round(pe(Number(a.mediaScore), 0, 5));
|
|
2408
2445
|
const t = Y(a.packetLossPct) ?? 0, i = Y(a.rttMs) ?? 0, n = Y(a.jitterMs) ?? 0, s = String(a.connectionState ?? "unknown").toLowerCase();
|
|
2409
2446
|
["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));
|
|
@@ -2413,7 +2450,7 @@ function tt(a) {
|
|
|
2413
2450
|
function me() {
|
|
2414
2451
|
return navigator.connection ?? null;
|
|
2415
2452
|
}
|
|
2416
|
-
function
|
|
2453
|
+
function nt() {
|
|
2417
2454
|
const a = Number(navigator.deviceMemory);
|
|
2418
2455
|
return Number.isFinite(a) ? a : null;
|
|
2419
2456
|
}
|
|
@@ -2430,37 +2467,37 @@ function B(a) {
|
|
|
2430
2467
|
function fe(a, e) {
|
|
2431
2468
|
return a instanceof Error ? a : new Error(e);
|
|
2432
2469
|
}
|
|
2433
|
-
const
|
|
2470
|
+
const at = "1.4.0";
|
|
2434
2471
|
export {
|
|
2435
2472
|
_e as AVATAR_MODEL_MAX_BYTES,
|
|
2436
2473
|
oe as AVATAR_TUNING_DEFAULTS,
|
|
2437
2474
|
x as AvatarModelError,
|
|
2438
|
-
|
|
2439
|
-
|
|
2475
|
+
Ue as BACKGROUND_PRESETS,
|
|
2476
|
+
Oe as OVERLAY_EFFECTS,
|
|
2440
2477
|
Ge as RtcClient,
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2478
|
+
Ze as RtcIncomingClient,
|
|
2479
|
+
st as SPEAKER_NOISE_FLOOR,
|
|
2480
|
+
rt as SPEAKER_TAKEOVER_RATIO,
|
|
2481
|
+
dt as createAvatarPipeline,
|
|
2482
|
+
ct as createCallRecorder,
|
|
2483
|
+
ot as createPictureInPictureController,
|
|
2484
|
+
gt as createRtcClient,
|
|
2485
|
+
yt as createRtcIncomingClient,
|
|
2486
|
+
lt as createVideoEffectsPipeline,
|
|
2450
2487
|
je as drawAvatar,
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2488
|
+
qe as headPoseFromMatrix,
|
|
2489
|
+
ut as importAvatarModel,
|
|
2490
|
+
ht as listArchiveModels,
|
|
2491
|
+
mt as listAvatarModels,
|
|
2455
2492
|
ve as neutralRig,
|
|
2456
2493
|
ge as pickActiveSpeaker,
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2494
|
+
pt as readAvatarModel,
|
|
2495
|
+
We as readAvatarModelMetadata,
|
|
2496
|
+
ft as removeAvatarModel,
|
|
2460
2497
|
Ve as rigFromBlendshapes,
|
|
2461
2498
|
se as scenePainters,
|
|
2462
2499
|
re as smoothRig,
|
|
2463
2500
|
ce as stylizedAvatarRenderer,
|
|
2464
2501
|
Ye as unlockRtcCallAudio,
|
|
2465
|
-
|
|
2502
|
+
at as version
|
|
2466
2503
|
};
|
package/dist/rtc-web.umd.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(k,F){typeof exports=="object"&&typeof module<"u"?F(exports):typeof define=="function"&&define.amd?define(["exports"],F):(k=typeof globalThis<"u"?globalThis:k||self,F(k.StreamingCdnRtc={}))})(this,(function(k){"use strict";function J(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 ce(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}},je={warmupSamples:3,poorSamplesToStepDown:2,goodSamplesToStepUp:6,criticalLossPct:8,criticalRttMs:800,poorLossPct:3,poorRttMs:350,goodLossPct:1,goodRttMs:250,sustainRatio:.8,bandwidthSustainRatio:.7};function Qe(a){const e=String(a??"").trim().toLowerCase();return!e||e==="none"?"":e==="cpu"||e==="bandwidth"?e:"other"}function _e(a,e,t,i,n={}){const s={...je,...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,v=Qe(e.qualityLimitationReason),p=e.packetLossPct??0,S=e.rttMs??0,b=e.availableOutgoingBitrateBps,y=V[a].maxBitrate,T=p>=s.criticalLossPct||S>=s.criticalRttMs||v==="cpu",f=v==="bandwidth"&&(b==null||b<y*s.bandwidthSustainRatio),M=T||p>=s.poorLossPct||S>=s.poorRttMs||f,I=l<o.length-1?V[o[l+1]].maxBitrate:null,D=v==="bandwidth"&&b!=null&&I!=null&&b>=I*s.bandwidthSustainRatio,P=b==null||b>=y*s.sustainRatio,L=p<s.goodLossPct&&S<s.goodRttMs&&(!v||D)&&P,w=M?t+1:0,R=L?i+1:0,m={quality:a,poorSamples:w,goodSamples:R,samplesSeen:d,changed:!1};return l>c?{quality:o[c],poorSamples:0,goodSamples:0,samplesSeen:d,changed:!0}:u?m:T&&l>0?{quality:o[l-1],poorSamples:0,goodSamples:0,samplesSeen:d,changed:!0}:w>=s.poorSamplesToStepDown&&l>0?{quality:o[l-1],poorSamples:0,goodSamples:0,samplesSeen:d,changed:!0}:R>=s.goodSamplesToStepUp&&l<c?{quality:o[l+1],poorSamples:0,goodSamples:0,samplesSeen:d,changed:!0}:m}function Fe(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 v=Number(d.roundTripTime??NaN);Number.isFinite(v)&&(o=v*1e3)}if(u==="candidate-pair"&&(d.nominated===!0||String(d.state??"")==="succeeded")){const v=Number(d.currentRoundTripTime??NaN);o==null&&Number.isFinite(v)&&(o=v*1e3);const p=Number(d.availableOutgoingBitrate??NaN);Number.isFinite(p)&&(r=p)}}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 G(a,e){const t=new URL(String(a),e);return t.protocol==="https:"?t.protocol="wss:":t.protocol==="http:"&&(t.protocol="ws:"),t}function Y(a,e){const t=new URL(String(a),e);return t.protocol==="wss:"?t.protocol="https:":t.protocol==="ws:"&&(t.protocol="http:"),t}const ze={audio:{active:!1,maxBitrate:0,maxFramerate:0,scaleResolutionDownBy:4}},He={active:!0,maxBitrate:4e6,maxFramerate:30,scaleResolutionDownBy:1};class z{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=z.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=G(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:z.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=le();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?He:this.quality==="audio"?ze.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 ce(n.values()))e.push({participantId:i.identity.participantId,level:s.level})}catch{}const t=J(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}=Fe(t,e.counters);e.counters=n;const s=_e(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??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)}`}const K={active:!0,maxBitrate:4e6,maxFramerate:30,scaleResolutionDownBy:1},de=[{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}],he={high:"a",medium:"b",low:"c"};class $e{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=Y(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=G(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:de.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:he[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:he[this.quality];n.encodings=s.map(r=>{if(i)return{...r,active:r.rid==="a",...r.rid==="a"?{maxBitrate:K.maxBitrate,maxFramerate:K.maxFramerate}:{}};const c=de.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?K: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 ce(t.values())){const o=s.mid?this.midToParticipant.get(s.mid):void 0;o&&i.push({participantId:o,level:s.level})}const n=J(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??We(),...e}))}stopStream(e){for(const t of e?.getTracks()??[])t.stop()}}function We(){return globalThis.crypto?.randomUUID?.()??`${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}function Je(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 Ge(a,e){const t=e.document??globalThis.document,i=e.fetchImplementation??globalThis.fetch.bind(globalThis),n=Y(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,v=0,p=[],S=!1;async function b(y,T,f,M="application/json"){const I=await i(`${n}${T}`,{method:y,headers:{authorization:`Bearer ${e.credential.signalingToken}`,"content-type":M},body:f===void 0?void 0:M==="application/json"?JSON.stringify(f):f});if(!I.ok)throw new Error(`Recording request failed with HTTP ${I.status}`);return I.json()}return{get recording(){return S},async start(){if(S)return;u=(await b("POST","/v1/rtc/recordings")).recordingId;const T=t.createElement("canvas");T.width=r,T.height=o;const f=T.getContext("2d"),M=new Map,I=(C,A)=>{let E=M.get(C);return E||(E=t.createElement("video"),E.muted=!0,E.playsInline=!0,E.srcObject=A,E.play().catch(()=>{}),M.set(C,E)),E};l=setInterval(()=>{const C=a.getRemoteStreams(),A=a.getLocalStream(),E=[...C.map(U=>({key:U.participant.participantId,stream:U.stream,label:U.participant.displayName})),...A?[{key:"local",stream:A,label:"me"}]:[]];f.fillStyle="#101418",f.fillRect(0,0,r,o);const x=Math.max(1,Math.ceil(Math.sqrt(E.length))),_=Math.max(1,Math.ceil(E.length/x)),j=r/x,g=o/_;E.forEach((U,Ve)=>{const Be=I(U.key,U.stream),re=Ve%x*j,oe=Math.floor(Ve/x)*g;Be.videoWidth>0&&f.drawImage(Be,re+2,oe+2,j-4,g-4),f.fillStyle="rgba(0,0,0,.6)",f.fillRect(re+8,oe+g-30,Math.min(j-16,12+U.label.length*9),22),f.fillStyle="#fff",f.font="14px system-ui",f.fillText(U.label,re+14,oe+g-14)})},66);const D=T.captureStream(15);d=new AudioContext;const P=d.createMediaStreamDestination(),L=new Set,w=()=>{const C=[...a.getRemoteStreams().map(A=>A.stream),...a.getLocalStream()?[a.getLocalStream()]:[]];for(const A of C)for(const E of A.getAudioTracks())L.has(E.id)||(L.add(E.id),d.createMediaStreamSource(new MediaStream([E])).connect(P))};w();const R=setInterval(w,1e3),m=()=>clearInterval(R);for(const C of P.stream.getAudioTracks())D.addTrack(C);c=(e.mediaRecorderFactory??((C,A)=>new MediaRecorder(C,A)))(D,{mimeType:s}),p=[],c.addEventListener("dataavailable",C=>{C.data&&C.data.size>0&&p.push(C.data)}),c.addEventListener("stop",m),c.start(1e3),v=Date.now(),S=!0},async stop(){if(!S||!c||!u)throw new Error("The recorder is not running");S=!1;const y=c,T=new Promise(P=>y.addEventListener("stop",()=>P(),{once:!0}));y.stop(),await T,l&&clearInterval(l),l=null,await d?.close().catch(()=>{}),d=null;const f=new Blob(p,{type:s});p=[];const M=Math.max(1,Math.round((Date.now()-v)/1e3)),I=await b("PUT",`/v1/rtc/recordings/${encodeURIComponent(u)}/media`,f,s);await b("POST",`/v1/rtc/recordings/${encodeURIComponent(u)}/complete`,{durationSeconds:M});const D={recordingId:u,sizeBytes:I.sizeBytes,durationSeconds:M};return u=null,c=null,D},dispose(){l&&clearInterval(l),l=null;try{c?.stop()}catch{}c=null,d?.close().catch(()=>{}),d=null,S=!1}}}const ue=["none","blur","aurora","cybergrid","bokeh","sunset","image"],me=["none","snow","confetti","vignette"],X={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 Ye(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,v=null,p=!1;const S=t.createElement("video");S.muted=!0,S.playsInline=!0,S.srcObject=new MediaStream([r]),S.play().catch(()=>{});const b=t.createElement("canvas");b.width=i,b.height=n;const y=b.getContext("2d"),T=t.createElement("canvas");T.width=i,T.height=n;const f=T.getContext("2d"),M=()=>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 I=M();const D=()=>{if(s)return;const m=S.videoWidth,h=S.videoHeight;if(!m||!h)return;const C=Math.min(1,1280/Math.max(m,h)),A=Math.round(m*C),E=Math.round(h*C);A===i&&E===n||(i=A,n=E,b.width=i,b.height=n,T.width=i,T.height=n,I=M())};S.addEventListener?.("loadedmetadata",D),S.addEventListener?.("resize",D);function P(){if(v){f.clearRect(0,0,i,n),f.drawImage(v,0,0,i,n),f.globalCompositeOperation="source-in",f.drawImage(S,0,0,i,n),f.globalCompositeOperation="source-over",y.drawImage(T,0,0);return}const m=i*.42,h=m*9/16,C=i-m-24,A=n-h-24;y.save(),y.shadowColor="rgba(0,0,0,.5)",y.shadowBlur=24,y.beginPath(),y.roundRect(C,A,m,h,14),y.clip(),y.drawImage(S,C,A,m,h),y.restore()}function L(m){if(c==="none"?y.drawImage(S,0,0,i,n):c==="blur"?(y.filter="blur(14px)",y.drawImage(S,-16,-16,i+32,n+32),y.filter="none",v&&P()):c==="image"&&d?(y.drawImage(d,0,0,i,n),P()):((X[c]??X.aurora)(y,i,n,m),P()),l==="snow"||l==="confetti")for(const h of I)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 w=setInterval(()=>{L(Date.now()),u&&!p&&c!=="none"&&(p=!0,u.segment(S).then(m=>{v=m}).catch(()=>{v=null}).finally(()=>{p=!1}))},Math.round(1e3/o)),R=b.captureStream(o);return{track:R.getVideoTracks()[0],stream:R,setBackground(m,h){c=ue.includes(m)?m:"none",h!==void 0&&(d=h),c==="none"&&(v=null)},setOverlay(m){l=me.includes(m)?m:"none"},setSegmenter(m){u=m,m||(v=null)},dispose(){clearInterval(w),u?.close?.();for(const m of R.getTracks())m.stop();S.srcObject=null}}}function Z(){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 pe(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 fe(a,e,t=1){if(!a||a.length===0)return Z();const i=pe(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"),v=Math.min(1,Math.abs(i.yaw)/.35)*Math.max(0,Math.min(1,t)),p=(d+u)/2;return{blinkLeft:d+(p-d)*v,blinkRight:u+(p-u)*v,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 ee(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 ge(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,v=L=>Math.cos(L+c),p=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+p*.4,r*.92,r*.5,0,Math.PI,Math.PI*2),a.fill();const S=0;a.fillStyle="#e5b58a",a.beginPath();const b=u(S)*.9,y=r*.12+p;a.ellipse(b+d*r*.14,y,r*.11,r*.14,0,0,Math.PI*2),a.fill();for(const L of[-1,1]){const w=L*.42,R=v(w);if(R<=.12)continue;const m=u(w)*.82,h=Math.max(.2,R),C=L<0?e.blinkLeft:e.blinkRight,A=Math.max(.06,1-C),E=e.browUp*r*.14;a.strokeStyle="#4a2e18",a.lineWidth=r*.07,a.lineCap="round",a.beginPath(),a.moveTo(m-r*.2*h,-r*.42-E+p),a.quadraticCurveTo(m,-r*.52-E+p,m+r*.2*h,-r*.44-E+p),a.stroke();const x=-r*.18+p;a.fillStyle="#ffffff",a.beginPath(),a.ellipse(m,x,r*.16*h,r*.12*A,0,0,Math.PI*2),a.fill(),a.fillStyle="#22303c",a.beginPath(),a.ellipse(m+e.pupilX*r*.07*h,x+e.pupilY*r*.05,r*.06*h,r*.06*A,0,0,Math.PI*2),a.fill()}const T=Math.max(.25,v(0)),f=u(0)*.7,M=r*.55*T,I=r*(.04+e.jawOpen*.34),D=e.smile*r*.12,P=r*.42+p;a.fillStyle="#8c2f2f",a.beginPath(),a.moveTo(f-M/2,P-D),a.quadraticCurveTo(f,P+I,f+M/2,P-D),a.quadraticCurveTo(f,P-I*.25,f-M/2,P-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 te={mouthGain:1.7,blinkGain:1,eyeSync:1,smoothing:.4,avatarScale:1,avatarLift:0,lostHoldMs:400};function ie(){return{render(a,e,t,i,n){ge(a,e,t,i,n??void 0)}}}const ne=a=>Math.max(0,Math.min(1,a));function Ke(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??ie(),d=e.mode??"replace";const u={...te,...e.tuning},v=e.backdrop??"#101822",p=t.createElement("video");p.muted=!0,p.playsInline=!0,p.srcObject=new MediaStream([r]),p.play().catch(()=>{});const S=t.createElement("canvas");S.width=i,S.height=n;const b=S.getContext("2d"),y=()=>{if(s)return;const w=p.videoWidth,R=p.videoHeight;if(!w||!R)return;const m=Math.min(1,1280/Math.max(w,R)),h=Math.round(w*m),C=Math.round(R*m);(!(h===i||C===n)||h!==i||C!==n)&&(i=h,n=C,S.width=i,S.height=n)};p.addEventListener?.("loadedmetadata",y),p.addEventListener?.("resize",y);let T=Z(),f=null,M=0;function I(w){if(w.length<=454)return;const R=w[10],m=w[152],h=w[234],C=w[454],A=(h.x+C.x)/2*i,E=(h.y+C.y)/2*n,x=Math.hypot((m.x-R.x)*i,(m.y-R.y)*n),_=Math.max(.6,Math.cos(T.headPitch)),j={x:A,y:E,radius:Math.max(24,x/_*.8)};f=f?{x:f.x+(j.x-f.x)*.4,y:f.y+(j.y-f.y)*.4,radius:f.radius+(j.radius-f.radius)*.15}:j}function D(w){if(c&&p.readyState>=2){let m=null;try{m=c.detect(p,w)}catch{}const h=fe(m?.categories??null,m?.matrix??null,u.eyeSync);h.jawOpen=ne(h.jawOpen*u.mouthGain),h.blinkLeft=ne(h.blinkLeft*u.blinkGain),h.blinkRight=ne(h.blinkRight*u.blinkGain),h.tracked?(M=w,T=ee(T,h,u.smoothing),m?.landmarks&&I(m.landmarks)):T.tracked&&w-M>u.lostHoldMs&&(T=ee(T,{...T,jawOpen:0,browUp:0,smile:T.smile*.5,blinkLeft:.1,blinkRight:.1},.05))}d==="overlay"&&p.readyState>=2?b.drawImage(p,0,0,i,n):(b.fillStyle=v,b.fillRect(0,0,i,n));const R=f?{x:f.x,y:f.y+u.avatarLift*f.radius,radius:f.radius*u.avatarScale}:null;l?.render(b,T,i,n,R)}const P=setInterval(()=>D(Date.now()),Math.round(1e3/o)),L=S.captureStream(o);return{track:L.getVideoTracks()[0],stream:L,getRig:()=>({...T}),setMode(w){d=w==="overlay"?"overlay":"replace"},setTracker(w){c?.close?.(),c=w},setRenderer(w){l?.dispose?.(),l=w??ie()},setTuning(w){for(const R of Object.keys(te)){const m=w[R];typeof m=="number"&&Number.isFinite(m)&&(u[R]=m)}},getTuning:()=>({...u}),dispose(){clearInterval(P),c?.close?.(),l?.dispose?.();for(const w of L.getTracks())w.stop();p.srcObject=null}}}class B extends Error{constructor(e,t){super(t),this.code=e,this.name="ZipError"}code}const ye=101010256,ve=33639248,Se=67324752,ae=4294967295;function we(a){if(a.byteLength<4)return!1;const e=new DataView(a).getUint32(0,!0);return e===Se||e===ve||e===ye}function Xe(a){const e=Math.max(0,a.byteLength-22-65535);for(let t=a.byteLength-22;t>=e;t-=1)if(a.getUint32(t,!0)===ye)return t;throw new B("zip_unreadable","The archive index could not be found.")}function ke(a){const e=new DataView(a),t=Xe(e),i=e.getUint16(t+10,!0);let n=e.getUint32(t+16,!0);if(n===ae)throw new B("zip_unsupported","ZIP64 archives are not supported.");const s=new TextDecoder,o=[];for(let r=0;r<i;r+=1){if(n+46>a.byteLength||e.getUint32(n,!0)!==ve)throw new B("zip_unreadable","The archive index is malformed.");const c=e.getUint16(n+8,!0),l=e.getUint16(n+10,!0),d=e.getUint32(n+20,!0),u=e.getUint32(n+24,!0),v=e.getUint16(n+28,!0),p=e.getUint16(n+30,!0),S=e.getUint16(n+32,!0),b=e.getUint32(n+42,!0),y=s.decode(new Uint8Array(a,n+46,v));o.push({path:y,bytes:u,compressedBytes:d,compressionMethod:l,encrypted:(c&1)!==0,localHeaderOffset:b}),n+=46+v+p+S}return o}async function Ze(a,e){if(e.encrypted)throw new B("zip_encrypted","The archive is password protected.");if(e.bytes===ae||e.compressedBytes===ae)throw new B("zip_unsupported","ZIP64 archives are not supported.");const t=new DataView(a),i=e.localHeaderOffset;if(i+30>a.byteLength||t.getUint32(i,!0)!==Se)throw new B("zip_unreadable","An archive entry header is malformed.");const n=t.getUint16(i+26,!0),s=t.getUint16(i+28,!0),o=i+30+n+s;if(o+e.compressedBytes>a.byteLength)throw new B("zip_unreadable","An archive entry runs past the end of the file.");const r=a.slice(o,o+e.compressedBytes);if(e.compressionMethod===0)return r;if(e.compressionMethod!==8)throw new B("zip_unsupported",`Archive compression method ${e.compressionMethod} is not supported.`);if(typeof DecompressionStream>"u")throw new B("zip_unsupported","This browser cannot expand compressed archives.");const c=new Blob([r]).stream().pipeThrough(new DecompressionStream("deflate-raw"));return new Response(c).arrayBuffer()}async function et(a){const e=typeof File<"u"&&a instanceof File?await a.arrayBuffer():a;if(!we(e))return[];try{return be(ke(e)).map(t=>t.path)}catch{return[]}}const Te=64*1024*1024,tt="streaming-cdn-avatars",H="models",it=1179937895,nt=1313821514;class N extends Error{constructor(e,t,i=[]){super(t),this.code=e,this.name="AvatarModelError",this.choices=i}code;choices}function be(a){return a.filter(e=>/\.vrm$/i.test(e.path)&&!e.path.endsWith("/")&&!e.path.split("/").some(t=>t==="__MACOSX"||t.startsWith("._")))}function q(a){const e=typeof a=="string"?a.trim():"";return e.length>0?e:null}function Ce(a){const e=new DataView(a);if(a.byteLength<20||e.getUint32(0,!0)!==it)throw new N("model_not_glb","The file is not a binary glTF (.vrm / .glb) model.");let t=12,i=null;for(;t+8<=a.byteLength;){const r=e.getUint32(t,!0),c=e.getUint32(t+4,!0),l=t+8;if(l+r>a.byteLength)break;if(c===nt){try{const d=new TextDecoder().decode(new Uint8Array(a,l,r)).replace(/[\s\u0000]+$/,"");i=JSON.parse(d)}catch{throw new N("model_unreadable","The model's glTF descriptor could not be read.")}break}t=l+r+(4-r%4)%4}const n=i?.extensions??{},s=n.VRM?.meta,o=n.VRMC_vrm?.meta;if(!s&&!o)return null;if(o){const r=Array.isArray(o.authors)?o.authors.filter(c=>typeof c=="string"):[];return{title:q(o.name),author:r.length?r.join(", "):null,allowedUser:q(o.avatarPermission),commercialUse:q(o.commercialUsage),licenseName:q(o.licenseUrl)?"VRM 1.0":null,licenseUrl:q(o.licenseUrl)??q(o.otherLicenseUrl)}}return{title:q(s?.title),author:q(s?.author),allowedUser:q(s?.allowedUserName),commercialUse:q(s?.commercialUssageName),licenseName:q(s?.licenseName),licenseUrl:q(s?.otherLicenseUrl)}}function at(){return new Promise((a,e)=>{if(typeof indexedDB>"u"){e(new N("storage_unavailable","This browser cannot store imported avatar models."));return}const t=indexedDB.open(tt,1);t.onupgradeneeded=()=>{t.result.objectStoreNames.contains(H)||t.result.createObjectStore(H,{keyPath:"id"})},t.onsuccess=()=>a(t.result),t.onerror=()=>e(new N("storage_unavailable","Imported avatar storage could not be opened."))})}async function $(a,e){const t=await at();try{return await new Promise((i,n)=>{const s=e(t.transaction(H,a).objectStore(H));s.onsuccess=()=>i(s.result),s.onerror=()=>n(new N("storage_unavailable","The imported avatar could not be stored."))})}finally{t.close()}}const Ee=a=>({id:a.id,name:a.name,bytes:a.bytes,importedAt:a.importedAt,license:a.license});async function st(a,e={}){const t=e.maxBytes??Te,i=typeof File<"u"&&a instanceof File;let n=i?a.name:null,s=i?await a.arrayBuffer():a;if(we(s)){const l=await rt(s,e.entryPath,t);s=l.buffer,n=l.path.split("/").pop()??n}if(s.byteLength>t)throw new N("model_too_large","The model is larger than the "+Math.round(t/1024/1024)+" MB limit.");const o=Ce(s);if(!o)throw new N("model_not_vrm","The file is a glTF model but carries no VRM character data.");const r=o.title||(n?n.replace(/\.(vrm|glb)$/i,""):null),c={id:"avatar_"+Date.now().toString(36)+"_"+Math.random().toString(36).slice(2,8),name:e.name?.trim()||r||"Imported avatar",bytes:s.byteLength,importedAt:new Date().toISOString(),license:o,data:s};return await $("readwrite",l=>l.put(c)),Ee(c)}async function rt(a,e,t){let i;try{i=be(ke(a))}catch(s){throw s instanceof B?new N(Re(s.code),s.message):new N("archive_unreadable","The archive could not be read.")}if(i.length===0)throw new N("archive_no_model","The archive contains no .vrm character.");const n=e?i.find(s=>s.path===e):i.length===1?i[0]:null;if(!n)throw new N("archive_many_models","The archive contains several characters; choose one.",i.map(s=>s.path));if(n.bytes>t)throw new N("model_too_large","The character inside the archive is larger than the "+Math.round(t/1024/1024)+" MB limit.");try{return{buffer:await Ze(a,n),path:n.path}}catch(s){throw s instanceof B?new N(Re(s.code),s.message):new N("archive_unreadable","The character could not be expanded from the archive.")}}function Re(a){return a==="zip_encrypted"?"archive_encrypted":a==="zip_unsupported"?"archive_unsupported":"archive_unreadable"}async function ot(){return(await $("readonly",e=>e.getAll())??[]).map(Ee).sort((e,t)=>t.importedAt.localeCompare(e.importedAt))}async function ct(a){return(await $("readonly",t=>t.get(a)))?.data??null}async function lt(a){await $("readwrite",e=>e.delete(a))}class Me 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"?$e:z;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:xe}),this.options.autoJoin&&await this.join(),this}catch(e){this.state="failed";const t=qe(e,"RTC initialization failed");throw this.emit("error",Q(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=qe(t,"RTC connection failed");throw this.emit("error",Q(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(Oe)}getLocalStream(){return this.client?.getLocalStream()??null}getRemoteStreams(){return(this.client?.getRemoteStreams()??[]).map(e=>({participant:Oe(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(v=>v.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 v=await fetch(c,{method:"HEAD",cache:"no-store",credentials:"omit"});l=Math.round(performance.now()-u),v.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:Ne()?.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 pt(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:gt(),effectiveConnectionType:Ne()?.effectiveType??null},...i?.sample??{},...t,sampleType:e,sampledAt:new Date().toISOString()},o=ft(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",Q(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 dt(a){return new Me(a).initialize()}let W=null;function Ie(){if(W)return W;const a=globalThis,e=a.AudioContext??a.webkitAudioContext;return e?(W=new e,W):null}async function Ae(){const a=Ie();return a?(a.state==="suspended"&&await a.resume().catch(()=>{}),a.state==="running"):!1}class ht{options;media=null;cadenceTimer=null;oscillators=new Set;constructor(e){this.options=e}unlock(){return Ae()}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=Ie();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 ht(e),this.mount=Le(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=Le(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,mt(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",Q(s)),!this.stopped&&this.options.reconnect!==!1&&this.scheduleReconnect();return}if(this.stopped||this.socket)return;const i=G(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=Y(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=De("Decline","#fff","#a32920","#d9a09b"),r=De("Answer","#fff","#08745f","#08745f");o.addEventListener("click",()=>{this.reject(e.id).catch(c=>this.emit("error",Q(c)))}),r.addEventListener("click",()=>{this.accept(e.id).catch(c=>this.emit("error",Q(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",Q(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",Q(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 ut(a){const e=new Pe(a);return a.autoConnect!==!1&&a.signalingUrl&&e.connect(),e}function Le(a){return a?typeof a=="string"?document.querySelector(a):a:null}function De(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 mt(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 Oe(a){return{id:a.participantId,name:a.displayName,role:"participant"}}async function pt(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,v=0,p=0,S=0,b=0,y=0,T=0,f=0,M=0,I=null,D=null,P=null,L=null,w=null,R=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)),v=Math.max(v,Number(g.frameWidth??0)),b=Math.max(b,Number(g.frameHeight??0)),y+=Number(g.framesDropped??0),T+=Number(g.freezeCount??0),f+=Number(g.totalFreezesDuration??0),M+=Number(g.concealedSamples??0),w||=i.get(String(g.codecId??""))??null),g.type==="outbound-rtp"&&!g.isRemote&&(p=Math.max(p,Number(g.frameWidth??0)),S=Math.max(S,Number(g.frameHeight??0)),o+=Number(g.bytesSent??0),R||=g.qualityLimitationReason?String(g.qualityLimitationReason):null,w||=i.get(String(g.codecId??""))??null),g.type==="candidate-pair"&&(g.selected||g.nominated)&&g.state==="succeeded"){I=Number.isFinite(g.currentRoundTripTime)?Number(g.currentRoundTripTime):I,D=Number.isFinite(g.availableOutgoingBitrate)?Number(g.availableOutgoingBitrate):D;const U=n.get(g.localCandidateId);P=U?.candidateType?String(U.candidateType):P,L=U?.protocol?String(U.protocol):L}});const m=Date.now(),h=e?Math.max(.001,(m-e.timestamp)/1e3):0,C=e?Math.max(0,Math.round((s-e.inboundBytes)*8/h)):null,A=e?Math.max(0,Math.round((o-e.outboundBytes)*8/h)):null,E=r+c,x=e?Math.max(0,r-e.packetsLost):r,_=e?Math.max(0,c-e.packetsReceived):c,j=x+_;return{counters:{timestamp:m,inboundBytes:s,outboundBytes:o,packetsLost:r,packetsReceived:c},sample:{rttMs:I===null?null:I*1e3,jitterMs:d?l/d*1e3:null,packetLossPct:j?x/j*100:null,packetLossCumulativePct:E?r/E*100:null,packetsLost:r,packetsReceived:c,inboundBitrateBps:C,outboundBitrateBps:A,availableOutgoingBitrateBps:D,framesPerSecond:u||null,frameWidth:v||null,frameHeight:b||null,inboundFrameWidth:v||null,inboundFrameHeight:b||null,outboundFrameWidth:p||null,outboundFrameHeight:S||null,framesDropped:y,freezeCount:T,freezeDurationMs:Math.round(f*1e3),concealedSamples:M,codec:w,transportProtocol:L,candidateType:P,qualityLimitationReason:R}}}function ft(a){let e=a.mediaScore==null?5:Math.round(Ue(Number(a.mediaScore),0,5));const t=se(a.packetLossPct)??0,i=se(a.rttMs)??0,n=se(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(Ue(4.5-t*.09-i*.0011-n*.006,1,4.5).toFixed(2));return{networkScore:e,estimatedMos:o}}function Ne(){return navigator.connection??null}function gt(){const a=Number(navigator.deviceMemory);return Number.isFinite(a)?a:null}function se(a){const e=Number(a);return Number.isFinite(e)?e:null}function Ue(a,e,t){return Math.min(t,Math.max(e,a))}function Q(a){return a instanceof Error?{name:a.name,message:a.message}:{name:"ServiceError",message:String(a??"Service request failed")}}function qe(a,e){return a instanceof Error?a:new Error(e)}const xe="1.3.25";k.AVATAR_MODEL_MAX_BYTES=Te,k.AVATAR_TUNING_DEFAULTS=te,k.AvatarModelError=N,k.BACKGROUND_PRESETS=ue,k.OVERLAY_EFFECTS=me,k.RtcClient=Me,k.RtcIncomingClient=Pe,k.SPEAKER_NOISE_FLOOR=.02,k.SPEAKER_TAKEOVER_RATIO=1.4,k.createAvatarPipeline=Ke,k.createCallRecorder=Ge,k.createPictureInPictureController=Je,k.createRtcClient=dt,k.createRtcIncomingClient=ut,k.createVideoEffectsPipeline=Ye,k.drawAvatar=ge,k.headPoseFromMatrix=pe,k.importAvatarModel=st,k.listArchiveModels=et,k.listAvatarModels=ot,k.neutralRig=Z,k.pickActiveSpeaker=J,k.readAvatarModel=ct,k.readAvatarModelMetadata=Ce,k.removeAvatarModel=lt,k.rigFromBlendshapes=fe,k.scenePainters=X,k.smoothRig=ee,k.stylizedAvatarRenderer=ie,k.unlockRtcCallAudio=Ae,k.version=xe,Object.defineProperty(k,Symbol.toStringTag,{value:"Module"})}));
|
|
1
|
+
(function(k,F){typeof exports=="object"&&typeof module<"u"?F(exports):typeof define=="function"&&define.amd?define(["exports"],F):(k=typeof globalThis<"u"?globalThis:k||self,F(k.StreamingCdnRtc={}))})(this,(function(k){"use strict";function J(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 ce(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}},je={warmupSamples:3,poorSamplesToStepDown:2,goodSamplesToStepUp:6,criticalLossPct:8,criticalRttMs:800,poorLossPct:3,poorRttMs:350,goodLossPct:1,goodRttMs:250,sustainRatio:.8,bandwidthSustainRatio:.7};function _e(a){const e=String(a??"").trim().toLowerCase();return!e||e==="none"?"":e==="cpu"||e==="bandwidth"?e:"other"}function Qe(a,e,t,i,n={}){const s={...je,...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,v=_e(e.qualityLimitationReason),p=e.packetLossPct??0,S=e.rttMs??0,b=e.availableOutgoingBitrateBps,y=V[a].maxBitrate,T=p>=s.criticalLossPct||S>=s.criticalRttMs||v==="cpu",f=v==="bandwidth"&&(b==null||b<y*s.bandwidthSustainRatio),M=T||p>=s.poorLossPct||S>=s.poorRttMs||f,I=l<o.length-1?V[o[l+1]].maxBitrate:null,D=v==="bandwidth"&&b!=null&&I!=null&&b>=I*s.bandwidthSustainRatio,P=b==null||b>=y*s.sustainRatio,L=p<s.goodLossPct&&S<s.goodRttMs&&(!v||D)&&P,w=M?t+1:0,R=L?i+1:0,m={quality:a,poorSamples:w,goodSamples:R,samplesSeen:d,changed:!1};return l>c?{quality:o[c],poorSamples:0,goodSamples:0,samplesSeen:d,changed:!0}:u?m:T&&l>0?{quality:o[l-1],poorSamples:0,goodSamples:0,samplesSeen:d,changed:!0}:w>=s.poorSamplesToStepDown&&l>0?{quality:o[l-1],poorSamples:0,goodSamples:0,samplesSeen:d,changed:!0}:R>=s.goodSamplesToStepUp&&l<c?{quality:o[l+1],poorSamples:0,goodSamples:0,samplesSeen:d,changed:!0}:m}function Fe(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 v=Number(d.roundTripTime??NaN);Number.isFinite(v)&&(o=v*1e3)}if(u==="candidate-pair"&&(d.nominated===!0||String(d.state??"")==="succeeded")){const v=Number(d.currentRoundTripTime??NaN);o==null&&Number.isFinite(v)&&(o=v*1e3);const p=Number(d.availableOutgoingBitrate??NaN);Number.isFinite(p)&&(r=p)}}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 G(a,e){const t=new URL(String(a),e);return t.protocol==="https:"?t.protocol="wss:":t.protocol==="http:"&&(t.protocol="ws:"),t}function Y(a,e){const t=new URL(String(a),e);return t.protocol==="wss:"?t.protocol="https:":t.protocol==="ws:"&&(t.protocol="http:"),t}const ze={audio:{active:!1,maxBitrate:0,maxFramerate:0,scaleResolutionDownBy:4}},He={active:!0,maxBitrate:4e6,maxFramerate:30,scaleResolutionDownBy:1};class z{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=z.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=G(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:z.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=le();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?He:this.quality==="audio"?ze.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 ce(n.values()))e.push({participantId:i.identity.participantId,level:s.level})}catch{}const t=J(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}=Fe(t,e.counters);e.counters=n;const s=Qe(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??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)}`}const K={active:!0,maxBitrate:4e6,maxFramerate:30,scaleResolutionDownBy:1},de=[{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}],he={high:"a",medium:"b",low:"c"};class We{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=Y(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=G(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:de.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:he[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:he[this.quality];n.encodings=s.map(r=>{if(i)return{...r,active:r.rid==="a",...r.rid==="a"?{maxBitrate:K.maxBitrate,maxFramerate:K.maxFramerate}:{}};const c=de.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?K: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 ce(t.values())){const o=s.mid?this.midToParticipant.get(s.mid):void 0;o&&i.push({participantId:o,level:s.level})}const n=J(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??$e(),...e}))}stopStream(e){for(const t of e?.getTracks()??[])t.stop()}}function $e(){return globalThis.crypto?.randomUUID?.()??`${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}function Je(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 Ge(a,e){const t=e.document??globalThis.document,i=e.fetchImplementation??globalThis.fetch.bind(globalThis),n=Y(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,v=0,p=[],S=!1;async function b(y,T,f,M="application/json"){const I=await i(`${n}${T}`,{method:y,headers:{authorization:`Bearer ${e.credential.signalingToken}`,"content-type":M},body:f===void 0?void 0:M==="application/json"?JSON.stringify(f):f});if(!I.ok)throw new Error(`Recording request failed with HTTP ${I.status}`);return I.json()}return{get recording(){return S},async start(){if(S)return;u=(await b("POST","/v1/rtc/recordings")).recordingId;const T=t.createElement("canvas");T.width=r,T.height=o;const f=T.getContext("2d"),M=new Map,I=(C,A)=>{let E=M.get(C);return E||(E=t.createElement("video"),E.muted=!0,E.playsInline=!0,E.srcObject=A,E.play().catch(()=>{}),M.set(C,E)),E};l=setInterval(()=>{const C=a.getRemoteStreams(),A=a.getLocalStream(),E=[...C.map(U=>({key:U.participant.participantId,stream:U.stream,label:U.participant.displayName})),...A?[{key:"local",stream:A,label:"me"}]:[]];f.fillStyle="#101418",f.fillRect(0,0,r,o);const x=Math.max(1,Math.ceil(Math.sqrt(E.length))),Q=Math.max(1,Math.ceil(E.length/x)),j=r/x,g=o/Q;E.forEach((U,Ve)=>{const Be=I(U.key,U.stream),re=Ve%x*j,oe=Math.floor(Ve/x)*g;Be.videoWidth>0&&f.drawImage(Be,re+2,oe+2,j-4,g-4),f.fillStyle="rgba(0,0,0,.6)",f.fillRect(re+8,oe+g-30,Math.min(j-16,12+U.label.length*9),22),f.fillStyle="#fff",f.font="14px system-ui",f.fillText(U.label,re+14,oe+g-14)})},66);const D=T.captureStream(15);d=new AudioContext;const P=d.createMediaStreamDestination(),L=new Set,w=()=>{const C=[...a.getRemoteStreams().map(A=>A.stream),...a.getLocalStream()?[a.getLocalStream()]:[]];for(const A of C)for(const E of A.getAudioTracks())L.has(E.id)||(L.add(E.id),d.createMediaStreamSource(new MediaStream([E])).connect(P))};w();const R=setInterval(w,1e3),m=()=>clearInterval(R);for(const C of P.stream.getAudioTracks())D.addTrack(C);c=(e.mediaRecorderFactory??((C,A)=>new MediaRecorder(C,A)))(D,{mimeType:s}),p=[],c.addEventListener("dataavailable",C=>{C.data&&C.data.size>0&&p.push(C.data)}),c.addEventListener("stop",m),c.start(1e3),v=Date.now(),S=!0},async stop(){if(!S||!c||!u)throw new Error("The recorder is not running");S=!1;const y=c,T=new Promise(P=>y.addEventListener("stop",()=>P(),{once:!0}));y.stop(),await T,l&&clearInterval(l),l=null,await d?.close().catch(()=>{}),d=null;const f=new Blob(p,{type:s});p=[];const M=Math.max(1,Math.round((Date.now()-v)/1e3)),I=await b("PUT",`/v1/rtc/recordings/${encodeURIComponent(u)}/media`,f,s);await b("POST",`/v1/rtc/recordings/${encodeURIComponent(u)}/complete`,{durationSeconds:M});const D={recordingId:u,sizeBytes:I.sizeBytes,durationSeconds:M};return u=null,c=null,D},dispose(){l&&clearInterval(l),l=null;try{c?.stop()}catch{}c=null,d?.close().catch(()=>{}),d=null,S=!1}}}const ue=["none","blur","aurora","cybergrid","bokeh","sunset","image"],me=["none","snow","confetti","vignette"],X={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 Ye(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,v=null,p=!1;const S=t.createElement("video");S.muted=!0,S.playsInline=!0,S.srcObject=new MediaStream([r]),S.play().catch(()=>{});const b=t.createElement("canvas");b.width=i,b.height=n;const y=b.getContext("2d"),T=t.createElement("canvas");T.width=i,T.height=n;const f=T.getContext("2d"),M=()=>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 I=M();const D=()=>{if(s)return;const m=S.videoWidth,h=S.videoHeight;if(!m||!h)return;const C=Math.min(1,1280/Math.max(m,h)),A=Math.round(m*C),E=Math.round(h*C);A===i&&E===n||(i=A,n=E,b.width=i,b.height=n,T.width=i,T.height=n,I=M())};S.addEventListener?.("loadedmetadata",D),S.addEventListener?.("resize",D);function P(){if(v){f.clearRect(0,0,i,n),f.drawImage(v,0,0,i,n),f.globalCompositeOperation="source-in",f.drawImage(S,0,0,i,n),f.globalCompositeOperation="source-over",y.drawImage(T,0,0);return}const m=i*.42,h=m*9/16,C=i-m-24,A=n-h-24;y.save(),y.shadowColor="rgba(0,0,0,.5)",y.shadowBlur=24,y.beginPath(),y.roundRect(C,A,m,h,14),y.clip(),y.drawImage(S,C,A,m,h),y.restore()}function L(m){if(c==="none"?y.drawImage(S,0,0,i,n):c==="blur"?(y.filter="blur(14px)",y.drawImage(S,-16,-16,i+32,n+32),y.filter="none",v&&P()):c==="image"&&d?(y.drawImage(d,0,0,i,n),P()):((X[c]??X.aurora)(y,i,n,m),P()),l==="snow"||l==="confetti")for(const h of I)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 w=setInterval(()=>{L(Date.now()),u&&!p&&c!=="none"&&(p=!0,u.segment(S).then(m=>{v=m}).catch(()=>{v=null}).finally(()=>{p=!1}))},Math.round(1e3/o)),R=b.captureStream(o);return{track:R.getVideoTracks()[0],stream:R,setBackground(m,h){c=ue.includes(m)?m:"none",h!==void 0&&(d=h),c==="none"&&(v=null)},setOverlay(m){l=me.includes(m)?m:"none"},setSegmenter(m){u=m,m||(v=null)},dispose(){clearInterval(w),u?.close?.();for(const m of R.getTracks())m.stop();S.srcObject=null}}}function Z(){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 pe(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 fe(a,e,t=1){if(!a||a.length===0)return Z();const i=pe(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"),v=Math.min(1,Math.abs(i.yaw)/.35)*Math.max(0,Math.min(1,t)),p=(d+u)/2;return{blinkLeft:d+(p-d)*v,blinkRight:u+(p-u)*v,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 ee(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 ge(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,v=L=>Math.cos(L+c),p=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+p*.4,r*.92,r*.5,0,Math.PI,Math.PI*2),a.fill();const S=0;a.fillStyle="#e5b58a",a.beginPath();const b=u(S)*.9,y=r*.12+p;a.ellipse(b+d*r*.14,y,r*.11,r*.14,0,0,Math.PI*2),a.fill();for(const L of[-1,1]){const w=L*.42,R=v(w);if(R<=.12)continue;const m=u(w)*.82,h=Math.max(.2,R),C=L<0?e.blinkLeft:e.blinkRight,A=Math.max(.06,1-C),E=e.browUp*r*.14;a.strokeStyle="#4a2e18",a.lineWidth=r*.07,a.lineCap="round",a.beginPath(),a.moveTo(m-r*.2*h,-r*.42-E+p),a.quadraticCurveTo(m,-r*.52-E+p,m+r*.2*h,-r*.44-E+p),a.stroke();const x=-r*.18+p;a.fillStyle="#ffffff",a.beginPath(),a.ellipse(m,x,r*.16*h,r*.12*A,0,0,Math.PI*2),a.fill(),a.fillStyle="#22303c",a.beginPath(),a.ellipse(m+e.pupilX*r*.07*h,x+e.pupilY*r*.05,r*.06*h,r*.06*A,0,0,Math.PI*2),a.fill()}const T=Math.max(.25,v(0)),f=u(0)*.7,M=r*.55*T,I=r*(.04+e.jawOpen*.34),D=e.smile*r*.12,P=r*.42+p;a.fillStyle="#8c2f2f",a.beginPath(),a.moveTo(f-M/2,P-D),a.quadraticCurveTo(f,P+I,f+M/2,P-D),a.quadraticCurveTo(f,P-I*.25,f-M/2,P-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 te={mouthGain:1.7,blinkGain:1,eyeSync:1,smoothing:.4,avatarScale:1,avatarLift:0,lostHoldMs:400};function ie(){return{render(a,e,t,i,n){ge(a,e,t,i,n??void 0)}}}const ne=a=>Math.max(0,Math.min(1,a));function Ke(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??ie(),d=e.mode??"replace";const u={...te,...e.tuning},v=e.backdrop??"#101822",p=t.createElement("video");p.muted=!0,p.playsInline=!0,p.srcObject=new MediaStream([r]),p.play().catch(()=>{});const S=t.createElement("canvas");S.width=i,S.height=n;const b=S.getContext("2d"),y=()=>{if(s)return;const w=p.videoWidth,R=p.videoHeight;if(!w||!R)return;const m=Math.min(1,1280/Math.max(w,R)),h=Math.round(w*m),C=Math.round(R*m);(!(h===i||C===n)||h!==i||C!==n)&&(i=h,n=C,S.width=i,S.height=n)};p.addEventListener?.("loadedmetadata",y),p.addEventListener?.("resize",y);let T=Z(),f=null,M=0;function I(w){if(w.length<=454)return;const R=w[10],m=w[152],h=w[234],C=w[454],A=(h.x+C.x)/2*i,E=(h.y+C.y)/2*n,x=Math.hypot((m.x-R.x)*i,(m.y-R.y)*n),Q=Math.max(.6,Math.cos(T.headPitch)),j={x:A,y:E,radius:Math.max(24,x/Q*.8)};f=f?{x:f.x+(j.x-f.x)*.4,y:f.y+(j.y-f.y)*.4,radius:f.radius+(j.radius-f.radius)*.15}:j}function D(w){if(c&&p.readyState>=2){let m=null;try{m=c.detect(p,w)}catch{}const h=fe(m?.categories??null,m?.matrix??null,u.eyeSync);h.jawOpen=ne(h.jawOpen*u.mouthGain),h.blinkLeft=ne(h.blinkLeft*u.blinkGain),h.blinkRight=ne(h.blinkRight*u.blinkGain),h.tracked?(M=w,T=ee(T,h,u.smoothing),m?.landmarks&&I(m.landmarks)):T.tracked&&w-M>u.lostHoldMs&&(T=ee(T,{...T,jawOpen:0,browUp:0,smile:T.smile*.5,blinkLeft:.1,blinkRight:.1},.05))}d==="overlay"&&p.readyState>=2?b.drawImage(p,0,0,i,n):(b.fillStyle=v,b.fillRect(0,0,i,n));const R=f?{x:f.x,y:f.y+u.avatarLift*f.radius,radius:f.radius*u.avatarScale}:null;l?.render(b,T,i,n,R)}const P=setInterval(()=>D(Date.now()),Math.round(1e3/o)),L=S.captureStream(o);return{track:L.getVideoTracks()[0],stream:L,getRig:()=>({...T}),setMode(w){d=w==="overlay"?"overlay":"replace"},setTracker(w){c?.close?.(),c=w},setRenderer(w){l?.dispose?.(),l=w??ie()},setTuning(w){for(const R of Object.keys(te)){const m=w[R];typeof m=="number"&&Number.isFinite(m)&&(u[R]=m)}},getTuning:()=>({...u}),dispose(){clearInterval(P),c?.close?.(),l?.dispose?.();for(const w of L.getTracks())w.stop();p.srcObject=null}}}class B extends Error{constructor(e,t){super(t),this.code=e,this.name="ZipError"}code}const ye=101010256,ve=33639248,Se=67324752,ae=4294967295;function we(a){if(a.byteLength<4)return!1;const e=new DataView(a).getUint32(0,!0);return e===Se||e===ve||e===ye}function Xe(a){const e=Math.max(0,a.byteLength-22-65535);for(let t=a.byteLength-22;t>=e;t-=1)if(a.getUint32(t,!0)===ye)return t;throw new B("zip_unreadable","The archive index could not be found.")}function ke(a){const e=new DataView(a),t=Xe(e),i=e.getUint16(t+10,!0);let n=e.getUint32(t+16,!0);if(n===ae)throw new B("zip_unsupported","ZIP64 archives are not supported.");const s=new TextDecoder,o=[];for(let r=0;r<i;r+=1){if(n+46>a.byteLength||e.getUint32(n,!0)!==ve)throw new B("zip_unreadable","The archive index is malformed.");const c=e.getUint16(n+8,!0),l=e.getUint16(n+10,!0),d=e.getUint32(n+20,!0),u=e.getUint32(n+24,!0),v=e.getUint16(n+28,!0),p=e.getUint16(n+30,!0),S=e.getUint16(n+32,!0),b=e.getUint32(n+42,!0),y=s.decode(new Uint8Array(a,n+46,v));o.push({path:y,bytes:u,compressedBytes:d,compressionMethod:l,encrypted:(c&1)!==0,localHeaderOffset:b}),n+=46+v+p+S}return o}async function Ze(a,e){if(e.encrypted)throw new B("zip_encrypted","The archive is password protected.");if(e.bytes===ae||e.compressedBytes===ae)throw new B("zip_unsupported","ZIP64 archives are not supported.");const t=new DataView(a),i=e.localHeaderOffset;if(i+30>a.byteLength||t.getUint32(i,!0)!==Se)throw new B("zip_unreadable","An archive entry header is malformed.");const n=t.getUint16(i+26,!0),s=t.getUint16(i+28,!0),o=i+30+n+s;if(o+e.compressedBytes>a.byteLength)throw new B("zip_unreadable","An archive entry runs past the end of the file.");const r=a.slice(o,o+e.compressedBytes);if(e.compressionMethod===0)return r;if(e.compressionMethod!==8)throw new B("zip_unsupported",`Archive compression method ${e.compressionMethod} is not supported.`);if(typeof DecompressionStream>"u")throw new B("zip_unsupported","This browser cannot expand compressed archives.");const c=new Blob([r]).stream().pipeThrough(new DecompressionStream("deflate-raw"));return new Response(c).arrayBuffer()}async function et(a){const e=typeof File<"u"&&a instanceof File?await a.arrayBuffer():a;if(!we(e))return[];try{return be(ke(e)).map(t=>t.path)}catch{return[]}}const Te=64*1024*1024,tt="streaming-cdn-avatars",H="models",it=1179937895,nt=1313821514;class N extends Error{constructor(e,t,i=[]){super(t),this.code=e,this.name="AvatarModelError",this.choices=i}code;choices}function be(a){return a.filter(e=>/\.vrm$/i.test(e.path)&&!e.path.endsWith("/")&&!e.path.split("/").some(t=>t==="__MACOSX"||t.startsWith("._")))}function q(a){const e=typeof a=="string"?a.trim():"";return e.length>0?e:null}function Ce(a){const e=new DataView(a);if(a.byteLength<20||e.getUint32(0,!0)!==it)throw new N("model_not_glb","The file is not a binary glTF (.vrm / .glb) model.");let t=12,i=null;for(;t+8<=a.byteLength;){const r=e.getUint32(t,!0),c=e.getUint32(t+4,!0),l=t+8;if(l+r>a.byteLength)break;if(c===nt){try{const d=new TextDecoder().decode(new Uint8Array(a,l,r)).replace(/[\s\u0000]+$/,"");i=JSON.parse(d)}catch{throw new N("model_unreadable","The model's glTF descriptor could not be read.")}break}t=l+r+(4-r%4)%4}const n=i?.extensions??{},s=n.VRM?.meta,o=n.VRMC_vrm?.meta;if(!s&&!o)return null;if(o){const r=Array.isArray(o.authors)?o.authors.filter(c=>typeof c=="string"):[];return{title:q(o.name),author:r.length?r.join(", "):null,allowedUser:q(o.avatarPermission),commercialUse:q(o.commercialUsage),licenseName:q(o.licenseUrl)?"VRM 1.0":null,licenseUrl:q(o.licenseUrl)??q(o.otherLicenseUrl)}}return{title:q(s?.title),author:q(s?.author),allowedUser:q(s?.allowedUserName),commercialUse:q(s?.commercialUssageName),licenseName:q(s?.licenseName),licenseUrl:q(s?.otherLicenseUrl)}}function at(){return new Promise((a,e)=>{if(typeof indexedDB>"u"){e(new N("storage_unavailable","This browser cannot store imported avatar models."));return}const t=indexedDB.open(tt,1);t.onupgradeneeded=()=>{t.result.objectStoreNames.contains(H)||t.result.createObjectStore(H,{keyPath:"id"})},t.onsuccess=()=>a(t.result),t.onerror=()=>e(new N("storage_unavailable","Imported avatar storage could not be opened."))})}async function W(a,e){const t=await at();try{return await new Promise((i,n)=>{const s=e(t.transaction(H,a).objectStore(H));s.onsuccess=()=>i(s.result),s.onerror=()=>n(new N("storage_unavailable","The imported avatar could not be stored."))})}finally{t.close()}}const Ee=a=>({id:a.id,name:a.name,bytes:a.bytes,importedAt:a.importedAt,license:a.license});async function st(a,e={}){const t=e.maxBytes??Te,i=typeof File<"u"&&a instanceof File;let n=i?a.name:null,s=i?await a.arrayBuffer():a;if(we(s)){const l=await rt(s,e.entryPath,t);s=l.buffer,n=l.path.split("/").pop()??n}if(s.byteLength>t)throw new N("model_too_large","The model is larger than the "+Math.round(t/1024/1024)+" MB limit.");const o=Ce(s);if(!o)throw new N("model_not_vrm","The file is a glTF model but carries no VRM character data.");const r=o.title||(n?n.replace(/\.(vrm|glb)$/i,""):null),c={id:"avatar_"+Date.now().toString(36)+"_"+Math.random().toString(36).slice(2,8),name:e.name?.trim()||r||"Imported avatar",bytes:s.byteLength,importedAt:new Date().toISOString(),license:o,data:s};return await W("readwrite",l=>l.put(c)),Ee(c)}async function rt(a,e,t){let i;try{i=be(ke(a))}catch(s){throw s instanceof B?new N(Re(s.code),s.message):new N("archive_unreadable","The archive could not be read.")}if(i.length===0)throw new N("archive_no_model","The archive contains no .vrm character.");const n=e?i.find(s=>s.path===e):i.length===1?i[0]:null;if(!n)throw new N("archive_many_models","The archive contains several characters; choose one.",i.map(s=>s.path));if(n.bytes>t)throw new N("model_too_large","The character inside the archive is larger than the "+Math.round(t/1024/1024)+" MB limit.");try{return{buffer:await Ze(a,n),path:n.path}}catch(s){throw s instanceof B?new N(Re(s.code),s.message):new N("archive_unreadable","The character could not be expanded from the archive.")}}function Re(a){return a==="zip_encrypted"?"archive_encrypted":a==="zip_unsupported"?"archive_unsupported":"archive_unreadable"}async function ot(){return(await W("readonly",e=>e.getAll())??[]).map(Ee).sort((e,t)=>t.importedAt.localeCompare(e.importedAt))}async function ct(a){return(await W("readonly",t=>t.get(a)))?.data??null}async function lt(a){await W("readwrite",e=>e.delete(a))}class Me 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"?We:z;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:xe}),this.options.autoJoin&&await this.join(),this}catch(e){this.state="failed";const t=qe(e,"RTC initialization failed");throw this.emit("error",_(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=qe(t,"RTC connection failed");throw this.emit("error",_(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(Oe)}getLocalStream(){return this.client?.getLocalStream()??null}getRemoteStreams(){return(this.client?.getRemoteStreams()??[]).map(e=>({participant:Oe(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(v=>v.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 v=await fetch(c,{method:"HEAD",cache:"no-store",credentials:"omit"});l=Math.round(performance.now()-u),v.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:Ne()?.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 ft(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:yt(),effectiveConnectionType:Ne()?.effectiveType??null},...i?.sample??{},...t,sampleType:e,sampledAt:new Date().toISOString()},o=gt(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",_(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 dt(a){return new Me(a).initialize()}let $=null;function Ie(){if($)return $;const a=globalThis,e=a.AudioContext??a.webkitAudioContext;return e?($=new e,$):null}async function Ae(){const a=Ie();return a?(a.state==="suspended"&&await a.resume().catch(()=>{}),a.state==="running"):!1}class ht{options;media=null;cadenceTimer=null;oscillators=new Set;constructor(e){this.options=e}unlock(){return Ae()}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=Ie();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)}}}const ut=6e4;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;callGeneration=0;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 ht(e),this.mount=Le(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=Le(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.clearInvitationExpiry(e),()=>{this.mediaClients.get(e)===t&&(this.mediaClients.delete(e),this.activeCallIds.has(e)&&this.armCallWatchdog(e))}}armCallWatchdog(e){this.scheduleInvitationExpiry(e,new Date(Date.now()+ut).toISOString())}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=this.callGeneration,s=(async()=>{await this.callAudio.unlock(),this.callAudio.startRingback();let o;try{o=await this.request("/v1/rtc/client/calls",{method:"POST",headers:e.idempotencyKey?{"idempotency-key":e.idempotencyKey}:void 0,body:JSON.stringify(i)})}catch(c){throw this.callAudio.stop(),c}!o.credential&&o.callerCredential&&(o.credential=o.callerCredential);const r=String(o.call?.id??"")||null;if(r&&n===this.callGeneration){this.outgoingCallId=r,this.activeCallIds.add(r);const c=o.call?.expiresAt;this.scheduleInvitationExpiry(r,typeof c=="string"?c:void 0)}else r&&this.callAction(r,"cancel").catch(()=>{});return this.emit("outgoingcall",o),o})();this.startCallRequest=s;try{return await s}finally{this.startCallRequest===s&&(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.armCallWatchdog(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");let t;try{return t=await this.callAction(e,"reject")}finally{this.releaseMedia(e),this.showTerminal(e,"Call declined"),this.emit("callrejected",t??{call:{id:e}})}}async busy(e=this.currentCall?.id){if(!e)throw new Error("No incoming call is available");try{return await this.callAction(e,"busy")}finally{this.releaseMedia(e),this.showTerminal(e,"Line busy")}}async end(e){this.callAudio.stop();try{return await this.callAction(e,"end")}finally{this.releaseMedia(e),this.showTerminal(e,"Call ended")}}async cancel(e){this.callAudio.stop();try{return await this.callAction(e,"cancel")}finally{this.releaseMedia(e),this.showTerminal(e,"Call cancelled")}}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"];i.includes(e.status)&&this.clearInvitationExpiry(e.id),e.status==="accepted"&&(this.mediaClients.has(e.id)?this.clearInvitationExpiry(e.id):this.armCallWatchdog(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,pt(e.status)),i.includes(e.status)&&this.releaseMedia(e.id),this.emit("callupdated",e)}disconnect(){this.stopped=!0,this.callGeneration+=1,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",_(s)),!this.stopped&&this.options.reconnect!==!1&&this.scheduleReconnect();return}if(this.stopped||this.socket)return;const i=G(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=Y(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=De("Decline","#fff","#a32920","#d9a09b"),r=De("Answer","#fff","#08745f","#08745f");o.addEventListener("click",()=>{this.reject(e.id).catch(c=>this.emit("error",_(c)))}),r.addEventListener("click",()=>{this.accept(e.id).catch(c=>this.emit("error",_(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",_(i)),this.stopped||(this.tokenRefreshTimer=setTimeout(()=>this.scheduleTokenRefresh(),3e4))})},Math.min(t,2147e6))}releaseMedia(e){this.clearInvitationExpiry(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",_(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 mt(a){const e=new Pe(a);return a.autoConnect!==!1&&a.signalingUrl&&e.connect(),e}function Le(a){return a?typeof a=="string"?document.querySelector(a):a:null}function De(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 pt(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 Oe(a){return{id:a.participantId,name:a.displayName,role:"participant"}}async function ft(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,v=0,p=0,S=0,b=0,y=0,T=0,f=0,M=0,I=null,D=null,P=null,L=null,w=null,R=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)),v=Math.max(v,Number(g.frameWidth??0)),b=Math.max(b,Number(g.frameHeight??0)),y+=Number(g.framesDropped??0),T+=Number(g.freezeCount??0),f+=Number(g.totalFreezesDuration??0),M+=Number(g.concealedSamples??0),w||=i.get(String(g.codecId??""))??null),g.type==="outbound-rtp"&&!g.isRemote&&(p=Math.max(p,Number(g.frameWidth??0)),S=Math.max(S,Number(g.frameHeight??0)),o+=Number(g.bytesSent??0),R||=g.qualityLimitationReason?String(g.qualityLimitationReason):null,w||=i.get(String(g.codecId??""))??null),g.type==="candidate-pair"&&(g.selected||g.nominated)&&g.state==="succeeded"){I=Number.isFinite(g.currentRoundTripTime)?Number(g.currentRoundTripTime):I,D=Number.isFinite(g.availableOutgoingBitrate)?Number(g.availableOutgoingBitrate):D;const U=n.get(g.localCandidateId);P=U?.candidateType?String(U.candidateType):P,L=U?.protocol?String(U.protocol):L}});const m=Date.now(),h=e?Math.max(.001,(m-e.timestamp)/1e3):0,C=e?Math.max(0,Math.round((s-e.inboundBytes)*8/h)):null,A=e?Math.max(0,Math.round((o-e.outboundBytes)*8/h)):null,E=r+c,x=e?Math.max(0,r-e.packetsLost):r,Q=e?Math.max(0,c-e.packetsReceived):c,j=x+Q;return{counters:{timestamp:m,inboundBytes:s,outboundBytes:o,packetsLost:r,packetsReceived:c},sample:{rttMs:I===null?null:I*1e3,jitterMs:d?l/d*1e3:null,packetLossPct:j?x/j*100:null,packetLossCumulativePct:E?r/E*100:null,packetsLost:r,packetsReceived:c,inboundBitrateBps:C,outboundBitrateBps:A,availableOutgoingBitrateBps:D,framesPerSecond:u||null,frameWidth:v||null,frameHeight:b||null,inboundFrameWidth:v||null,inboundFrameHeight:b||null,outboundFrameWidth:p||null,outboundFrameHeight:S||null,framesDropped:y,freezeCount:T,freezeDurationMs:Math.round(f*1e3),concealedSamples:M,codec:w,transportProtocol:L,candidateType:P,qualityLimitationReason:R}}}function gt(a){let e=a.mediaScore==null?5:Math.round(Ue(Number(a.mediaScore),0,5));const t=se(a.packetLossPct)??0,i=se(a.rttMs)??0,n=se(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(Ue(4.5-t*.09-i*.0011-n*.006,1,4.5).toFixed(2));return{networkScore:e,estimatedMos:o}}function Ne(){return navigator.connection??null}function yt(){const a=Number(navigator.deviceMemory);return Number.isFinite(a)?a:null}function se(a){const e=Number(a);return Number.isFinite(e)?e:null}function Ue(a,e,t){return Math.min(t,Math.max(e,a))}function _(a){return a instanceof Error?{name:a.name,message:a.message}:{name:"ServiceError",message:String(a??"Service request failed")}}function qe(a,e){return a instanceof Error?a:new Error(e)}const xe="1.4.0";k.AVATAR_MODEL_MAX_BYTES=Te,k.AVATAR_TUNING_DEFAULTS=te,k.AvatarModelError=N,k.BACKGROUND_PRESETS=ue,k.OVERLAY_EFFECTS=me,k.RtcClient=Me,k.RtcIncomingClient=Pe,k.SPEAKER_NOISE_FLOOR=.02,k.SPEAKER_TAKEOVER_RATIO=1.4,k.createAvatarPipeline=Ke,k.createCallRecorder=Ge,k.createPictureInPictureController=Je,k.createRtcClient=dt,k.createRtcIncomingClient=mt,k.createVideoEffectsPipeline=Ye,k.drawAvatar=ge,k.headPoseFromMatrix=pe,k.importAvatarModel=st,k.listArchiveModels=et,k.listAvatarModels=ot,k.neutralRig=Z,k.pickActiveSpeaker=J,k.readAvatarModel=ct,k.readAvatarModelMetadata=Ce,k.removeAvatarModel=lt,k.rigFromBlendshapes=fe,k.scenePainters=X,k.smoothRig=ee,k.stylizedAvatarRenderer=ie,k.unlockRtcCallAudio=Ae,k.version=xe,Object.defineProperty(k,Symbol.toStringTag,{value:"Module"})}));
|