@streaming-cdn/rtc-web 1.3.14 → 1.3.15
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 +36 -1
- package/dist/call-recorder.d.ts +48 -0
- package/dist/index.d.ts +5 -1
- package/dist/rtc-web.es.js +472 -278
- 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 Ce = 0.02, Ie = 1.4;
|
|
2
|
+
function ie(r, e, t = 0.02, i = 1.4) {
|
|
3
|
+
const n = r.filter((o) => o.level >= t);
|
|
4
4
|
if (n.length === 0) return null;
|
|
5
|
-
const s = n.reduce((
|
|
5
|
+
const s = n.reduce((o, c) => c.level > o.level ? c : o), a = n.find((o) => o.participantId === e);
|
|
6
6
|
return a ? s.participantId === e ? e : s.level >= a.level * i ? s.participantId : e : s.participantId;
|
|
7
7
|
}
|
|
8
|
-
function
|
|
8
|
+
function ne(r) {
|
|
9
9
|
const e = [];
|
|
10
|
-
for (const t of
|
|
10
|
+
for (const t of r) {
|
|
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 M = {
|
|
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
|
+
}, ae = {
|
|
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 re(r) {
|
|
35
|
+
const e = String(r ?? "").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 oe(r, e, t, i, n = {}) {
|
|
39
|
+
const s = { ...ae, ...n.thresholds }, a = ["low", "medium", "high"], o = n.ceiling && n.ceiling !== "audio" ? n.ceiling : "high", c = a.indexOf(o), d = a.indexOf(r), l = (n.samplesSeen ?? s.warmupSamples) + 1, h = l <= s.warmupSamples, v = re(e.qualityLimitationReason), g = e.packetLossPct ?? 0, k = e.rttMs ?? 0, p = e.availableOutgoingBitrateBps, C = M[r].maxBitrate, S = g >= s.criticalLossPct || k >= s.criticalRttMs || v === "cpu", y = S || g >= s.poorLossPct || k >= s.poorRttMs || v === "bandwidth", T = p == null || p >= C * s.sustainRatio, I = g < s.goodLossPct && k < s.goodRttMs && !v && T, R = y ? t + 1 : 0, E = I ? i + 1 : 0, f = { quality: r, poorSamples: R, goodSamples: E, samplesSeen: l, changed: !1 };
|
|
40
|
+
return d > c ? { quality: a[c], poorSamples: 0, goodSamples: 0, samplesSeen: l, changed: !0 } : h ? f : S && d > 0 ? { quality: a[d - 1], poorSamples: 0, goodSamples: 0, samplesSeen: l, changed: !0 } : R >= s.poorSamplesToStepDown && d > 0 ? { quality: a[d - 1], poorSamples: 0, goodSamples: 0, samplesSeen: l, changed: !0 } : E >= s.goodSamplesToStepUp && d < c ? { quality: a[d + 1], poorSamples: 0, goodSamples: 0, samplesSeen: l, changed: !0 } : f;
|
|
41
41
|
}
|
|
42
|
-
function
|
|
43
|
-
const t =
|
|
44
|
-
let i = 0, n = 0, s = null, a = null,
|
|
45
|
-
for (const
|
|
46
|
-
const
|
|
47
|
-
if (
|
|
48
|
-
n += Number(
|
|
49
|
-
const
|
|
50
|
-
Number.isFinite(
|
|
42
|
+
function ce(r, e) {
|
|
43
|
+
const t = r instanceof Map ? r : new Map(r);
|
|
44
|
+
let i = 0, n = 0, s = null, a = null, o = null;
|
|
45
|
+
for (const l of t.values()) {
|
|
46
|
+
const h = String(l.type ?? "");
|
|
47
|
+
if (h === "outbound-rtp" && String(l.kind ?? "") === "video" && (i += Number(l.packetsSent ?? 0), s = l.qualityLimitationReason ?? s), h === "remote-inbound-rtp" && String(l.kind ?? "") === "video") {
|
|
48
|
+
n += Number(l.packetsLost ?? 0);
|
|
49
|
+
const v = Number(l.roundTripTime ?? NaN);
|
|
50
|
+
Number.isFinite(v) && (a = v * 1e3);
|
|
51
51
|
}
|
|
52
|
-
if (
|
|
53
|
-
const
|
|
54
|
-
a == null && Number.isFinite(
|
|
55
|
-
const
|
|
56
|
-
Number.isFinite(
|
|
52
|
+
if (h === "candidate-pair" && (l.nominated === !0 || String(l.state ?? "") === "succeeded")) {
|
|
53
|
+
const v = Number(l.currentRoundTripTime ?? NaN);
|
|
54
|
+
a == null && Number.isFinite(v) && (a = v * 1e3);
|
|
55
|
+
const g = Number(l.availableOutgoingBitrate ?? NaN);
|
|
56
|
+
Number.isFinite(g) && (o = g);
|
|
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, h = n - e.packetsLost;
|
|
63
|
+
l > 0 && h >= 0 && (d = h / (l + h) * 100);
|
|
64
64
|
}
|
|
65
65
|
return {
|
|
66
|
-
sample: { packetLossPct:
|
|
67
|
-
counters:
|
|
66
|
+
sample: { packetLossPct: d, rttMs: a, availableOutgoingBitrateBps: o, qualityLimitationReason: s },
|
|
67
|
+
counters: c
|
|
68
68
|
};
|
|
69
69
|
}
|
|
70
|
-
const
|
|
70
|
+
const le = {
|
|
71
71
|
audio: { active: !1, maxBitrate: 0, maxFramerate: 0, scaleResolutionDownBy: 4 }
|
|
72
|
-
},
|
|
73
|
-
class
|
|
72
|
+
}, de = { active: !0, maxBitrate: 4e6, maxFramerate: 30, scaleResolutionDownBy: 1 };
|
|
73
|
+
class Q {
|
|
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", a = this.localStream?.getTracks().find((
|
|
156
|
+
const s = e.kind === "audioinput" ? "audio" : "video", a = this.localStream?.getTracks().find((o) => o.kind === s);
|
|
157
157
|
if (this.localStream || (this.localStream = new MediaStream()), a && (this.localStream.removeTrack(a), a.stop()), this.localStream.addTrack(n), s === "video") {
|
|
158
|
-
this.screenStream?.getVideoTracks().some((
|
|
158
|
+
this.screenStream?.getVideoTracks().some((o) => o.readyState === "live") || await this.replaceVideoTrack(n);
|
|
159
159
|
return;
|
|
160
160
|
}
|
|
161
|
-
for (const
|
|
162
|
-
const
|
|
163
|
-
|
|
161
|
+
for (const o of this.peers.values()) {
|
|
162
|
+
const c = o.connection.getSenders().find((d) => d.track?.kind === s);
|
|
163
|
+
c ? await c.replaceTrack(n) : o.connection.addTrack(n, this.localStream);
|
|
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 = Q.clampToCeiling(t.quality, e), t.poorSamples = 0, t.goodSamples = 0;
|
|
172
172
|
await this.applyVideoQuality();
|
|
173
173
|
}
|
|
174
174
|
static clampToCeiling(e, t) {
|
|
@@ -214,31 +214,31 @@ 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 =
|
|
217
|
+
const n = new Promise((c, d) => {
|
|
218
|
+
this.resolveJoin = c, this.rejectJoin = d;
|
|
219
219
|
}), s = new WebSocket(i, ["rtc-client", t.signalingToken]), a = ++this.socketGeneration;
|
|
220
220
|
this.socket = s;
|
|
221
|
-
const
|
|
221
|
+
const o = 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 !== a || await this.handleSignal(
|
|
228
|
-
}).catch((
|
|
229
|
-
const
|
|
230
|
-
this.options.onEvent("signalingerror", { message:
|
|
227
|
+
this.socket !== s || this.socketGeneration !== a || await this.handleSignal(d);
|
|
228
|
+
}).catch((l) => {
|
|
229
|
+
const h = l instanceof Error ? l.message : "RTC signaling processing failed";
|
|
230
|
+
this.options.onEvent("signalingerror", { message: h }), this.joined || this.rejectJoin?.(new Error(h));
|
|
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 });
|
|
240
240
|
} finally {
|
|
241
|
-
clearTimeout(
|
|
241
|
+
clearTimeout(o), this.resolveJoin = null, this.rejectJoin = null;
|
|
242
242
|
}
|
|
243
243
|
}
|
|
244
244
|
async handleSignal(e) {
|
|
@@ -273,8 +273,8 @@ class A {
|
|
|
273
273
|
const s = n.connection.signalingState !== "stable", a = !this.shouldInitiateOffer(t.sender);
|
|
274
274
|
if (s && !a) 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 o = await n.connection.createAnswer();
|
|
277
|
+
await n.connection.setLocalDescription(o), this.send({
|
|
278
278
|
type: "rtc.answer",
|
|
279
279
|
targetParticipantId: t.sender.participantId,
|
|
280
280
|
negotiationId: t.negotiationId,
|
|
@@ -313,19 +313,19 @@ class A {
|
|
|
313
313
|
makingOffer: !1,
|
|
314
314
|
offerTask: null,
|
|
315
315
|
activeNegotiationId: null,
|
|
316
|
-
quality:
|
|
316
|
+
quality: Q.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 a = this.screenStream?.getVideoTracks().some((
|
|
325
|
-
return
|
|
326
|
-
|
|
327
|
-
}), i.addEventListener("track", (
|
|
328
|
-
n.getTracks().some((
|
|
323
|
+
for (const c of this.localStream?.getAudioTracks() ?? []) i.addTrack(c, this.localStream);
|
|
324
|
+
const a = this.screenStream?.getVideoTracks().some((c) => c.readyState === "live") ? this.screenStream : this.localStream, o = a?.getVideoTracks()[0];
|
|
325
|
+
return o && a && (s.videoSender = i.addTrack(o, a)), !this.localStream?.getAudioTracks().length && this.options.receiveAudio && i.addTransceiver("audio", { direction: "recvonly" }), !o && this.options.receiveVideo && (s.videoTransceiver = i.addTransceiver("video", { direction: "recvonly" }), s.videoSender = s.videoTransceiver.sender), this.applyCodecPreference(i), this.applyVideoQuality(i), i.addEventListener("icecandidate", (c) => {
|
|
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 = H();
|
|
355
355
|
e.activeNegotiationId = i, this.send({
|
|
356
356
|
type: "rtc.offer",
|
|
357
357
|
targetParticipantId: e.identity.participantId,
|
|
@@ -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
|
-
a.encodings =
|
|
394
|
+
const n = !!(t && i.track?.id === t), s = n ? de : this.quality === "audio" ? le.audio : M[e.quality], a = i.getParameters(), o = a.encodings?.length ? a.encodings : [{}];
|
|
395
|
+
a.encodings = o.map((c) => ({ ...c, ...s })), a.degradationPreference = n ? "maintain-resolution" : "maintain-framerate", await i.setParameters(a);
|
|
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 ne(n.values()))
|
|
415
415
|
e.push({ participantId: i.identity.participantId, level: s.level });
|
|
416
416
|
} catch {
|
|
417
417
|
}
|
|
418
|
-
const t =
|
|
418
|
+
const t = ie(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 } = ce(t, e.counters);
|
|
432
432
|
e.counters = n;
|
|
433
|
-
const s =
|
|
433
|
+
const s = oe(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 ?? H(),
|
|
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 H() {
|
|
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 U = { active: !0, maxBitrate: 4e6, maxFramerate: 30, scaleResolutionDownBy: 1 }, _ = [
|
|
502
|
+
{ rid: "a", maxBitrate: M.high.maxBitrate, maxFramerate: M.high.maxFramerate },
|
|
503
|
+
{ rid: "b", maxBitrate: M.medium.maxBitrate, maxFramerate: M.medium.maxFramerate, scaleResolutionDownBy: 2 },
|
|
504
|
+
{ rid: "c", maxBitrate: M.low.maxBitrate, maxFramerate: M.low.maxFramerate, scaleResolutionDownBy: 4 }
|
|
505
|
+
], W = { high: "a", medium: "b", low: "c" };
|
|
506
|
+
class he {
|
|
507
507
|
options;
|
|
508
508
|
apiBase;
|
|
509
509
|
socket = null;
|
|
@@ -594,13 +594,13 @@ 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", a = this.localStream?.getTracks().find((
|
|
597
|
+
const s = e.kind === "audioinput" ? "audio" : "video", a = this.localStream?.getTracks().find((c) => c.kind === s);
|
|
598
598
|
if (this.localStream || (this.localStream = new MediaStream()), a && (this.localStream.removeTrack(a), a.stop()), this.localStream.addTrack(n), s === "video") {
|
|
599
|
-
this.screenStream?.getVideoTracks().some((
|
|
599
|
+
this.screenStream?.getVideoTracks().some((c) => c.readyState === "live") || await this.replaceVideoTrack(n);
|
|
600
600
|
return;
|
|
601
601
|
}
|
|
602
|
-
const
|
|
603
|
-
|
|
602
|
+
const o = this.connection?.getSenders().find((c) => c.track?.kind === "audio");
|
|
603
|
+
o && await o.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 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 =
|
|
641
|
+
const n = new Promise((c, d) => {
|
|
642
|
+
this.resolveJoin = c, this.rejectJoin = d;
|
|
643
643
|
}), s = new WebSocket(i, ["rtc-client", t.signalingToken]), a = ++this.socketGeneration;
|
|
644
644
|
this.socket = s;
|
|
645
|
-
const
|
|
645
|
+
const o = 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 !== a || await this.handleSignal(
|
|
652
|
-
}).catch((
|
|
653
|
-
const
|
|
654
|
-
this.options.onEvent("signalingerror", { message:
|
|
651
|
+
this.socket !== s || this.socketGeneration !== a || await this.handleSignal(d);
|
|
652
|
+
}).catch((l) => {
|
|
653
|
+
const h = l instanceof Error ? l.message : "RTC signaling processing failed";
|
|
654
|
+
this.options.onEvent("signalingerror", { message: h }), this.joined || this.rejectJoin?.(new Error(h));
|
|
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 });
|
|
664
664
|
} finally {
|
|
665
|
-
clearTimeout(
|
|
665
|
+
clearTimeout(o), this.resolveJoin = null, this.rejectJoin = null;
|
|
666
666
|
}
|
|
667
667
|
}
|
|
668
668
|
async handleSignal(e) {
|
|
@@ -710,15 +710,15 @@ class se {
|
|
|
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", (o) => this.handleRemoteTrack(o)), 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((o) => t.addTransceiver(o, {
|
|
719
719
|
direction: "sendonly",
|
|
720
720
|
streams: this.localStream ? [this.localStream] : [],
|
|
721
|
-
...
|
|
721
|
+
...o.kind === "video" ? { sendEncodings: _.map((c) => ({ ...c })) } : {}
|
|
722
722
|
})), s = await t.createOffer();
|
|
723
723
|
await t.setLocalDescription(s);
|
|
724
724
|
const a = await this.sfuFetch(
|
|
@@ -726,14 +726,14 @@ class se {
|
|
|
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((o) => ({
|
|
730
730
|
location: "local",
|
|
731
|
-
mid:
|
|
732
|
-
trackName: this.trackName(
|
|
731
|
+
mid: o.mid,
|
|
732
|
+
trackName: this.trackName(o.sender.track)
|
|
733
733
|
}))
|
|
734
734
|
}
|
|
735
735
|
);
|
|
736
|
-
a.sessionDescription && await t.setRemoteDescription(a.sessionDescription), this.videoSender = t.getSenders().find((
|
|
736
|
+
a.sessionDescription && await t.setRemoteDescription(a.sessionDescription), this.videoSender = t.getSenders().find((o) => o.track?.kind === "video") ?? null, await this.applyVideoQuality();
|
|
737
737
|
}
|
|
738
738
|
trackName(e) {
|
|
739
739
|
return `${this.options.credential.participantId}/${e.kind}`;
|
|
@@ -747,7 +747,7 @@ class se {
|
|
|
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((o) => ({ trackName: String(o.trackName ?? ""), simulcast: o.simulcast === !0 })).filter((o) => !!o.trackName), s = n.map((o) => o.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 ?? {} });
|
|
@@ -762,13 +762,13 @@ class se {
|
|
|
762
762
|
mids: /* @__PURE__ */ new Map()
|
|
763
763
|
};
|
|
764
764
|
a.sessionId = i;
|
|
765
|
-
for (const
|
|
766
|
-
|
|
765
|
+
for (const o of n)
|
|
766
|
+
o.simulcast && a.simulcastNames.add(o.trackName);
|
|
767
767
|
this.remotes.set(e.participantId, a), this.negotiationQueue = this.negotiationQueue.then(async () => {
|
|
768
|
-
const
|
|
769
|
-
|
|
770
|
-
}).catch((
|
|
771
|
-
this.options.onEvent("signalingerror", { message:
|
|
768
|
+
const o = s.filter((c) => !a.trackNames.includes(c));
|
|
769
|
+
o.length !== 0 && await this.subscribe(a, o);
|
|
770
|
+
}).catch((o) => {
|
|
771
|
+
this.options.onEvent("signalingerror", { message: o instanceof Error ? o.message : "SFU subscribe failed" });
|
|
772
772
|
}), await this.negotiationQueue;
|
|
773
773
|
}
|
|
774
774
|
async subscribe(e, t) {
|
|
@@ -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: W[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 a = this.quality === "audio" ? null :
|
|
834
|
-
n.encodings = s.map((
|
|
833
|
+
const a = this.quality === "audio" ? null : W[this.quality];
|
|
834
|
+
n.encodings = s.map((o) => {
|
|
835
835
|
if (i)
|
|
836
|
-
return { ...
|
|
837
|
-
const
|
|
838
|
-
return { ...
|
|
836
|
+
return { ...o, active: o.rid === "a", ...o.rid === "a" ? { maxBitrate: U.maxBitrate, maxFramerate: U.maxFramerate } : {} };
|
|
837
|
+
const c = _.find((l) => l.rid === o.rid), d = a != null && String(o.rid ?? "") >= a;
|
|
838
|
+
return { ...o, ...c ?? {}, active: d };
|
|
839
839
|
});
|
|
840
840
|
} else {
|
|
841
|
-
const a = this.quality === "audio" ?
|
|
842
|
-
n.encodings = s.map((
|
|
841
|
+
const a = this.quality === "audio" ? M.audio : M[this.quality], o = i ? U : a;
|
|
842
|
+
n.encodings = s.map((c) => ({ ...c, ...o }));
|
|
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
|
|
908
|
+
for (const s of ne(t.values())) {
|
|
909
909
|
const a = s.mid ? this.midToParticipant.get(s.mid) : void 0;
|
|
910
910
|
a && i.push({ participantId: a, level: s.level });
|
|
911
911
|
}
|
|
912
|
-
const n =
|
|
912
|
+
const n = ie(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 ?? ue(),
|
|
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 ue() {
|
|
938
938
|
return globalThis.crypto?.randomUUID?.() ?? `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
939
939
|
}
|
|
940
|
-
function
|
|
940
|
+
function Ee(r, e = {}) {
|
|
941
941
|
const t = e.document ?? globalThis.document, i = !!(t && t.pictureInPictureEnabled);
|
|
942
942
|
let n = null;
|
|
943
943
|
function s() {
|
|
944
944
|
return n || (n = t.createElement("video"), n.muted = !0, n.playsInline = !0, n.style.position = "fixed", n.style.width = "1px", n.style.height = "1px", n.style.opacity = "0", n.style.pointerEvents = "none", n.addEventListener("leavepictureinpicture", () => e.onLeave?.()), t.body.appendChild(n), n);
|
|
945
945
|
}
|
|
946
|
-
function a(
|
|
947
|
-
const
|
|
948
|
-
return
|
|
946
|
+
function a(o) {
|
|
947
|
+
const c = r.getRemoteStreams();
|
|
948
|
+
return o ? c.find((d) => d.participant.participantId === o)?.stream ?? null : c[0]?.stream ?? r.getLocalStream();
|
|
949
949
|
}
|
|
950
950
|
return {
|
|
951
951
|
supported: i,
|
|
952
|
-
async enter(
|
|
952
|
+
async enter(o) {
|
|
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 = a(o);
|
|
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,196 @@ function ve(c, e = {}) {
|
|
|
965
965
|
}
|
|
966
966
|
};
|
|
967
967
|
}
|
|
968
|
-
|
|
968
|
+
function Re(r, e) {
|
|
969
|
+
const t = e.document ?? globalThis.document, i = e.fetchImplementation ?? globalThis.fetch.bind(globalThis), n = new URL(e.credential.signalingUrl, globalThis.location?.href).origin, s = e.mimeType ?? "video/webm", a = e.height ?? 720, o = Math.round(a * 16 / 9);
|
|
970
|
+
let c = null, d = null, l = null, h = null, v = 0, g = [], k = !1;
|
|
971
|
+
async function p(C, S, y, T = "application/json") {
|
|
972
|
+
const I = await i(`${n}${S}`, {
|
|
973
|
+
method: C,
|
|
974
|
+
headers: {
|
|
975
|
+
authorization: `Bearer ${e.credential.signalingToken}`,
|
|
976
|
+
"content-type": T
|
|
977
|
+
},
|
|
978
|
+
body: y === void 0 ? void 0 : T === "application/json" ? JSON.stringify(y) : y
|
|
979
|
+
});
|
|
980
|
+
if (!I.ok) throw new Error(`Recording request failed with HTTP ${I.status}`);
|
|
981
|
+
return I.json();
|
|
982
|
+
}
|
|
983
|
+
return {
|
|
984
|
+
get recording() {
|
|
985
|
+
return k;
|
|
986
|
+
},
|
|
987
|
+
async start() {
|
|
988
|
+
if (k) return;
|
|
989
|
+
h = (await p("POST", "/v1/rtc/recordings")).recordingId;
|
|
990
|
+
const S = t.createElement("canvas");
|
|
991
|
+
S.width = o, S.height = a;
|
|
992
|
+
const y = S.getContext("2d"), T = /* @__PURE__ */ new Map(), I = (b, P) => {
|
|
993
|
+
let w = T.get(b);
|
|
994
|
+
return w || (w = t.createElement("video"), w.muted = !0, w.playsInline = !0, w.srcObject = P, w.play().catch(() => {
|
|
995
|
+
}), T.set(b, w)), w;
|
|
996
|
+
};
|
|
997
|
+
d = setInterval(() => {
|
|
998
|
+
const b = r.getRemoteStreams(), P = r.getLocalStream(), w = [
|
|
999
|
+
...b.map((D) => ({ key: D.participant.participantId, stream: D.stream, label: D.participant.displayName })),
|
|
1000
|
+
...P ? [{ key: "local", stream: P, label: "me" }] : []
|
|
1001
|
+
];
|
|
1002
|
+
y.fillStyle = "#101418", y.fillRect(0, 0, o, a);
|
|
1003
|
+
const N = Math.max(1, Math.ceil(Math.sqrt(w.length))), m = Math.max(1, Math.ceil(w.length / N)), A = o / N, V = a / m;
|
|
1004
|
+
w.forEach((D, z) => {
|
|
1005
|
+
const J = I(D.key, D.stream), j = z % N * A, B = Math.floor(z / N) * V;
|
|
1006
|
+
J.videoWidth > 0 && y.drawImage(J, j + 2, B + 2, A - 4, V - 4), y.fillStyle = "rgba(0,0,0,.6)", y.fillRect(j + 8, B + V - 30, Math.min(A - 16, 12 + D.label.length * 9), 22), y.fillStyle = "#fff", y.font = "14px system-ui", y.fillText(D.label, j + 14, B + V - 14);
|
|
1007
|
+
});
|
|
1008
|
+
}, 66);
|
|
1009
|
+
const R = S.captureStream(15);
|
|
1010
|
+
l = new AudioContext();
|
|
1011
|
+
const E = l.createMediaStreamDestination(), f = /* @__PURE__ */ new Set(), u = () => {
|
|
1012
|
+
const b = [
|
|
1013
|
+
...r.getRemoteStreams().map((P) => P.stream),
|
|
1014
|
+
...r.getLocalStream() ? [r.getLocalStream()] : []
|
|
1015
|
+
];
|
|
1016
|
+
for (const P of b)
|
|
1017
|
+
for (const w of P.getAudioTracks())
|
|
1018
|
+
f.has(w.id) || (f.add(w.id), l.createMediaStreamSource(new MediaStream([w])).connect(E));
|
|
1019
|
+
};
|
|
1020
|
+
u();
|
|
1021
|
+
const q = setInterval(u, 1e3), L = () => clearInterval(q);
|
|
1022
|
+
for (const b of E.stream.getAudioTracks()) R.addTrack(b);
|
|
1023
|
+
c = (e.mediaRecorderFactory ?? ((b, P) => new MediaRecorder(b, P)))(R, { mimeType: s }), g = [], c.addEventListener("dataavailable", (b) => {
|
|
1024
|
+
b.data && b.data.size > 0 && g.push(b.data);
|
|
1025
|
+
}), c.addEventListener("stop", L), c.start(1e3), v = Date.now(), k = !0;
|
|
1026
|
+
},
|
|
1027
|
+
async stop() {
|
|
1028
|
+
if (!k || !c || !h) throw new Error("The recorder is not running");
|
|
1029
|
+
k = !1;
|
|
1030
|
+
const C = c, S = new Promise((E) => C.addEventListener("stop", () => E(), { once: !0 }));
|
|
1031
|
+
C.stop(), await S, d && clearInterval(d), d = null, await l?.close().catch(() => {
|
|
1032
|
+
}), l = null;
|
|
1033
|
+
const y = new Blob(g, { type: s });
|
|
1034
|
+
g = [];
|
|
1035
|
+
const T = Math.max(1, Math.round((Date.now() - v) / 1e3)), I = await p(
|
|
1036
|
+
"PUT",
|
|
1037
|
+
`/v1/rtc/recordings/${encodeURIComponent(h)}/media`,
|
|
1038
|
+
y,
|
|
1039
|
+
s
|
|
1040
|
+
);
|
|
1041
|
+
await p("POST", `/v1/rtc/recordings/${encodeURIComponent(h)}/complete`, { durationSeconds: T });
|
|
1042
|
+
const R = { recordingId: h, sizeBytes: I.sizeBytes, durationSeconds: T };
|
|
1043
|
+
return h = null, c = null, R;
|
|
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, k = !1;
|
|
1053
|
+
}
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1056
|
+
const me = ["none", "blur", "aurora", "cybergrid", "bokeh", "sunset", "image"], pe = ["none", "snow", "confetti", "vignette"], G = {
|
|
1057
|
+
aurora(r, e, t, i) {
|
|
1058
|
+
const n = r.createLinearGradient(0, 0, 0, t);
|
|
1059
|
+
n.addColorStop(0, "#050b1e"), n.addColorStop(1, "#0b2540"), r.fillStyle = n, r.fillRect(0, 0, e, t);
|
|
1060
|
+
for (let s = 0; s < 3; s += 1) {
|
|
1061
|
+
r.beginPath();
|
|
1062
|
+
for (let a = 0; a <= e; a += 16) {
|
|
1063
|
+
const o = t * 0.35 + s * 40 + Math.sin(a / 140 + i / 900 + s) * 60;
|
|
1064
|
+
a === 0 ? r.moveTo(a, o) : r.lineTo(a, o);
|
|
1065
|
+
}
|
|
1066
|
+
r.lineTo(e, 0), r.lineTo(0, 0), r.closePath(), r.fillStyle = `hsla(${140 + s * 40 + Math.sin(i / 1300) * 20}, 80%, 55%, 0.14)`, r.fill();
|
|
1067
|
+
}
|
|
1068
|
+
},
|
|
1069
|
+
cybergrid(r, e, t, i) {
|
|
1070
|
+
r.fillStyle = "#0a0118", r.fillRect(0, 0, e, t), r.strokeStyle = "rgba(255,0,180,0.5)", r.lineWidth = 1.5;
|
|
1071
|
+
const n = t * 0.45;
|
|
1072
|
+
for (let a = 0; a < 12; a += 1) {
|
|
1073
|
+
const o = n + (a * 34 + i / 18) % (t - n);
|
|
1074
|
+
r.beginPath(), r.moveTo(0, o), r.lineTo(e, o), r.stroke();
|
|
1075
|
+
}
|
|
1076
|
+
for (let a = -8; a <= 8; a += 1)
|
|
1077
|
+
r.beginPath(), r.moveTo(e / 2 + a * 24, n), r.lineTo(e / 2 + a * e * 0.12, t), r.stroke();
|
|
1078
|
+
const s = r.createLinearGradient(0, t * 0.1, 0, n);
|
|
1079
|
+
s.addColorStop(0, "#ffd34d"), s.addColorStop(1, "#ff2d78"), r.fillStyle = s, r.beginPath(), r.arc(e / 2, n, t * 0.22, Math.PI, 0), r.fill();
|
|
1080
|
+
},
|
|
1081
|
+
bokeh(r, e, t, i) {
|
|
1082
|
+
r.fillStyle = "#14100c", r.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, a = (Math.sin(n * 78.233) * 0.5 + 0.5) * t + Math.sin(i / 1500 + n) * 12, o = 14 + n % 5 * 12;
|
|
1085
|
+
r.fillStyle = `hsla(${35 + n % 4 * 12}, 90%, 60%, ${0.1 + n % 3 * 0.05})`, r.beginPath(), r.arc(s, a, o, 0, Math.PI * 2), r.fill();
|
|
1086
|
+
}
|
|
1087
|
+
},
|
|
1088
|
+
sunset(r, e, t, i) {
|
|
1089
|
+
const n = r.createLinearGradient(0, 0, 0, t);
|
|
1090
|
+
n.addColorStop(0, "#2b1055"), n.addColorStop(0.55, "#d1548a"), n.addColorStop(1, "#ffb36b"), r.fillStyle = n, r.fillRect(0, 0, e, t), r.fillStyle = "#ffdf8e", r.beginPath(), r.arc(e * 0.5, t * 0.62 + Math.sin(i / 4e3) * 4, t * 0.13, 0, Math.PI * 2), r.fill(), r.fillStyle = "rgba(20,8,40,0.85)", r.fillRect(0, t * 0.78, e, t * 0.22);
|
|
1091
|
+
}
|
|
1092
|
+
};
|
|
1093
|
+
function Pe(r, e = {}) {
|
|
1094
|
+
const t = e.document ?? globalThis.document, i = e.width ?? 1280, n = e.height ?? 720, s = e.frameRate ?? 24, a = r instanceof MediaStreamTrack ? r : r.getVideoTracks()[0];
|
|
1095
|
+
if (!a) throw new Error("A video track is required");
|
|
1096
|
+
let o = e.background ?? "none", c = e.overlay ?? "none", d = e.backgroundImage ?? null, l = e.segmenter ?? null, h = null, v = !1;
|
|
1097
|
+
const g = t.createElement("video");
|
|
1098
|
+
g.muted = !0, g.playsInline = !0, g.srcObject = new MediaStream([a]), g.play().catch(() => {
|
|
1099
|
+
});
|
|
1100
|
+
const k = t.createElement("canvas");
|
|
1101
|
+
k.width = i, k.height = n;
|
|
1102
|
+
const p = k.getContext("2d"), C = t.createElement("canvas");
|
|
1103
|
+
C.width = i, C.height = n;
|
|
1104
|
+
const S = C.getContext("2d"), y = Array.from({ length: 80 }, (f, u) => ({
|
|
1105
|
+
x: (Math.sin(u * 12.9898) * 0.5 + 0.5) * i,
|
|
1106
|
+
y: (Math.sin(u * 78.233) * 0.5 + 0.5) * n,
|
|
1107
|
+
speed: 0.6 + u % 5 * 0.5,
|
|
1108
|
+
drift: Math.sin(u) * 0.6,
|
|
1109
|
+
size: 2 + u % 4,
|
|
1110
|
+
hue: u * 47 % 360
|
|
1111
|
+
}));
|
|
1112
|
+
function T() {
|
|
1113
|
+
if (h) {
|
|
1114
|
+
S.clearRect(0, 0, i, n), S.drawImage(h, 0, 0, i, n), S.globalCompositeOperation = "source-in", S.drawImage(g, 0, 0, i, n), S.globalCompositeOperation = "source-over", p.drawImage(C, 0, 0);
|
|
1115
|
+
return;
|
|
1116
|
+
}
|
|
1117
|
+
const f = i * 0.42, u = f * 9 / 16, q = i - f - 24, L = n - u - 24;
|
|
1118
|
+
p.save(), p.shadowColor = "rgba(0,0,0,.5)", p.shadowBlur = 24, p.beginPath(), p.roundRect(q, L, f, u, 14), p.clip(), p.drawImage(g, q, L, f, u), p.restore();
|
|
1119
|
+
}
|
|
1120
|
+
function I(f) {
|
|
1121
|
+
if (o === "none" ? p.drawImage(g, 0, 0, i, n) : o === "blur" ? (p.filter = "blur(14px)", p.drawImage(g, -16, -16, i + 32, n + 32), p.filter = "none", h && T()) : o === "image" && d ? (p.drawImage(d, 0, 0, i, n), T()) : ((G[o] ?? G.aurora)(p, i, n, f), T()), c === "snow" || c === "confetti")
|
|
1122
|
+
for (const u of y)
|
|
1123
|
+
u.y += u.speed * (c === "confetti" ? 1.6 : 1), u.x += u.drift, u.y > n && (u.y = -6, u.x = Math.random() * i), p.fillStyle = c === "snow" ? "rgba(255,255,255,.85)" : `hsl(${u.hue}, 90%, 60%)`, c === "confetti" ? p.fillRect(u.x, u.y, u.size + 2, u.size) : (p.beginPath(), p.arc(u.x, u.y, u.size / 2, 0, Math.PI * 2), p.fill());
|
|
1124
|
+
else if (c === "vignette") {
|
|
1125
|
+
const u = p.createRadialGradient(i / 2, n / 2, n * 0.4, i / 2, n / 2, n * 0.85);
|
|
1126
|
+
u.addColorStop(0, "rgba(0,0,0,0)"), u.addColorStop(1, "rgba(0,0,0,0.55)"), p.fillStyle = u, p.fillRect(0, 0, i, n);
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
const R = setInterval(() => {
|
|
1130
|
+
I(Date.now()), l && !v && o !== "none" && (v = !0, l.segment(g).then((f) => {
|
|
1131
|
+
h = f;
|
|
1132
|
+
}).catch(() => {
|
|
1133
|
+
h = null;
|
|
1134
|
+
}).finally(() => {
|
|
1135
|
+
v = !1;
|
|
1136
|
+
}));
|
|
1137
|
+
}, Math.round(1e3 / s)), E = k.captureStream(s);
|
|
1138
|
+
return {
|
|
1139
|
+
track: E.getVideoTracks()[0],
|
|
1140
|
+
stream: E,
|
|
1141
|
+
setBackground(f, u) {
|
|
1142
|
+
o = me.includes(f) ? f : "none", u !== void 0 && (d = u), o === "none" && (h = null);
|
|
1143
|
+
},
|
|
1144
|
+
setOverlay(f) {
|
|
1145
|
+
c = pe.includes(f) ? f : "none";
|
|
1146
|
+
},
|
|
1147
|
+
setSegmenter(f) {
|
|
1148
|
+
l = f, f || (h = null);
|
|
1149
|
+
},
|
|
1150
|
+
dispose() {
|
|
1151
|
+
clearInterval(R), l?.close?.();
|
|
1152
|
+
for (const f of E.getTracks()) f.stop();
|
|
1153
|
+
g.srcObject = null;
|
|
1154
|
+
}
|
|
1155
|
+
};
|
|
1156
|
+
}
|
|
1157
|
+
class fe extends EventTarget {
|
|
969
1158
|
mode;
|
|
970
1159
|
options;
|
|
971
1160
|
client = null;
|
|
@@ -999,7 +1188,7 @@ class re extends EventTarget {
|
|
|
999
1188
|
async initialize() {
|
|
1000
1189
|
if (this.client) return this;
|
|
1001
1190
|
try {
|
|
1002
|
-
const t = (this.options.transport === "auto" ? this.mode === "meeting" ? "sfu" : "mesh" : this.options.transport ?? "mesh") === "sfu" ?
|
|
1191
|
+
const t = (this.options.transport === "auto" ? this.mode === "meeting" ? "sfu" : "mesh" : this.options.transport ?? "mesh") === "sfu" ? he : Q;
|
|
1003
1192
|
return this.client = new t({
|
|
1004
1193
|
credential: {
|
|
1005
1194
|
signalingToken: this.options.credential.signalingToken,
|
|
@@ -1018,19 +1207,19 @@ class re extends EventTarget {
|
|
|
1018
1207
|
iceTransportPolicy: this.options.iceTransportPolicy ?? "all",
|
|
1019
1208
|
localStream: this.options.localStream,
|
|
1020
1209
|
onEvent: (i, n) => this.handleNativeEvent(i, n)
|
|
1021
|
-
}), await this.client.initialize(), this.startStats(), this.emit("initialized", { mode: this.mode, sdkVersion:
|
|
1210
|
+
}), await this.client.initialize(), this.startStats(), this.emit("initialized", { mode: this.mode, sdkVersion: be }), this.options.autoJoin && await this.join(), this;
|
|
1022
1211
|
} catch (e) {
|
|
1023
1212
|
this.state = "failed";
|
|
1024
|
-
const t =
|
|
1025
|
-
throw this.emit("error",
|
|
1213
|
+
const t = te(e, "RTC initialization failed");
|
|
1214
|
+
throw this.emit("error", x(t)), t;
|
|
1026
1215
|
}
|
|
1027
1216
|
}
|
|
1028
1217
|
async join() {
|
|
1029
1218
|
const e = this.requireClient();
|
|
1030
1219
|
return this.joinPromise ? this.joinPromise : (this.state = "joining", this.emit("joining", {}), this.joinPromise = e.join().catch((t) => {
|
|
1031
1220
|
this.state = "failed";
|
|
1032
|
-
const i =
|
|
1033
|
-
throw this.emit("error",
|
|
1221
|
+
const i = te(t, "RTC connection failed");
|
|
1222
|
+
throw this.emit("error", x(i)), i;
|
|
1034
1223
|
}).finally(() => {
|
|
1035
1224
|
this.joinPromise = null;
|
|
1036
1225
|
}), this.joinPromise);
|
|
@@ -1068,14 +1257,14 @@ class re extends EventTarget {
|
|
|
1068
1257
|
return navigator.mediaDevices?.enumerateDevices ? navigator.mediaDevices.enumerateDevices() : [];
|
|
1069
1258
|
}
|
|
1070
1259
|
getParticipants() {
|
|
1071
|
-
return this.requireClient().getParticipants().map(
|
|
1260
|
+
return this.requireClient().getParticipants().map(X);
|
|
1072
1261
|
}
|
|
1073
1262
|
getLocalStream() {
|
|
1074
1263
|
return this.client?.getLocalStream() ?? null;
|
|
1075
1264
|
}
|
|
1076
1265
|
getRemoteStreams() {
|
|
1077
1266
|
return (this.client?.getRemoteStreams() ?? []).map((e) => ({
|
|
1078
|
-
participant:
|
|
1267
|
+
participant: X(e.participant),
|
|
1079
1268
|
stream: e.stream
|
|
1080
1269
|
}));
|
|
1081
1270
|
}
|
|
@@ -1144,45 +1333,45 @@ class re extends EventTarget {
|
|
|
1144
1333
|
let n = "unavailable";
|
|
1145
1334
|
if (e.requestMedia !== !1 && navigator.mediaDevices?.getUserMedia)
|
|
1146
1335
|
try {
|
|
1147
|
-
(await navigator.mediaDevices.getUserMedia({ audio: this.options.audio, video: this.options.video })).getTracks().forEach((
|
|
1336
|
+
(await navigator.mediaDevices.getUserMedia({ audio: this.options.audio, video: this.options.video })).getTracks().forEach((v) => v.stop()), n = "granted";
|
|
1148
1337
|
} catch {
|
|
1149
1338
|
n = "denied", t.push("media_permission_denied");
|
|
1150
1339
|
}
|
|
1151
|
-
const s = navigator.mediaDevices?.enumerateDevices ? await navigator.mediaDevices.enumerateDevices().catch(() => []) : [], a = s.filter((
|
|
1152
|
-
this.options.audio && a === 0 && t.push("microphone_unavailable"), this.options.video &&
|
|
1153
|
-
const
|
|
1154
|
-
let
|
|
1155
|
-
if (
|
|
1156
|
-
const
|
|
1340
|
+
const s = navigator.mediaDevices?.enumerateDevices ? await navigator.mediaDevices.enumerateDevices().catch(() => []) : [], a = s.filter((h) => h.kind === "audioinput").length, o = s.filter((h) => h.kind === "videoinput").length;
|
|
1341
|
+
this.options.audio && a === 0 && t.push("microphone_unavailable"), this.options.video && o === 0 && t.push("camera_unavailable");
|
|
1342
|
+
const c = e.probeUrl ?? this.options.preflightProbeUrl;
|
|
1343
|
+
let d = null;
|
|
1344
|
+
if (c) {
|
|
1345
|
+
const h = performance.now();
|
|
1157
1346
|
try {
|
|
1158
|
-
const
|
|
1159
|
-
|
|
1347
|
+
const v = await fetch(c, { method: "HEAD", cache: "no-store", credentials: "omit" });
|
|
1348
|
+
d = Math.round(performance.now() - h), v.ok || t.push("network_probe_failed");
|
|
1160
1349
|
} catch {
|
|
1161
1350
|
t.push("network_probe_failed");
|
|
1162
1351
|
}
|
|
1163
1352
|
}
|
|
1164
|
-
const
|
|
1353
|
+
const l = {
|
|
1165
1354
|
passed: t.length === 0,
|
|
1166
1355
|
sampledAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1167
1356
|
secureContext: i,
|
|
1168
1357
|
mediaPermission: n,
|
|
1169
1358
|
microphoneCount: a,
|
|
1170
|
-
cameraCount:
|
|
1171
|
-
probeLatencyMs:
|
|
1172
|
-
effectiveConnectionType:
|
|
1359
|
+
cameraCount: o,
|
|
1360
|
+
probeLatencyMs: d,
|
|
1361
|
+
effectiveConnectionType: Z()?.effectiveType ?? null,
|
|
1173
1362
|
issues: t
|
|
1174
1363
|
};
|
|
1175
|
-
return this.emit("preflight",
|
|
1364
|
+
return this.emit("preflight", l), this.options.autoReportStats && await this.reportQuality({
|
|
1176
1365
|
sampleType: "preflight",
|
|
1177
|
-
sampledAt:
|
|
1178
|
-
connectionState:
|
|
1179
|
-
rttMs:
|
|
1180
|
-
effectiveConnectionType:
|
|
1366
|
+
sampledAt: l.sampledAt,
|
|
1367
|
+
connectionState: l.passed ? "ready" : "failed",
|
|
1368
|
+
rttMs: l.probeLatencyMs,
|
|
1369
|
+
effectiveConnectionType: l.effectiveConnectionType
|
|
1181
1370
|
}).catch(() => {
|
|
1182
|
-
}),
|
|
1371
|
+
}), l;
|
|
1183
1372
|
}
|
|
1184
1373
|
async collectQualitySample(e = "runtime") {
|
|
1185
|
-
const t = this.options.statsProvider ? await this.options.statsProvider() : {}, i = this.peerConnection ? await
|
|
1374
|
+
const t = this.options.statsProvider ? await this.options.statsProvider() : {}, i = this.peerConnection ? await ke(this.peerConnection, this.previousCounters) : null;
|
|
1186
1375
|
i?.counters && (this.previousCounters = i.counters);
|
|
1187
1376
|
const s = { ...{
|
|
1188
1377
|
sampleType: e,
|
|
@@ -1211,9 +1400,9 @@ class re extends EventTarget {
|
|
|
1211
1400
|
transportProtocol: null,
|
|
1212
1401
|
candidateType: null,
|
|
1213
1402
|
qualityLimitationReason: null,
|
|
1214
|
-
deviceMemoryGb:
|
|
1215
|
-
effectiveConnectionType:
|
|
1216
|
-
}, ...i?.sample ?? {}, ...t, sampleType: e, sampledAt: (/* @__PURE__ */ new Date()).toISOString() }, a =
|
|
1403
|
+
deviceMemoryGb: we(),
|
|
1404
|
+
effectiveConnectionType: Z()?.effectiveType ?? null
|
|
1405
|
+
}, ...i?.sample ?? {}, ...t, sampleType: e, sampledAt: (/* @__PURE__ */ new Date()).toISOString() }, a = Te(s);
|
|
1217
1406
|
return s.networkScore = a.networkScore, s.estimatedMos = a.estimatedMos, this.latestQuality = s, s;
|
|
1218
1407
|
}
|
|
1219
1408
|
async reportQuality(e) {
|
|
@@ -1264,7 +1453,7 @@ class re extends EventTarget {
|
|
|
1264
1453
|
const i = Date.now(), n = e !== "runtime" || i - this.lastTelemetryAt >= this.options.telemetryInterval;
|
|
1265
1454
|
this.options.autoReportStats && n && (await this.reportQuality(t), this.lastTelemetryAt = i);
|
|
1266
1455
|
} catch (t) {
|
|
1267
|
-
this.emit("telemetryerror",
|
|
1456
|
+
this.emit("telemetryerror", x(t));
|
|
1268
1457
|
} finally {
|
|
1269
1458
|
this.statsRunning = !1;
|
|
1270
1459
|
}
|
|
@@ -1281,8 +1470,8 @@ class re extends EventTarget {
|
|
|
1281
1470
|
}
|
|
1282
1471
|
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
1472
|
this.poorQualitySamples = n ? this.poorQualitySamples + 1 : 0, this.goodQualitySamples = s ? this.goodQualitySamples + 1 : 0;
|
|
1284
|
-
const a = ["low", "medium", "high"],
|
|
1285
|
-
i &&
|
|
1473
|
+
const a = ["low", "medium", "high"], o = a.indexOf(this.effectiveQuality);
|
|
1474
|
+
i && o > 0 ? (this.poorQualitySamples = 0, await this.applyEffectiveQuality("low", "network")) : this.poorQualitySamples >= 2 && o > 0 ? (this.poorQualitySamples = 0, await this.applyEffectiveQuality(a[o - 1], "network")) : this.goodQualitySamples >= 6 && o >= 0 && o < a.length - 1 && (this.goodQualitySamples = 0, await this.applyEffectiveQuality(a[o + 1], "network"));
|
|
1286
1475
|
}
|
|
1287
1476
|
async applyEffectiveQuality(e, t) {
|
|
1288
1477
|
if (this.effectiveQuality === e && this.client) return;
|
|
@@ -1298,21 +1487,21 @@ class re extends EventTarget {
|
|
|
1298
1487
|
this.dispatchEvent(new CustomEvent(e, { detail: i })), this.options.onEvent?.(i);
|
|
1299
1488
|
}
|
|
1300
1489
|
}
|
|
1301
|
-
async function
|
|
1302
|
-
return new
|
|
1490
|
+
async function Me(r) {
|
|
1491
|
+
return new fe(r).initialize();
|
|
1303
1492
|
}
|
|
1304
|
-
let
|
|
1305
|
-
function
|
|
1306
|
-
if (
|
|
1307
|
-
const
|
|
1308
|
-
return e ? (
|
|
1493
|
+
let O = null;
|
|
1494
|
+
function se() {
|
|
1495
|
+
if (O) return O;
|
|
1496
|
+
const r = globalThis, e = r.AudioContext ?? r.webkitAudioContext;
|
|
1497
|
+
return e ? (O = new e(), O) : null;
|
|
1309
1498
|
}
|
|
1310
|
-
async function
|
|
1311
|
-
const
|
|
1312
|
-
return
|
|
1313
|
-
}),
|
|
1499
|
+
async function ge() {
|
|
1500
|
+
const r = se();
|
|
1501
|
+
return r ? (r.state === "suspended" && await r.resume().catch(() => {
|
|
1502
|
+
}), r.state === "running") : !1;
|
|
1314
1503
|
}
|
|
1315
|
-
class
|
|
1504
|
+
class ve {
|
|
1316
1505
|
options;
|
|
1317
1506
|
media = null;
|
|
1318
1507
|
cadenceTimer = null;
|
|
@@ -1321,7 +1510,7 @@ class ce {
|
|
|
1321
1510
|
this.options = e;
|
|
1322
1511
|
}
|
|
1323
1512
|
unlock() {
|
|
1324
|
-
return
|
|
1513
|
+
return ge();
|
|
1325
1514
|
}
|
|
1326
1515
|
startIncoming() {
|
|
1327
1516
|
this.start("incoming", this.options.ringtoneUrl);
|
|
@@ -1349,17 +1538,17 @@ class ce {
|
|
|
1349
1538
|
i(), this.cadenceTimer = setInterval(i, e === "incoming" ? 3e3 : 3500);
|
|
1350
1539
|
}
|
|
1351
1540
|
playBuiltInTone(e) {
|
|
1352
|
-
const t =
|
|
1541
|
+
const t = se();
|
|
1353
1542
|
if (!t || t.state !== "running") return;
|
|
1354
1543
|
const i = e === "incoming" ? 1.25 : 1, n = e === "incoming" ? [440, 480] : [425], s = t.createGain(), a = t.currentTime;
|
|
1355
1544
|
s.gain.setValueAtTime(1e-4, a), s.gain.exponentialRampToValueAtTime(0.045, a + 0.025), s.gain.setValueAtTime(0.045, a + i - 0.06), s.gain.exponentialRampToValueAtTime(1e-4, a + i), s.connect(t.destination);
|
|
1356
|
-
for (const
|
|
1357
|
-
const
|
|
1358
|
-
|
|
1545
|
+
for (const o of n) {
|
|
1546
|
+
const c = t.createOscillator();
|
|
1547
|
+
c.type = "sine", c.frequency.value = o, c.connect(s), this.oscillators.add(c), c.onended = () => this.oscillators.delete(c), c.start(a), c.stop(a + i + 0.02);
|
|
1359
1548
|
}
|
|
1360
1549
|
}
|
|
1361
1550
|
}
|
|
1362
|
-
class
|
|
1551
|
+
class ye extends EventTarget {
|
|
1363
1552
|
options;
|
|
1364
1553
|
socket = null;
|
|
1365
1554
|
stopped = !1;
|
|
@@ -1386,14 +1575,14 @@ class le extends EventTarget {
|
|
|
1386
1575
|
if (e.token && !e.token.startsWith("rtcc_")) throw new Error("A valid RTC client token is required");
|
|
1387
1576
|
if (!e.signalingUrl && !e.apiBaseUrl && !globalThis.location?.origin)
|
|
1388
1577
|
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
|
|
1578
|
+
this.options = e, this.currentToken = e.token ?? null, this.currentTokenExpiresAt = e.tokenExpiresAt, this.callAudio = new ve(e), this.mount = K(e.mount), this.scheduleTokenRefresh();
|
|
1390
1579
|
}
|
|
1391
1580
|
connect() {
|
|
1392
1581
|
if (!this.options.signalingUrl) throw new Error("signalingUrl is required to connect signaling");
|
|
1393
1582
|
return this.stopped = !1, this.scheduleTokenRefresh(), this.openSocket("initial"), this;
|
|
1394
1583
|
}
|
|
1395
1584
|
setMount(e) {
|
|
1396
|
-
this.clearUi(), this.mount =
|
|
1585
|
+
this.clearUi(), this.mount = K(e), this.currentCall && this.renderIncoming(this.currentCall);
|
|
1397
1586
|
}
|
|
1398
1587
|
/** Binds media lifetime to a call. Terminal call states automatically leave and destroy it. */
|
|
1399
1588
|
attachMediaClient(e, t) {
|
|
@@ -1479,7 +1668,7 @@ class le extends EventTarget {
|
|
|
1479
1668
|
/** Applies a call state received through the application's own push or signaling channel. */
|
|
1480
1669
|
handleCallUpdate(e) {
|
|
1481
1670
|
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,
|
|
1671
|
+
t && e.status === "accepted" ? (this.activeCallIds.add(e.id), this.callAudio.stop(), this.dismiss(e.id)) : t && ["rejected", "busy", "cancelled", "ended", "failed", "missed"].includes(e.status) && this.showTerminal(e.id, Se(e.status)), ["rejected", "busy", "cancelled", "ended", "failed", "missed"].includes(e.status) && this.releaseMedia(e.id), this.emit("callupdated", e);
|
|
1483
1672
|
}
|
|
1484
1673
|
disconnect() {
|
|
1485
1674
|
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 +1684,7 @@ class le extends EventTarget {
|
|
|
1495
1684
|
try {
|
|
1496
1685
|
t = await this.resolveToken(e, e === "reconnect");
|
|
1497
1686
|
} catch (s) {
|
|
1498
|
-
this.emit("tokenerror",
|
|
1687
|
+
this.emit("tokenerror", x(s)), !this.stopped && this.options.reconnect !== !1 && this.scheduleReconnect();
|
|
1499
1688
|
return;
|
|
1500
1689
|
}
|
|
1501
1690
|
if (this.stopped || this.socket) return;
|
|
@@ -1518,14 +1707,14 @@ class le extends EventTarget {
|
|
|
1518
1707
|
}), n.addEventListener("close", (s) => {
|
|
1519
1708
|
if (this.socket !== n) return;
|
|
1520
1709
|
this.socket = null, this.heartbeatTimer && clearInterval(this.heartbeatTimer), this.heartbeatTimer = null;
|
|
1521
|
-
const a = !this.stopped && this.options.reconnect !== !1,
|
|
1710
|
+
const a = !this.stopped && this.options.reconnect !== !1, o = {
|
|
1522
1711
|
code: s.code,
|
|
1523
1712
|
reason: s.reason,
|
|
1524
1713
|
wasClean: s.wasClean,
|
|
1525
1714
|
willReconnect: a,
|
|
1526
1715
|
attempt: this.reconnectAttempt + 1
|
|
1527
1716
|
};
|
|
1528
|
-
a && (this.lastDisconnect = { code: s.code, reason: s.reason, wasClean: s.wasClean, at: Date.now() }), this.emit("signalingdisconnected",
|
|
1717
|
+
a && (this.lastDisconnect = { code: s.code, reason: s.reason, wasClean: s.wasClean, at: Date.now() }), this.emit("signalingdisconnected", o), this.reportSignalingDiagnostic({ event: a ? "reconnecting" : "disconnected", ...o }), a && this.scheduleReconnect();
|
|
1529
1718
|
}), n.addEventListener("error", () => {
|
|
1530
1719
|
const s = { message: "RTC signaling connection failed", reconnecting: !this.stopped && this.options.reconnect !== !1, attempt: this.reconnectAttempt + 1 };
|
|
1531
1720
|
this.emit("signalingerror", s), this.reportSignalingDiagnostic({ event: "error", attempt: s.attempt });
|
|
@@ -1559,16 +1748,16 @@ class le extends EventTarget {
|
|
|
1559
1748
|
async request(e, t, i = !1) {
|
|
1560
1749
|
const n = this.options.apiBaseUrl ?? this.options.signalingUrl ?? globalThis.location?.origin;
|
|
1561
1750
|
if (!n) throw new Error("apiBaseUrl is required for call actions");
|
|
1562
|
-
const s = new URL(n, globalThis.location?.href), a = await this.resolveToken("initial"),
|
|
1563
|
-
|
|
1564
|
-
const
|
|
1565
|
-
if (
|
|
1751
|
+
const s = new URL(n, globalThis.location?.href), a = await this.resolveToken("initial"), o = new Headers(t.headers);
|
|
1752
|
+
o.set("authorization", `Bearer ${a}`), o.set("content-type", "application/json");
|
|
1753
|
+
const c = await fetch(new URL(e, s.origin), { ...t, headers: o }), d = await c.json().catch(() => ({}));
|
|
1754
|
+
if (c.status === 401 && this.options.tokenProvider && !i)
|
|
1566
1755
|
return await this.resolveToken("unauthorized", !0), this.request(e, t, !0);
|
|
1567
|
-
if (!
|
|
1568
|
-
const
|
|
1569
|
-
throw new Error(typeof
|
|
1756
|
+
if (!c.ok) {
|
|
1757
|
+
const l = d.error;
|
|
1758
|
+
throw new Error(typeof l == "string" ? l : l?.message || `RTC API failed with HTTP ${c.status}`);
|
|
1570
1759
|
}
|
|
1571
|
-
return
|
|
1760
|
+
return d;
|
|
1572
1761
|
}
|
|
1573
1762
|
renderIncoming(e) {
|
|
1574
1763
|
if (!this.mount) return;
|
|
@@ -1591,12 +1780,12 @@ class le extends EventTarget {
|
|
|
1591
1780
|
n.textContent = e.caller.name, n.style.fontSize = "20px";
|
|
1592
1781
|
const s = document.createElement("div");
|
|
1593
1782
|
Object.assign(s.style, { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "10px" });
|
|
1594
|
-
const a =
|
|
1783
|
+
const a = Y("Decline", "#fff", "#a32920", "#d9a09b"), o = Y("Answer", "#fff", "#08745f", "#08745f");
|
|
1595
1784
|
a.addEventListener("click", () => {
|
|
1596
|
-
this.reject(e.id).catch((
|
|
1597
|
-
}),
|
|
1598
|
-
this.accept(e.id).catch((
|
|
1599
|
-
}), s.appendChild(a), s.appendChild(
|
|
1785
|
+
this.reject(e.id).catch((c) => this.emit("error", x(c)));
|
|
1786
|
+
}), o.addEventListener("click", () => {
|
|
1787
|
+
this.accept(e.id).catch((c) => this.emit("error", x(c)));
|
|
1788
|
+
}), s.appendChild(a), s.appendChild(o), t.appendChild(i), t.appendChild(n), t.appendChild(s), this.mount.replaceChildren(t);
|
|
1600
1789
|
}
|
|
1601
1790
|
dismiss(e) {
|
|
1602
1791
|
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 +1852,7 @@ class le extends EventTarget {
|
|
|
1663
1852
|
const t = Math.max(0, e - this.refreshSkew(e));
|
|
1664
1853
|
this.tokenRefreshTimer = setTimeout(() => {
|
|
1665
1854
|
this.tokenRefreshTimer = null, this.resolveToken("expiring", !0).catch((i) => {
|
|
1666
|
-
this.emit("tokenerror",
|
|
1855
|
+
this.emit("tokenerror", x(i)), this.stopped || (this.tokenRefreshTimer = setTimeout(() => this.scheduleTokenRefresh(), 3e4));
|
|
1667
1856
|
});
|
|
1668
1857
|
}, Math.min(t, 2147e6));
|
|
1669
1858
|
}
|
|
@@ -1677,23 +1866,23 @@ class le extends EventTarget {
|
|
|
1677
1866
|
this.options.reportSignalingDiagnostics === !1 || this.stopped || this.request("/v1/rtc/client/signaling-events", {
|
|
1678
1867
|
method: "POST",
|
|
1679
1868
|
body: JSON.stringify({ ...e, occurredAt: (/* @__PURE__ */ new Date()).toISOString() })
|
|
1680
|
-
}).catch((t) => this.emit("diagnosticserror",
|
|
1869
|
+
}).catch((t) => this.emit("diagnosticserror", x(t)));
|
|
1681
1870
|
}
|
|
1682
1871
|
emit(e, t) {
|
|
1683
1872
|
const i = { type: e, detail: t, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
|
|
1684
1873
|
this.dispatchEvent(new CustomEvent(e, { detail: i })), this.options.onEvent?.(i);
|
|
1685
1874
|
}
|
|
1686
1875
|
}
|
|
1687
|
-
function
|
|
1688
|
-
const e = new
|
|
1689
|
-
return
|
|
1876
|
+
function Ae(r) {
|
|
1877
|
+
const e = new ye(r);
|
|
1878
|
+
return r.autoConnect !== !1 && r.signalingUrl && e.connect(), e;
|
|
1690
1879
|
}
|
|
1691
|
-
function
|
|
1692
|
-
return
|
|
1880
|
+
function K(r) {
|
|
1881
|
+
return r ? typeof r == "string" ? document.querySelector(r) : r : null;
|
|
1693
1882
|
}
|
|
1694
|
-
function
|
|
1883
|
+
function Y(r, e, t, i) {
|
|
1695
1884
|
const n = document.createElement("button");
|
|
1696
|
-
return n.type = "button", n.textContent =
|
|
1885
|
+
return n.type = "button", n.textContent = r, Object.assign(n.style, {
|
|
1697
1886
|
minHeight: "44px",
|
|
1698
1887
|
border: `1px solid ${i}`,
|
|
1699
1888
|
borderRadius: "6px",
|
|
@@ -1704,7 +1893,7 @@ function U(c, e, t, i) {
|
|
|
1704
1893
|
fontWeight: "700"
|
|
1705
1894
|
}), n;
|
|
1706
1895
|
}
|
|
1707
|
-
function
|
|
1896
|
+
function Se(r) {
|
|
1708
1897
|
return {
|
|
1709
1898
|
rejected: "Call declined",
|
|
1710
1899
|
busy: "Line busy",
|
|
@@ -1712,91 +1901,96 @@ function de(c) {
|
|
|
1712
1901
|
ended: "Call ended",
|
|
1713
1902
|
failed: "Call failed",
|
|
1714
1903
|
missed: "No answer"
|
|
1715
|
-
}[
|
|
1904
|
+
}[r] ?? "Call ended";
|
|
1716
1905
|
}
|
|
1717
|
-
function
|
|
1906
|
+
function X(r) {
|
|
1718
1907
|
return {
|
|
1719
|
-
id:
|
|
1720
|
-
name:
|
|
1908
|
+
id: r.participantId,
|
|
1909
|
+
name: r.displayName,
|
|
1721
1910
|
role: "participant"
|
|
1722
1911
|
};
|
|
1723
1912
|
}
|
|
1724
|
-
async function
|
|
1725
|
-
const t = await
|
|
1726
|
-
let s = 0, a = 0,
|
|
1727
|
-
t.forEach((
|
|
1728
|
-
n.set(
|
|
1729
|
-
}), t.forEach((
|
|
1730
|
-
if (
|
|
1731
|
-
y = Number.isFinite(
|
|
1732
|
-
const
|
|
1733
|
-
|
|
1913
|
+
async function ke(r, e) {
|
|
1914
|
+
const t = await r.getStats(), i = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Map();
|
|
1915
|
+
let s = 0, a = 0, o = 0, c = 0, d = 0, l = 0, h = 0, v = 0, g = 0, k = 0, p = 0, C = 0, S = 0, y = null, T = null, I = null, R = null, E = null, f = null;
|
|
1916
|
+
t.forEach((m) => {
|
|
1917
|
+
n.set(m.id, m), m.type === "codec" && i.set(m.id, String(m.mimeType ?? ""));
|
|
1918
|
+
}), t.forEach((m) => {
|
|
1919
|
+
if (m.type === "inbound-rtp" && !m.isRemote && (s += Number(m.bytesReceived ?? 0), o += Math.max(0, Number(m.packetsLost ?? 0)), c += Math.max(0, Number(m.packetsReceived ?? 0)), Number.isFinite(m.jitter) && (d += Number(m.jitter), l += 1), h = Math.max(h, Number(m.framesPerSecond ?? 0)), v = Math.max(v, Number(m.frameWidth ?? 0)), g = Math.max(g, Number(m.frameHeight ?? 0)), k += Number(m.framesDropped ?? 0), p += Number(m.freezeCount ?? 0), C += Number(m.totalFreezesDuration ?? 0), S += Number(m.concealedSamples ?? 0), E ||= i.get(String(m.codecId ?? "")) ?? null), m.type === "outbound-rtp" && !m.isRemote && (a += Number(m.bytesSent ?? 0), f ||= m.qualityLimitationReason ? String(m.qualityLimitationReason) : null, E ||= i.get(String(m.codecId ?? "")) ?? null), m.type === "candidate-pair" && (m.selected || m.nominated) && m.state === "succeeded") {
|
|
1920
|
+
y = Number.isFinite(m.currentRoundTripTime) ? Number(m.currentRoundTripTime) : y, T = Number.isFinite(m.availableOutgoingBitrate) ? Number(m.availableOutgoingBitrate) : T;
|
|
1921
|
+
const A = n.get(m.localCandidateId);
|
|
1922
|
+
I = A?.candidateType ? String(A.candidateType) : I, R = A?.protocol ? String(A.protocol) : R;
|
|
1734
1923
|
}
|
|
1735
1924
|
});
|
|
1736
|
-
const
|
|
1925
|
+
const u = Date.now(), q = e ? Math.max(1e-3, (u - e.timestamp) / 1e3) : 0, L = e ? Math.max(0, Math.round((s - e.inboundBytes) * 8 / q)) : null, $ = e ? Math.max(0, Math.round((a - e.outboundBytes) * 8 / q)) : null, b = o + c, P = e ? Math.max(0, o - e.packetsLost) : o, w = e ? Math.max(0, c - e.packetsReceived) : c, N = P + w;
|
|
1737
1926
|
return {
|
|
1738
|
-
counters: { timestamp:
|
|
1927
|
+
counters: { timestamp: u, inboundBytes: s, outboundBytes: a, packetsLost: o, packetsReceived: c },
|
|
1739
1928
|
sample: {
|
|
1740
1929
|
rttMs: y === null ? null : y * 1e3,
|
|
1741
|
-
jitterMs:
|
|
1742
|
-
packetLossPct:
|
|
1743
|
-
packetLossCumulativePct:
|
|
1744
|
-
packetsLost:
|
|
1745
|
-
packetsReceived:
|
|
1746
|
-
inboundBitrateBps:
|
|
1747
|
-
outboundBitrateBps:
|
|
1748
|
-
availableOutgoingBitrateBps:
|
|
1749
|
-
framesPerSecond:
|
|
1750
|
-
frameWidth:
|
|
1751
|
-
frameHeight:
|
|
1752
|
-
framesDropped:
|
|
1753
|
-
freezeCount:
|
|
1754
|
-
freezeDurationMs: Math.round(
|
|
1755
|
-
concealedSamples:
|
|
1756
|
-
codec:
|
|
1757
|
-
transportProtocol:
|
|
1758
|
-
candidateType:
|
|
1759
|
-
qualityLimitationReason:
|
|
1930
|
+
jitterMs: l ? d / l * 1e3 : null,
|
|
1931
|
+
packetLossPct: N ? P / N * 100 : null,
|
|
1932
|
+
packetLossCumulativePct: b ? o / b * 100 : null,
|
|
1933
|
+
packetsLost: o,
|
|
1934
|
+
packetsReceived: c,
|
|
1935
|
+
inboundBitrateBps: L,
|
|
1936
|
+
outboundBitrateBps: $,
|
|
1937
|
+
availableOutgoingBitrateBps: T,
|
|
1938
|
+
framesPerSecond: h || null,
|
|
1939
|
+
frameWidth: v || null,
|
|
1940
|
+
frameHeight: g || null,
|
|
1941
|
+
framesDropped: k,
|
|
1942
|
+
freezeCount: p,
|
|
1943
|
+
freezeDurationMs: Math.round(C * 1e3),
|
|
1944
|
+
concealedSamples: S,
|
|
1945
|
+
codec: E,
|
|
1946
|
+
transportProtocol: R,
|
|
1947
|
+
candidateType: I,
|
|
1948
|
+
qualityLimitationReason: f
|
|
1760
1949
|
}
|
|
1761
1950
|
};
|
|
1762
1951
|
}
|
|
1763
|
-
function
|
|
1764
|
-
let e =
|
|
1765
|
-
const t =
|
|
1952
|
+
function Te(r) {
|
|
1953
|
+
let e = r.mediaScore == null ? 5 : Math.round(ee(Number(r.mediaScore), 0, 5));
|
|
1954
|
+
const t = F(r.packetLossPct) ?? 0, i = F(r.rttMs) ?? 0, n = F(r.jitterMs) ?? 0, s = String(r.connectionState ?? "unknown").toLowerCase();
|
|
1766
1955
|
["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 a = Number(
|
|
1956
|
+
const a = Number(ee(4.5 - t * 0.09 - i * 11e-4 - n * 6e-3, 1, 4.5).toFixed(2));
|
|
1768
1957
|
return { networkScore: e, estimatedMos: a };
|
|
1769
1958
|
}
|
|
1770
|
-
function
|
|
1959
|
+
function Z() {
|
|
1771
1960
|
return navigator.connection ?? null;
|
|
1772
1961
|
}
|
|
1773
|
-
function
|
|
1774
|
-
const
|
|
1775
|
-
return Number.isFinite(
|
|
1962
|
+
function we() {
|
|
1963
|
+
const r = Number(navigator.deviceMemory);
|
|
1964
|
+
return Number.isFinite(r) ? r : null;
|
|
1776
1965
|
}
|
|
1777
|
-
function
|
|
1778
|
-
const e = Number(
|
|
1966
|
+
function F(r) {
|
|
1967
|
+
const e = Number(r);
|
|
1779
1968
|
return Number.isFinite(e) ? e : null;
|
|
1780
1969
|
}
|
|
1781
|
-
function
|
|
1782
|
-
return Math.min(t, Math.max(e,
|
|
1970
|
+
function ee(r, e, t) {
|
|
1971
|
+
return Math.min(t, Math.max(e, r));
|
|
1783
1972
|
}
|
|
1784
|
-
function
|
|
1785
|
-
return
|
|
1973
|
+
function x(r) {
|
|
1974
|
+
return r instanceof Error ? { name: r.name, message: r.message } : { name: "ServiceError", message: String(r ?? "Service request failed") };
|
|
1786
1975
|
}
|
|
1787
|
-
function
|
|
1788
|
-
return
|
|
1976
|
+
function te(r, e) {
|
|
1977
|
+
return r instanceof Error ? r : new Error(e);
|
|
1789
1978
|
}
|
|
1790
|
-
const
|
|
1979
|
+
const be = "1.3.15";
|
|
1791
1980
|
export {
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
fe as
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1981
|
+
me as BACKGROUND_PRESETS,
|
|
1982
|
+
pe as OVERLAY_EFFECTS,
|
|
1983
|
+
fe as RtcClient,
|
|
1984
|
+
ye as RtcIncomingClient,
|
|
1985
|
+
Ce as SPEAKER_NOISE_FLOOR,
|
|
1986
|
+
Ie as SPEAKER_TAKEOVER_RATIO,
|
|
1987
|
+
Re as createCallRecorder,
|
|
1988
|
+
Ee as createPictureInPictureController,
|
|
1989
|
+
Me as createRtcClient,
|
|
1990
|
+
Ae as createRtcIncomingClient,
|
|
1991
|
+
Pe as createVideoEffectsPipeline,
|
|
1992
|
+
ie as pickActiveSpeaker,
|
|
1993
|
+
G as scenePainters,
|
|
1994
|
+
ge as unlockRtcCallAudio,
|
|
1995
|
+
be as version
|
|
1802
1996
|
};
|