@streaming-cdn/rtc-web 1.3.14 → 1.3.16
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 +70 -1
- package/dist/avatar-rig.d.ts +63 -0
- package/dist/avatar.d.ts +88 -0
- package/dist/call-recorder.d.ts +48 -0
- package/dist/index.d.ts +9 -1
- package/dist/rtc-web.es.js +641 -277
- package/dist/rtc-web.umd.js +1 -1
- package/dist/video-effects.d.ts +46 -0
- 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((r,
|
|
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 O = {
|
|
18
18
|
audio: { active: !1, maxBitrate: 0, maxFramerate: 0, scaleResolutionDownBy: 4 },
|
|
19
19
|
low: { active: !0, maxBitrate: 35e4, maxFramerate: 15, scaleResolutionDownBy: 4 },
|
|
20
20
|
medium: { active: !0, maxBitrate: 11e5, maxFramerate: 24, scaleResolutionDownBy: 2 },
|
|
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,46 +31,46 @@ const f = {
|
|
|
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), d = o.indexOf(a), l = (n.samplesSeen ?? s.warmupSamples) + 1, u = l <= s.warmupSamples, y = ue(e.qualityLimitationReason), f = e.packetLossPct ?? 0, w = e.rttMs ?? 0, m = e.availableOutgoingBitrateBps, S = O[a].maxBitrate, T = f >= s.criticalLossPct || w >= s.criticalRttMs || y === "cpu", k = T || f >= s.poorLossPct || w >= s.poorRttMs || y === "bandwidth", b = m == null || m >= S * s.sustainRatio, M = f < s.goodLossPct && w < s.goodRttMs && !y && b, I = k ? t + 1 : 0, g = M ? i + 1 : 0, p = { quality: a, poorSamples: I, goodSamples: g, samplesSeen: l, changed: !1 };
|
|
40
|
+
return d > c ? { quality: o[c], poorSamples: 0, goodSamples: 0, samplesSeen: l, changed: !0 } : u ? p : T && d > 0 ? { quality: o[d - 1], poorSamples: 0, goodSamples: 0, samplesSeen: l, changed: !0 } : I >= s.poorSamplesToStepDown && d > 0 ? { quality: o[d - 1], poorSamples: 0, goodSamples: 0, samplesSeen: l, changed: !0 } : g >= s.goodSamplesToStepUp && d < c ? { quality: o[d + 1], poorSamples: 0, goodSamples: 0, samplesSeen: l, changed: !0 } : p;
|
|
41
41
|
}
|
|
42
|
-
function
|
|
43
|
-
const t =
|
|
44
|
-
let i = 0, n = 0, s = null,
|
|
45
|
-
for (const
|
|
46
|
-
const u = String(
|
|
47
|
-
if (u === "outbound-rtp" && String(
|
|
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 l of t.values()) {
|
|
46
|
+
const u = String(l.type ?? "");
|
|
47
|
+
if (u === "outbound-rtp" && String(l.kind ?? "") === "video" && (i += Number(l.packetsSent ?? 0), s = l.qualityLimitationReason ?? s), u === "remote-inbound-rtp" && String(l.kind ?? "") === "video") {
|
|
48
|
+
n += Number(l.packetsLost ?? 0);
|
|
49
|
+
const y = Number(l.roundTripTime ?? NaN);
|
|
50
|
+
Number.isFinite(y) && (o = y * 1e3);
|
|
51
51
|
}
|
|
52
|
-
if (u === "candidate-pair" && (
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
Number.isFinite(
|
|
52
|
+
if (u === "candidate-pair" && (l.nominated === !0 || String(l.state ?? "") === "succeeded")) {
|
|
53
|
+
const y = Number(l.currentRoundTripTime ?? NaN);
|
|
54
|
+
o == null && Number.isFinite(y) && (o = y * 1e3);
|
|
55
|
+
const f = Number(l.availableOutgoingBitrate ?? NaN);
|
|
56
|
+
Number.isFinite(f) && (r = f);
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
-
const
|
|
60
|
-
let
|
|
59
|
+
const c = { packetsSent: i, packetsLost: n, timestamp: Date.now() };
|
|
60
|
+
let d = null;
|
|
61
61
|
if (e) {
|
|
62
|
-
const
|
|
63
|
-
|
|
62
|
+
const l = i - e.packetsSent, u = n - e.packetsLost;
|
|
63
|
+
l > 0 && u >= 0 && (d = u / (l + u) * 100);
|
|
64
64
|
}
|
|
65
65
|
return {
|
|
66
|
-
sample: { packetLossPct:
|
|
67
|
-
counters:
|
|
66
|
+
sample: { packetLossPct: d, rttMs: o, availableOutgoingBitrateBps: r, qualityLimitationReason: s },
|
|
67
|
+
counters: c
|
|
68
68
|
};
|
|
69
69
|
}
|
|
70
|
-
const
|
|
70
|
+
const fe = {
|
|
71
71
|
audio: { active: !1, maxBitrate: 0, maxFramerate: 0, scaleResolutionDownBy: 4 }
|
|
72
|
-
},
|
|
73
|
-
class
|
|
72
|
+
}, ge = { active: !0, maxBitrate: 4e6, maxFramerate: 30, scaleResolutionDownBy: 1 };
|
|
73
|
+
class x {
|
|
74
74
|
options;
|
|
75
75
|
socket = null;
|
|
76
76
|
localStream = null;
|
|
@@ -153,14 +153,14 @@ class A {
|
|
|
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()),
|
|
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
158
|
this.screenStream?.getVideoTracks().some((r) => r.readyState === "live") || await this.replaceVideoTrack(n);
|
|
159
159
|
return;
|
|
160
160
|
}
|
|
161
161
|
for (const r of this.peers.values()) {
|
|
162
|
-
const
|
|
163
|
-
|
|
162
|
+
const c = r.connection.getSenders().find((d) => d.track?.kind === s);
|
|
163
|
+
c ? await c.replaceTrack(n) : r.connection.addTrack(n, this.localStream);
|
|
164
164
|
}
|
|
165
165
|
await this.applyVideoQuality();
|
|
166
166
|
}
|
|
@@ -168,7 +168,7 @@ class A {
|
|
|
168
168
|
this.quality = e;
|
|
169
169
|
for (const t of this.localStream?.getVideoTracks() ?? []) t.enabled = e !== "audio";
|
|
170
170
|
for (const t of this.peers.values())
|
|
171
|
-
t.quality =
|
|
171
|
+
t.quality = x.clampToCeiling(t.quality, e), t.poorSamples = 0, t.goodSamples = 0;
|
|
172
172
|
await this.applyVideoQuality();
|
|
173
173
|
}
|
|
174
174
|
static clampToCeiling(e, t) {
|
|
@@ -214,26 +214,26 @@ class A {
|
|
|
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((
|
|
218
|
-
this.resolveJoin =
|
|
219
|
-
}), s = new WebSocket(i, ["rtc-client", t.signalingToken]),
|
|
217
|
+
const n = new Promise((c, d) => {
|
|
218
|
+
this.resolveJoin = c, this.rejectJoin = d;
|
|
219
|
+
}), s = new WebSocket(i, ["rtc-client", t.signalingToken]), o = ++this.socketGeneration;
|
|
220
220
|
this.socket = s;
|
|
221
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
|
-
}), s.addEventListener("message", (
|
|
225
|
-
const
|
|
224
|
+
}), s.addEventListener("message", (c) => {
|
|
225
|
+
const d = String(c.data);
|
|
226
226
|
this.signalQueue = this.signalQueue.then(async () => {
|
|
227
|
-
this.socket !== s || this.socketGeneration !==
|
|
228
|
-
}).catch((
|
|
229
|
-
const u =
|
|
227
|
+
this.socket !== s || this.socketGeneration !== o || await this.handleSignal(d);
|
|
228
|
+
}).catch((l) => {
|
|
229
|
+
const u = l instanceof Error ? l.message : "RTC signaling processing failed";
|
|
230
230
|
this.options.onEvent("signalingerror", { message: u }), this.joined || this.rejectJoin?.(new Error(u));
|
|
231
231
|
});
|
|
232
|
-
}), s.addEventListener("error", () => this.rejectJoin?.(new Error("RTC signaling connection failed"))), s.addEventListener("close", (
|
|
232
|
+
}), s.addEventListener("error", () => this.rejectJoin?.(new Error("RTC signaling connection failed"))), s.addEventListener("close", (c) => {
|
|
233
233
|
if (this.socket !== s) return;
|
|
234
|
-
this.socket = null, this.stopHeartbeat(), this.stopQualitySampling(), !this.joined && !e && this.rejectJoin?.(new Error(
|
|
235
|
-
const
|
|
236
|
-
this.joined = !1,
|
|
234
|
+
this.socket = null, this.stopHeartbeat(), this.stopQualitySampling(), !this.joined && !e && this.rejectJoin?.(new Error(c.reason || "RTC signaling connection closed"));
|
|
235
|
+
const d = !this.stopped && (this.joined || e);
|
|
236
|
+
this.joined = !1, d ? (this.resetPeers(), this.options.onEvent("reconnecting", { reason: c.reason, code: c.code, attempt: this.reconnectAttempt + 1 }), this.scheduleReconnect()) : this.stopped || this.options.onEvent("disconnected", { reason: c.reason, code: c.code });
|
|
237
237
|
});
|
|
238
238
|
try {
|
|
239
239
|
await n, this.joined = !0, this.options.onEvent("connected", { participantId: t.participantId, resumed: e });
|
|
@@ -270,8 +270,8 @@ class A {
|
|
|
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
276
|
const r = await n.connection.createAnswer();
|
|
277
277
|
await n.connection.setLocalDescription(r), this.send({
|
|
@@ -313,19 +313,19 @@ class A {
|
|
|
313
313
|
makingOffer: !1,
|
|
314
314
|
offerTask: null,
|
|
315
315
|
activeNegotiationId: null,
|
|
316
|
-
quality:
|
|
316
|
+
quality: x.startingQuality(this.quality),
|
|
317
317
|
poorSamples: 0,
|
|
318
318
|
goodSamples: 0,
|
|
319
319
|
samplesSeen: 0,
|
|
320
320
|
counters: null
|
|
321
321
|
};
|
|
322
322
|
this.peers.set(e.participantId, s);
|
|
323
|
-
for (const
|
|
324
|
-
const
|
|
325
|
-
return r &&
|
|
326
|
-
|
|
327
|
-
}), i.addEventListener("track", (
|
|
328
|
-
n.getTracks().some((
|
|
323
|
+
for (const c of this.localStream?.getAudioTracks() ?? []) i.addTrack(c, this.localStream);
|
|
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
|
+
c.candidate && this.send({ type: "rtc.ice", targetParticipantId: e.participantId, candidate: c.candidate.toJSON() });
|
|
327
|
+
}), i.addEventListener("track", (c) => {
|
|
328
|
+
n.getTracks().some((d) => d.id === c.track.id) || n.addTrack(c.track), this.options.onEvent("remotestream", { participant: e, stream: n, track: c.track });
|
|
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 A {
|
|
|
351
351
|
});
|
|
352
352
|
if (e.connection.signalingState !== "stable") return;
|
|
353
353
|
await e.connection.setLocalDescription(t);
|
|
354
|
-
const i =
|
|
354
|
+
const i = W();
|
|
355
355
|
e.activeNegotiationId = i, this.send({
|
|
356
356
|
type: "rtc.offer",
|
|
357
357
|
targetParticipantId: e.identity.participantId,
|
|
@@ -371,10 +371,10 @@ class A {
|
|
|
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 A {
|
|
|
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 : O[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 A {
|
|
|
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 A {
|
|
|
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 A {
|
|
|
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 ?? W(),
|
|
491
491
|
...e
|
|
492
492
|
}));
|
|
493
493
|
}
|
|
@@ -495,15 +495,15 @@ class A {
|
|
|
495
495
|
for (const t of e?.getTracks() ?? []) t.stop();
|
|
496
496
|
}
|
|
497
497
|
}
|
|
498
|
-
function
|
|
498
|
+
function W() {
|
|
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 $ = { active: !0, maxBitrate: 4e6, maxFramerate: 30, scaleResolutionDownBy: 1 }, Y = [
|
|
502
|
+
{ rid: "a", maxBitrate: O.high.maxBitrate, maxFramerate: O.high.maxFramerate },
|
|
503
|
+
{ rid: "b", maxBitrate: O.medium.maxBitrate, maxFramerate: O.medium.maxFramerate, scaleResolutionDownBy: 2 },
|
|
504
|
+
{ rid: "c", maxBitrate: O.low.maxBitrate, maxFramerate: O.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,12 +594,12 @@ class se {
|
|
|
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()),
|
|
599
|
-
this.screenStream?.getVideoTracks().some((
|
|
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
|
+
this.screenStream?.getVideoTracks().some((c) => c.readyState === "live") || await this.replaceVideoTrack(n);
|
|
600
600
|
return;
|
|
601
601
|
}
|
|
602
|
-
const r = this.connection?.getSenders().find((
|
|
602
|
+
const r = this.connection?.getSenders().find((c) => c.track?.kind === "audio");
|
|
603
603
|
r && await r.replaceTrack(n);
|
|
604
604
|
}
|
|
605
605
|
/** One uplink serves everyone, so the requested quality applies directly. */
|
|
@@ -638,26 +638,26 @@ class se {
|
|
|
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((
|
|
642
|
-
this.resolveJoin =
|
|
643
|
-
}), s = new WebSocket(i, ["rtc-client", t.signalingToken]),
|
|
641
|
+
const n = new Promise((c, d) => {
|
|
642
|
+
this.resolveJoin = c, this.rejectJoin = d;
|
|
643
|
+
}), s = new WebSocket(i, ["rtc-client", t.signalingToken]), o = ++this.socketGeneration;
|
|
644
644
|
this.socket = s;
|
|
645
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
|
-
}), s.addEventListener("message", (
|
|
649
|
-
const
|
|
648
|
+
}), s.addEventListener("message", (c) => {
|
|
649
|
+
const d = String(c.data);
|
|
650
650
|
this.signalQueue = this.signalQueue.then(async () => {
|
|
651
|
-
this.socket !== s || this.socketGeneration !==
|
|
652
|
-
}).catch((
|
|
653
|
-
const u =
|
|
651
|
+
this.socket !== s || this.socketGeneration !== o || await this.handleSignal(d);
|
|
652
|
+
}).catch((l) => {
|
|
653
|
+
const u = l instanceof Error ? l.message : "RTC signaling processing failed";
|
|
654
654
|
this.options.onEvent("signalingerror", { message: u }), this.joined || this.rejectJoin?.(new Error(u));
|
|
655
655
|
});
|
|
656
|
-
}), s.addEventListener("error", () => this.rejectJoin?.(new Error("RTC signaling connection failed"))), s.addEventListener("close", (
|
|
656
|
+
}), s.addEventListener("error", () => this.rejectJoin?.(new Error("RTC signaling connection failed"))), s.addEventListener("close", (c) => {
|
|
657
657
|
if (this.socket !== s) return;
|
|
658
|
-
this.socket = null, this.stopHeartbeat(), !this.joined && !e && this.rejectJoin?.(new Error(
|
|
659
|
-
const
|
|
660
|
-
this.joined = !1,
|
|
658
|
+
this.socket = null, this.stopHeartbeat(), !this.joined && !e && this.rejectJoin?.(new Error(c.reason || "RTC signaling connection closed"));
|
|
659
|
+
const d = !this.stopped && (this.joined || e);
|
|
660
|
+
this.joined = !1, d ? (this.options.onEvent("reconnecting", { reason: c.reason, code: c.code, attempt: this.reconnectAttempt + 1 }), this.scheduleReconnect()) : this.stopped || this.options.onEvent("disconnected", { reason: c.reason, code: c.code });
|
|
661
661
|
});
|
|
662
662
|
try {
|
|
663
663
|
await n, this.joined = !0, this.options.onEvent("connected", { participantId: t.participantId, resumed: e });
|
|
@@ -718,10 +718,10 @@ class se {
|
|
|
718
718
|
const n = e.map((r) => t.addTransceiver(r, {
|
|
719
719
|
direction: "sendonly",
|
|
720
720
|
streams: this.localStream ? [this.localStream] : [],
|
|
721
|
-
...r.kind === "video" ? { sendEncodings:
|
|
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
|
{
|
|
@@ -733,7 +733,7 @@ class se {
|
|
|
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}`;
|
|
@@ -753,7 +753,7 @@ class se {
|
|
|
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,12 +761,12 @@ class se {
|
|
|
761
761
|
simulcastNames: /* @__PURE__ */ new Set(),
|
|
762
762
|
mids: /* @__PURE__ */ new Map()
|
|
763
763
|
};
|
|
764
|
-
|
|
764
|
+
o.sessionId = i;
|
|
765
765
|
for (const r of n)
|
|
766
|
-
r.simulcast &&
|
|
767
|
-
this.remotes.set(e.participantId,
|
|
768
|
-
const r = s.filter((
|
|
769
|
-
r.length !== 0 && await this.subscribe(
|
|
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
770
|
}).catch((r) => {
|
|
771
771
|
this.options.onEvent("signalingerror", { message: r instanceof Error ? r.message : "SFU subscribe failed" });
|
|
772
772
|
}), await this.negotiationQueue;
|
|
@@ -818,7 +818,7 @@ class se {
|
|
|
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 se {
|
|
|
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
|
|
833
|
+
const o = this.quality === "audio" ? null : G[this.quality];
|
|
834
834
|
n.encodings = s.map((r) => {
|
|
835
835
|
if (i)
|
|
836
|
-
return { ...r, active: r.rid === "a", ...r.rid === "a" ? { maxBitrate:
|
|
837
|
-
const
|
|
838
|
-
return { ...r, ...
|
|
836
|
+
return { ...r, active: r.rid === "a", ...r.rid === "a" ? { maxBitrate: $.maxBitrate, maxFramerate: $.maxFramerate } : {} };
|
|
837
|
+
const c = Y.find((l) => l.rid === r.rid), d = o != null && String(r.rid ?? "") >= o;
|
|
838
|
+
return { ...r, ...c ?? {}, active: d };
|
|
839
839
|
});
|
|
840
840
|
} else {
|
|
841
|
-
const
|
|
842
|
-
n.encodings = s.map((
|
|
841
|
+
const o = this.quality === "audio" ? O.audio : O[this.quality], r = i ? $ : 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 se {
|
|
|
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 se {
|
|
|
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 se {
|
|
|
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
|
|
948
|
-
return r ?
|
|
946
|
+
function o(r) {
|
|
947
|
+
const c = a.getRemoteStreams();
|
|
948
|
+
return r ? c.find((d) => d.participant.participantId === r)?.stream ?? null : c[0]?.stream ?? a.getLocalStream();
|
|
949
949
|
}
|
|
950
950
|
return {
|
|
951
951
|
supported: i,
|
|
952
952
|
async enter(r) {
|
|
953
953
|
if (!i) throw new Error("This browser does not support picture-in-picture");
|
|
954
|
-
const
|
|
955
|
-
if (!
|
|
956
|
-
const
|
|
957
|
-
|
|
958
|
-
}), await
|
|
954
|
+
const c = o(r);
|
|
955
|
+
if (!c) throw new Error("No video stream is available for picture-in-picture");
|
|
956
|
+
const d = s();
|
|
957
|
+
d.srcObject !== c && (d.srcObject = c), await d.play().catch(() => {
|
|
958
|
+
}), await d.requestPictureInPicture();
|
|
959
959
|
},
|
|
960
960
|
async exit() {
|
|
961
961
|
t.pictureInPictureElement && typeof t.exitPictureInPicture == "function" && await t.exitPictureInPicture();
|
|
@@ -965,7 +965,358 @@ function ve(c, e = {}) {
|
|
|
965
965
|
}
|
|
966
966
|
};
|
|
967
967
|
}
|
|
968
|
-
|
|
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, d = null, l = null, u = null, y = 0, f = [], w = !1;
|
|
971
|
+
async function m(S, T, k, b = "application/json") {
|
|
972
|
+
const M = await i(`${n}${T}`, {
|
|
973
|
+
method: S,
|
|
974
|
+
headers: {
|
|
975
|
+
authorization: `Bearer ${e.credential.signalingToken}`,
|
|
976
|
+
"content-type": b
|
|
977
|
+
},
|
|
978
|
+
body: k === void 0 ? void 0 : b === "application/json" ? JSON.stringify(k) : k
|
|
979
|
+
});
|
|
980
|
+
if (!M.ok) throw new Error(`Recording request failed with HTTP ${M.status}`);
|
|
981
|
+
return M.json();
|
|
982
|
+
}
|
|
983
|
+
return {
|
|
984
|
+
get recording() {
|
|
985
|
+
return w;
|
|
986
|
+
},
|
|
987
|
+
async start() {
|
|
988
|
+
if (w) return;
|
|
989
|
+
u = (await m("POST", "/v1/rtc/recordings")).recordingId;
|
|
990
|
+
const T = t.createElement("canvas");
|
|
991
|
+
T.width = r, T.height = o;
|
|
992
|
+
const k = T.getContext("2d"), b = /* @__PURE__ */ new Map(), M = (E, P) => {
|
|
993
|
+
let C = b.get(E);
|
|
994
|
+
return C || (C = t.createElement("video"), C.muted = !0, C.playsInline = !0, C.srcObject = P, C.play().catch(() => {
|
|
995
|
+
}), b.set(E, C)), C;
|
|
996
|
+
};
|
|
997
|
+
d = setInterval(() => {
|
|
998
|
+
const E = a.getRemoteStreams(), P = a.getLocalStream(), C = [
|
|
999
|
+
...E.map((j) => ({ key: j.participant.participantId, stream: j.stream, label: j.participant.displayName })),
|
|
1000
|
+
...P ? [{ key: "local", stream: P, label: "me" }] : []
|
|
1001
|
+
];
|
|
1002
|
+
k.fillStyle = "#101418", k.fillRect(0, 0, r, o);
|
|
1003
|
+
const D = Math.max(1, Math.ceil(Math.sqrt(C.length))), v = Math.max(1, Math.ceil(C.length / D)), N = r / D, U = o / v;
|
|
1004
|
+
C.forEach((j, H) => {
|
|
1005
|
+
const _ = M(j.key, j.stream), B = H % D * N, F = Math.floor(H / D) * U;
|
|
1006
|
+
_.videoWidth > 0 && k.drawImage(_, B + 2, F + 2, N - 4, U - 4), k.fillStyle = "rgba(0,0,0,.6)", k.fillRect(B + 8, F + U - 30, Math.min(N - 16, 12 + j.label.length * 9), 22), k.fillStyle = "#fff", k.font = "14px system-ui", k.fillText(j.label, B + 14, F + U - 14);
|
|
1007
|
+
});
|
|
1008
|
+
}, 66);
|
|
1009
|
+
const I = T.captureStream(15);
|
|
1010
|
+
l = new AudioContext();
|
|
1011
|
+
const g = l.createMediaStreamDestination(), p = /* @__PURE__ */ new Set(), h = () => {
|
|
1012
|
+
const E = [
|
|
1013
|
+
...a.getRemoteStreams().map((P) => P.stream),
|
|
1014
|
+
...a.getLocalStream() ? [a.getLocalStream()] : []
|
|
1015
|
+
];
|
|
1016
|
+
for (const P of E)
|
|
1017
|
+
for (const C of P.getAudioTracks())
|
|
1018
|
+
p.has(C.id) || (p.add(C.id), l.createMediaStreamSource(new MediaStream([C])).connect(g));
|
|
1019
|
+
};
|
|
1020
|
+
h();
|
|
1021
|
+
const R = setInterval(h, 1e3), L = () => clearInterval(R);
|
|
1022
|
+
for (const E of g.stream.getAudioTracks()) I.addTrack(E);
|
|
1023
|
+
c = (e.mediaRecorderFactory ?? ((E, P) => new MediaRecorder(E, P)))(I, { mimeType: s }), f = [], c.addEventListener("dataavailable", (E) => {
|
|
1024
|
+
E.data && E.data.size > 0 && f.push(E.data);
|
|
1025
|
+
}), c.addEventListener("stop", L), c.start(1e3), y = Date.now(), w = !0;
|
|
1026
|
+
},
|
|
1027
|
+
async stop() {
|
|
1028
|
+
if (!w || !c || !u) throw new Error("The recorder is not running");
|
|
1029
|
+
w = !1;
|
|
1030
|
+
const S = c, T = new Promise((g) => S.addEventListener("stop", () => g(), { once: !0 }));
|
|
1031
|
+
S.stop(), await T, d && clearInterval(d), d = null, await l?.close().catch(() => {
|
|
1032
|
+
}), l = null;
|
|
1033
|
+
const k = new Blob(f, { type: s });
|
|
1034
|
+
f = [];
|
|
1035
|
+
const b = Math.max(1, Math.round((Date.now() - y) / 1e3)), M = await m(
|
|
1036
|
+
"PUT",
|
|
1037
|
+
`/v1/rtc/recordings/${encodeURIComponent(u)}/media`,
|
|
1038
|
+
k,
|
|
1039
|
+
s
|
|
1040
|
+
);
|
|
1041
|
+
await m("POST", `/v1/rtc/recordings/${encodeURIComponent(u)}/complete`, { durationSeconds: b });
|
|
1042
|
+
const I = { recordingId: u, sizeBytes: M.sizeBytes, durationSeconds: b };
|
|
1043
|
+
return u = null, c = null, I;
|
|
1044
|
+
},
|
|
1045
|
+
dispose() {
|
|
1046
|
+
d && clearInterval(d), d = null;
|
|
1047
|
+
try {
|
|
1048
|
+
c?.stop();
|
|
1049
|
+
} catch {
|
|
1050
|
+
}
|
|
1051
|
+
c = null, l?.close().catch(() => {
|
|
1052
|
+
}), l = null, w = !1;
|
|
1053
|
+
}
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
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
|
+
for (let s = 0; s < 3; s += 1) {
|
|
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
|
+
}
|
|
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
|
+
}
|
|
1068
|
+
},
|
|
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
|
+
const n = t * 0.45;
|
|
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
|
+
}
|
|
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
|
+
},
|
|
1081
|
+
bokeh(a, e, t, i) {
|
|
1082
|
+
a.fillStyle = "#14100c", a.fillRect(0, 0, e, t);
|
|
1083
|
+
for (let n = 0; n < 26; n += 1) {
|
|
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
|
+
}
|
|
1087
|
+
},
|
|
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
|
+
}
|
|
1092
|
+
};
|
|
1093
|
+
function Ve(a, e = {}) {
|
|
1094
|
+
const t = e.document ?? globalThis.document, i = e.width ?? 1280, n = e.height ?? 720, s = e.frameRate ?? 24, o = a instanceof MediaStreamTrack ? a : a.getVideoTracks()[0];
|
|
1095
|
+
if (!o) throw new Error("A video track is required");
|
|
1096
|
+
let r = e.background ?? "none", c = e.overlay ?? "none", d = e.backgroundImage ?? null, l = e.segmenter ?? null, u = null, y = !1;
|
|
1097
|
+
const f = t.createElement("video");
|
|
1098
|
+
f.muted = !0, f.playsInline = !0, f.srcObject = new MediaStream([o]), f.play().catch(() => {
|
|
1099
|
+
});
|
|
1100
|
+
const w = t.createElement("canvas");
|
|
1101
|
+
w.width = i, w.height = n;
|
|
1102
|
+
const m = w.getContext("2d"), S = t.createElement("canvas");
|
|
1103
|
+
S.width = i, S.height = n;
|
|
1104
|
+
const T = S.getContext("2d"), k = Array.from({ length: 80 }, (p, h) => ({
|
|
1105
|
+
x: (Math.sin(h * 12.9898) * 0.5 + 0.5) * i,
|
|
1106
|
+
y: (Math.sin(h * 78.233) * 0.5 + 0.5) * n,
|
|
1107
|
+
speed: 0.6 + h % 5 * 0.5,
|
|
1108
|
+
drift: Math.sin(h) * 0.6,
|
|
1109
|
+
size: 2 + h % 4,
|
|
1110
|
+
hue: h * 47 % 360
|
|
1111
|
+
}));
|
|
1112
|
+
function b() {
|
|
1113
|
+
if (u) {
|
|
1114
|
+
T.clearRect(0, 0, i, n), T.drawImage(u, 0, 0, i, n), T.globalCompositeOperation = "source-in", T.drawImage(f, 0, 0, i, n), T.globalCompositeOperation = "source-over", m.drawImage(S, 0, 0);
|
|
1115
|
+
return;
|
|
1116
|
+
}
|
|
1117
|
+
const p = i * 0.42, h = p * 9 / 16, R = i - p - 24, L = n - h - 24;
|
|
1118
|
+
m.save(), m.shadowColor = "rgba(0,0,0,.5)", m.shadowBlur = 24, m.beginPath(), m.roundRect(R, L, p, h, 14), m.clip(), m.drawImage(f, R, L, p, h), m.restore();
|
|
1119
|
+
}
|
|
1120
|
+
function M(p) {
|
|
1121
|
+
if (r === "none" ? m.drawImage(f, 0, 0, i, n) : r === "blur" ? (m.filter = "blur(14px)", m.drawImage(f, -16, -16, i + 32, n + 32), m.filter = "none", u && b()) : r === "image" && d ? (m.drawImage(d, 0, 0, i, n), b()) : ((K[r] ?? K.aurora)(m, i, n, p), b()), c === "snow" || c === "confetti")
|
|
1122
|
+
for (const h of k)
|
|
1123
|
+
h.y += h.speed * (c === "confetti" ? 1.6 : 1), h.x += h.drift, h.y > n && (h.y = -6, h.x = Math.random() * i), m.fillStyle = c === "snow" ? "rgba(255,255,255,.85)" : `hsl(${h.hue}, 90%, 60%)`, c === "confetti" ? m.fillRect(h.x, h.y, h.size + 2, h.size) : (m.beginPath(), m.arc(h.x, h.y, h.size / 2, 0, Math.PI * 2), m.fill());
|
|
1124
|
+
else if (c === "vignette") {
|
|
1125
|
+
const h = m.createRadialGradient(i / 2, n / 2, n * 0.4, i / 2, n / 2, n * 0.85);
|
|
1126
|
+
h.addColorStop(0, "rgba(0,0,0,0)"), h.addColorStop(1, "rgba(0,0,0,0.55)"), m.fillStyle = h, m.fillRect(0, 0, i, n);
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
const I = setInterval(() => {
|
|
1130
|
+
M(Date.now()), l && !y && r !== "none" && (y = !0, l.segment(f).then((p) => {
|
|
1131
|
+
u = p;
|
|
1132
|
+
}).catch(() => {
|
|
1133
|
+
u = null;
|
|
1134
|
+
}).finally(() => {
|
|
1135
|
+
y = !1;
|
|
1136
|
+
}));
|
|
1137
|
+
}, Math.round(1e3 / s)), g = w.captureStream(s);
|
|
1138
|
+
return {
|
|
1139
|
+
track: g.getVideoTracks()[0],
|
|
1140
|
+
stream: g,
|
|
1141
|
+
setBackground(p, h) {
|
|
1142
|
+
r = Se.includes(p) ? p : "none", h !== void 0 && (d = h), r === "none" && (u = null);
|
|
1143
|
+
},
|
|
1144
|
+
setOverlay(p) {
|
|
1145
|
+
c = ke.includes(p) ? p : "none";
|
|
1146
|
+
},
|
|
1147
|
+
setSegmenter(p) {
|
|
1148
|
+
l = p, p || (u = null);
|
|
1149
|
+
},
|
|
1150
|
+
dispose() {
|
|
1151
|
+
clearInterval(I), l?.close?.();
|
|
1152
|
+
for (const p of g.getTracks()) p.stop();
|
|
1153
|
+
f.srcObject = null;
|
|
1154
|
+
}
|
|
1155
|
+
};
|
|
1156
|
+
}
|
|
1157
|
+
function le() {
|
|
1158
|
+
return {
|
|
1159
|
+
blinkLeft: 0,
|
|
1160
|
+
blinkRight: 0,
|
|
1161
|
+
jawOpen: 0,
|
|
1162
|
+
smile: 0,
|
|
1163
|
+
browUp: 0,
|
|
1164
|
+
pupilX: 0,
|
|
1165
|
+
pupilY: 0,
|
|
1166
|
+
headYaw: 0,
|
|
1167
|
+
headPitch: 0,
|
|
1168
|
+
headRoll: 0,
|
|
1169
|
+
tracked: !1
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
function A(a, e) {
|
|
1173
|
+
const t = a.find((i) => i.categoryName === e);
|
|
1174
|
+
return t ? Math.max(0, Math.min(1, t.score)) : 0;
|
|
1175
|
+
}
|
|
1176
|
+
function we(a) {
|
|
1177
|
+
if (!a || a.length < 16) return { yaw: 0, pitch: 0, roll: 0 };
|
|
1178
|
+
a[0], a[4];
|
|
1179
|
+
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);
|
|
1180
|
+
return { yaw: o, pitch: r, roll: c };
|
|
1181
|
+
}
|
|
1182
|
+
function Te(a, e, t = 1) {
|
|
1183
|
+
if (!a || a.length === 0) return le();
|
|
1184
|
+
const i = we(e), n = (A(a, "eyeLookOutRight") + A(a, "eyeLookInLeft")) / 2, s = (A(a, "eyeLookOutLeft") + A(a, "eyeLookInRight")) / 2, o = (A(a, "eyeLookUpLeft") + A(a, "eyeLookUpRight")) / 2, r = (A(a, "eyeLookDownLeft") + A(a, "eyeLookDownRight")) / 2, c = (A(a, "mouthSmileLeft") + A(a, "mouthSmileRight")) / 2, d = (A(a, "mouthFrownLeft") + A(a, "mouthFrownRight")) / 2, l = A(a, "eyeBlinkLeft"), u = A(a, "eyeBlinkRight"), y = Math.min(1, Math.abs(i.yaw) / 0.35) * Math.max(0, Math.min(1, t)), f = (l + u) / 2;
|
|
1185
|
+
return {
|
|
1186
|
+
blinkLeft: l + (f - l) * y,
|
|
1187
|
+
blinkRight: u + (f - u) * y,
|
|
1188
|
+
jawOpen: A(a, "jawOpen"),
|
|
1189
|
+
smile: c - d,
|
|
1190
|
+
browUp: (A(a, "browInnerUp") + A(a, "browOuterUpLeft") + A(a, "browOuterUpRight")) / 3,
|
|
1191
|
+
pupilX: n - s,
|
|
1192
|
+
pupilY: r - o,
|
|
1193
|
+
headYaw: i.yaw,
|
|
1194
|
+
headPitch: i.pitch,
|
|
1195
|
+
headRoll: i.roll,
|
|
1196
|
+
tracked: !0
|
|
1197
|
+
};
|
|
1198
|
+
}
|
|
1199
|
+
function X(a, e, t = 0.4) {
|
|
1200
|
+
const i = (n, s) => n + (s - n) * t;
|
|
1201
|
+
return {
|
|
1202
|
+
blinkLeft: i(a.blinkLeft, e.blinkLeft),
|
|
1203
|
+
blinkRight: i(a.blinkRight, e.blinkRight),
|
|
1204
|
+
jawOpen: i(a.jawOpen, e.jawOpen),
|
|
1205
|
+
smile: i(a.smile, e.smile),
|
|
1206
|
+
browUp: i(a.browUp, e.browUp),
|
|
1207
|
+
pupilX: i(a.pupilX, e.pupilX),
|
|
1208
|
+
pupilY: i(a.pupilY, e.pupilY),
|
|
1209
|
+
headYaw: i(a.headYaw, e.headYaw),
|
|
1210
|
+
headPitch: i(a.headPitch, e.headPitch),
|
|
1211
|
+
headRoll: i(a.headRoll, e.headRoll),
|
|
1212
|
+
tracked: e.tracked
|
|
1213
|
+
};
|
|
1214
|
+
}
|
|
1215
|
+
function be(a, e, t, i, n) {
|
|
1216
|
+
n || (a.fillStyle = "#101822", a.fillRect(0, 0, t, i));
|
|
1217
|
+
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;
|
|
1218
|
+
a.save(), a.translate(s, o), a.rotate(e.headRoll * 0.6);
|
|
1219
|
+
const c = Math.max(-1.35, Math.min(1.35, e.headYaw)), d = Math.max(-0.9, Math.min(0.9, e.headPitch)), l = Math.sin(c), u = (p) => Math.sin(p + c) * r, y = (p) => Math.cos(p + c), f = Math.sin(d) * r * 0.22;
|
|
1220
|
+
a.fillStyle = "#3a2a1c", a.beginPath(), a.ellipse(-l * 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(l) * 0.12), r * 1.12, 0, 0, Math.PI * 2), a.fill(), a.fillStyle = "#3a2a1c", a.beginPath(), a.ellipse(-l * r * 0.28, -r * 0.72 + f * 0.4, r * 0.92, r * 0.5, 0, Math.PI, Math.PI * 2), a.fill();
|
|
1221
|
+
const w = 0;
|
|
1222
|
+
a.fillStyle = "#e5b58a", a.beginPath();
|
|
1223
|
+
const m = u(w) * 0.9, S = r * 0.12 + f;
|
|
1224
|
+
a.ellipse(m + l * r * 0.14, S, r * 0.11, r * 0.14, 0, 0, Math.PI * 2), a.fill();
|
|
1225
|
+
for (const p of [-1, 1]) {
|
|
1226
|
+
const h = p * 0.42, R = y(h);
|
|
1227
|
+
if (R <= 0.12) continue;
|
|
1228
|
+
const L = u(h) * 0.82, q = Math.max(0.2, R), E = p < 0 ? e.blinkLeft : e.blinkRight, P = Math.max(0.06, 1 - E), C = e.browUp * r * 0.14;
|
|
1229
|
+
a.strokeStyle = "#4a2e18", a.lineWidth = r * 0.07, a.lineCap = "round", a.beginPath(), a.moveTo(L - r * 0.2 * q, -r * 0.42 - C + f), a.quadraticCurveTo(L, -r * 0.52 - C + f, L + r * 0.2 * q, -r * 0.44 - C + f), a.stroke();
|
|
1230
|
+
const D = -r * 0.18 + f;
|
|
1231
|
+
a.fillStyle = "#ffffff", a.beginPath(), a.ellipse(L, D, r * 0.16 * q, r * 0.12 * P, 0, 0, Math.PI * 2), a.fill(), a.fillStyle = "#22303c", a.beginPath(), a.ellipse(
|
|
1232
|
+
L + e.pupilX * r * 0.07 * q,
|
|
1233
|
+
D + e.pupilY * r * 0.05,
|
|
1234
|
+
r * 0.06 * q,
|
|
1235
|
+
r * 0.06 * P,
|
|
1236
|
+
0,
|
|
1237
|
+
0,
|
|
1238
|
+
Math.PI * 2
|
|
1239
|
+
), a.fill();
|
|
1240
|
+
}
|
|
1241
|
+
const T = Math.max(0.25, y(0)), k = u(0) * 0.7, b = r * 0.55 * T, M = r * (0.04 + e.jawOpen * 0.34), I = e.smile * r * 0.12, g = r * 0.42 + f;
|
|
1242
|
+
a.fillStyle = "#8c2f2f", a.beginPath(), a.moveTo(k - b / 2, g - I), a.quadraticCurveTo(k, g + M, k + b / 2, g - I), a.quadraticCurveTo(k, g - M * 0.25, k - b / 2, g - I), 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");
|
|
1243
|
+
}
|
|
1244
|
+
const Z = {
|
|
1245
|
+
mouthGain: 1.7,
|
|
1246
|
+
blinkGain: 1,
|
|
1247
|
+
eyeSync: 1,
|
|
1248
|
+
smoothing: 0.4,
|
|
1249
|
+
avatarScale: 1,
|
|
1250
|
+
avatarLift: 0,
|
|
1251
|
+
lostHoldMs: 400
|
|
1252
|
+
};
|
|
1253
|
+
function ee() {
|
|
1254
|
+
return {
|
|
1255
|
+
render(a, e, t, i, n) {
|
|
1256
|
+
be(a, e, t, i, n ?? void 0);
|
|
1257
|
+
}
|
|
1258
|
+
};
|
|
1259
|
+
}
|
|
1260
|
+
const z = (a) => Math.max(0, Math.min(1, a));
|
|
1261
|
+
function Ue(a, e = {}) {
|
|
1262
|
+
const t = e.document ?? globalThis.document, i = e.width ?? 1280, n = e.height ?? 720, s = e.frameRate ?? 24, o = a instanceof MediaStreamTrack ? a : a.getVideoTracks()[0];
|
|
1263
|
+
if (!o) throw new Error("A video track is required");
|
|
1264
|
+
let r = e.tracker ?? null, c = e.renderer ?? ee(), d = e.mode ?? "replace";
|
|
1265
|
+
const l = { ...Z, ...e.tuning }, u = e.backdrop ?? "#101822", y = t.createElement("video");
|
|
1266
|
+
y.muted = !0, y.playsInline = !0, y.srcObject = new MediaStream([o]), y.play().catch(() => {
|
|
1267
|
+
});
|
|
1268
|
+
const f = t.createElement("canvas");
|
|
1269
|
+
f.width = i, f.height = n;
|
|
1270
|
+
const w = f.getContext("2d");
|
|
1271
|
+
let m = le(), S = null, T = 0;
|
|
1272
|
+
function k(g) {
|
|
1273
|
+
if (g.length <= 454) return;
|
|
1274
|
+
const p = g[10], h = g[152], R = g[234], L = g[454], q = (R.x + L.x) / 2 * i, E = (R.y + L.y) / 2 * n, P = Math.hypot((h.x - p.x) * i, (h.y - p.y) * n), C = Math.max(0.6, Math.cos(m.headPitch)), D = { x: q, y: E, radius: Math.max(24, P / C * 0.8) };
|
|
1275
|
+
S = S ? { x: S.x + (D.x - S.x) * 0.4, y: S.y + (D.y - S.y) * 0.4, radius: S.radius + (D.radius - S.radius) * 0.15 } : D;
|
|
1276
|
+
}
|
|
1277
|
+
function b(g) {
|
|
1278
|
+
if (r && y.readyState >= 2) {
|
|
1279
|
+
let h = null;
|
|
1280
|
+
try {
|
|
1281
|
+
h = r.detect(y, g);
|
|
1282
|
+
} catch {
|
|
1283
|
+
}
|
|
1284
|
+
const R = Te(h?.categories ?? null, h?.matrix ?? null, l.eyeSync);
|
|
1285
|
+
R.jawOpen = z(R.jawOpen * l.mouthGain), R.blinkLeft = z(R.blinkLeft * l.blinkGain), R.blinkRight = z(R.blinkRight * l.blinkGain), R.tracked ? (T = g, m = X(m, R, l.smoothing), h?.landmarks && k(h.landmarks)) : m.tracked && g - T > l.lostHoldMs && (m = X(m, { ...m, jawOpen: 0, browUp: 0, smile: m.smile * 0.5, blinkLeft: 0.1, blinkRight: 0.1 }, 0.05));
|
|
1286
|
+
}
|
|
1287
|
+
d === "overlay" && y.readyState >= 2 ? w.drawImage(y, 0, 0, i, n) : (w.fillStyle = u, w.fillRect(0, 0, i, n));
|
|
1288
|
+
const p = d === "overlay" && S ? { x: S.x, y: S.y + l.avatarLift * S.radius, radius: S.radius * l.avatarScale } : null;
|
|
1289
|
+
c?.render(w, m, i, n, p);
|
|
1290
|
+
}
|
|
1291
|
+
const M = setInterval(() => b(Date.now()), Math.round(1e3 / s)), I = f.captureStream(s);
|
|
1292
|
+
return {
|
|
1293
|
+
track: I.getVideoTracks()[0],
|
|
1294
|
+
stream: I,
|
|
1295
|
+
getRig: () => ({ ...m }),
|
|
1296
|
+
setMode(g) {
|
|
1297
|
+
d = g === "overlay" ? "overlay" : "replace";
|
|
1298
|
+
},
|
|
1299
|
+
setTracker(g) {
|
|
1300
|
+
r?.close?.(), r = g;
|
|
1301
|
+
},
|
|
1302
|
+
setRenderer(g) {
|
|
1303
|
+
c?.dispose?.(), c = g ?? ee();
|
|
1304
|
+
},
|
|
1305
|
+
setTuning(g) {
|
|
1306
|
+
for (const p of Object.keys(Z)) {
|
|
1307
|
+
const h = g[p];
|
|
1308
|
+
typeof h == "number" && Number.isFinite(h) && (l[p] = h);
|
|
1309
|
+
}
|
|
1310
|
+
},
|
|
1311
|
+
getTuning: () => ({ ...l }),
|
|
1312
|
+
dispose() {
|
|
1313
|
+
clearInterval(M), r?.close?.(), c?.dispose?.();
|
|
1314
|
+
for (const g of I.getTracks()) g.stop();
|
|
1315
|
+
y.srcObject = null;
|
|
1316
|
+
}
|
|
1317
|
+
};
|
|
1318
|
+
}
|
|
1319
|
+
class Ce extends EventTarget {
|
|
969
1320
|
mode;
|
|
970
1321
|
options;
|
|
971
1322
|
client = null;
|
|
@@ -999,7 +1350,7 @@ class re extends EventTarget {
|
|
|
999
1350
|
async initialize() {
|
|
1000
1351
|
if (this.client) return this;
|
|
1001
1352
|
try {
|
|
1002
|
-
const t = (this.options.transport === "auto" ? this.mode === "meeting" ? "sfu" : "mesh" : this.options.transport ?? "mesh") === "sfu" ?
|
|
1353
|
+
const t = (this.options.transport === "auto" ? this.mode === "meeting" ? "sfu" : "mesh" : this.options.transport ?? "mesh") === "sfu" ? ye : x;
|
|
1003
1354
|
return this.client = new t({
|
|
1004
1355
|
credential: {
|
|
1005
1356
|
signalingToken: this.options.credential.signalingToken,
|
|
@@ -1018,19 +1369,19 @@ class re extends EventTarget {
|
|
|
1018
1369
|
iceTransportPolicy: this.options.iceTransportPolicy ?? "all",
|
|
1019
1370
|
localStream: this.options.localStream,
|
|
1020
1371
|
onEvent: (i, n) => this.handleNativeEvent(i, n)
|
|
1021
|
-
}), await this.client.initialize(), this.startStats(), this.emit("initialized", { mode: this.mode, sdkVersion:
|
|
1372
|
+
}), await this.client.initialize(), this.startStats(), this.emit("initialized", { mode: this.mode, sdkVersion: De }), this.options.autoJoin && await this.join(), this;
|
|
1022
1373
|
} catch (e) {
|
|
1023
1374
|
this.state = "failed";
|
|
1024
|
-
const t =
|
|
1025
|
-
throw this.emit("error",
|
|
1375
|
+
const t = re(e, "RTC initialization failed");
|
|
1376
|
+
throw this.emit("error", V(t)), t;
|
|
1026
1377
|
}
|
|
1027
1378
|
}
|
|
1028
1379
|
async join() {
|
|
1029
1380
|
const e = this.requireClient();
|
|
1030
1381
|
return this.joinPromise ? this.joinPromise : (this.state = "joining", this.emit("joining", {}), this.joinPromise = e.join().catch((t) => {
|
|
1031
1382
|
this.state = "failed";
|
|
1032
|
-
const i =
|
|
1033
|
-
throw this.emit("error",
|
|
1383
|
+
const i = re(t, "RTC connection failed");
|
|
1384
|
+
throw this.emit("error", V(i)), i;
|
|
1034
1385
|
}).finally(() => {
|
|
1035
1386
|
this.joinPromise = null;
|
|
1036
1387
|
}), this.joinPromise);
|
|
@@ -1068,14 +1419,14 @@ class re extends EventTarget {
|
|
|
1068
1419
|
return navigator.mediaDevices?.enumerateDevices ? navigator.mediaDevices.enumerateDevices() : [];
|
|
1069
1420
|
}
|
|
1070
1421
|
getParticipants() {
|
|
1071
|
-
return this.requireClient().getParticipants().map(
|
|
1422
|
+
return this.requireClient().getParticipants().map(ne);
|
|
1072
1423
|
}
|
|
1073
1424
|
getLocalStream() {
|
|
1074
1425
|
return this.client?.getLocalStream() ?? null;
|
|
1075
1426
|
}
|
|
1076
1427
|
getRemoteStreams() {
|
|
1077
1428
|
return (this.client?.getRemoteStreams() ?? []).map((e) => ({
|
|
1078
|
-
participant:
|
|
1429
|
+
participant: ne(e.participant),
|
|
1079
1430
|
stream: e.stream
|
|
1080
1431
|
}));
|
|
1081
1432
|
}
|
|
@@ -1144,45 +1495,45 @@ class re extends EventTarget {
|
|
|
1144
1495
|
let n = "unavailable";
|
|
1145
1496
|
if (e.requestMedia !== !1 && navigator.mediaDevices?.getUserMedia)
|
|
1146
1497
|
try {
|
|
1147
|
-
(await navigator.mediaDevices.getUserMedia({ audio: this.options.audio, video: this.options.video })).getTracks().forEach((
|
|
1498
|
+
(await navigator.mediaDevices.getUserMedia({ audio: this.options.audio, video: this.options.video })).getTracks().forEach((y) => y.stop()), n = "granted";
|
|
1148
1499
|
} catch {
|
|
1149
1500
|
n = "denied", t.push("media_permission_denied");
|
|
1150
1501
|
}
|
|
1151
|
-
const s = navigator.mediaDevices?.enumerateDevices ? await navigator.mediaDevices.enumerateDevices().catch(() => []) : [],
|
|
1152
|
-
this.options.audio &&
|
|
1153
|
-
const
|
|
1154
|
-
let
|
|
1155
|
-
if (
|
|
1502
|
+
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;
|
|
1503
|
+
this.options.audio && o === 0 && t.push("microphone_unavailable"), this.options.video && r === 0 && t.push("camera_unavailable");
|
|
1504
|
+
const c = e.probeUrl ?? this.options.preflightProbeUrl;
|
|
1505
|
+
let d = null;
|
|
1506
|
+
if (c) {
|
|
1156
1507
|
const u = performance.now();
|
|
1157
1508
|
try {
|
|
1158
|
-
const
|
|
1159
|
-
|
|
1509
|
+
const y = await fetch(c, { method: "HEAD", cache: "no-store", credentials: "omit" });
|
|
1510
|
+
d = Math.round(performance.now() - u), y.ok || t.push("network_probe_failed");
|
|
1160
1511
|
} catch {
|
|
1161
1512
|
t.push("network_probe_failed");
|
|
1162
1513
|
}
|
|
1163
1514
|
}
|
|
1164
|
-
const
|
|
1515
|
+
const l = {
|
|
1165
1516
|
passed: t.length === 0,
|
|
1166
1517
|
sampledAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1167
1518
|
secureContext: i,
|
|
1168
1519
|
mediaPermission: n,
|
|
1169
|
-
microphoneCount:
|
|
1520
|
+
microphoneCount: o,
|
|
1170
1521
|
cameraCount: r,
|
|
1171
|
-
probeLatencyMs:
|
|
1172
|
-
effectiveConnectionType:
|
|
1522
|
+
probeLatencyMs: d,
|
|
1523
|
+
effectiveConnectionType: ae()?.effectiveType ?? null,
|
|
1173
1524
|
issues: t
|
|
1174
1525
|
};
|
|
1175
|
-
return this.emit("preflight",
|
|
1526
|
+
return this.emit("preflight", l), this.options.autoReportStats && await this.reportQuality({
|
|
1176
1527
|
sampleType: "preflight",
|
|
1177
|
-
sampledAt:
|
|
1178
|
-
connectionState:
|
|
1179
|
-
rttMs:
|
|
1180
|
-
effectiveConnectionType:
|
|
1528
|
+
sampledAt: l.sampledAt,
|
|
1529
|
+
connectionState: l.passed ? "ready" : "failed",
|
|
1530
|
+
rttMs: l.probeLatencyMs,
|
|
1531
|
+
effectiveConnectionType: l.effectiveConnectionType
|
|
1181
1532
|
}).catch(() => {
|
|
1182
|
-
}),
|
|
1533
|
+
}), l;
|
|
1183
1534
|
}
|
|
1184
1535
|
async collectQualitySample(e = "runtime") {
|
|
1185
|
-
const t = this.options.statsProvider ? await this.options.statsProvider() : {}, i = this.peerConnection ? await
|
|
1536
|
+
const t = this.options.statsProvider ? await this.options.statsProvider() : {}, i = this.peerConnection ? await Pe(this.peerConnection, this.previousCounters) : null;
|
|
1186
1537
|
i?.counters && (this.previousCounters = i.counters);
|
|
1187
1538
|
const s = { ...{
|
|
1188
1539
|
sampleType: e,
|
|
@@ -1211,10 +1562,10 @@ class re extends EventTarget {
|
|
|
1211
1562
|
transportProtocol: null,
|
|
1212
1563
|
candidateType: null,
|
|
1213
1564
|
qualityLimitationReason: null,
|
|
1214
|
-
deviceMemoryGb:
|
|
1215
|
-
effectiveConnectionType:
|
|
1216
|
-
}, ...i?.sample ?? {}, ...t, sampleType: e, sampledAt: (/* @__PURE__ */ new Date()).toISOString() },
|
|
1217
|
-
return s.networkScore =
|
|
1565
|
+
deviceMemoryGb: Le(),
|
|
1566
|
+
effectiveConnectionType: ae()?.effectiveType ?? null
|
|
1567
|
+
}, ...i?.sample ?? {}, ...t, sampleType: e, sampledAt: (/* @__PURE__ */ new Date()).toISOString() }, o = Ae(s);
|
|
1568
|
+
return s.networkScore = o.networkScore, s.estimatedMos = o.estimatedMos, this.latestQuality = s, s;
|
|
1218
1569
|
}
|
|
1219
1570
|
async reportQuality(e) {
|
|
1220
1571
|
const t = this.options.credential.telemetryToken, i = this.options.telemetryEndpoint ?? this.options.credential.telemetryEndpoint;
|
|
@@ -1264,7 +1615,7 @@ class re extends EventTarget {
|
|
|
1264
1615
|
const i = Date.now(), n = e !== "runtime" || i - this.lastTelemetryAt >= this.options.telemetryInterval;
|
|
1265
1616
|
this.options.autoReportStats && n && (await this.reportQuality(t), this.lastTelemetryAt = i);
|
|
1266
1617
|
} catch (t) {
|
|
1267
|
-
this.emit("telemetryerror",
|
|
1618
|
+
this.emit("telemetryerror", V(t));
|
|
1268
1619
|
} finally {
|
|
1269
1620
|
this.statsRunning = !1;
|
|
1270
1621
|
}
|
|
@@ -1281,8 +1632,8 @@ class re extends EventTarget {
|
|
|
1281
1632
|
}
|
|
1282
1633
|
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);
|
|
1283
1634
|
this.poorQualitySamples = n ? this.poorQualitySamples + 1 : 0, this.goodQualitySamples = s ? this.goodQualitySamples + 1 : 0;
|
|
1284
|
-
const
|
|
1285
|
-
i && r > 0 ? (this.poorQualitySamples = 0, await this.applyEffectiveQuality("low", "network")) : this.poorQualitySamples >= 2 && r > 0 ? (this.poorQualitySamples = 0, await this.applyEffectiveQuality(
|
|
1635
|
+
const o = ["low", "medium", "high"], r = o.indexOf(this.effectiveQuality);
|
|
1636
|
+
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"));
|
|
1286
1637
|
}
|
|
1287
1638
|
async applyEffectiveQuality(e, t) {
|
|
1288
1639
|
if (this.effectiveQuality === e && this.client) return;
|
|
@@ -1298,21 +1649,21 @@ class re extends EventTarget {
|
|
|
1298
1649
|
this.dispatchEvent(new CustomEvent(e, { detail: i })), this.options.onEvent?.(i);
|
|
1299
1650
|
}
|
|
1300
1651
|
}
|
|
1301
|
-
async function
|
|
1302
|
-
return new
|
|
1652
|
+
async function Qe(a) {
|
|
1653
|
+
return new Ce(a).initialize();
|
|
1303
1654
|
}
|
|
1304
|
-
let
|
|
1305
|
-
function
|
|
1306
|
-
if (
|
|
1307
|
-
const
|
|
1308
|
-
return e ? (
|
|
1655
|
+
let Q = null;
|
|
1656
|
+
function de() {
|
|
1657
|
+
if (Q) return Q;
|
|
1658
|
+
const a = globalThis, e = a.AudioContext ?? a.webkitAudioContext;
|
|
1659
|
+
return e ? (Q = new e(), Q) : null;
|
|
1309
1660
|
}
|
|
1310
|
-
async function
|
|
1311
|
-
const
|
|
1312
|
-
return
|
|
1313
|
-
}),
|
|
1661
|
+
async function Re() {
|
|
1662
|
+
const a = de();
|
|
1663
|
+
return a ? (a.state === "suspended" && await a.resume().catch(() => {
|
|
1664
|
+
}), a.state === "running") : !1;
|
|
1314
1665
|
}
|
|
1315
|
-
class
|
|
1666
|
+
class Ie {
|
|
1316
1667
|
options;
|
|
1317
1668
|
media = null;
|
|
1318
1669
|
cadenceTimer = null;
|
|
@@ -1321,7 +1672,7 @@ class ce {
|
|
|
1321
1672
|
this.options = e;
|
|
1322
1673
|
}
|
|
1323
1674
|
unlock() {
|
|
1324
|
-
return
|
|
1675
|
+
return Re();
|
|
1325
1676
|
}
|
|
1326
1677
|
startIncoming() {
|
|
1327
1678
|
this.start("incoming", this.options.ringtoneUrl);
|
|
@@ -1349,17 +1700,17 @@ class ce {
|
|
|
1349
1700
|
i(), this.cadenceTimer = setInterval(i, e === "incoming" ? 3e3 : 3500);
|
|
1350
1701
|
}
|
|
1351
1702
|
playBuiltInTone(e) {
|
|
1352
|
-
const t =
|
|
1703
|
+
const t = de();
|
|
1353
1704
|
if (!t || t.state !== "running") return;
|
|
1354
|
-
const i = e === "incoming" ? 1.25 : 1, n = e === "incoming" ? [440, 480] : [425], s = t.createGain(),
|
|
1355
|
-
s.gain.setValueAtTime(1e-4,
|
|
1705
|
+
const i = e === "incoming" ? 1.25 : 1, n = e === "incoming" ? [440, 480] : [425], s = t.createGain(), o = t.currentTime;
|
|
1706
|
+
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);
|
|
1356
1707
|
for (const r of n) {
|
|
1357
|
-
const
|
|
1358
|
-
|
|
1708
|
+
const c = t.createOscillator();
|
|
1709
|
+
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);
|
|
1359
1710
|
}
|
|
1360
1711
|
}
|
|
1361
1712
|
}
|
|
1362
|
-
class
|
|
1713
|
+
class Ee extends EventTarget {
|
|
1363
1714
|
options;
|
|
1364
1715
|
socket = null;
|
|
1365
1716
|
stopped = !1;
|
|
@@ -1386,14 +1737,14 @@ class le extends EventTarget {
|
|
|
1386
1737
|
if (e.token && !e.token.startsWith("rtcc_")) throw new Error("A valid RTC client token is required");
|
|
1387
1738
|
if (!e.signalingUrl && !e.apiBaseUrl && !globalThis.location?.origin)
|
|
1388
1739
|
throw new Error("signalingUrl or apiBaseUrl is required outside a browser");
|
|
1389
|
-
this.options = e, this.currentToken = e.token ?? null, this.currentTokenExpiresAt = e.tokenExpiresAt, this.callAudio = new
|
|
1740
|
+
this.options = e, this.currentToken = e.token ?? null, this.currentTokenExpiresAt = e.tokenExpiresAt, this.callAudio = new Ie(e), this.mount = te(e.mount), this.scheduleTokenRefresh();
|
|
1390
1741
|
}
|
|
1391
1742
|
connect() {
|
|
1392
1743
|
if (!this.options.signalingUrl) throw new Error("signalingUrl is required to connect signaling");
|
|
1393
1744
|
return this.stopped = !1, this.scheduleTokenRefresh(), this.openSocket("initial"), this;
|
|
1394
1745
|
}
|
|
1395
1746
|
setMount(e) {
|
|
1396
|
-
this.clearUi(), this.mount =
|
|
1747
|
+
this.clearUi(), this.mount = te(e), this.currentCall && this.renderIncoming(this.currentCall);
|
|
1397
1748
|
}
|
|
1398
1749
|
/** Binds media lifetime to a call. Terminal call states automatically leave and destroy it. */
|
|
1399
1750
|
attachMediaClient(e, t) {
|
|
@@ -1421,8 +1772,8 @@ class le extends EventTarget {
|
|
|
1421
1772
|
headers: e.idempotencyKey ? { "idempotency-key": e.idempotencyKey } : void 0,
|
|
1422
1773
|
body: JSON.stringify(i)
|
|
1423
1774
|
});
|
|
1424
|
-
} catch (
|
|
1425
|
-
throw this.callAudio.stop(),
|
|
1775
|
+
} catch (o) {
|
|
1776
|
+
throw this.callAudio.stop(), o;
|
|
1426
1777
|
}
|
|
1427
1778
|
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;
|
|
1428
1779
|
})();
|
|
@@ -1479,7 +1830,7 @@ class le extends EventTarget {
|
|
|
1479
1830
|
/** Applies a call state received through the application's own push or signaling channel. */
|
|
1480
1831
|
handleCallUpdate(e) {
|
|
1481
1832
|
const t = e.id === this.currentCall?.id || e.id === this.outgoingCallId;
|
|
1482
|
-
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,
|
|
1833
|
+
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, Me(e.status)), ["rejected", "busy", "cancelled", "ended", "failed", "missed"].includes(e.status) && this.releaseMedia(e.id), this.emit("callupdated", e);
|
|
1483
1834
|
}
|
|
1484
1835
|
disconnect() {
|
|
1485
1836
|
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();
|
|
@@ -1495,7 +1846,7 @@ class le extends EventTarget {
|
|
|
1495
1846
|
try {
|
|
1496
1847
|
t = await this.resolveToken(e, e === "reconnect");
|
|
1497
1848
|
} catch (s) {
|
|
1498
|
-
this.emit("tokenerror",
|
|
1849
|
+
this.emit("tokenerror", V(s)), !this.stopped && this.options.reconnect !== !1 && this.scheduleReconnect();
|
|
1499
1850
|
return;
|
|
1500
1851
|
}
|
|
1501
1852
|
if (this.stopped || this.socket) return;
|
|
@@ -1503,8 +1854,8 @@ class le extends EventTarget {
|
|
|
1503
1854
|
i.protocol = i.protocol === "https:" ? "wss:" : "ws:";
|
|
1504
1855
|
const n = new WebSocket(i, ["rtc-client", t]);
|
|
1505
1856
|
this.socket = n, n.addEventListener("open", () => {
|
|
1506
|
-
const s = this.reconnectAttempt > 0,
|
|
1507
|
-
this.reconnectAttempt = 0, this.heartbeatDeadline = Date.now() + 45e3, this.emit("signalingconnected", { resumed: s }),
|
|
1857
|
+
const s = this.reconnectAttempt > 0, o = this.lastDisconnect;
|
|
1858
|
+
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(() => {
|
|
1508
1859
|
if (n.readyState === WebSocket.OPEN) {
|
|
1509
1860
|
if (Date.now() > this.heartbeatDeadline) {
|
|
1510
1861
|
n.close(4e3, "Signaling heartbeat timed out");
|
|
@@ -1518,14 +1869,14 @@ class le extends EventTarget {
|
|
|
1518
1869
|
}), n.addEventListener("close", (s) => {
|
|
1519
1870
|
if (this.socket !== n) return;
|
|
1520
1871
|
this.socket = null, this.heartbeatTimer && clearInterval(this.heartbeatTimer), this.heartbeatTimer = null;
|
|
1521
|
-
const
|
|
1872
|
+
const o = !this.stopped && this.options.reconnect !== !1, r = {
|
|
1522
1873
|
code: s.code,
|
|
1523
1874
|
reason: s.reason,
|
|
1524
1875
|
wasClean: s.wasClean,
|
|
1525
|
-
willReconnect:
|
|
1876
|
+
willReconnect: o,
|
|
1526
1877
|
attempt: this.reconnectAttempt + 1
|
|
1527
1878
|
};
|
|
1528
|
-
|
|
1879
|
+
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();
|
|
1529
1880
|
}), n.addEventListener("error", () => {
|
|
1530
1881
|
const s = { message: "RTC signaling connection failed", reconnecting: !this.stopped && this.options.reconnect !== !1, attempt: this.reconnectAttempt + 1 };
|
|
1531
1882
|
this.emit("signalingerror", s), this.reportSignalingDiagnostic({ event: "error", attempt: s.attempt });
|
|
@@ -1559,16 +1910,16 @@ class le extends EventTarget {
|
|
|
1559
1910
|
async request(e, t, i = !1) {
|
|
1560
1911
|
const n = this.options.apiBaseUrl ?? this.options.signalingUrl ?? globalThis.location?.origin;
|
|
1561
1912
|
if (!n) throw new Error("apiBaseUrl is required for call actions");
|
|
1562
|
-
const s = new URL(n, globalThis.location?.href),
|
|
1563
|
-
r.set("authorization", `Bearer ${
|
|
1564
|
-
const
|
|
1565
|
-
if (
|
|
1913
|
+
const s = new URL(n, globalThis.location?.href), o = await this.resolveToken("initial"), r = new Headers(t.headers);
|
|
1914
|
+
r.set("authorization", `Bearer ${o}`), r.set("content-type", "application/json");
|
|
1915
|
+
const c = await fetch(new URL(e, s.origin), { ...t, headers: r }), d = await c.json().catch(() => ({}));
|
|
1916
|
+
if (c.status === 401 && this.options.tokenProvider && !i)
|
|
1566
1917
|
return await this.resolveToken("unauthorized", !0), this.request(e, t, !0);
|
|
1567
|
-
if (!
|
|
1568
|
-
const
|
|
1569
|
-
throw new Error(typeof
|
|
1918
|
+
if (!c.ok) {
|
|
1919
|
+
const l = d.error;
|
|
1920
|
+
throw new Error(typeof l == "string" ? l : l?.message || `RTC API failed with HTTP ${c.status}`);
|
|
1570
1921
|
}
|
|
1571
|
-
return
|
|
1922
|
+
return d;
|
|
1572
1923
|
}
|
|
1573
1924
|
renderIncoming(e) {
|
|
1574
1925
|
if (!this.mount) return;
|
|
@@ -1591,12 +1942,12 @@ class le extends EventTarget {
|
|
|
1591
1942
|
n.textContent = e.caller.name, n.style.fontSize = "20px";
|
|
1592
1943
|
const s = document.createElement("div");
|
|
1593
1944
|
Object.assign(s.style, { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "10px" });
|
|
1594
|
-
const
|
|
1595
|
-
|
|
1596
|
-
this.reject(e.id).catch((
|
|
1945
|
+
const o = ie("Decline", "#fff", "#a32920", "#d9a09b"), r = ie("Answer", "#fff", "#08745f", "#08745f");
|
|
1946
|
+
o.addEventListener("click", () => {
|
|
1947
|
+
this.reject(e.id).catch((c) => this.emit("error", V(c)));
|
|
1597
1948
|
}), r.addEventListener("click", () => {
|
|
1598
|
-
this.accept(e.id).catch((
|
|
1599
|
-
}), s.appendChild(
|
|
1949
|
+
this.accept(e.id).catch((c) => this.emit("error", V(c)));
|
|
1950
|
+
}), s.appendChild(o), s.appendChild(r), t.appendChild(i), t.appendChild(n), t.appendChild(s), this.mount.replaceChildren(t);
|
|
1600
1951
|
}
|
|
1601
1952
|
dismiss(e) {
|
|
1602
1953
|
e && this.currentCall?.id !== e && this.outgoingCallId !== e || (this.currentCall = null, (!e || this.outgoingCallId === e) && (this.outgoingCallId = null), this.callAudio.stop(), this.clearUi());
|
|
@@ -1663,7 +2014,7 @@ class le extends EventTarget {
|
|
|
1663
2014
|
const t = Math.max(0, e - this.refreshSkew(e));
|
|
1664
2015
|
this.tokenRefreshTimer = setTimeout(() => {
|
|
1665
2016
|
this.tokenRefreshTimer = null, this.resolveToken("expiring", !0).catch((i) => {
|
|
1666
|
-
this.emit("tokenerror",
|
|
2017
|
+
this.emit("tokenerror", V(i)), this.stopped || (this.tokenRefreshTimer = setTimeout(() => this.scheduleTokenRefresh(), 3e4));
|
|
1667
2018
|
});
|
|
1668
2019
|
}, Math.min(t, 2147e6));
|
|
1669
2020
|
}
|
|
@@ -1677,23 +2028,23 @@ class le extends EventTarget {
|
|
|
1677
2028
|
this.options.reportSignalingDiagnostics === !1 || this.stopped || this.request("/v1/rtc/client/signaling-events", {
|
|
1678
2029
|
method: "POST",
|
|
1679
2030
|
body: JSON.stringify({ ...e, occurredAt: (/* @__PURE__ */ new Date()).toISOString() })
|
|
1680
|
-
}).catch((t) => this.emit("diagnosticserror",
|
|
2031
|
+
}).catch((t) => this.emit("diagnosticserror", V(t)));
|
|
1681
2032
|
}
|
|
1682
2033
|
emit(e, t) {
|
|
1683
2034
|
const i = { type: e, detail: t, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
|
|
1684
2035
|
this.dispatchEvent(new CustomEvent(e, { detail: i })), this.options.onEvent?.(i);
|
|
1685
2036
|
}
|
|
1686
2037
|
}
|
|
1687
|
-
function
|
|
1688
|
-
const e = new
|
|
1689
|
-
return
|
|
2038
|
+
function xe(a) {
|
|
2039
|
+
const e = new Ee(a);
|
|
2040
|
+
return a.autoConnect !== !1 && a.signalingUrl && e.connect(), e;
|
|
1690
2041
|
}
|
|
1691
|
-
function
|
|
1692
|
-
return
|
|
2042
|
+
function te(a) {
|
|
2043
|
+
return a ? typeof a == "string" ? document.querySelector(a) : a : null;
|
|
1693
2044
|
}
|
|
1694
|
-
function
|
|
2045
|
+
function ie(a, e, t, i) {
|
|
1695
2046
|
const n = document.createElement("button");
|
|
1696
|
-
return n.type = "button", n.textContent =
|
|
2047
|
+
return n.type = "button", n.textContent = a, Object.assign(n.style, {
|
|
1697
2048
|
minHeight: "44px",
|
|
1698
2049
|
border: `1px solid ${i}`,
|
|
1699
2050
|
borderRadius: "6px",
|
|
@@ -1704,7 +2055,7 @@ function U(c, e, t, i) {
|
|
|
1704
2055
|
fontWeight: "700"
|
|
1705
2056
|
}), n;
|
|
1706
2057
|
}
|
|
1707
|
-
function
|
|
2058
|
+
function Me(a) {
|
|
1708
2059
|
return {
|
|
1709
2060
|
rejected: "Call declined",
|
|
1710
2061
|
busy: "Line busy",
|
|
@@ -1712,91 +2063,104 @@ function de(c) {
|
|
|
1712
2063
|
ended: "Call ended",
|
|
1713
2064
|
failed: "Call failed",
|
|
1714
2065
|
missed: "No answer"
|
|
1715
|
-
}[
|
|
2066
|
+
}[a] ?? "Call ended";
|
|
1716
2067
|
}
|
|
1717
|
-
function
|
|
2068
|
+
function ne(a) {
|
|
1718
2069
|
return {
|
|
1719
|
-
id:
|
|
1720
|
-
name:
|
|
2070
|
+
id: a.participantId,
|
|
2071
|
+
name: a.displayName,
|
|
1721
2072
|
role: "participant"
|
|
1722
2073
|
};
|
|
1723
2074
|
}
|
|
1724
|
-
async function
|
|
1725
|
-
const t = await
|
|
1726
|
-
let s = 0,
|
|
1727
|
-
t.forEach((
|
|
1728
|
-
n.set(
|
|
1729
|
-
}), t.forEach((
|
|
1730
|
-
if (
|
|
1731
|
-
|
|
1732
|
-
const
|
|
1733
|
-
|
|
2075
|
+
async function Pe(a, e) {
|
|
2076
|
+
const t = await a.getStats(), i = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Map();
|
|
2077
|
+
let s = 0, o = 0, r = 0, c = 0, d = 0, l = 0, u = 0, y = 0, f = 0, w = 0, m = 0, S = 0, T = 0, k = null, b = null, M = null, I = null, g = null, p = null;
|
|
2078
|
+
t.forEach((v) => {
|
|
2079
|
+
n.set(v.id, v), v.type === "codec" && i.set(v.id, String(v.mimeType ?? ""));
|
|
2080
|
+
}), t.forEach((v) => {
|
|
2081
|
+
if (v.type === "inbound-rtp" && !v.isRemote && (s += Number(v.bytesReceived ?? 0), r += Math.max(0, Number(v.packetsLost ?? 0)), c += Math.max(0, Number(v.packetsReceived ?? 0)), Number.isFinite(v.jitter) && (d += Number(v.jitter), l += 1), u = Math.max(u, Number(v.framesPerSecond ?? 0)), y = Math.max(y, Number(v.frameWidth ?? 0)), f = Math.max(f, Number(v.frameHeight ?? 0)), w += Number(v.framesDropped ?? 0), m += Number(v.freezeCount ?? 0), S += Number(v.totalFreezesDuration ?? 0), T += Number(v.concealedSamples ?? 0), g ||= i.get(String(v.codecId ?? "")) ?? null), v.type === "outbound-rtp" && !v.isRemote && (o += Number(v.bytesSent ?? 0), p ||= v.qualityLimitationReason ? String(v.qualityLimitationReason) : null, g ||= i.get(String(v.codecId ?? "")) ?? null), v.type === "candidate-pair" && (v.selected || v.nominated) && v.state === "succeeded") {
|
|
2082
|
+
k = Number.isFinite(v.currentRoundTripTime) ? Number(v.currentRoundTripTime) : k, b = Number.isFinite(v.availableOutgoingBitrate) ? Number(v.availableOutgoingBitrate) : b;
|
|
2083
|
+
const N = n.get(v.localCandidateId);
|
|
2084
|
+
M = N?.candidateType ? String(N.candidateType) : M, I = N?.protocol ? String(N.protocol) : I;
|
|
1734
2085
|
}
|
|
1735
2086
|
});
|
|
1736
|
-
const
|
|
2087
|
+
const h = Date.now(), R = e ? Math.max(1e-3, (h - e.timestamp) / 1e3) : 0, L = e ? Math.max(0, Math.round((s - e.inboundBytes) * 8 / R)) : null, q = e ? Math.max(0, Math.round((o - e.outboundBytes) * 8 / R)) : null, E = r + c, P = e ? Math.max(0, r - e.packetsLost) : r, C = e ? Math.max(0, c - e.packetsReceived) : c, D = P + C;
|
|
1737
2088
|
return {
|
|
1738
|
-
counters: { timestamp:
|
|
2089
|
+
counters: { timestamp: h, inboundBytes: s, outboundBytes: o, packetsLost: r, packetsReceived: c },
|
|
1739
2090
|
sample: {
|
|
1740
|
-
rttMs:
|
|
1741
|
-
jitterMs:
|
|
1742
|
-
packetLossPct:
|
|
1743
|
-
packetLossCumulativePct:
|
|
2091
|
+
rttMs: k === null ? null : k * 1e3,
|
|
2092
|
+
jitterMs: l ? d / l * 1e3 : null,
|
|
2093
|
+
packetLossPct: D ? P / D * 100 : null,
|
|
2094
|
+
packetLossCumulativePct: E ? r / E * 100 : null,
|
|
1744
2095
|
packetsLost: r,
|
|
1745
|
-
packetsReceived:
|
|
1746
|
-
inboundBitrateBps:
|
|
1747
|
-
outboundBitrateBps:
|
|
2096
|
+
packetsReceived: c,
|
|
2097
|
+
inboundBitrateBps: L,
|
|
2098
|
+
outboundBitrateBps: q,
|
|
1748
2099
|
availableOutgoingBitrateBps: b,
|
|
1749
2100
|
framesPerSecond: u || null,
|
|
1750
|
-
frameWidth:
|
|
1751
|
-
frameHeight:
|
|
1752
|
-
framesDropped:
|
|
1753
|
-
freezeCount:
|
|
1754
|
-
freezeDurationMs: Math.round(
|
|
1755
|
-
concealedSamples:
|
|
1756
|
-
codec:
|
|
1757
|
-
transportProtocol:
|
|
1758
|
-
candidateType:
|
|
1759
|
-
qualityLimitationReason:
|
|
2101
|
+
frameWidth: y || null,
|
|
2102
|
+
frameHeight: f || null,
|
|
2103
|
+
framesDropped: w,
|
|
2104
|
+
freezeCount: m,
|
|
2105
|
+
freezeDurationMs: Math.round(S * 1e3),
|
|
2106
|
+
concealedSamples: T,
|
|
2107
|
+
codec: g,
|
|
2108
|
+
transportProtocol: I,
|
|
2109
|
+
candidateType: M,
|
|
2110
|
+
qualityLimitationReason: p
|
|
1760
2111
|
}
|
|
1761
2112
|
};
|
|
1762
2113
|
}
|
|
1763
|
-
function
|
|
1764
|
-
let e =
|
|
1765
|
-
const t =
|
|
2114
|
+
function Ae(a) {
|
|
2115
|
+
let e = a.mediaScore == null ? 5 : Math.round(se(Number(a.mediaScore), 0, 5));
|
|
2116
|
+
const t = J(a.packetLossPct) ?? 0, i = J(a.rttMs) ?? 0, n = J(a.jitterMs) ?? 0, s = String(a.connectionState ?? "unknown").toLowerCase();
|
|
1766
2117
|
["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));
|
|
1767
|
-
const
|
|
1768
|
-
return { networkScore: e, estimatedMos:
|
|
2118
|
+
const o = Number(se(4.5 - t * 0.09 - i * 11e-4 - n * 6e-3, 1, 4.5).toFixed(2));
|
|
2119
|
+
return { networkScore: e, estimatedMos: o };
|
|
1769
2120
|
}
|
|
1770
|
-
function
|
|
2121
|
+
function ae() {
|
|
1771
2122
|
return navigator.connection ?? null;
|
|
1772
2123
|
}
|
|
1773
|
-
function
|
|
1774
|
-
const
|
|
1775
|
-
return Number.isFinite(
|
|
2124
|
+
function Le() {
|
|
2125
|
+
const a = Number(navigator.deviceMemory);
|
|
2126
|
+
return Number.isFinite(a) ? a : null;
|
|
1776
2127
|
}
|
|
1777
|
-
function
|
|
1778
|
-
const e = Number(
|
|
2128
|
+
function J(a) {
|
|
2129
|
+
const e = Number(a);
|
|
1779
2130
|
return Number.isFinite(e) ? e : null;
|
|
1780
2131
|
}
|
|
1781
|
-
function
|
|
1782
|
-
return Math.min(t, Math.max(e,
|
|
2132
|
+
function se(a, e, t) {
|
|
2133
|
+
return Math.min(t, Math.max(e, a));
|
|
1783
2134
|
}
|
|
1784
|
-
function
|
|
1785
|
-
return
|
|
2135
|
+
function V(a) {
|
|
2136
|
+
return a instanceof Error ? { name: a.name, message: a.message } : { name: "ServiceError", message: String(a ?? "Service request failed") };
|
|
1786
2137
|
}
|
|
1787
|
-
function
|
|
1788
|
-
return
|
|
2138
|
+
function re(a, e) {
|
|
2139
|
+
return a instanceof Error ? a : new Error(e);
|
|
1789
2140
|
}
|
|
1790
|
-
const
|
|
2141
|
+
const De = "1.3.16";
|
|
1791
2142
|
export {
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
2143
|
+
Z as AVATAR_TUNING_DEFAULTS,
|
|
2144
|
+
Se as BACKGROUND_PRESETS,
|
|
2145
|
+
ke as OVERLAY_EFFECTS,
|
|
2146
|
+
Ce as RtcClient,
|
|
2147
|
+
Ee as RtcIncomingClient,
|
|
2148
|
+
qe as SPEAKER_NOISE_FLOOR,
|
|
2149
|
+
Oe as SPEAKER_TAKEOVER_RATIO,
|
|
2150
|
+
Ue as createAvatarPipeline,
|
|
2151
|
+
je as createCallRecorder,
|
|
2152
|
+
Ne as createPictureInPictureController,
|
|
2153
|
+
Qe as createRtcClient,
|
|
2154
|
+
xe as createRtcIncomingClient,
|
|
2155
|
+
Ve as createVideoEffectsPipeline,
|
|
2156
|
+
be as drawAvatar,
|
|
2157
|
+
we as headPoseFromMatrix,
|
|
2158
|
+
le as neutralRig,
|
|
2159
|
+
oe as pickActiveSpeaker,
|
|
2160
|
+
Te as rigFromBlendshapes,
|
|
2161
|
+
K as scenePainters,
|
|
2162
|
+
X as smoothRig,
|
|
2163
|
+
ee as stylizedAvatarRenderer,
|
|
2164
|
+
Re as unlockRtcCallAudio,
|
|
2165
|
+
De as version
|
|
1802
2166
|
};
|