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