@streaming-cdn/rtc-web 1.3.16 → 1.3.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/rtc-web.es.js +295 -275
- package/dist/rtc-web.umd.js +1 -1
- package/package.json +1 -1
package/dist/rtc-web.es.js
CHANGED
|
@@ -14,7 +14,7 @@ function ce(a) {
|
|
|
14
14
|
}
|
|
15
15
|
return e;
|
|
16
16
|
}
|
|
17
|
-
const
|
|
17
|
+
const N = {
|
|
18
18
|
audio: { active: !1, maxBitrate: 0, maxFramerate: 0, scaleResolutionDownBy: 4 },
|
|
19
19
|
low: { active: !0, maxBitrate: 35e4, maxFramerate: 15, scaleResolutionDownBy: 4 },
|
|
20
20
|
medium: { active: !0, maxBitrate: 11e5, maxFramerate: 24, scaleResolutionDownBy: 2 },
|
|
@@ -36,41 +36,41 @@ function ue(a) {
|
|
|
36
36
|
return !e || e === "none" ? "" : e === "cpu" || e === "bandwidth" ? e : "other";
|
|
37
37
|
}
|
|
38
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),
|
|
40
|
-
return
|
|
39
|
+
const s = { ...he, ...n.thresholds }, o = ["low", "medium", "high"], r = n.ceiling && n.ceiling !== "audio" ? n.ceiling : "high", c = o.indexOf(r), l = o.indexOf(a), d = (n.samplesSeen ?? s.warmupSamples) + 1, u = d <= s.warmupSamples, S = ue(e.qualityLimitationReason), f = e.packetLossPct ?? 0, v = e.rttMs ?? 0, I = e.availableOutgoingBitrateBps, y = N[a].maxBitrate, k = f >= s.criticalLossPct || v >= s.criticalRttMs || S === "cpu", p = k || f >= s.poorLossPct || v >= s.poorRttMs || S === "bandwidth", C = I == null || I >= y * s.sustainRatio, P = f < s.goodLossPct && v < s.goodRttMs && !S && C, L = p ? t + 1 : 0, M = P ? i + 1 : 0, A = { quality: a, poorSamples: L, goodSamples: M, samplesSeen: d, changed: !1 };
|
|
40
|
+
return l > c ? { quality: o[c], poorSamples: 0, goodSamples: 0, samplesSeen: d, changed: !0 } : u ? A : k && l > 0 ? { quality: o[l - 1], poorSamples: 0, goodSamples: 0, samplesSeen: d, changed: !0 } : L >= s.poorSamplesToStepDown && l > 0 ? { quality: o[l - 1], poorSamples: 0, goodSamples: 0, samplesSeen: d, changed: !0 } : M >= s.goodSamplesToStepUp && l < c ? { quality: o[l + 1], poorSamples: 0, goodSamples: 0, samplesSeen: d, changed: !0 } : A;
|
|
41
41
|
}
|
|
42
42
|
function pe(a, e) {
|
|
43
43
|
const t = a instanceof Map ? a : new Map(a);
|
|
44
44
|
let i = 0, n = 0, s = null, o = null, r = null;
|
|
45
|
-
for (const
|
|
46
|
-
const u = String(
|
|
47
|
-
if (u === "outbound-rtp" && String(
|
|
48
|
-
n += Number(
|
|
49
|
-
const
|
|
50
|
-
Number.isFinite(
|
|
45
|
+
for (const d of t.values()) {
|
|
46
|
+
const u = String(d.type ?? "");
|
|
47
|
+
if (u === "outbound-rtp" && String(d.kind ?? "") === "video" && (i += Number(d.packetsSent ?? 0), s = d.qualityLimitationReason ?? s), u === "remote-inbound-rtp" && String(d.kind ?? "") === "video") {
|
|
48
|
+
n += Number(d.packetsLost ?? 0);
|
|
49
|
+
const S = Number(d.roundTripTime ?? NaN);
|
|
50
|
+
Number.isFinite(S) && (o = S * 1e3);
|
|
51
51
|
}
|
|
52
|
-
if (u === "candidate-pair" && (
|
|
53
|
-
const
|
|
54
|
-
o == null && Number.isFinite(
|
|
55
|
-
const f = Number(
|
|
52
|
+
if (u === "candidate-pair" && (d.nominated === !0 || String(d.state ?? "") === "succeeded")) {
|
|
53
|
+
const S = Number(d.currentRoundTripTime ?? NaN);
|
|
54
|
+
o == null && Number.isFinite(S) && (o = S * 1e3);
|
|
55
|
+
const f = Number(d.availableOutgoingBitrate ?? NaN);
|
|
56
56
|
Number.isFinite(f) && (r = f);
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
const c = { packetsSent: i, packetsLost: n, timestamp: Date.now() };
|
|
60
|
-
let
|
|
60
|
+
let l = null;
|
|
61
61
|
if (e) {
|
|
62
|
-
const
|
|
63
|
-
|
|
62
|
+
const d = i - e.packetsSent, u = n - e.packetsLost;
|
|
63
|
+
d > 0 && u >= 0 && (l = u / (d + u) * 100);
|
|
64
64
|
}
|
|
65
65
|
return {
|
|
66
|
-
sample: { packetLossPct:
|
|
66
|
+
sample: { packetLossPct: l, rttMs: o, availableOutgoingBitrateBps: r, qualityLimitationReason: s },
|
|
67
67
|
counters: c
|
|
68
68
|
};
|
|
69
69
|
}
|
|
70
70
|
const fe = {
|
|
71
71
|
audio: { active: !1, maxBitrate: 0, maxFramerate: 0, scaleResolutionDownBy: 4 }
|
|
72
72
|
}, ge = { active: !0, maxBitrate: 4e6, maxFramerate: 30, scaleResolutionDownBy: 1 };
|
|
73
|
-
class
|
|
73
|
+
class Q {
|
|
74
74
|
options;
|
|
75
75
|
socket = null;
|
|
76
76
|
localStream = null;
|
|
@@ -159,7 +159,7 @@ class x {
|
|
|
159
159
|
return;
|
|
160
160
|
}
|
|
161
161
|
for (const r of this.peers.values()) {
|
|
162
|
-
const c = r.connection.getSenders().find((
|
|
162
|
+
const c = r.connection.getSenders().find((l) => l.track?.kind === s);
|
|
163
163
|
c ? await c.replaceTrack(n) : r.connection.addTrack(n, this.localStream);
|
|
164
164
|
}
|
|
165
165
|
await this.applyVideoQuality();
|
|
@@ -168,7 +168,7 @@ class x {
|
|
|
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,26 +214,26 @@ class x {
|
|
|
214
214
|
async openSocket(e) {
|
|
215
215
|
const { credential: t } = this.options, i = new URL(t.signalingUrl, globalThis.location?.href);
|
|
216
216
|
i.protocol = i.protocol === "https:" ? "wss:" : "ws:";
|
|
217
|
-
const n = new Promise((c,
|
|
218
|
-
this.resolveJoin = c, this.rejectJoin =
|
|
217
|
+
const n = new Promise((c, l) => {
|
|
218
|
+
this.resolveJoin = c, this.rejectJoin = l;
|
|
219
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
224
|
}), s.addEventListener("message", (c) => {
|
|
225
|
-
const
|
|
225
|
+
const l = String(c.data);
|
|
226
226
|
this.signalQueue = this.signalQueue.then(async () => {
|
|
227
|
-
this.socket !== s || this.socketGeneration !== o || await this.handleSignal(
|
|
228
|
-
}).catch((
|
|
229
|
-
const u =
|
|
227
|
+
this.socket !== s || this.socketGeneration !== o || await this.handleSignal(l);
|
|
228
|
+
}).catch((d) => {
|
|
229
|
+
const u = d instanceof Error ? d.message : "RTC signaling processing failed";
|
|
230
230
|
this.options.onEvent("signalingerror", { message: u }), this.joined || this.rejectJoin?.(new Error(u));
|
|
231
231
|
});
|
|
232
232
|
}), s.addEventListener("error", () => this.rejectJoin?.(new Error("RTC signaling connection failed"))), s.addEventListener("close", (c) => {
|
|
233
233
|
if (this.socket !== s) return;
|
|
234
234
|
this.socket = null, this.stopHeartbeat(), this.stopQualitySampling(), !this.joined && !e && this.rejectJoin?.(new Error(c.reason || "RTC signaling connection closed"));
|
|
235
|
-
const
|
|
236
|
-
this.joined = !1,
|
|
235
|
+
const l = !this.stopped && (this.joined || e);
|
|
236
|
+
this.joined = !1, l ? (this.resetPeers(), this.options.onEvent("reconnecting", { reason: c.reason, code: c.code, attempt: this.reconnectAttempt + 1 }), this.scheduleReconnect()) : this.stopped || this.options.onEvent("disconnected", { reason: c.reason, code: c.code });
|
|
237
237
|
});
|
|
238
238
|
try {
|
|
239
239
|
await n, this.joined = !0, this.options.onEvent("connected", { participantId: t.participantId, resumed: e });
|
|
@@ -313,7 +313,7 @@ class x {
|
|
|
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,
|
|
@@ -325,7 +325,7 @@ class x {
|
|
|
325
325
|
return r && o && (s.videoSender = i.addTrack(r, o)), !this.localStream?.getAudioTracks().length && this.options.receiveAudio && i.addTransceiver("audio", { direction: "recvonly" }), !r && this.options.receiveVideo && (s.videoTransceiver = i.addTransceiver("video", { direction: "recvonly" }), s.videoSender = s.videoTransceiver.sender), this.applyCodecPreference(i), this.applyVideoQuality(i), i.addEventListener("icecandidate", (c) => {
|
|
326
326
|
c.candidate && this.send({ type: "rtc.ice", targetParticipantId: e.participantId, candidate: c.candidate.toJSON() });
|
|
327
327
|
}), i.addEventListener("track", (c) => {
|
|
328
|
-
n.getTracks().some((
|
|
328
|
+
n.getTracks().some((l) => l.id === c.track.id) || n.addTrack(c.track), this.options.onEvent("remotestream", { participant: e, stream: n, track: c.track });
|
|
329
329
|
}), i.addEventListener("connectionstatechange", () => {
|
|
330
330
|
["failed", "closed"].includes(i.connectionState) && this.removePeer(e.participantId), this.options.onEvent("peerconnectionstate", { participant: e, state: i.connectionState });
|
|
331
331
|
}), s;
|
|
@@ -351,7 +351,7 @@ class x {
|
|
|
351
351
|
});
|
|
352
352
|
if (e.connection.signalingState !== "stable") return;
|
|
353
353
|
await e.connection.setLocalDescription(t);
|
|
354
|
-
const i =
|
|
354
|
+
const i = _();
|
|
355
355
|
e.activeNegotiationId = i, this.send({
|
|
356
356
|
type: "rtc.offer",
|
|
357
357
|
targetParticipantId: e.identity.participantId,
|
|
@@ -391,7 +391,7 @@ class x {
|
|
|
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 ? ge : this.quality === "audio" ? fe.audio :
|
|
394
|
+
const n = !!(t && i.track?.id === t), s = n ? ge : this.quality === "audio" ? fe.audio : N[e.quality], o = i.getParameters(), r = o.encodings?.length ? o.encodings : [{}];
|
|
395
395
|
o.encodings = r.map((c) => ({ ...c, ...s })), o.degradationPreference = n ? "maintain-resolution" : "maintain-framerate", await i.setParameters(o);
|
|
396
396
|
}
|
|
397
397
|
}
|
|
@@ -487,7 +487,7 @@ class x {
|
|
|
487
487
|
}
|
|
488
488
|
send(e) {
|
|
489
489
|
this.socket?.readyState === WebSocket.OPEN && this.socket.send(JSON.stringify({
|
|
490
|
-
clientMessageId: e.clientMessageId ??
|
|
490
|
+
clientMessageId: e.clientMessageId ?? _(),
|
|
491
491
|
...e
|
|
492
492
|
}));
|
|
493
493
|
}
|
|
@@ -495,13 +495,13 @@ class x {
|
|
|
495
495
|
for (const t of e?.getTracks() ?? []) t.stop();
|
|
496
496
|
}
|
|
497
497
|
}
|
|
498
|
-
function
|
|
498
|
+
function _() {
|
|
499
499
|
return globalThis.crypto?.randomUUID?.() ?? `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
500
500
|
}
|
|
501
|
-
const
|
|
502
|
-
{ rid: "a", maxBitrate:
|
|
503
|
-
{ rid: "b", maxBitrate:
|
|
504
|
-
{ rid: "c", maxBitrate:
|
|
501
|
+
const z = { active: !0, maxBitrate: 4e6, maxFramerate: 30, scaleResolutionDownBy: 1 }, Y = [
|
|
502
|
+
{ rid: "a", maxBitrate: N.high.maxBitrate, maxFramerate: N.high.maxFramerate },
|
|
503
|
+
{ rid: "b", maxBitrate: N.medium.maxBitrate, maxFramerate: N.medium.maxFramerate, scaleResolutionDownBy: 2 },
|
|
504
|
+
{ rid: "c", maxBitrate: N.low.maxBitrate, maxFramerate: N.low.maxFramerate, scaleResolutionDownBy: 4 }
|
|
505
505
|
], G = { high: "a", medium: "b", low: "c" };
|
|
506
506
|
class ye {
|
|
507
507
|
options;
|
|
@@ -638,26 +638,26 @@ class ye {
|
|
|
638
638
|
async openSocket(e) {
|
|
639
639
|
const { credential: t } = this.options, i = new URL(t.signalingUrl, globalThis.location?.href);
|
|
640
640
|
i.protocol = i.protocol === "https:" ? "wss:" : "ws:";
|
|
641
|
-
const n = new Promise((c,
|
|
642
|
-
this.resolveJoin = c, this.rejectJoin =
|
|
641
|
+
const n = new Promise((c, l) => {
|
|
642
|
+
this.resolveJoin = c, this.rejectJoin = l;
|
|
643
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
648
|
}), s.addEventListener("message", (c) => {
|
|
649
|
-
const
|
|
649
|
+
const l = String(c.data);
|
|
650
650
|
this.signalQueue = this.signalQueue.then(async () => {
|
|
651
|
-
this.socket !== s || this.socketGeneration !== o || await this.handleSignal(
|
|
652
|
-
}).catch((
|
|
653
|
-
const u =
|
|
651
|
+
this.socket !== s || this.socketGeneration !== o || await this.handleSignal(l);
|
|
652
|
+
}).catch((d) => {
|
|
653
|
+
const u = d instanceof Error ? d.message : "RTC signaling processing failed";
|
|
654
654
|
this.options.onEvent("signalingerror", { message: u }), this.joined || this.rejectJoin?.(new Error(u));
|
|
655
655
|
});
|
|
656
656
|
}), s.addEventListener("error", () => this.rejectJoin?.(new Error("RTC signaling connection failed"))), s.addEventListener("close", (c) => {
|
|
657
657
|
if (this.socket !== s) return;
|
|
658
658
|
this.socket = null, this.stopHeartbeat(), !this.joined && !e && this.rejectJoin?.(new Error(c.reason || "RTC signaling connection closed"));
|
|
659
|
-
const
|
|
660
|
-
this.joined = !1,
|
|
659
|
+
const l = !this.stopped && (this.joined || e);
|
|
660
|
+
this.joined = !1, l ? (this.options.onEvent("reconnecting", { reason: c.reason, code: c.code, attempt: this.reconnectAttempt + 1 }), this.scheduleReconnect()) : this.stopped || this.options.onEvent("disconnected", { reason: c.reason, code: c.code });
|
|
661
661
|
});
|
|
662
662
|
try {
|
|
663
663
|
await n, this.joined = !0, this.options.onEvent("connected", { participantId: t.participantId, resumed: e });
|
|
@@ -833,12 +833,12 @@ class ye {
|
|
|
833
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 c = Y.find((
|
|
838
|
-
return { ...r, ...c ?? {}, active:
|
|
836
|
+
return { ...r, active: r.rid === "a", ...r.rid === "a" ? { maxBitrate: z.maxBitrate, maxFramerate: z.maxFramerate } : {} };
|
|
837
|
+
const c = Y.find((d) => d.rid === r.rid), l = o != null && String(r.rid ?? "") >= o;
|
|
838
|
+
return { ...r, ...c ?? {}, active: l };
|
|
839
839
|
});
|
|
840
840
|
} else {
|
|
841
|
-
const o = this.quality === "audio" ?
|
|
841
|
+
const o = this.quality === "audio" ? N.audio : N[this.quality], r = i ? z : o;
|
|
842
842
|
n.encodings = s.map((c) => ({ ...c, ...r }));
|
|
843
843
|
}
|
|
844
844
|
n.degradationPreference = i ? "maintain-resolution" : "maintain-framerate", await e.setParameters(n);
|
|
@@ -945,7 +945,7 @@ function Ne(a, e = {}) {
|
|
|
945
945
|
}
|
|
946
946
|
function o(r) {
|
|
947
947
|
const c = a.getRemoteStreams();
|
|
948
|
-
return r ? c.find((
|
|
948
|
+
return r ? c.find((l) => l.participant.participantId === r)?.stream ?? null : c[0]?.stream ?? a.getLocalStream();
|
|
949
949
|
}
|
|
950
950
|
return {
|
|
951
951
|
supported: i,
|
|
@@ -953,9 +953,9 @@ function Ne(a, e = {}) {
|
|
|
953
953
|
if (!i) throw new Error("This browser does not support picture-in-picture");
|
|
954
954
|
const c = o(r);
|
|
955
955
|
if (!c) throw new Error("No video stream is available for picture-in-picture");
|
|
956
|
-
const
|
|
957
|
-
|
|
958
|
-
}), await
|
|
956
|
+
const l = s();
|
|
957
|
+
l.srcObject !== c && (l.srcObject = c), await l.play().catch(() => {
|
|
958
|
+
}), await l.requestPictureInPicture();
|
|
959
959
|
},
|
|
960
960
|
async exit() {
|
|
961
961
|
t.pictureInPictureElement && typeof t.exitPictureInPicture == "function" && await t.exitPictureInPicture();
|
|
@@ -967,89 +967,89 @@ function Ne(a, e = {}) {
|
|
|
967
967
|
}
|
|
968
968
|
function je(a, e) {
|
|
969
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,
|
|
971
|
-
async function
|
|
972
|
-
const
|
|
973
|
-
method:
|
|
970
|
+
let c = null, l = null, d = null, u = null, S = 0, f = [], v = !1;
|
|
971
|
+
async function I(y, k, p, C = "application/json") {
|
|
972
|
+
const P = await i(`${n}${k}`, {
|
|
973
|
+
method: y,
|
|
974
974
|
headers: {
|
|
975
975
|
authorization: `Bearer ${e.credential.signalingToken}`,
|
|
976
|
-
"content-type":
|
|
976
|
+
"content-type": C
|
|
977
977
|
},
|
|
978
|
-
body:
|
|
978
|
+
body: p === void 0 ? void 0 : C === "application/json" ? JSON.stringify(p) : p
|
|
979
979
|
});
|
|
980
|
-
if (!
|
|
981
|
-
return
|
|
980
|
+
if (!P.ok) throw new Error(`Recording request failed with HTTP ${P.status}`);
|
|
981
|
+
return P.json();
|
|
982
982
|
}
|
|
983
983
|
return {
|
|
984
984
|
get recording() {
|
|
985
|
-
return
|
|
985
|
+
return v;
|
|
986
986
|
},
|
|
987
987
|
async start() {
|
|
988
|
-
if (
|
|
989
|
-
u = (await
|
|
990
|
-
const
|
|
991
|
-
|
|
992
|
-
const
|
|
993
|
-
let
|
|
994
|
-
return
|
|
995
|
-
}),
|
|
988
|
+
if (v) return;
|
|
989
|
+
u = (await I("POST", "/v1/rtc/recordings")).recordingId;
|
|
990
|
+
const k = t.createElement("canvas");
|
|
991
|
+
k.width = r, k.height = o;
|
|
992
|
+
const p = k.getContext("2d"), C = /* @__PURE__ */ new Map(), P = (T, R) => {
|
|
993
|
+
let b = C.get(T);
|
|
994
|
+
return b || (b = t.createElement("video"), b.muted = !0, b.playsInline = !0, b.srcObject = R, b.play().catch(() => {
|
|
995
|
+
}), C.set(T, b)), b;
|
|
996
996
|
};
|
|
997
|
-
|
|
998
|
-
const
|
|
999
|
-
...
|
|
1000
|
-
...
|
|
997
|
+
l = setInterval(() => {
|
|
998
|
+
const T = a.getRemoteStreams(), R = a.getLocalStream(), b = [
|
|
999
|
+
...T.map((j) => ({ key: j.participant.participantId, stream: j.stream, label: j.participant.displayName })),
|
|
1000
|
+
...R ? [{ key: "local", stream: R, label: "me" }] : []
|
|
1001
1001
|
];
|
|
1002
|
-
|
|
1003
|
-
const
|
|
1004
|
-
|
|
1005
|
-
const
|
|
1006
|
-
|
|
1002
|
+
p.fillStyle = "#101418", p.fillRect(0, 0, r, o);
|
|
1003
|
+
const O = Math.max(1, Math.ceil(Math.sqrt(b.length))), g = Math.max(1, Math.ceil(b.length / O)), q = r / O, x = o / g;
|
|
1004
|
+
b.forEach((j, J) => {
|
|
1005
|
+
const W = P(j.key, j.stream), B = J % O * q, F = Math.floor(J / O) * x;
|
|
1006
|
+
W.videoWidth > 0 && p.drawImage(W, B + 2, F + 2, q - 4, x - 4), p.fillStyle = "rgba(0,0,0,.6)", p.fillRect(B + 8, F + x - 30, Math.min(q - 16, 12 + j.label.length * 9), 22), p.fillStyle = "#fff", p.font = "14px system-ui", p.fillText(j.label, B + 14, F + x - 14);
|
|
1007
1007
|
});
|
|
1008
1008
|
}, 66);
|
|
1009
|
-
const
|
|
1010
|
-
|
|
1011
|
-
const
|
|
1012
|
-
const
|
|
1013
|
-
...a.getRemoteStreams().map((
|
|
1009
|
+
const L = k.captureStream(15);
|
|
1010
|
+
d = new AudioContext();
|
|
1011
|
+
const M = d.createMediaStreamDestination(), A = /* @__PURE__ */ new Set(), w = () => {
|
|
1012
|
+
const T = [
|
|
1013
|
+
...a.getRemoteStreams().map((R) => R.stream),
|
|
1014
1014
|
...a.getLocalStream() ? [a.getLocalStream()] : []
|
|
1015
1015
|
];
|
|
1016
|
-
for (const
|
|
1017
|
-
for (const
|
|
1018
|
-
|
|
1016
|
+
for (const R of T)
|
|
1017
|
+
for (const b of R.getAudioTracks())
|
|
1018
|
+
A.has(b.id) || (A.add(b.id), d.createMediaStreamSource(new MediaStream([b])).connect(M));
|
|
1019
1019
|
};
|
|
1020
|
-
|
|
1021
|
-
const
|
|
1022
|
-
for (const
|
|
1023
|
-
c = (e.mediaRecorderFactory ?? ((
|
|
1024
|
-
|
|
1025
|
-
}), c.addEventListener("stop",
|
|
1020
|
+
w();
|
|
1021
|
+
const E = setInterval(w, 1e3), m = () => clearInterval(E);
|
|
1022
|
+
for (const T of M.stream.getAudioTracks()) L.addTrack(T);
|
|
1023
|
+
c = (e.mediaRecorderFactory ?? ((T, R) => new MediaRecorder(T, R)))(L, { mimeType: s }), f = [], c.addEventListener("dataavailable", (T) => {
|
|
1024
|
+
T.data && T.data.size > 0 && f.push(T.data);
|
|
1025
|
+
}), c.addEventListener("stop", m), c.start(1e3), S = Date.now(), v = !0;
|
|
1026
1026
|
},
|
|
1027
1027
|
async stop() {
|
|
1028
|
-
if (!
|
|
1029
|
-
|
|
1030
|
-
const
|
|
1031
|
-
|
|
1032
|
-
}),
|
|
1033
|
-
const
|
|
1028
|
+
if (!v || !c || !u) throw new Error("The recorder is not running");
|
|
1029
|
+
v = !1;
|
|
1030
|
+
const y = c, k = new Promise((M) => y.addEventListener("stop", () => M(), { once: !0 }));
|
|
1031
|
+
y.stop(), await k, l && clearInterval(l), l = null, await d?.close().catch(() => {
|
|
1032
|
+
}), d = null;
|
|
1033
|
+
const p = new Blob(f, { type: s });
|
|
1034
1034
|
f = [];
|
|
1035
|
-
const
|
|
1035
|
+
const C = Math.max(1, Math.round((Date.now() - S) / 1e3)), P = await I(
|
|
1036
1036
|
"PUT",
|
|
1037
1037
|
`/v1/rtc/recordings/${encodeURIComponent(u)}/media`,
|
|
1038
|
-
|
|
1038
|
+
p,
|
|
1039
1039
|
s
|
|
1040
1040
|
);
|
|
1041
|
-
await
|
|
1042
|
-
const
|
|
1043
|
-
return u = null, c = null,
|
|
1041
|
+
await I("POST", `/v1/rtc/recordings/${encodeURIComponent(u)}/complete`, { durationSeconds: C });
|
|
1042
|
+
const L = { recordingId: u, sizeBytes: P.sizeBytes, durationSeconds: C };
|
|
1043
|
+
return u = null, c = null, L;
|
|
1044
1044
|
},
|
|
1045
1045
|
dispose() {
|
|
1046
|
-
|
|
1046
|
+
l && clearInterval(l), l = null;
|
|
1047
1047
|
try {
|
|
1048
1048
|
c?.stop();
|
|
1049
1049
|
} catch {
|
|
1050
1050
|
}
|
|
1051
|
-
c = null,
|
|
1052
|
-
}),
|
|
1051
|
+
c = null, d?.close().catch(() => {
|
|
1052
|
+
}), d = null, v = !1;
|
|
1053
1053
|
}
|
|
1054
1054
|
};
|
|
1055
1055
|
}
|
|
@@ -1091,17 +1091,19 @@ const Se = ["none", "blur", "aurora", "cybergrid", "bokeh", "sunset", "image"],
|
|
|
1091
1091
|
}
|
|
1092
1092
|
};
|
|
1093
1093
|
function Ve(a, e = {}) {
|
|
1094
|
-
const t = e.document ?? globalThis.document
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1094
|
+
const t = e.document ?? globalThis.document;
|
|
1095
|
+
let i = e.width ?? 1280, n = e.height ?? 720;
|
|
1096
|
+
const s = !!(e.width || e.height), o = e.frameRate ?? 24, r = a instanceof MediaStreamTrack ? a : a.getVideoTracks()[0];
|
|
1097
|
+
if (!r) throw new Error("A video track is required");
|
|
1098
|
+
let c = e.background ?? "none", l = e.overlay ?? "none", d = e.backgroundImage ?? null, u = e.segmenter ?? null, S = null, f = !1;
|
|
1099
|
+
const v = t.createElement("video");
|
|
1100
|
+
v.muted = !0, v.playsInline = !0, v.srcObject = new MediaStream([r]), v.play().catch(() => {
|
|
1099
1101
|
});
|
|
1100
|
-
const
|
|
1101
|
-
|
|
1102
|
-
const
|
|
1103
|
-
|
|
1104
|
-
const
|
|
1102
|
+
const I = t.createElement("canvas");
|
|
1103
|
+
I.width = i, I.height = n;
|
|
1104
|
+
const y = I.getContext("2d"), k = t.createElement("canvas");
|
|
1105
|
+
k.width = i, k.height = n;
|
|
1106
|
+
const p = k.getContext("2d"), C = () => Array.from({ length: 80 }, (m, h) => ({
|
|
1105
1107
|
x: (Math.sin(h * 12.9898) * 0.5 + 0.5) * i,
|
|
1106
1108
|
y: (Math.sin(h * 78.233) * 0.5 + 0.5) * n,
|
|
1107
1109
|
speed: 0.6 + h % 5 * 0.5,
|
|
@@ -1109,48 +1111,57 @@ function Ve(a, e = {}) {
|
|
|
1109
1111
|
size: 2 + h % 4,
|
|
1110
1112
|
hue: h * 47 % 360
|
|
1111
1113
|
}));
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1114
|
+
let P = C();
|
|
1115
|
+
const L = () => {
|
|
1116
|
+
if (s) return;
|
|
1117
|
+
const m = v.videoWidth, h = v.videoHeight;
|
|
1118
|
+
if (!m || !h) return;
|
|
1119
|
+
const T = Math.min(1, 1280 / Math.max(m, h)), R = Math.round(m * T), b = Math.round(h * T);
|
|
1120
|
+
R === i && b === n || (i = R, n = b, I.width = i, I.height = n, k.width = i, k.height = n, P = C());
|
|
1121
|
+
};
|
|
1122
|
+
v.addEventListener?.("loadedmetadata", L), v.addEventListener?.("resize", L);
|
|
1123
|
+
function M() {
|
|
1124
|
+
if (S) {
|
|
1125
|
+
p.clearRect(0, 0, i, n), p.drawImage(S, 0, 0, i, n), p.globalCompositeOperation = "source-in", p.drawImage(v, 0, 0, i, n), p.globalCompositeOperation = "source-over", y.drawImage(k, 0, 0);
|
|
1115
1126
|
return;
|
|
1116
1127
|
}
|
|
1117
|
-
const
|
|
1118
|
-
|
|
1119
|
-
}
|
|
1120
|
-
function
|
|
1121
|
-
if (
|
|
1122
|
-
for (const h of
|
|
1123
|
-
h.y += h.speed * (
|
|
1124
|
-
else if (
|
|
1125
|
-
const h =
|
|
1126
|
-
h.addColorStop(0, "rgba(0,0,0,0)"), h.addColorStop(1, "rgba(0,0,0,0.55)"),
|
|
1128
|
+
const m = i * 0.42, h = m * 9 / 16, T = i - m - 24, R = n - h - 24;
|
|
1129
|
+
y.save(), y.shadowColor = "rgba(0,0,0,.5)", y.shadowBlur = 24, y.beginPath(), y.roundRect(T, R, m, h, 14), y.clip(), y.drawImage(v, T, R, m, h), y.restore();
|
|
1130
|
+
}
|
|
1131
|
+
function A(m) {
|
|
1132
|
+
if (c === "none" ? y.drawImage(v, 0, 0, i, n) : c === "blur" ? (y.filter = "blur(14px)", y.drawImage(v, -16, -16, i + 32, n + 32), y.filter = "none", S && M()) : c === "image" && d ? (y.drawImage(d, 0, 0, i, n), M()) : ((K[c] ?? K.aurora)(y, i, n, m), M()), l === "snow" || l === "confetti")
|
|
1133
|
+
for (const h of P)
|
|
1134
|
+
h.y += h.speed * (l === "confetti" ? 1.6 : 1), h.x += h.drift, h.y > n && (h.y = -6, h.x = Math.random() * i), y.fillStyle = l === "snow" ? "rgba(255,255,255,.85)" : `hsl(${h.hue}, 90%, 60%)`, l === "confetti" ? y.fillRect(h.x, h.y, h.size + 2, h.size) : (y.beginPath(), y.arc(h.x, h.y, h.size / 2, 0, Math.PI * 2), y.fill());
|
|
1135
|
+
else if (l === "vignette") {
|
|
1136
|
+
const h = y.createRadialGradient(i / 2, n / 2, n * 0.4, i / 2, n / 2, n * 0.85);
|
|
1137
|
+
h.addColorStop(0, "rgba(0,0,0,0)"), h.addColorStop(1, "rgba(0,0,0,0.55)"), y.fillStyle = h, y.fillRect(0, 0, i, n);
|
|
1127
1138
|
}
|
|
1128
1139
|
}
|
|
1129
|
-
const
|
|
1130
|
-
|
|
1131
|
-
|
|
1140
|
+
const w = setInterval(() => {
|
|
1141
|
+
A(Date.now()), u && !f && c !== "none" && (f = !0, u.segment(v).then((m) => {
|
|
1142
|
+
S = m;
|
|
1132
1143
|
}).catch(() => {
|
|
1133
|
-
|
|
1144
|
+
S = null;
|
|
1134
1145
|
}).finally(() => {
|
|
1135
|
-
|
|
1146
|
+
f = !1;
|
|
1136
1147
|
}));
|
|
1137
|
-
}, Math.round(1e3 /
|
|
1148
|
+
}, Math.round(1e3 / o)), E = I.captureStream(o);
|
|
1138
1149
|
return {
|
|
1139
|
-
track:
|
|
1140
|
-
stream:
|
|
1141
|
-
setBackground(
|
|
1142
|
-
|
|
1150
|
+
track: E.getVideoTracks()[0],
|
|
1151
|
+
stream: E,
|
|
1152
|
+
setBackground(m, h) {
|
|
1153
|
+
c = Se.includes(m) ? m : "none", h !== void 0 && (d = h), c === "none" && (S = null);
|
|
1143
1154
|
},
|
|
1144
|
-
setOverlay(
|
|
1145
|
-
|
|
1155
|
+
setOverlay(m) {
|
|
1156
|
+
l = ke.includes(m) ? m : "none";
|
|
1146
1157
|
},
|
|
1147
|
-
setSegmenter(
|
|
1148
|
-
|
|
1158
|
+
setSegmenter(m) {
|
|
1159
|
+
u = m, m || (S = null);
|
|
1149
1160
|
},
|
|
1150
1161
|
dispose() {
|
|
1151
|
-
clearInterval(
|
|
1152
|
-
for (const
|
|
1153
|
-
|
|
1162
|
+
clearInterval(w), u?.close?.();
|
|
1163
|
+
for (const m of E.getTracks()) m.stop();
|
|
1164
|
+
v.srcObject = null;
|
|
1154
1165
|
}
|
|
1155
1166
|
};
|
|
1156
1167
|
}
|
|
@@ -1169,7 +1180,7 @@ function le() {
|
|
|
1169
1180
|
tracked: !1
|
|
1170
1181
|
};
|
|
1171
1182
|
}
|
|
1172
|
-
function
|
|
1183
|
+
function D(a, e) {
|
|
1173
1184
|
const t = a.find((i) => i.categoryName === e);
|
|
1174
1185
|
return t ? Math.max(0, Math.min(1, t.score)) : 0;
|
|
1175
1186
|
}
|
|
@@ -1181,13 +1192,13 @@ function we(a) {
|
|
|
1181
1192
|
}
|
|
1182
1193
|
function Te(a, e, t = 1) {
|
|
1183
1194
|
if (!a || a.length === 0) return le();
|
|
1184
|
-
const i = we(e), n = (
|
|
1195
|
+
const i = we(e), n = (D(a, "eyeLookOutRight") + D(a, "eyeLookInLeft")) / 2, s = (D(a, "eyeLookOutLeft") + D(a, "eyeLookInRight")) / 2, o = (D(a, "eyeLookUpLeft") + D(a, "eyeLookUpRight")) / 2, r = (D(a, "eyeLookDownLeft") + D(a, "eyeLookDownRight")) / 2, c = (D(a, "mouthSmileLeft") + D(a, "mouthSmileRight")) / 2, l = (D(a, "mouthFrownLeft") + D(a, "mouthFrownRight")) / 2, d = D(a, "eyeBlinkLeft"), u = D(a, "eyeBlinkRight"), S = Math.min(1, Math.abs(i.yaw) / 0.35) * Math.max(0, Math.min(1, t)), f = (d + u) / 2;
|
|
1185
1196
|
return {
|
|
1186
|
-
blinkLeft:
|
|
1187
|
-
blinkRight: u + (f - u) *
|
|
1188
|
-
jawOpen:
|
|
1189
|
-
smile: c -
|
|
1190
|
-
browUp: (
|
|
1197
|
+
blinkLeft: d + (f - d) * S,
|
|
1198
|
+
blinkRight: u + (f - u) * S,
|
|
1199
|
+
jawOpen: D(a, "jawOpen"),
|
|
1200
|
+
smile: c - l,
|
|
1201
|
+
browUp: (D(a, "browInnerUp") + D(a, "browOuterUpLeft") + D(a, "browOuterUpRight")) / 3,
|
|
1191
1202
|
pupilX: n - s,
|
|
1192
1203
|
pupilY: r - o,
|
|
1193
1204
|
headYaw: i.yaw,
|
|
@@ -1216,30 +1227,30 @@ function be(a, e, t, i, n) {
|
|
|
1216
1227
|
n || (a.fillStyle = "#101822", a.fillRect(0, 0, t, i));
|
|
1217
1228
|
const s = n ? n.x + e.headYaw * n.radius * 0.1 : t / 2 + e.headYaw * t * 0.18, o = n ? n.y + e.headPitch * n.radius * 0.1 : i / 2 + e.headPitch * i * 0.18, r = n ? n.radius : Math.min(t, i) * 0.28;
|
|
1218
1229
|
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)),
|
|
1220
|
-
a.fillStyle = "#3a2a1c", a.beginPath(), a.ellipse(-
|
|
1221
|
-
const
|
|
1230
|
+
const c = Math.max(-1.35, Math.min(1.35, e.headYaw)), l = Math.max(-0.9, Math.min(0.9, e.headPitch)), d = Math.sin(c), u = (A) => Math.sin(A + c) * r, S = (A) => Math.cos(A + c), f = Math.sin(l) * r * 0.22;
|
|
1231
|
+
a.fillStyle = "#3a2a1c", a.beginPath(), a.ellipse(-d * r * 0.35, -r * 0.25, r * 0.98, r * 0.95, 0, 0, Math.PI * 2), a.fill(), a.fillStyle = e.tracked ? "#f2c9a0" : "#5a6470", a.beginPath(), a.ellipse(0, 0, r * (1 - Math.abs(d) * 0.12), r * 1.12, 0, 0, Math.PI * 2), a.fill(), a.fillStyle = "#3a2a1c", a.beginPath(), a.ellipse(-d * r * 0.28, -r * 0.72 + f * 0.4, r * 0.92, r * 0.5, 0, Math.PI, Math.PI * 2), a.fill();
|
|
1232
|
+
const v = 0;
|
|
1222
1233
|
a.fillStyle = "#e5b58a", a.beginPath();
|
|
1223
|
-
const
|
|
1224
|
-
a.ellipse(
|
|
1225
|
-
for (const
|
|
1226
|
-
const
|
|
1227
|
-
if (
|
|
1228
|
-
const
|
|
1229
|
-
a.strokeStyle = "#4a2e18", a.lineWidth = r * 0.07, a.lineCap = "round", a.beginPath(), a.moveTo(
|
|
1230
|
-
const
|
|
1231
|
-
a.fillStyle = "#ffffff", a.beginPath(), a.ellipse(
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
r * 0.06 *
|
|
1235
|
-
r * 0.06 *
|
|
1234
|
+
const I = u(v) * 0.9, y = r * 0.12 + f;
|
|
1235
|
+
a.ellipse(I + d * r * 0.14, y, r * 0.11, r * 0.14, 0, 0, Math.PI * 2), a.fill();
|
|
1236
|
+
for (const A of [-1, 1]) {
|
|
1237
|
+
const w = A * 0.42, E = S(w);
|
|
1238
|
+
if (E <= 0.12) continue;
|
|
1239
|
+
const m = u(w) * 0.82, h = Math.max(0.2, E), T = A < 0 ? e.blinkLeft : e.blinkRight, R = Math.max(0.06, 1 - T), b = e.browUp * r * 0.14;
|
|
1240
|
+
a.strokeStyle = "#4a2e18", a.lineWidth = r * 0.07, a.lineCap = "round", a.beginPath(), a.moveTo(m - r * 0.2 * h, -r * 0.42 - b + f), a.quadraticCurveTo(m, -r * 0.52 - b + f, m + r * 0.2 * h, -r * 0.44 - b + f), a.stroke();
|
|
1241
|
+
const O = -r * 0.18 + f;
|
|
1242
|
+
a.fillStyle = "#ffffff", a.beginPath(), a.ellipse(m, O, r * 0.16 * h, r * 0.12 * R, 0, 0, Math.PI * 2), a.fill(), a.fillStyle = "#22303c", a.beginPath(), a.ellipse(
|
|
1243
|
+
m + e.pupilX * r * 0.07 * h,
|
|
1244
|
+
O + e.pupilY * r * 0.05,
|
|
1245
|
+
r * 0.06 * h,
|
|
1246
|
+
r * 0.06 * R,
|
|
1236
1247
|
0,
|
|
1237
1248
|
0,
|
|
1238
1249
|
Math.PI * 2
|
|
1239
1250
|
), a.fill();
|
|
1240
1251
|
}
|
|
1241
|
-
const
|
|
1242
|
-
a.fillStyle = "#8c2f2f", a.beginPath(), a.moveTo(
|
|
1252
|
+
const k = Math.max(0.25, S(0)), p = u(0) * 0.7, C = r * 0.55 * k, P = r * (0.04 + e.jawOpen * 0.34), L = e.smile * r * 0.12, M = r * 0.42 + f;
|
|
1253
|
+
a.fillStyle = "#8c2f2f", a.beginPath(), a.moveTo(p - C / 2, M - L), a.quadraticCurveTo(p, M + P, p + C / 2, M - L), a.quadraticCurveTo(p, M - P * 0.25, p - C / 2, M - L), a.closePath(), a.fill(), a.restore(), !e.tracked && !n && (a.fillStyle = "#9fb0bd", a.font = `${Math.round(i * 0.045)}px system-ui`, a.textAlign = "center", a.fillText("No face detected", t / 2, i * 0.9), a.textAlign = "left");
|
|
1243
1254
|
}
|
|
1244
1255
|
const Z = {
|
|
1245
1256
|
mouthGain: 1.7,
|
|
@@ -1257,62 +1268,71 @@ function ee() {
|
|
|
1257
1268
|
}
|
|
1258
1269
|
};
|
|
1259
1270
|
}
|
|
1260
|
-
const
|
|
1261
|
-
function
|
|
1262
|
-
const t = e.document ?? globalThis.document
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1271
|
+
const $ = (a) => Math.max(0, Math.min(1, a));
|
|
1272
|
+
function xe(a, e = {}) {
|
|
1273
|
+
const t = e.document ?? globalThis.document;
|
|
1274
|
+
let i = e.width ?? 1280, n = e.height ?? 720;
|
|
1275
|
+
const s = !!(e.width || e.height), o = e.frameRate ?? 24, r = a instanceof MediaStreamTrack ? a : a.getVideoTracks()[0];
|
|
1276
|
+
if (!r) throw new Error("A video track is required");
|
|
1277
|
+
let c = e.tracker ?? null, l = e.renderer ?? ee(), d = e.mode ?? "replace";
|
|
1278
|
+
const u = { ...Z, ...e.tuning }, S = e.backdrop ?? "#101822", f = t.createElement("video");
|
|
1279
|
+
f.muted = !0, f.playsInline = !0, f.srcObject = new MediaStream([r]), f.play().catch(() => {
|
|
1267
1280
|
});
|
|
1268
|
-
const
|
|
1269
|
-
|
|
1270
|
-
const
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
if (
|
|
1274
|
-
const
|
|
1275
|
-
|
|
1276
|
-
}
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1281
|
+
const v = t.createElement("canvas");
|
|
1282
|
+
v.width = i, v.height = n;
|
|
1283
|
+
const I = v.getContext("2d"), y = () => {
|
|
1284
|
+
if (s) return;
|
|
1285
|
+
const w = f.videoWidth, E = f.videoHeight;
|
|
1286
|
+
if (!w || !E) return;
|
|
1287
|
+
const m = Math.min(1, 1280 / Math.max(w, E)), h = Math.round(w * m), T = Math.round(E * m);
|
|
1288
|
+
(!(h === i || T === n) || h !== i || T !== n) && (i = h, n = T, v.width = i, v.height = n);
|
|
1289
|
+
};
|
|
1290
|
+
f.addEventListener?.("loadedmetadata", y), f.addEventListener?.("resize", y);
|
|
1291
|
+
let k = le(), p = null, C = 0;
|
|
1292
|
+
function P(w) {
|
|
1293
|
+
if (w.length <= 454) return;
|
|
1294
|
+
const E = w[10], m = w[152], h = w[234], T = w[454], R = (h.x + T.x) / 2 * i, b = (h.y + T.y) / 2 * n, O = Math.hypot((m.x - E.x) * i, (m.y - E.y) * n), g = Math.max(0.6, Math.cos(k.headPitch)), q = { x: R, y: b, radius: Math.max(24, O / g * 0.8) };
|
|
1295
|
+
p = p ? { x: p.x + (q.x - p.x) * 0.4, y: p.y + (q.y - p.y) * 0.4, radius: p.radius + (q.radius - p.radius) * 0.15 } : q;
|
|
1296
|
+
}
|
|
1297
|
+
function L(w) {
|
|
1298
|
+
if (c && f.readyState >= 2) {
|
|
1299
|
+
let m = null;
|
|
1280
1300
|
try {
|
|
1281
|
-
|
|
1301
|
+
m = c.detect(f, w);
|
|
1282
1302
|
} catch {
|
|
1283
1303
|
}
|
|
1284
|
-
const
|
|
1285
|
-
|
|
1304
|
+
const h = Te(m?.categories ?? null, m?.matrix ?? null, u.eyeSync);
|
|
1305
|
+
h.jawOpen = $(h.jawOpen * u.mouthGain), h.blinkLeft = $(h.blinkLeft * u.blinkGain), h.blinkRight = $(h.blinkRight * u.blinkGain), h.tracked ? (C = w, k = X(k, h, u.smoothing), m?.landmarks && P(m.landmarks)) : k.tracked && w - C > u.lostHoldMs && (k = X(k, { ...k, jawOpen: 0, browUp: 0, smile: k.smile * 0.5, blinkLeft: 0.1, blinkRight: 0.1 }, 0.05));
|
|
1286
1306
|
}
|
|
1287
|
-
d === "overlay" &&
|
|
1288
|
-
const
|
|
1289
|
-
|
|
1307
|
+
d === "overlay" && f.readyState >= 2 ? I.drawImage(f, 0, 0, i, n) : (I.fillStyle = S, I.fillRect(0, 0, i, n));
|
|
1308
|
+
const E = d === "overlay" && p ? { x: p.x, y: p.y + u.avatarLift * p.radius, radius: p.radius * u.avatarScale } : null;
|
|
1309
|
+
l?.render(I, k, i, n, E);
|
|
1290
1310
|
}
|
|
1291
|
-
const M = setInterval(() =>
|
|
1311
|
+
const M = setInterval(() => L(Date.now()), Math.round(1e3 / o)), A = v.captureStream(o);
|
|
1292
1312
|
return {
|
|
1293
|
-
track:
|
|
1294
|
-
stream:
|
|
1295
|
-
getRig: () => ({ ...
|
|
1296
|
-
setMode(
|
|
1297
|
-
d =
|
|
1313
|
+
track: A.getVideoTracks()[0],
|
|
1314
|
+
stream: A,
|
|
1315
|
+
getRig: () => ({ ...k }),
|
|
1316
|
+
setMode(w) {
|
|
1317
|
+
d = w === "overlay" ? "overlay" : "replace";
|
|
1298
1318
|
},
|
|
1299
|
-
setTracker(
|
|
1300
|
-
|
|
1319
|
+
setTracker(w) {
|
|
1320
|
+
c?.close?.(), c = w;
|
|
1301
1321
|
},
|
|
1302
|
-
setRenderer(
|
|
1303
|
-
|
|
1322
|
+
setRenderer(w) {
|
|
1323
|
+
l?.dispose?.(), l = w ?? ee();
|
|
1304
1324
|
},
|
|
1305
|
-
setTuning(
|
|
1306
|
-
for (const
|
|
1307
|
-
const
|
|
1308
|
-
typeof
|
|
1325
|
+
setTuning(w) {
|
|
1326
|
+
for (const E of Object.keys(Z)) {
|
|
1327
|
+
const m = w[E];
|
|
1328
|
+
typeof m == "number" && Number.isFinite(m) && (u[E] = m);
|
|
1309
1329
|
}
|
|
1310
1330
|
},
|
|
1311
|
-
getTuning: () => ({ ...
|
|
1331
|
+
getTuning: () => ({ ...u }),
|
|
1312
1332
|
dispose() {
|
|
1313
|
-
clearInterval(M),
|
|
1314
|
-
for (const
|
|
1315
|
-
|
|
1333
|
+
clearInterval(M), c?.close?.(), l?.dispose?.();
|
|
1334
|
+
for (const w of A.getTracks()) w.stop();
|
|
1335
|
+
f.srcObject = null;
|
|
1316
1336
|
}
|
|
1317
1337
|
};
|
|
1318
1338
|
}
|
|
@@ -1350,7 +1370,7 @@ class Ce extends EventTarget {
|
|
|
1350
1370
|
async initialize() {
|
|
1351
1371
|
if (this.client) return this;
|
|
1352
1372
|
try {
|
|
1353
|
-
const t = (this.options.transport === "auto" ? this.mode === "meeting" ? "sfu" : "mesh" : this.options.transport ?? "mesh") === "sfu" ? ye :
|
|
1373
|
+
const t = (this.options.transport === "auto" ? this.mode === "meeting" ? "sfu" : "mesh" : this.options.transport ?? "mesh") === "sfu" ? ye : Q;
|
|
1354
1374
|
return this.client = new t({
|
|
1355
1375
|
credential: {
|
|
1356
1376
|
signalingToken: this.options.credential.signalingToken,
|
|
@@ -1495,42 +1515,42 @@ class Ce extends EventTarget {
|
|
|
1495
1515
|
let n = "unavailable";
|
|
1496
1516
|
if (e.requestMedia !== !1 && navigator.mediaDevices?.getUserMedia)
|
|
1497
1517
|
try {
|
|
1498
|
-
(await navigator.mediaDevices.getUserMedia({ audio: this.options.audio, video: this.options.video })).getTracks().forEach((
|
|
1518
|
+
(await navigator.mediaDevices.getUserMedia({ audio: this.options.audio, video: this.options.video })).getTracks().forEach((S) => S.stop()), n = "granted";
|
|
1499
1519
|
} catch {
|
|
1500
1520
|
n = "denied", t.push("media_permission_denied");
|
|
1501
1521
|
}
|
|
1502
1522
|
const s = navigator.mediaDevices?.enumerateDevices ? await navigator.mediaDevices.enumerateDevices().catch(() => []) : [], o = s.filter((u) => u.kind === "audioinput").length, r = s.filter((u) => u.kind === "videoinput").length;
|
|
1503
1523
|
this.options.audio && o === 0 && t.push("microphone_unavailable"), this.options.video && r === 0 && t.push("camera_unavailable");
|
|
1504
1524
|
const c = e.probeUrl ?? this.options.preflightProbeUrl;
|
|
1505
|
-
let
|
|
1525
|
+
let l = null;
|
|
1506
1526
|
if (c) {
|
|
1507
1527
|
const u = performance.now();
|
|
1508
1528
|
try {
|
|
1509
|
-
const
|
|
1510
|
-
|
|
1529
|
+
const S = await fetch(c, { method: "HEAD", cache: "no-store", credentials: "omit" });
|
|
1530
|
+
l = Math.round(performance.now() - u), S.ok || t.push("network_probe_failed");
|
|
1511
1531
|
} catch {
|
|
1512
1532
|
t.push("network_probe_failed");
|
|
1513
1533
|
}
|
|
1514
1534
|
}
|
|
1515
|
-
const
|
|
1535
|
+
const d = {
|
|
1516
1536
|
passed: t.length === 0,
|
|
1517
1537
|
sampledAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1518
1538
|
secureContext: i,
|
|
1519
1539
|
mediaPermission: n,
|
|
1520
1540
|
microphoneCount: o,
|
|
1521
1541
|
cameraCount: r,
|
|
1522
|
-
probeLatencyMs:
|
|
1542
|
+
probeLatencyMs: l,
|
|
1523
1543
|
effectiveConnectionType: ae()?.effectiveType ?? null,
|
|
1524
1544
|
issues: t
|
|
1525
1545
|
};
|
|
1526
|
-
return this.emit("preflight",
|
|
1546
|
+
return this.emit("preflight", d), this.options.autoReportStats && await this.reportQuality({
|
|
1527
1547
|
sampleType: "preflight",
|
|
1528
|
-
sampledAt:
|
|
1529
|
-
connectionState:
|
|
1530
|
-
rttMs:
|
|
1531
|
-
effectiveConnectionType:
|
|
1548
|
+
sampledAt: d.sampledAt,
|
|
1549
|
+
connectionState: d.passed ? "ready" : "failed",
|
|
1550
|
+
rttMs: d.probeLatencyMs,
|
|
1551
|
+
effectiveConnectionType: d.effectiveConnectionType
|
|
1532
1552
|
}).catch(() => {
|
|
1533
|
-
}),
|
|
1553
|
+
}), d;
|
|
1534
1554
|
}
|
|
1535
1555
|
async collectQualitySample(e = "runtime") {
|
|
1536
1556
|
const t = this.options.statsProvider ? await this.options.statsProvider() : {}, i = this.peerConnection ? await Pe(this.peerConnection, this.previousCounters) : null;
|
|
@@ -1649,14 +1669,14 @@ class Ce extends EventTarget {
|
|
|
1649
1669
|
this.dispatchEvent(new CustomEvent(e, { detail: i })), this.options.onEvent?.(i);
|
|
1650
1670
|
}
|
|
1651
1671
|
}
|
|
1652
|
-
async function
|
|
1672
|
+
async function Ue(a) {
|
|
1653
1673
|
return new Ce(a).initialize();
|
|
1654
1674
|
}
|
|
1655
|
-
let
|
|
1675
|
+
let U = null;
|
|
1656
1676
|
function de() {
|
|
1657
|
-
if (
|
|
1677
|
+
if (U) return U;
|
|
1658
1678
|
const a = globalThis, e = a.AudioContext ?? a.webkitAudioContext;
|
|
1659
|
-
return e ? (
|
|
1679
|
+
return e ? (U = new e(), U) : null;
|
|
1660
1680
|
}
|
|
1661
1681
|
async function Re() {
|
|
1662
1682
|
const a = de();
|
|
@@ -1710,7 +1730,7 @@ class Ie {
|
|
|
1710
1730
|
}
|
|
1711
1731
|
}
|
|
1712
1732
|
}
|
|
1713
|
-
class
|
|
1733
|
+
class Me extends EventTarget {
|
|
1714
1734
|
options;
|
|
1715
1735
|
socket = null;
|
|
1716
1736
|
stopped = !1;
|
|
@@ -1830,7 +1850,7 @@ class Ee extends EventTarget {
|
|
|
1830
1850
|
/** Applies a call state received through the application's own push or signaling channel. */
|
|
1831
1851
|
handleCallUpdate(e) {
|
|
1832
1852
|
const t = e.id === this.currentCall?.id || e.id === this.outgoingCallId;
|
|
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,
|
|
1853
|
+
t && e.status === "accepted" ? (this.activeCallIds.add(e.id), this.callAudio.stop(), this.dismiss(e.id)) : t && ["rejected", "busy", "cancelled", "ended", "failed", "missed"].includes(e.status) && this.showTerminal(e.id, Ee(e.status)), ["rejected", "busy", "cancelled", "ended", "failed", "missed"].includes(e.status) && this.releaseMedia(e.id), this.emit("callupdated", e);
|
|
1834
1854
|
}
|
|
1835
1855
|
disconnect() {
|
|
1836
1856
|
this.stopped = !0, this.reconnectTimer && clearTimeout(this.reconnectTimer), this.heartbeatTimer && clearInterval(this.heartbeatTimer), this.tokenRefreshTimer && clearTimeout(this.tokenRefreshTimer), this.reconnectTimer = null, this.heartbeatTimer = null, this.tokenRefreshTimer = null, this.socket?.close(1e3, "Client disconnected"), this.socket = null, this.callAudio.stop(), this.dismiss();
|
|
@@ -1912,14 +1932,14 @@ class Ee extends EventTarget {
|
|
|
1912
1932
|
if (!n) throw new Error("apiBaseUrl is required for call actions");
|
|
1913
1933
|
const s = new URL(n, globalThis.location?.href), o = await this.resolveToken("initial"), r = new Headers(t.headers);
|
|
1914
1934
|
r.set("authorization", `Bearer ${o}`), r.set("content-type", "application/json");
|
|
1915
|
-
const c = await fetch(new URL(e, s.origin), { ...t, headers: r }),
|
|
1935
|
+
const c = await fetch(new URL(e, s.origin), { ...t, headers: r }), l = await c.json().catch(() => ({}));
|
|
1916
1936
|
if (c.status === 401 && this.options.tokenProvider && !i)
|
|
1917
1937
|
return await this.resolveToken("unauthorized", !0), this.request(e, t, !0);
|
|
1918
1938
|
if (!c.ok) {
|
|
1919
|
-
const
|
|
1920
|
-
throw new Error(typeof
|
|
1939
|
+
const d = l.error;
|
|
1940
|
+
throw new Error(typeof d == "string" ? d : d?.message || `RTC API failed with HTTP ${c.status}`);
|
|
1921
1941
|
}
|
|
1922
|
-
return
|
|
1942
|
+
return l;
|
|
1923
1943
|
}
|
|
1924
1944
|
renderIncoming(e) {
|
|
1925
1945
|
if (!this.mount) return;
|
|
@@ -2035,8 +2055,8 @@ class Ee extends EventTarget {
|
|
|
2035
2055
|
this.dispatchEvent(new CustomEvent(e, { detail: i })), this.options.onEvent?.(i);
|
|
2036
2056
|
}
|
|
2037
2057
|
}
|
|
2038
|
-
function
|
|
2039
|
-
const e = new
|
|
2058
|
+
function Qe(a) {
|
|
2059
|
+
const e = new Me(a);
|
|
2040
2060
|
return a.autoConnect !== !1 && a.signalingUrl && e.connect(), e;
|
|
2041
2061
|
}
|
|
2042
2062
|
function te(a) {
|
|
@@ -2055,7 +2075,7 @@ function ie(a, e, t, i) {
|
|
|
2055
2075
|
fontWeight: "700"
|
|
2056
2076
|
}), n;
|
|
2057
2077
|
}
|
|
2058
|
-
function
|
|
2078
|
+
function Ee(a) {
|
|
2059
2079
|
return {
|
|
2060
2080
|
rejected: "Call declined",
|
|
2061
2081
|
busy: "Line busy",
|
|
@@ -2074,46 +2094,46 @@ function ne(a) {
|
|
|
2074
2094
|
}
|
|
2075
2095
|
async function Pe(a, e) {
|
|
2076
2096
|
const t = await a.getStats(), i = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Map();
|
|
2077
|
-
let s = 0, o = 0, r = 0, c = 0,
|
|
2078
|
-
t.forEach((
|
|
2079
|
-
n.set(
|
|
2080
|
-
}), t.forEach((
|
|
2081
|
-
if (
|
|
2082
|
-
|
|
2083
|
-
const
|
|
2084
|
-
|
|
2097
|
+
let s = 0, o = 0, r = 0, c = 0, l = 0, d = 0, u = 0, S = 0, f = 0, v = 0, I = 0, y = 0, k = 0, p = null, C = null, P = null, L = null, M = null, A = null;
|
|
2098
|
+
t.forEach((g) => {
|
|
2099
|
+
n.set(g.id, g), g.type === "codec" && i.set(g.id, String(g.mimeType ?? ""));
|
|
2100
|
+
}), t.forEach((g) => {
|
|
2101
|
+
if (g.type === "inbound-rtp" && !g.isRemote && (s += Number(g.bytesReceived ?? 0), r += Math.max(0, Number(g.packetsLost ?? 0)), c += Math.max(0, Number(g.packetsReceived ?? 0)), Number.isFinite(g.jitter) && (l += Number(g.jitter), d += 1), u = Math.max(u, Number(g.framesPerSecond ?? 0)), S = Math.max(S, Number(g.frameWidth ?? 0)), f = Math.max(f, Number(g.frameHeight ?? 0)), v += Number(g.framesDropped ?? 0), I += Number(g.freezeCount ?? 0), y += Number(g.totalFreezesDuration ?? 0), k += Number(g.concealedSamples ?? 0), M ||= i.get(String(g.codecId ?? "")) ?? null), g.type === "outbound-rtp" && !g.isRemote && (o += Number(g.bytesSent ?? 0), A ||= g.qualityLimitationReason ? String(g.qualityLimitationReason) : null, M ||= i.get(String(g.codecId ?? "")) ?? null), g.type === "candidate-pair" && (g.selected || g.nominated) && g.state === "succeeded") {
|
|
2102
|
+
p = Number.isFinite(g.currentRoundTripTime) ? Number(g.currentRoundTripTime) : p, C = Number.isFinite(g.availableOutgoingBitrate) ? Number(g.availableOutgoingBitrate) : C;
|
|
2103
|
+
const q = n.get(g.localCandidateId);
|
|
2104
|
+
P = q?.candidateType ? String(q.candidateType) : P, L = q?.protocol ? String(q.protocol) : L;
|
|
2085
2105
|
}
|
|
2086
2106
|
});
|
|
2087
|
-
const
|
|
2107
|
+
const w = Date.now(), E = e ? Math.max(1e-3, (w - e.timestamp) / 1e3) : 0, m = e ? Math.max(0, Math.round((s - e.inboundBytes) * 8 / E)) : null, h = e ? Math.max(0, Math.round((o - e.outboundBytes) * 8 / E)) : null, T = r + c, R = e ? Math.max(0, r - e.packetsLost) : r, b = e ? Math.max(0, c - e.packetsReceived) : c, O = R + b;
|
|
2088
2108
|
return {
|
|
2089
|
-
counters: { timestamp:
|
|
2109
|
+
counters: { timestamp: w, inboundBytes: s, outboundBytes: o, packetsLost: r, packetsReceived: c },
|
|
2090
2110
|
sample: {
|
|
2091
|
-
rttMs:
|
|
2092
|
-
jitterMs:
|
|
2093
|
-
packetLossPct:
|
|
2094
|
-
packetLossCumulativePct:
|
|
2111
|
+
rttMs: p === null ? null : p * 1e3,
|
|
2112
|
+
jitterMs: d ? l / d * 1e3 : null,
|
|
2113
|
+
packetLossPct: O ? R / O * 100 : null,
|
|
2114
|
+
packetLossCumulativePct: T ? r / T * 100 : null,
|
|
2095
2115
|
packetsLost: r,
|
|
2096
2116
|
packetsReceived: c,
|
|
2097
|
-
inboundBitrateBps:
|
|
2098
|
-
outboundBitrateBps:
|
|
2099
|
-
availableOutgoingBitrateBps:
|
|
2117
|
+
inboundBitrateBps: m,
|
|
2118
|
+
outboundBitrateBps: h,
|
|
2119
|
+
availableOutgoingBitrateBps: C,
|
|
2100
2120
|
framesPerSecond: u || null,
|
|
2101
|
-
frameWidth:
|
|
2121
|
+
frameWidth: S || null,
|
|
2102
2122
|
frameHeight: f || null,
|
|
2103
|
-
framesDropped:
|
|
2104
|
-
freezeCount:
|
|
2105
|
-
freezeDurationMs: Math.round(
|
|
2106
|
-
concealedSamples:
|
|
2107
|
-
codec:
|
|
2108
|
-
transportProtocol:
|
|
2109
|
-
candidateType:
|
|
2110
|
-
qualityLimitationReason:
|
|
2123
|
+
framesDropped: v,
|
|
2124
|
+
freezeCount: I,
|
|
2125
|
+
freezeDurationMs: Math.round(y * 1e3),
|
|
2126
|
+
concealedSamples: k,
|
|
2127
|
+
codec: M,
|
|
2128
|
+
transportProtocol: L,
|
|
2129
|
+
candidateType: P,
|
|
2130
|
+
qualityLimitationReason: A
|
|
2111
2131
|
}
|
|
2112
2132
|
};
|
|
2113
2133
|
}
|
|
2114
2134
|
function Ae(a) {
|
|
2115
2135
|
let e = a.mediaScore == null ? 5 : Math.round(se(Number(a.mediaScore), 0, 5));
|
|
2116
|
-
const t =
|
|
2136
|
+
const t = H(a.packetLossPct) ?? 0, i = H(a.rttMs) ?? 0, n = H(a.jitterMs) ?? 0, s = String(a.connectionState ?? "unknown").toLowerCase();
|
|
2117
2137
|
["failed", "closed", "disconnected"].includes(s) ? e = 0 : ["reconnecting", "checking"].includes(s) && (e = Math.min(e, 2)), t >= 15 ? e = Math.min(e, 0) : t >= 8 ? e = Math.min(e, 1) : t >= 3 ? e = Math.min(e, 2) : t >= 1 && (e = Math.min(e, 3)), i >= 1200 ? e = Math.min(e, 0) : i >= 800 ? e = Math.min(e, 1) : i >= 400 ? e = Math.min(e, 2) : i >= 250 && (e = Math.min(e, 3)), n >= 150 ? e = Math.min(e, 1) : n >= 80 ? e = Math.min(e, 2) : n >= 40 && (e = Math.min(e, 3));
|
|
2118
2138
|
const o = Number(se(4.5 - t * 0.09 - i * 11e-4 - n * 6e-3, 1, 4.5).toFixed(2));
|
|
2119
2139
|
return { networkScore: e, estimatedMos: o };
|
|
@@ -2125,7 +2145,7 @@ function Le() {
|
|
|
2125
2145
|
const a = Number(navigator.deviceMemory);
|
|
2126
2146
|
return Number.isFinite(a) ? a : null;
|
|
2127
2147
|
}
|
|
2128
|
-
function
|
|
2148
|
+
function H(a) {
|
|
2129
2149
|
const e = Number(a);
|
|
2130
2150
|
return Number.isFinite(e) ? e : null;
|
|
2131
2151
|
}
|
|
@@ -2138,20 +2158,20 @@ function V(a) {
|
|
|
2138
2158
|
function re(a, e) {
|
|
2139
2159
|
return a instanceof Error ? a : new Error(e);
|
|
2140
2160
|
}
|
|
2141
|
-
const De = "1.3.
|
|
2161
|
+
const De = "1.3.17";
|
|
2142
2162
|
export {
|
|
2143
2163
|
Z as AVATAR_TUNING_DEFAULTS,
|
|
2144
2164
|
Se as BACKGROUND_PRESETS,
|
|
2145
2165
|
ke as OVERLAY_EFFECTS,
|
|
2146
2166
|
Ce as RtcClient,
|
|
2147
|
-
|
|
2167
|
+
Me as RtcIncomingClient,
|
|
2148
2168
|
qe as SPEAKER_NOISE_FLOOR,
|
|
2149
2169
|
Oe as SPEAKER_TAKEOVER_RATIO,
|
|
2150
|
-
|
|
2170
|
+
xe as createAvatarPipeline,
|
|
2151
2171
|
je as createCallRecorder,
|
|
2152
2172
|
Ne as createPictureInPictureController,
|
|
2153
|
-
|
|
2154
|
-
|
|
2173
|
+
Ue as createRtcClient,
|
|
2174
|
+
Qe as createRtcIncomingClient,
|
|
2155
2175
|
Ve as createVideoEffectsPipeline,
|
|
2156
2176
|
be as drawAvatar,
|
|
2157
2177
|
we as headPoseFromMatrix,
|