livekit-client 2.18.3 → 2.18.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/livekit-client.esm.mjs +703 -334
- package/dist/livekit-client.esm.mjs.map +1 -1
- package/dist/livekit-client.umd.js +1 -1
- package/dist/livekit-client.umd.js.map +1 -1
- package/dist/src/api/SignalClient.d.ts.map +1 -1
- package/dist/src/room/PCTransport.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +12 -4
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/RegionUrlProvider.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +3 -0
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/data-track/incoming/IncomingDataTrackManager.d.ts +4 -0
- package/dist/src/room/data-track/incoming/IncomingDataTrackManager.d.ts.map +1 -1
- package/dist/src/room/events.d.ts +3 -1
- package/dist/src/room/events.d.ts.map +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts +8 -0
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/RemoteParticipant.d.ts +4 -3
- package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
- package/dist/src/room/track/LocalTrack.d.ts +7 -0
- package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
- package/dist/src/room/track/LocalVideoTrack.d.ts +12 -1
- package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
- package/dist/src/room/types.d.ts +1 -1
- package/dist/src/room/types.d.ts.map +1 -1
- package/dist/ts4.2/room/RTCEngine.d.ts +12 -4
- package/dist/ts4.2/room/Room.d.ts +3 -0
- package/dist/ts4.2/room/data-track/incoming/IncomingDataTrackManager.d.ts +4 -0
- package/dist/ts4.2/room/events.d.ts +3 -1
- package/dist/ts4.2/room/participant/LocalParticipant.d.ts +8 -0
- package/dist/ts4.2/room/participant/RemoteParticipant.d.ts +4 -3
- package/dist/ts4.2/room/track/LocalTrack.d.ts +7 -0
- package/dist/ts4.2/room/track/LocalVideoTrack.d.ts +12 -1
- package/dist/ts4.2/room/types.d.ts +1 -1
- package/package.json +3 -3
- package/src/api/SignalClient.ts +4 -0
- package/src/room/PCTransport.ts +10 -8
- package/src/room/RTCEngine.ts +59 -28
- package/src/room/RegionUrlProvider.ts +7 -0
- package/src/room/Room.ts +93 -23
- package/src/room/data-track/incoming/IncomingDataTrackManager.test.ts +331 -16
- package/src/room/data-track/incoming/IncomingDataTrackManager.ts +92 -41
- package/src/room/events.ts +2 -0
- package/src/room/participant/LocalParticipant.ts +70 -5
- package/src/room/participant/RemoteParticipant.ts +14 -2
- package/src/room/token-source/TokenSource.test.ts +337 -0
- package/src/room/token-source/test-tokens.ts +28 -0
- package/src/room/token-source/utils.test.ts +12 -20
- package/src/room/track/LocalTrack.ts +15 -1
- package/src/room/track/LocalVideoTrack.ts +126 -2
- package/src/room/track/RemoteVideoTrack.ts +8 -2
- package/src/room/types.ts +2 -1
- package/src/utils/deferrable-map.ts +2 -2
|
@@ -8704,6 +8704,11 @@ function wrapPeerConnectionEvent(window, eventNameToWrap, wrapper) {
|
|
|
8704
8704
|
if (!window.RTCPeerConnection) {
|
|
8705
8705
|
return;
|
|
8706
8706
|
}
|
|
8707
|
+
const addEventListener = Object.getOwnPropertyDescriptor(EventTarget.prototype, 'addEventListener');
|
|
8708
|
+
if (!addEventListener.writable) {
|
|
8709
|
+
log$4('Unable to polyfill events');
|
|
8710
|
+
return;
|
|
8711
|
+
}
|
|
8707
8712
|
const proto = window.RTCPeerConnection.prototype;
|
|
8708
8713
|
const nativeAddEventListener = proto.addEventListener;
|
|
8709
8714
|
proto.addEventListener = function (nativeEventName, cb) {
|
|
@@ -8846,7 +8851,7 @@ function detectBrowser(window) {
|
|
|
8846
8851
|
// Chrome 74 removed webkitGetUserMedia on http as well so we need the
|
|
8847
8852
|
// more complicated fallback to webkitRTCPeerConnection.
|
|
8848
8853
|
result.browser = 'chrome';
|
|
8849
|
-
result.version = parseInt(extractVersion(navigator.userAgent, /Chrom(e|ium)\/(\d+)\./, 2));
|
|
8854
|
+
result.version = parseInt(extractVersion(navigator.userAgent, /Chrom(e|ium)\/(\d+)\./, 2)) || null;
|
|
8850
8855
|
} else if (window.RTCPeerConnection && navigator.userAgent.match(/AppleWebKit\/(\d+)\./)) {
|
|
8851
8856
|
// Safari.
|
|
8852
8857
|
result.browser = 'safari';
|
|
@@ -9120,7 +9125,11 @@ function shimGetUserMedia$2(window, browserDetails) {
|
|
|
9120
9125
|
function shimMediaStream(window) {
|
|
9121
9126
|
window.MediaStream = window.MediaStream || window.webkitMediaStream;
|
|
9122
9127
|
}
|
|
9123
|
-
function shimOnTrack$1(window) {
|
|
9128
|
+
function shimOnTrack$1(window, browserDetails) {
|
|
9129
|
+
if (browserDetails.version > 102) {
|
|
9130
|
+
// Unified plan is supported so no need to do anything.
|
|
9131
|
+
return;
|
|
9132
|
+
}
|
|
9124
9133
|
if (typeof window === 'object' && window.RTCPeerConnection && !('ontrack' in window.RTCPeerConnection.prototype)) {
|
|
9125
9134
|
Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {
|
|
9126
9135
|
get() {
|
|
@@ -9283,7 +9292,10 @@ function shimGetSendersWithDtmf(window) {
|
|
|
9283
9292
|
});
|
|
9284
9293
|
}
|
|
9285
9294
|
}
|
|
9286
|
-
function shimSenderReceiverGetStats(window) {
|
|
9295
|
+
function shimSenderReceiverGetStats(window, browserDetails) {
|
|
9296
|
+
if (browserDetails.version >= 67) {
|
|
9297
|
+
return;
|
|
9298
|
+
}
|
|
9287
9299
|
if (!(typeof window === 'object' && window.RTCPeerConnection && window.RTCRtpSender && window.RTCRtpReceiver)) {
|
|
9288
9300
|
return;
|
|
9289
9301
|
}
|
|
@@ -9650,6 +9662,10 @@ function shimPeerConnection$1(window, browserDetails) {
|
|
|
9650
9662
|
|
|
9651
9663
|
// Attempt to fix ONN in plan-b mode.
|
|
9652
9664
|
function fixNegotiationNeeded(window, browserDetails) {
|
|
9665
|
+
if (browserDetails.version > 102) {
|
|
9666
|
+
// Plan-B is no longer supported.
|
|
9667
|
+
return;
|
|
9668
|
+
}
|
|
9653
9669
|
wrapPeerConnectionEvent(window, 'negotiationneeded', e => {
|
|
9654
9670
|
const pc = e.target;
|
|
9655
9671
|
if (browserDetails.version < 72 || pc.getConfiguration && pc.getConfiguration().sdpSemantics === 'plan-b') {
|
|
@@ -9785,6 +9801,15 @@ function shimPeerConnection(window, browserDetails) {
|
|
|
9785
9801
|
window.RTCPeerConnection.prototype[method] = methodObj[method];
|
|
9786
9802
|
});
|
|
9787
9803
|
}
|
|
9804
|
+
}
|
|
9805
|
+
function shimGetStats(window, browserDetails) {
|
|
9806
|
+
if (typeof window !== 'object' || !(window.RTCPeerConnection || window.mozRTCPeerConnection)) {
|
|
9807
|
+
return; // probably media.peerconnection.enabled=false in about:config
|
|
9808
|
+
}
|
|
9809
|
+
if (browserDetails.version >= 151) {
|
|
9810
|
+
// https://bugzilla.mozilla.org/show_bug.cgi?id=1056433
|
|
9811
|
+
return;
|
|
9812
|
+
}
|
|
9788
9813
|
const modernStatsTypes = {
|
|
9789
9814
|
inboundrtp: 'inbound-rtp',
|
|
9790
9815
|
outboundrtp: 'outbound-rtp',
|
|
@@ -9795,6 +9820,10 @@ function shimPeerConnection(window, browserDetails) {
|
|
|
9795
9820
|
const nativeGetStats = window.RTCPeerConnection.prototype.getStats;
|
|
9796
9821
|
window.RTCPeerConnection.prototype.getStats = function getStats() {
|
|
9797
9822
|
const [selector, onSucc, onErr] = arguments;
|
|
9823
|
+
if (this.signalingState === 'closed') {
|
|
9824
|
+
// No longer required in FF151+
|
|
9825
|
+
return Promise.resolve(new Map());
|
|
9826
|
+
}
|
|
9798
9827
|
return nativeGetStats.apply(this, [selector || null]).then(stats => {
|
|
9799
9828
|
if (browserDetails.version < 53 && !onSucc) {
|
|
9800
9829
|
// Shim only promise getStats with spec-hyphens in type names
|
|
@@ -10009,7 +10038,7 @@ function shimCreateAnswer(window) {
|
|
|
10009
10038
|
}
|
|
10010
10039
|
return origCreateAnswer.apply(this, arguments);
|
|
10011
10040
|
};
|
|
10012
|
-
}var firefoxShim=/*#__PURE__*/Object.freeze({__proto__:null,shimAddTransceiver:shimAddTransceiver,shimCreateAnswer:shimCreateAnswer,shimCreateOffer:shimCreateOffer,shimGetDisplayMedia:shimGetDisplayMedia,shimGetParameters:shimGetParameters,shimGetUserMedia:shimGetUserMedia$1,shimOnTrack:shimOnTrack,shimPeerConnection:shimPeerConnection,shimRTCDataChannel:shimRTCDataChannel,shimReceiverGetStats:shimReceiverGetStats,shimRemoveStream:shimRemoveStream,shimSenderGetStats:shimSenderGetStats});/*
|
|
10041
|
+
}var firefoxShim=/*#__PURE__*/Object.freeze({__proto__:null,shimAddTransceiver:shimAddTransceiver,shimCreateAnswer:shimCreateAnswer,shimCreateOffer:shimCreateOffer,shimGetDisplayMedia:shimGetDisplayMedia,shimGetParameters:shimGetParameters,shimGetStats:shimGetStats,shimGetUserMedia:shimGetUserMedia$1,shimOnTrack:shimOnTrack,shimPeerConnection:shimPeerConnection,shimRTCDataChannel:shimRTCDataChannel,shimReceiverGetStats:shimReceiverGetStats,shimRemoveStream:shimRemoveStream,shimSenderGetStats:shimSenderGetStats});/*
|
|
10013
10042
|
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
|
|
10014
10043
|
*
|
|
10015
10044
|
* Use of this source code is governed by a BSD-style license
|
|
@@ -10444,7 +10473,7 @@ function requireSdp() {
|
|
|
10444
10473
|
const type = candidate.type;
|
|
10445
10474
|
sdp.push('typ');
|
|
10446
10475
|
sdp.push(type);
|
|
10447
|
-
if (type !== 'host' && candidate.relatedAddress && candidate.relatedPort) {
|
|
10476
|
+
if (type !== 'host' && candidate.relatedAddress && candidate.relatedPort !== undefined) {
|
|
10448
10477
|
sdp.push('raddr');
|
|
10449
10478
|
sdp.push(candidate.relatedAddress);
|
|
10450
10479
|
sdp.push('rport');
|
|
@@ -10517,6 +10546,8 @@ function requireSdp() {
|
|
|
10517
10546
|
// Parses a fmtp line, returns dictionary. Sample input:
|
|
10518
10547
|
// a=fmtp:96 vbr=on;cng=on
|
|
10519
10548
|
// Also deals with vbr=on; cng=on
|
|
10549
|
+
// Non-key-value such as telephone-events `0-15` get parsed as
|
|
10550
|
+
// {`0-15`:undefined}
|
|
10520
10551
|
SDPUtils.parseFmtp = function (line) {
|
|
10521
10552
|
const parsed = {};
|
|
10522
10553
|
let kv;
|
|
@@ -11290,15 +11321,21 @@ function shimMaxMessageSize(window, browserDetails) {
|
|
|
11290
11321
|
return origSetRemoteDescription.apply(this, arguments);
|
|
11291
11322
|
};
|
|
11292
11323
|
}
|
|
11293
|
-
function shimSendThrowTypeError(window) {
|
|
11324
|
+
function shimSendThrowTypeError(window, browserDetails) {
|
|
11294
11325
|
if (!(window.RTCPeerConnection && 'createDataChannel' in window.RTCPeerConnection.prototype)) {
|
|
11295
11326
|
return;
|
|
11296
11327
|
}
|
|
11328
|
+
if (browserDetails.browser === 'chrome' && browserDetails.version > 149) {
|
|
11329
|
+
// Fixed by https://issues.chromium.org/issues/490588131
|
|
11330
|
+
return;
|
|
11331
|
+
}
|
|
11297
11332
|
|
|
11298
11333
|
// Note: Although Firefox >= 57 has a native implementation, the maximum
|
|
11299
11334
|
// message size can be reset for all data channels at a later stage.
|
|
11300
11335
|
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831
|
|
11301
|
-
|
|
11336
|
+
if (browserDetails.browser === 'firefox' && browserDetails.version > 60) {
|
|
11337
|
+
return;
|
|
11338
|
+
}
|
|
11302
11339
|
function wrapDcSend(dc, pc) {
|
|
11303
11340
|
const origDataChannelSend = dc.send;
|
|
11304
11341
|
dc.send = function send() {
|
|
@@ -11536,16 +11573,16 @@ function adapterFactory() {
|
|
|
11536
11573
|
shimGetUserMedia$2(window, browserDetails);
|
|
11537
11574
|
shimMediaStream(window);
|
|
11538
11575
|
shimPeerConnection$1(window, browserDetails);
|
|
11539
|
-
shimOnTrack$1(window);
|
|
11576
|
+
shimOnTrack$1(window, browserDetails);
|
|
11540
11577
|
shimAddTrackRemoveTrack(window, browserDetails);
|
|
11541
11578
|
shimGetSendersWithDtmf(window);
|
|
11542
|
-
shimSenderReceiverGetStats(window);
|
|
11579
|
+
shimSenderReceiverGetStats(window, browserDetails);
|
|
11543
11580
|
fixNegotiationNeeded(window, browserDetails);
|
|
11544
11581
|
shimRTCIceCandidate(window);
|
|
11545
11582
|
shimRTCIceCandidateRelayProtocol(window);
|
|
11546
11583
|
shimConnectionState(window);
|
|
11547
11584
|
shimMaxMessageSize(window, browserDetails);
|
|
11548
|
-
shimSendThrowTypeError(window);
|
|
11585
|
+
shimSendThrowTypeError(window, browserDetails);
|
|
11549
11586
|
removeExtmapAllowMixed(window, browserDetails);
|
|
11550
11587
|
break;
|
|
11551
11588
|
case 'firefox':
|
|
@@ -11562,6 +11599,7 @@ function adapterFactory() {
|
|
|
11562
11599
|
shimParameterlessSetLocalDescription(window);
|
|
11563
11600
|
shimGetUserMedia$1(window, browserDetails);
|
|
11564
11601
|
shimPeerConnection(window, browserDetails);
|
|
11602
|
+
shimGetStats(window, browserDetails);
|
|
11565
11603
|
shimOnTrack(window);
|
|
11566
11604
|
shimRemoveStream(window);
|
|
11567
11605
|
shimSenderGetStats(window);
|
|
@@ -11574,7 +11612,7 @@ function adapterFactory() {
|
|
|
11574
11612
|
shimRTCIceCandidate(window);
|
|
11575
11613
|
shimConnectionState(window);
|
|
11576
11614
|
shimMaxMessageSize(window, browserDetails);
|
|
11577
|
-
shimSendThrowTypeError(window);
|
|
11615
|
+
shimSendThrowTypeError(window, browserDetails);
|
|
11578
11616
|
break;
|
|
11579
11617
|
case 'safari':
|
|
11580
11618
|
if (!safariShim || !options.shimSafari) {
|
|
@@ -11599,7 +11637,7 @@ function adapterFactory() {
|
|
|
11599
11637
|
shimRTCIceCandidate(window);
|
|
11600
11638
|
shimRTCIceCandidateRelayProtocol(window);
|
|
11601
11639
|
shimMaxMessageSize(window, browserDetails);
|
|
11602
|
-
shimSendThrowTypeError(window);
|
|
11640
|
+
shimSendThrowTypeError(window, browserDetails);
|
|
11603
11641
|
removeExtmapAllowMixed(window, browserDetails);
|
|
11604
11642
|
break;
|
|
11605
11643
|
default:
|
|
@@ -11706,7 +11744,7 @@ function getMatch(exp, ua) {
|
|
|
11706
11744
|
}
|
|
11707
11745
|
function getOSVersion(ua) {
|
|
11708
11746
|
return ua.includes('mac os') ? getMatch(/\(.+?(\d+_\d+(:?_\d+)?)/, ua, 1).replace(/_/g, '.') : undefined;
|
|
11709
|
-
}var version$1 = "2.18.
|
|
11747
|
+
}var version$1 = "2.18.5";const version = version$1;
|
|
11710
11748
|
const protocolVersion = 16;/** Base error that all LiveKit specific custom errors inherit from. */
|
|
11711
11749
|
class LivekitError extends Error {
|
|
11712
11750
|
constructor(code, message, options) {
|
|
@@ -12460,6 +12498,8 @@ var EngineEvent;
|
|
|
12460
12498
|
EngineEvent["DataTrackSubscriberHandles"] = "dataTrackSubscriberHandles";
|
|
12461
12499
|
EngineEvent["DataTrackPacketReceived"] = "dataTrackPacketReceived";
|
|
12462
12500
|
EngineEvent["Joined"] = "joined";
|
|
12501
|
+
EngineEvent["TokenRefreshed"] = "tokenRefreshed";
|
|
12502
|
+
EngineEvent["ServerRegionsReported"] = "serverRegionsReported";
|
|
12463
12503
|
})(EngineEvent || (EngineEvent = {}));
|
|
12464
12504
|
var TrackEvent;
|
|
12465
12505
|
(function (TrackEvent) {
|
|
@@ -16015,6 +16055,10 @@ class SignalClient {
|
|
|
16015
16055
|
const resp = yield fetch(validateUrl);
|
|
16016
16056
|
switch (resp.status) {
|
|
16017
16057
|
case 404:
|
|
16058
|
+
const errorMsg = yield resp.text();
|
|
16059
|
+
if (errorMsg.includes('requested room does not exist')) {
|
|
16060
|
+
return ConnectionError.notAllowed(errorMsg, resp.status);
|
|
16061
|
+
}
|
|
16018
16062
|
return ConnectionError.serviceNotFound('v1 RTC path not found. Consider upgrading your LiveKit server version', 'v0-rtc');
|
|
16019
16063
|
case 401:
|
|
16020
16064
|
case 403:
|
|
@@ -17120,6 +17164,7 @@ class PCTransport extends eventsExports.EventEmitter {
|
|
|
17120
17164
|
if (!this._pc) {
|
|
17121
17165
|
return;
|
|
17122
17166
|
}
|
|
17167
|
+
this.pendingInitialOffer = undefined;
|
|
17123
17168
|
this._pc.close();
|
|
17124
17169
|
this._pc.onconnectionstatechange = null;
|
|
17125
17170
|
this._pc.oniceconnectionstatechange = null;
|
|
@@ -17207,7 +17252,7 @@ class PCTransport extends eventsExports.EventEmitter {
|
|
|
17207
17252
|
this.remoteStereoMids = stereoMids;
|
|
17208
17253
|
this.remoteNackMids = nackMids;
|
|
17209
17254
|
} else if (sd.type === 'answer') {
|
|
17210
|
-
if (this.pendingInitialOffer) {
|
|
17255
|
+
if (this.pendingInitialOffer && this._pc) {
|
|
17211
17256
|
const initialOffer = this.pendingInitialOffer;
|
|
17212
17257
|
this.pendingInitialOffer = undefined;
|
|
17213
17258
|
const sdpParsed = libExports.parse((_a = initialOffer.sdp) !== null && _a !== void 0 ? _a : '');
|
|
@@ -17529,8 +17574,9 @@ class PCTransport extends eventsExports.EventEmitter {
|
|
|
17529
17574
|
}
|
|
17530
17575
|
setMungedSDP(sd, munged, remote) {
|
|
17531
17576
|
return __awaiter(this, void 0, void 0, function* () {
|
|
17577
|
+
var _a, _b;
|
|
17578
|
+
const originalSdp = sd.sdp;
|
|
17532
17579
|
if (munged) {
|
|
17533
|
-
const originalSdp = sd.sdp;
|
|
17534
17580
|
sd.sdp = munged;
|
|
17535
17581
|
try {
|
|
17536
17582
|
this.log.debug("setting munged ".concat(remote ? 'remote' : 'local', " description"), this.logContext);
|
|
@@ -17543,16 +17589,17 @@ class PCTransport extends eventsExports.EventEmitter {
|
|
|
17543
17589
|
} catch (e) {
|
|
17544
17590
|
this.log.warn("not able to set ".concat(sd.type, ", falling back to unmodified sdp"), Object.assign(Object.assign({}, this.logContext), {
|
|
17545
17591
|
error: e,
|
|
17546
|
-
|
|
17592
|
+
mungedSdp: munged,
|
|
17593
|
+
originalSdp
|
|
17547
17594
|
}));
|
|
17548
17595
|
sd.sdp = originalSdp;
|
|
17549
17596
|
}
|
|
17550
17597
|
}
|
|
17551
17598
|
try {
|
|
17552
17599
|
if (remote) {
|
|
17553
|
-
yield this.
|
|
17600
|
+
yield (_a = this._pc) === null || _a === void 0 ? void 0 : _a.setRemoteDescription(sd);
|
|
17554
17601
|
} else {
|
|
17555
|
-
yield this.
|
|
17602
|
+
yield (_b = this._pc) === null || _b === void 0 ? void 0 : _b.setLocalDescription(sd);
|
|
17556
17603
|
}
|
|
17557
17604
|
} catch (e) {
|
|
17558
17605
|
let msg = 'unknown error';
|
|
@@ -17565,6 +17612,9 @@ class PCTransport extends eventsExports.EventEmitter {
|
|
|
17565
17612
|
error: msg,
|
|
17566
17613
|
sdp: sd.sdp
|
|
17567
17614
|
};
|
|
17615
|
+
if (munged && munged !== originalSdp) {
|
|
17616
|
+
fields.mungedSdp = munged;
|
|
17617
|
+
}
|
|
17568
17618
|
if (!remote && this.pc.remoteDescription) {
|
|
17569
17619
|
fields.remoteSdp = this.pc.remoteDescription;
|
|
17570
17620
|
}
|
|
@@ -17588,9 +17638,6 @@ class PCTransport extends eventsExports.EventEmitter {
|
|
|
17588
17638
|
let maxID = 0;
|
|
17589
17639
|
sdp.media.forEach(m => {
|
|
17590
17640
|
var _a;
|
|
17591
|
-
if (m.type !== 'video') {
|
|
17592
|
-
return;
|
|
17593
|
-
}
|
|
17594
17641
|
(_a = m.ext) === null || _a === void 0 ? void 0 : _a.forEach(ext => {
|
|
17595
17642
|
if (ext.value > maxID) {
|
|
17596
17643
|
maxID = ext.value;
|
|
@@ -18057,192 +18104,6 @@ class PCTransportManager {
|
|
|
18057
18104
|
}();
|
|
18058
18105
|
});
|
|
18059
18106
|
}
|
|
18060
|
-
}const DEFAULT_MAX_AGE_MS = 5000;
|
|
18061
|
-
const STOP_REFETCH_DELAY_MS = 30000;
|
|
18062
|
-
class RegionUrlProvider {
|
|
18063
|
-
static fetchRegionSettings(serverUrl, token, signal) {
|
|
18064
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
18065
|
-
const unlock = yield RegionUrlProvider.fetchLock.lock();
|
|
18066
|
-
try {
|
|
18067
|
-
const regionSettingsResponse = yield fetch("".concat(getCloudConfigUrl(serverUrl), "/regions"), {
|
|
18068
|
-
headers: {
|
|
18069
|
-
authorization: "Bearer ".concat(token)
|
|
18070
|
-
},
|
|
18071
|
-
signal
|
|
18072
|
-
});
|
|
18073
|
-
if (regionSettingsResponse.ok) {
|
|
18074
|
-
const maxAge = extractMaxAgeFromRequestHeaders(regionSettingsResponse.headers);
|
|
18075
|
-
const maxAgeInMs = maxAge ? maxAge * 1000 : DEFAULT_MAX_AGE_MS;
|
|
18076
|
-
const regionSettings = yield regionSettingsResponse.json();
|
|
18077
|
-
return {
|
|
18078
|
-
regionSettings,
|
|
18079
|
-
updatedAtInMs: Date.now(),
|
|
18080
|
-
maxAgeInMs
|
|
18081
|
-
};
|
|
18082
|
-
} else {
|
|
18083
|
-
if (regionSettingsResponse.status === 401) {
|
|
18084
|
-
throw ConnectionError.notAllowed("Could not fetch region settings: ".concat(regionSettingsResponse.statusText), regionSettingsResponse.status);
|
|
18085
|
-
} else {
|
|
18086
|
-
throw ConnectionError.internal("Could not fetch region settings: ".concat(regionSettingsResponse.statusText));
|
|
18087
|
-
}
|
|
18088
|
-
}
|
|
18089
|
-
} catch (e) {
|
|
18090
|
-
if (e instanceof ConnectionError) {
|
|
18091
|
-
// rethrow connection errors
|
|
18092
|
-
throw e;
|
|
18093
|
-
} else if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
18094
|
-
throw ConnectionError.cancelled("Region fetching was aborted");
|
|
18095
|
-
} else {
|
|
18096
|
-
// wrap other errors as connection errors
|
|
18097
|
-
throw ConnectionError.serverUnreachable("Could not fetch region settings, ".concat(e instanceof Error ? "".concat(e.name, ": ").concat(e.message) : e));
|
|
18098
|
-
}
|
|
18099
|
-
} finally {
|
|
18100
|
-
unlock();
|
|
18101
|
-
}
|
|
18102
|
-
});
|
|
18103
|
-
}
|
|
18104
|
-
static scheduleRefetch(url, token, maxAgeInMs) {
|
|
18105
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
18106
|
-
const timeout = RegionUrlProvider.settingsTimeouts.get(url.hostname);
|
|
18107
|
-
clearTimeout(timeout);
|
|
18108
|
-
RegionUrlProvider.settingsTimeouts.set(url.hostname, setTimeout(() => __awaiter(this, void 0, void 0, function* () {
|
|
18109
|
-
try {
|
|
18110
|
-
const newSettings = yield RegionUrlProvider.fetchRegionSettings(url, token);
|
|
18111
|
-
RegionUrlProvider.updateCachedRegionSettings(url, token, newSettings);
|
|
18112
|
-
} catch (error) {
|
|
18113
|
-
if (error instanceof ConnectionError && error.reason === ConnectionErrorReason.NotAllowed) {
|
|
18114
|
-
livekitLogger.debug('token is not valid, cancelling auto region refresh');
|
|
18115
|
-
return;
|
|
18116
|
-
}
|
|
18117
|
-
livekitLogger.debug('auto refetching of region settings failed', {
|
|
18118
|
-
error
|
|
18119
|
-
});
|
|
18120
|
-
// continue retrying with the same max age
|
|
18121
|
-
RegionUrlProvider.scheduleRefetch(url, token, maxAgeInMs);
|
|
18122
|
-
}
|
|
18123
|
-
}), maxAgeInMs));
|
|
18124
|
-
});
|
|
18125
|
-
}
|
|
18126
|
-
static updateCachedRegionSettings(url, token, settings) {
|
|
18127
|
-
RegionUrlProvider.cache.set(url.hostname, settings);
|
|
18128
|
-
RegionUrlProvider.scheduleRefetch(url, token, settings.maxAgeInMs);
|
|
18129
|
-
}
|
|
18130
|
-
static stopRefetch(hostname) {
|
|
18131
|
-
const timeout = RegionUrlProvider.settingsTimeouts.get(hostname);
|
|
18132
|
-
if (timeout) {
|
|
18133
|
-
clearTimeout(timeout);
|
|
18134
|
-
RegionUrlProvider.settingsTimeouts.delete(hostname);
|
|
18135
|
-
}
|
|
18136
|
-
}
|
|
18137
|
-
static scheduleCleanup(hostname) {
|
|
18138
|
-
let tracker = RegionUrlProvider.connectionTrackers.get(hostname);
|
|
18139
|
-
if (!tracker) {
|
|
18140
|
-
return;
|
|
18141
|
-
}
|
|
18142
|
-
// Cancel any existing cleanup timeout
|
|
18143
|
-
if (tracker.cleanupTimeout) {
|
|
18144
|
-
clearTimeout(tracker.cleanupTimeout);
|
|
18145
|
-
}
|
|
18146
|
-
// Schedule cleanup to stop refetch after delay
|
|
18147
|
-
tracker.cleanupTimeout = setTimeout(() => {
|
|
18148
|
-
const currentTracker = RegionUrlProvider.connectionTrackers.get(hostname);
|
|
18149
|
-
if (currentTracker && currentTracker.connectionCount === 0) {
|
|
18150
|
-
livekitLogger.debug('stopping region refetch after disconnect delay', {
|
|
18151
|
-
hostname
|
|
18152
|
-
});
|
|
18153
|
-
RegionUrlProvider.stopRefetch(hostname);
|
|
18154
|
-
}
|
|
18155
|
-
if (currentTracker) {
|
|
18156
|
-
currentTracker.cleanupTimeout = undefined;
|
|
18157
|
-
}
|
|
18158
|
-
}, STOP_REFETCH_DELAY_MS);
|
|
18159
|
-
}
|
|
18160
|
-
static cancelCleanup(hostname) {
|
|
18161
|
-
const tracker = RegionUrlProvider.connectionTrackers.get(hostname);
|
|
18162
|
-
if (tracker === null || tracker === void 0 ? void 0 : tracker.cleanupTimeout) {
|
|
18163
|
-
clearTimeout(tracker.cleanupTimeout);
|
|
18164
|
-
tracker.cleanupTimeout = undefined;
|
|
18165
|
-
}
|
|
18166
|
-
}
|
|
18167
|
-
notifyConnected() {
|
|
18168
|
-
const hostname = this.serverUrl.hostname;
|
|
18169
|
-
let tracker = RegionUrlProvider.connectionTrackers.get(hostname);
|
|
18170
|
-
if (!tracker) {
|
|
18171
|
-
tracker = {
|
|
18172
|
-
connectionCount: 0
|
|
18173
|
-
};
|
|
18174
|
-
RegionUrlProvider.connectionTrackers.set(hostname, tracker);
|
|
18175
|
-
}
|
|
18176
|
-
tracker.connectionCount++;
|
|
18177
|
-
// Cancel any scheduled cleanup since we have an active connection
|
|
18178
|
-
RegionUrlProvider.cancelCleanup(hostname);
|
|
18179
|
-
}
|
|
18180
|
-
notifyDisconnected() {
|
|
18181
|
-
const hostname = this.serverUrl.hostname;
|
|
18182
|
-
const tracker = RegionUrlProvider.connectionTrackers.get(hostname);
|
|
18183
|
-
if (!tracker) {
|
|
18184
|
-
return;
|
|
18185
|
-
}
|
|
18186
|
-
tracker.connectionCount = Math.max(0, tracker.connectionCount - 1);
|
|
18187
|
-
// If no more connections, schedule cleanup
|
|
18188
|
-
if (tracker.connectionCount === 0) {
|
|
18189
|
-
RegionUrlProvider.scheduleCleanup(hostname);
|
|
18190
|
-
}
|
|
18191
|
-
}
|
|
18192
|
-
constructor(url, token) {
|
|
18193
|
-
this.attemptedRegions = [];
|
|
18194
|
-
this.serverUrl = new URL(url);
|
|
18195
|
-
this.token = token;
|
|
18196
|
-
}
|
|
18197
|
-
updateToken(token) {
|
|
18198
|
-
this.token = token;
|
|
18199
|
-
}
|
|
18200
|
-
isCloud() {
|
|
18201
|
-
return isCloud(this.serverUrl);
|
|
18202
|
-
}
|
|
18203
|
-
getServerUrl() {
|
|
18204
|
-
return this.serverUrl;
|
|
18205
|
-
}
|
|
18206
|
-
/** @internal */
|
|
18207
|
-
fetchRegionSettings(abortSignal) {
|
|
18208
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
18209
|
-
return RegionUrlProvider.fetchRegionSettings(this.serverUrl, this.token, abortSignal);
|
|
18210
|
-
});
|
|
18211
|
-
}
|
|
18212
|
-
getNextBestRegionUrl(abortSignal) {
|
|
18213
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
18214
|
-
if (!this.isCloud()) {
|
|
18215
|
-
throw Error('region availability is only supported for LiveKit Cloud domains');
|
|
18216
|
-
}
|
|
18217
|
-
let cachedSettings = RegionUrlProvider.cache.get(this.serverUrl.hostname);
|
|
18218
|
-
if (!cachedSettings || Date.now() - cachedSettings.updatedAtInMs > cachedSettings.maxAgeInMs) {
|
|
18219
|
-
cachedSettings = yield this.fetchRegionSettings(abortSignal);
|
|
18220
|
-
RegionUrlProvider.updateCachedRegionSettings(this.serverUrl, this.token, cachedSettings);
|
|
18221
|
-
}
|
|
18222
|
-
const regionsLeft = cachedSettings.regionSettings.regions.filter(region => !this.attemptedRegions.find(attempted => attempted.url === region.url));
|
|
18223
|
-
if (regionsLeft.length > 0) {
|
|
18224
|
-
const nextRegion = regionsLeft[0];
|
|
18225
|
-
this.attemptedRegions.push(nextRegion);
|
|
18226
|
-
livekitLogger.debug("next region: ".concat(nextRegion.region));
|
|
18227
|
-
return nextRegion.url;
|
|
18228
|
-
} else {
|
|
18229
|
-
return null;
|
|
18230
|
-
}
|
|
18231
|
-
});
|
|
18232
|
-
}
|
|
18233
|
-
resetAttempts() {
|
|
18234
|
-
this.attemptedRegions = [];
|
|
18235
|
-
}
|
|
18236
|
-
setServerReportedRegions(settings) {
|
|
18237
|
-
RegionUrlProvider.updateCachedRegionSettings(this.serverUrl, this.token, settings);
|
|
18238
|
-
}
|
|
18239
|
-
}
|
|
18240
|
-
RegionUrlProvider.cache = new Map();
|
|
18241
|
-
RegionUrlProvider.settingsTimeouts = new Map();
|
|
18242
|
-
RegionUrlProvider.connectionTrackers = new Map();
|
|
18243
|
-
RegionUrlProvider.fetchLock = new _();
|
|
18244
|
-
function getCloudConfigUrl(serverUrl) {
|
|
18245
|
-
return "".concat(serverUrl.protocol.replace('ws', 'http'), "//").concat(serverUrl.host, "/settings");
|
|
18246
18107
|
}// SPDX-FileCopyrightText: 2024 LiveKit, Inc.
|
|
18247
18108
|
//
|
|
18248
18109
|
// SPDX-License-Identifier: Apache-2.0
|
|
@@ -18708,10 +18569,23 @@ class LocalTrack extends Track {
|
|
|
18708
18569
|
if (stopProcessor && this.processor) {
|
|
18709
18570
|
yield this.internalStopProcessor();
|
|
18710
18571
|
}
|
|
18711
|
-
return this;
|
|
18712
18572
|
} finally {
|
|
18713
18573
|
unlock();
|
|
18714
18574
|
}
|
|
18575
|
+
yield this.onSenderTrackSwapped();
|
|
18576
|
+
return this;
|
|
18577
|
+
});
|
|
18578
|
+
}
|
|
18579
|
+
/**
|
|
18580
|
+
* Hook invoked after the MediaStreamTrack on the sender has been swapped
|
|
18581
|
+
* (via replaceTrack, setProcessor, or stopProcessor). Fires outside the
|
|
18582
|
+
* trackChangeLock so subclasses can do asynchronous work such as polling
|
|
18583
|
+
* for new dimensions without blocking other track operations.
|
|
18584
|
+
*/
|
|
18585
|
+
onSenderTrackSwapped() {
|
|
18586
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18587
|
+
// base implementation is a no-op; LocalVideoTrack overrides this to
|
|
18588
|
+
// recompute sender encoding parameters.
|
|
18715
18589
|
});
|
|
18716
18590
|
}
|
|
18717
18591
|
restart(constraints, isUnmuting) {
|
|
@@ -18956,6 +18830,7 @@ class LocalTrack extends Track {
|
|
|
18956
18830
|
} finally {
|
|
18957
18831
|
unlock();
|
|
18958
18832
|
}
|
|
18833
|
+
yield _this3.onSenderTrackSwapped();
|
|
18959
18834
|
}();
|
|
18960
18835
|
});
|
|
18961
18836
|
}
|
|
@@ -18978,6 +18853,7 @@ class LocalTrack extends Track {
|
|
|
18978
18853
|
} finally {
|
|
18979
18854
|
unlock();
|
|
18980
18855
|
}
|
|
18856
|
+
yield _this4.onSenderTrackSwapped();
|
|
18981
18857
|
}();
|
|
18982
18858
|
});
|
|
18983
18859
|
}
|
|
@@ -19921,46 +19797,146 @@ class LocalVideoTrack extends LocalTrack {
|
|
|
19921
19797
|
if (e_3) throw e_3.error;
|
|
19922
19798
|
}
|
|
19923
19799
|
}
|
|
19800
|
+
// The new MediaStreamTrack may have different dimensions than the previous one
|
|
19801
|
+
// (e.g. switching between cameras with different native resolutions), which would
|
|
19802
|
+
// leave the sender's encoding parameters (scaleResolutionDownBy, maxBitrate, etc.)
|
|
19803
|
+
// based on the old dimensions. Recompute them so the encoded output matches the
|
|
19804
|
+
// new source.
|
|
19805
|
+
yield this.onSenderTrackSwapped();
|
|
19924
19806
|
});
|
|
19925
19807
|
}
|
|
19926
|
-
|
|
19927
|
-
|
|
19928
|
-
|
|
19929
|
-
get: () => super.setProcessor
|
|
19930
|
-
}
|
|
19931
|
-
});
|
|
19932
|
-
return __awaiter(this, arguments, void 0, function (processor) {
|
|
19933
|
-
var _this = this;
|
|
19934
|
-
let showProcessedStreamLocally = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
|
|
19935
|
-
return function* () {
|
|
19936
|
-
var _a, e_4, _b, _c;
|
|
19937
|
-
var _d, _e;
|
|
19938
|
-
yield _super.setProcessor.call(_this, processor, showProcessedStreamLocally);
|
|
19939
|
-
if ((_d = _this.processor) === null || _d === void 0 ? void 0 : _d.processedTrack) {
|
|
19940
|
-
try {
|
|
19941
|
-
for (var _f = true, _g = __asyncValues(_this.simulcastCodecs.values()), _h; _h = yield _g.next(), _a = _h.done, !_a; _f = true) {
|
|
19942
|
-
_c = _h.value;
|
|
19943
|
-
_f = false;
|
|
19944
|
-
const sc = _c;
|
|
19945
|
-
yield (_e = sc.sender) === null || _e === void 0 ? void 0 : _e.replaceTrack(_this.processor.processedTrack);
|
|
19946
|
-
}
|
|
19947
|
-
} catch (e_4_1) {
|
|
19948
|
-
e_4 = {
|
|
19949
|
-
error: e_4_1
|
|
19950
|
-
};
|
|
19951
|
-
} finally {
|
|
19952
|
-
try {
|
|
19953
|
-
if (!_f && !_a && (_b = _g.return)) yield _b.call(_g);
|
|
19954
|
-
} finally {
|
|
19955
|
-
if (e_4) throw e_4.error;
|
|
19956
|
-
}
|
|
19957
|
-
}
|
|
19958
|
-
}
|
|
19959
|
-
}();
|
|
19808
|
+
onSenderTrackSwapped() {
|
|
19809
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
19810
|
+
yield this.refreshSenderEncodings();
|
|
19960
19811
|
});
|
|
19961
19812
|
}
|
|
19962
|
-
|
|
19963
|
-
|
|
19813
|
+
/**
|
|
19814
|
+
* Recomputes encoding parameters for this track's senders based on the current
|
|
19815
|
+
* MediaStreamTrack dimensions and reapplies them via setParameters. This is a no-op
|
|
19816
|
+
* if the track hasn't been published yet or if the track is in performance-optimized
|
|
19817
|
+
* mode (which manages its own encodings).
|
|
19818
|
+
*/
|
|
19819
|
+
refreshSenderEncodings() {
|
|
19820
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
19821
|
+
var _a;
|
|
19822
|
+
if (!this.sender || !this.publishOptions || this.optimizeForPerformance) {
|
|
19823
|
+
return;
|
|
19824
|
+
}
|
|
19825
|
+
const unlock = yield this.senderLock.lock();
|
|
19826
|
+
try {
|
|
19827
|
+
let dims;
|
|
19828
|
+
try {
|
|
19829
|
+
dims = yield this.waitForDimensions();
|
|
19830
|
+
} catch (e) {
|
|
19831
|
+
this.log.warn('could not determine new track dimensions, skipping encoding recompute', Object.assign(Object.assign({}, this.logContext), {
|
|
19832
|
+
error: e
|
|
19833
|
+
}));
|
|
19834
|
+
return;
|
|
19835
|
+
}
|
|
19836
|
+
if (this.lastEncodedDimensions && this.lastEncodedDimensions.width === dims.width && this.lastEncodedDimensions.height === dims.height) {
|
|
19837
|
+
return;
|
|
19838
|
+
}
|
|
19839
|
+
const isScreenShare = this.source === Track.Source.ScreenShare;
|
|
19840
|
+
const newEncodings = computeVideoEncodings(isScreenShare, dims.width, dims.height, Object.assign({}, this.publishOptions));
|
|
19841
|
+
yield this.applyEncodingsToSender(this.sender, newEncodings);
|
|
19842
|
+
this.encodings = newEncodings;
|
|
19843
|
+
this.lastEncodedDimensions = dims;
|
|
19844
|
+
for (const [codec, sc] of this.simulcastCodecs) {
|
|
19845
|
+
if (!sc.sender || ((_a = sc.sender.transport) === null || _a === void 0 ? void 0 : _a.state) === 'closed') {
|
|
19846
|
+
continue;
|
|
19847
|
+
}
|
|
19848
|
+
if (!isBackupVideoCodec(codec)) {
|
|
19849
|
+
continue;
|
|
19850
|
+
}
|
|
19851
|
+
const backupOpts = Object.assign({}, this.publishOptions);
|
|
19852
|
+
const backupEncodings = computeTrackBackupEncodings(this, codec, backupOpts);
|
|
19853
|
+
if (!backupEncodings) {
|
|
19854
|
+
continue;
|
|
19855
|
+
}
|
|
19856
|
+
yield this.applyEncodingsToSender(sc.sender, backupEncodings);
|
|
19857
|
+
sc.encodings = backupEncodings;
|
|
19858
|
+
}
|
|
19859
|
+
} catch (e) {
|
|
19860
|
+
this.log.warn('failed to apply recomputed encodings', Object.assign(Object.assign({}, this.logContext), {
|
|
19861
|
+
error: e
|
|
19862
|
+
}));
|
|
19863
|
+
} finally {
|
|
19864
|
+
unlock();
|
|
19865
|
+
}
|
|
19866
|
+
});
|
|
19867
|
+
}
|
|
19868
|
+
applyEncodingsToSender(sender, encodings) {
|
|
19869
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
19870
|
+
const params = sender.getParameters();
|
|
19871
|
+
if (!params.encodings || params.encodings.length !== encodings.length) {
|
|
19872
|
+
return;
|
|
19873
|
+
}
|
|
19874
|
+
params.encodings.forEach((existing, idx) => {
|
|
19875
|
+
// preserve disabled layers (dynacast / Firefox workaround in
|
|
19876
|
+
// setPublishingLayersForSender set scaleResolutionDownBy/maxBitrate to sentinel
|
|
19877
|
+
// values for disabled layers — don't clobber those).
|
|
19878
|
+
if (existing.active === false) {
|
|
19879
|
+
return;
|
|
19880
|
+
}
|
|
19881
|
+
const next = encodings[idx];
|
|
19882
|
+
if (next.scaleResolutionDownBy !== undefined) {
|
|
19883
|
+
existing.scaleResolutionDownBy = next.scaleResolutionDownBy;
|
|
19884
|
+
}
|
|
19885
|
+
if (next.maxBitrate !== undefined) {
|
|
19886
|
+
existing.maxBitrate = next.maxBitrate;
|
|
19887
|
+
}
|
|
19888
|
+
if (next.maxFramerate !== undefined) {
|
|
19889
|
+
existing.maxFramerate = next.maxFramerate;
|
|
19890
|
+
}
|
|
19891
|
+
if (next.priority !== undefined) {
|
|
19892
|
+
existing.priority = next.priority;
|
|
19893
|
+
existing.networkPriority = next.priority;
|
|
19894
|
+
}
|
|
19895
|
+
});
|
|
19896
|
+
this.log.debug('updating sender encodings after track restart', Object.assign(Object.assign({}, this.logContext), {
|
|
19897
|
+
encodings: params.encodings
|
|
19898
|
+
}));
|
|
19899
|
+
yield sender.setParameters(params);
|
|
19900
|
+
});
|
|
19901
|
+
}
|
|
19902
|
+
setProcessor(processor_1) {
|
|
19903
|
+
const _super = Object.create(null, {
|
|
19904
|
+
setProcessor: {
|
|
19905
|
+
get: () => super.setProcessor
|
|
19906
|
+
}
|
|
19907
|
+
});
|
|
19908
|
+
return __awaiter(this, arguments, void 0, function (processor) {
|
|
19909
|
+
var _this = this;
|
|
19910
|
+
let showProcessedStreamLocally = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
|
|
19911
|
+
return function* () {
|
|
19912
|
+
var _a, e_4, _b, _c;
|
|
19913
|
+
var _d, _e;
|
|
19914
|
+
yield _super.setProcessor.call(_this, processor, showProcessedStreamLocally);
|
|
19915
|
+
if ((_d = _this.processor) === null || _d === void 0 ? void 0 : _d.processedTrack) {
|
|
19916
|
+
try {
|
|
19917
|
+
for (var _f = true, _g = __asyncValues(_this.simulcastCodecs.values()), _h; _h = yield _g.next(), _a = _h.done, !_a; _f = true) {
|
|
19918
|
+
_c = _h.value;
|
|
19919
|
+
_f = false;
|
|
19920
|
+
const sc = _c;
|
|
19921
|
+
yield (_e = sc.sender) === null || _e === void 0 ? void 0 : _e.replaceTrack(_this.processor.processedTrack);
|
|
19922
|
+
}
|
|
19923
|
+
} catch (e_4_1) {
|
|
19924
|
+
e_4 = {
|
|
19925
|
+
error: e_4_1
|
|
19926
|
+
};
|
|
19927
|
+
} finally {
|
|
19928
|
+
try {
|
|
19929
|
+
if (!_f && !_a && (_b = _g.return)) yield _b.call(_g);
|
|
19930
|
+
} finally {
|
|
19931
|
+
if (e_4) throw e_4.error;
|
|
19932
|
+
}
|
|
19933
|
+
}
|
|
19934
|
+
}
|
|
19935
|
+
}();
|
|
19936
|
+
});
|
|
19937
|
+
}
|
|
19938
|
+
setDegradationPreference(preference) {
|
|
19939
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
19964
19940
|
this.degradationPreference = preference;
|
|
19965
19941
|
if (this.sender) {
|
|
19966
19942
|
try {
|
|
@@ -20333,6 +20309,7 @@ class RTCEngine extends eventsExports.EventEmitter {
|
|
|
20333
20309
|
/** specifies how often an initial join connection is allowed to retry */
|
|
20334
20310
|
this.maxJoinAttempts = 1;
|
|
20335
20311
|
this.shouldFailNext = false;
|
|
20312
|
+
this.shouldFailOnV1Path = false;
|
|
20336
20313
|
this.log = livekitLogger;
|
|
20337
20314
|
this.reliableDataSequence = 1;
|
|
20338
20315
|
this.reliableMessageBuffer = new DataPacketBuffer();
|
|
@@ -20343,6 +20320,7 @@ class RTCEngine extends eventsExports.EventEmitter {
|
|
|
20343
20320
|
this.midToTrackId = {};
|
|
20344
20321
|
/** used to indicate whether the browser is currently waiting to reconnect */
|
|
20345
20322
|
this.isWaitingForNetworkReconnect = false;
|
|
20323
|
+
this.bufferStatusLowClosingFuture = new Future();
|
|
20346
20324
|
this.handleDataChannel = _a => __awaiter(this, [_a], void 0, function (_ref) {
|
|
20347
20325
|
var _this = this;
|
|
20348
20326
|
let {
|
|
@@ -20491,10 +20469,10 @@ class RTCEngine extends eventsExports.EventEmitter {
|
|
|
20491
20469
|
}
|
|
20492
20470
|
this.log.debug("reconnecting in ".concat(delay, "ms"), this.logContext);
|
|
20493
20471
|
this.clearReconnectTimeout();
|
|
20494
|
-
if (this.token
|
|
20472
|
+
if (this.token) {
|
|
20495
20473
|
// token may have been refreshed, we do not want to recreate the regionUrlProvider
|
|
20496
20474
|
// since the current engine may have inherited a regional url
|
|
20497
|
-
this.
|
|
20475
|
+
this.emit(EngineEvent.TokenRefreshed, this.token);
|
|
20498
20476
|
}
|
|
20499
20477
|
this.reconnectTimeout = CriticalTimers.setTimeout(() => this.attemptReconnect(disconnectReason).finally(() => this.reconnectTimeout = undefined), delay);
|
|
20500
20478
|
};
|
|
@@ -20594,6 +20572,13 @@ class RTCEngine extends eventsExports.EventEmitter {
|
|
|
20594
20572
|
this.client.onRequestResponse = response => this.emit(EngineEvent.SignalRequestResponse, response);
|
|
20595
20573
|
this.client.onParticipantUpdate = updates => this.emit(EngineEvent.ParticipantUpdate, updates);
|
|
20596
20574
|
this.client.onJoined = joinResponse => this.emit(EngineEvent.Joined, joinResponse);
|
|
20575
|
+
this.on(EngineEvent.Closing, () => {
|
|
20576
|
+
var _a, _b;
|
|
20577
|
+
(_b = (_a = this.bufferStatusLowClosingFuture).reject) === null || _b === void 0 ? void 0 : _b.call(_a, new UnexpectedConnectionState('engine closed'));
|
|
20578
|
+
});
|
|
20579
|
+
// Swallow the rejection at the source so it doesn't surface as an unhandled promise rejection
|
|
20580
|
+
// when no waitForBufferStatusLow callers are attached.
|
|
20581
|
+
this.bufferStatusLowClosingFuture.promise.catch(() => {});
|
|
20597
20582
|
}
|
|
20598
20583
|
/** @internal */
|
|
20599
20584
|
get logContext() {
|
|
@@ -20634,6 +20619,10 @@ class RTCEngine extends eventsExports.EventEmitter {
|
|
|
20634
20619
|
if (abortSignal === null || abortSignal === void 0 ? void 0 : abortSignal.aborted) {
|
|
20635
20620
|
throw ConnectionError.cancelled('Connection aborted');
|
|
20636
20621
|
}
|
|
20622
|
+
if (!useV0Path && _this2.shouldFailOnV1Path) {
|
|
20623
|
+
_this2.shouldFailOnV1Path = false;
|
|
20624
|
+
throw ConnectionError.serviceNotFound('Simulated v1 path failure', 'v0-rtc');
|
|
20625
|
+
}
|
|
20637
20626
|
const joinResponse = yield _this2.client.join(url, token, opts, abortSignal, useV0Path, offerProto);
|
|
20638
20627
|
_this2._isClosed = false;
|
|
20639
20628
|
_this2.latestJoinResponse = joinResponse;
|
|
@@ -20683,6 +20672,10 @@ class RTCEngine extends eventsExports.EventEmitter {
|
|
|
20683
20672
|
}
|
|
20684
20673
|
} else if (e.reason === ConnectionErrorReason.ServiceNotFound) {
|
|
20685
20674
|
_this2.log.warn("Initial connection failed: ".concat(e.message, " \u2013 Retrying"));
|
|
20675
|
+
if (_this2.pcManager) {
|
|
20676
|
+
_this2.pcManager.onStateChange = undefined;
|
|
20677
|
+
yield _this2.cleanupPeerConnections();
|
|
20678
|
+
}
|
|
20686
20679
|
return _this2.join(url, token, opts, abortSignal, true);
|
|
20687
20680
|
}
|
|
20688
20681
|
}
|
|
@@ -20758,6 +20751,14 @@ class RTCEngine extends eventsExports.EventEmitter {
|
|
|
20758
20751
|
return __awaiter(this, void 0, void 0, function* () {
|
|
20759
20752
|
yield this.client.close();
|
|
20760
20753
|
this.client.resetCallbacks();
|
|
20754
|
+
// Any in-flight addTrack requests are orphaned by the signal reconnect — the new session
|
|
20755
|
+
// won't deliver `trackPublishedResponse` for them, so reject the pending resolvers and
|
|
20756
|
+
// clear the map. Otherwise a subsequent `addTrack` call with the same client id (e.g. a
|
|
20757
|
+
// publish retry after a `NegotiationError`) throws `TrackInvalidError`.
|
|
20758
|
+
for (const cid of Object.keys(this.pendingTrackResolvers)) {
|
|
20759
|
+
this.pendingTrackResolvers[cid].reject();
|
|
20760
|
+
}
|
|
20761
|
+
this.pendingTrackResolvers = {};
|
|
20761
20762
|
});
|
|
20762
20763
|
}
|
|
20763
20764
|
addTrack(req) {
|
|
@@ -20822,8 +20823,8 @@ class RTCEngine extends eventsExports.EventEmitter {
|
|
|
20822
20823
|
});
|
|
20823
20824
|
}
|
|
20824
20825
|
/* @internal */
|
|
20825
|
-
|
|
20826
|
-
this.
|
|
20826
|
+
setRegionStrategy(strategy) {
|
|
20827
|
+
this.regionStrategy = strategy;
|
|
20827
20828
|
}
|
|
20828
20829
|
configure(joinResponse, useSinglePeerConnection) {
|
|
20829
20830
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -20944,9 +20945,8 @@ class RTCEngine extends eventsExports.EventEmitter {
|
|
|
20944
20945
|
this.emit(EngineEvent.LocalTrackSubscribed, trackSid);
|
|
20945
20946
|
};
|
|
20946
20947
|
this.client.onTokenRefresh = token => {
|
|
20947
|
-
var _a;
|
|
20948
20948
|
this.token = token;
|
|
20949
|
-
|
|
20949
|
+
this.emit(EngineEvent.TokenRefreshed, token);
|
|
20950
20950
|
};
|
|
20951
20951
|
this.client.onRemoteMuteChanged = (trackSid, muted) => {
|
|
20952
20952
|
this.emit(EngineEvent.RemoteMute, trackSid, muted);
|
|
@@ -20982,13 +20982,9 @@ class RTCEngine extends eventsExports.EventEmitter {
|
|
|
20982
20982
|
this.log.debug('client leave request', Object.assign(Object.assign({}, this.logContext), {
|
|
20983
20983
|
reason: leave === null || leave === void 0 ? void 0 : leave.reason
|
|
20984
20984
|
}));
|
|
20985
|
-
if (leave.regions
|
|
20985
|
+
if (leave.regions) {
|
|
20986
20986
|
this.log.debug('updating regions', this.logContext);
|
|
20987
|
-
this.
|
|
20988
|
-
updatedAtInMs: Date.now(),
|
|
20989
|
-
maxAgeInMs: DEFAULT_MAX_AGE_MS,
|
|
20990
|
-
regionSettings: leave.regions
|
|
20991
|
-
});
|
|
20987
|
+
this.emit(EngineEvent.ServerRegionsReported, leave.regions);
|
|
20992
20988
|
}
|
|
20993
20989
|
switch (leave.action) {
|
|
20994
20990
|
case LeaveRequest_Action.DISCONNECT:
|
|
@@ -21292,17 +21288,17 @@ class RTCEngine extends eventsExports.EventEmitter {
|
|
|
21292
21288
|
if (this.client.currentState !== SignalConnectionState.CONNECTED) {
|
|
21293
21289
|
throw new SignalReconnectError('Signal connection got severed during reconnect');
|
|
21294
21290
|
}
|
|
21295
|
-
(_a = this.
|
|
21291
|
+
(_a = this.regionStrategy) === null || _a === void 0 ? void 0 : _a.resetAttempts();
|
|
21296
21292
|
// reconnect success
|
|
21297
21293
|
this.emit(EngineEvent.Restarted);
|
|
21298
21294
|
} catch (error) {
|
|
21299
|
-
const nextRegionUrl = yield (_b = this.
|
|
21295
|
+
const nextRegionUrl = yield (_b = this.regionStrategy) === null || _b === void 0 ? void 0 : _b.getNextUrl();
|
|
21300
21296
|
if (nextRegionUrl) {
|
|
21301
21297
|
yield this.restartConnection(nextRegionUrl);
|
|
21302
21298
|
return;
|
|
21303
21299
|
} else {
|
|
21304
21300
|
// no more regions to try (or we're not on cloud)
|
|
21305
|
-
(_c = this.
|
|
21301
|
+
(_c = this.regionStrategy) === null || _c === void 0 ? void 0 : _c.resetAttempts();
|
|
21306
21302
|
throw error;
|
|
21307
21303
|
}
|
|
21308
21304
|
}
|
|
@@ -21544,17 +21540,13 @@ class RTCEngine extends eventsExports.EventEmitter {
|
|
|
21544
21540
|
if (this.isBufferStatusLow(kind)) {
|
|
21545
21541
|
resolve();
|
|
21546
21542
|
} else {
|
|
21547
|
-
const onClosing = () => reject(new UnexpectedConnectionState('engine closed'));
|
|
21548
|
-
this.once(EngineEvent.Closing, onClosing);
|
|
21549
21543
|
const dc = this.dataChannelForKind(kind);
|
|
21550
21544
|
if (!dc) {
|
|
21551
21545
|
reject(new UnexpectedConnectionState("DataChannel not found, kind: ".concat(kind)));
|
|
21552
21546
|
return;
|
|
21553
21547
|
}
|
|
21554
|
-
|
|
21555
|
-
|
|
21556
|
-
resolve();
|
|
21557
|
-
}, {
|
|
21548
|
+
this.bufferStatusLowClosingFuture.promise.catch(e => reject(e));
|
|
21549
|
+
dc.addEventListener('bufferedamountlow', () => resolve(), {
|
|
21558
21550
|
once: true
|
|
21559
21551
|
});
|
|
21560
21552
|
}
|
|
@@ -21783,6 +21775,11 @@ class RTCEngine extends eventsExports.EventEmitter {
|
|
|
21783
21775
|
// debugging method to fail the next reconnect/resume attempt
|
|
21784
21776
|
this.shouldFailNext = true;
|
|
21785
21777
|
}
|
|
21778
|
+
/* @internal */
|
|
21779
|
+
failNextV1Path() {
|
|
21780
|
+
// debugging method to fail the next connection attempt for /rtc/v1 to trigger the fallback version
|
|
21781
|
+
this.shouldFailOnV1Path = true;
|
|
21782
|
+
}
|
|
21786
21783
|
dataChannelsInfo() {
|
|
21787
21784
|
const infos = [];
|
|
21788
21785
|
const getInfo = (dc, target) => {
|
|
@@ -21842,6 +21839,196 @@ function applyUserDataCompat(newObj, oldObj) {
|
|
|
21842
21839
|
const destinationIdentities = newObj.destinationIdentities.length !== 0 ? newObj.destinationIdentities : oldObj.destinationIdentities;
|
|
21843
21840
|
newObj.destinationIdentities = destinationIdentities;
|
|
21844
21841
|
oldObj.destinationIdentities = destinationIdentities;
|
|
21842
|
+
}const DEFAULT_MAX_AGE_MS = 5000;
|
|
21843
|
+
const STOP_REFETCH_DELAY_MS = 30000;
|
|
21844
|
+
class RegionUrlProvider {
|
|
21845
|
+
static fetchRegionSettings(serverUrl, token, signal) {
|
|
21846
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
21847
|
+
const unlock = yield RegionUrlProvider.fetchLock.lock();
|
|
21848
|
+
try {
|
|
21849
|
+
const regionSettingsResponse = yield fetch("".concat(getCloudConfigUrl(serverUrl), "/regions"), {
|
|
21850
|
+
headers: {
|
|
21851
|
+
authorization: "Bearer ".concat(token)
|
|
21852
|
+
},
|
|
21853
|
+
signal
|
|
21854
|
+
});
|
|
21855
|
+
if (regionSettingsResponse.ok) {
|
|
21856
|
+
const maxAge = extractMaxAgeFromRequestHeaders(regionSettingsResponse.headers);
|
|
21857
|
+
const maxAgeInMs = maxAge ? maxAge * 1000 : DEFAULT_MAX_AGE_MS;
|
|
21858
|
+
const regionSettings = yield regionSettingsResponse.json();
|
|
21859
|
+
return {
|
|
21860
|
+
regionSettings,
|
|
21861
|
+
updatedAtInMs: Date.now(),
|
|
21862
|
+
maxAgeInMs
|
|
21863
|
+
};
|
|
21864
|
+
} else {
|
|
21865
|
+
if (regionSettingsResponse.status === 401) {
|
|
21866
|
+
throw ConnectionError.notAllowed("Could not fetch region settings: ".concat(regionSettingsResponse.statusText), regionSettingsResponse.status);
|
|
21867
|
+
} else {
|
|
21868
|
+
throw ConnectionError.internal("Could not fetch region settings: ".concat(regionSettingsResponse.statusText));
|
|
21869
|
+
}
|
|
21870
|
+
}
|
|
21871
|
+
} catch (e) {
|
|
21872
|
+
if (e instanceof ConnectionError) {
|
|
21873
|
+
// rethrow connection errors
|
|
21874
|
+
throw e;
|
|
21875
|
+
} else if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
21876
|
+
throw ConnectionError.cancelled("Region fetching was aborted");
|
|
21877
|
+
} else {
|
|
21878
|
+
// wrap other errors as connection errors
|
|
21879
|
+
throw ConnectionError.serverUnreachable("Could not fetch region settings, ".concat(e instanceof Error ? "".concat(e.name, ": ").concat(e.message) : e));
|
|
21880
|
+
}
|
|
21881
|
+
} finally {
|
|
21882
|
+
unlock();
|
|
21883
|
+
}
|
|
21884
|
+
});
|
|
21885
|
+
}
|
|
21886
|
+
static scheduleRefetch(url, token, maxAgeInMs) {
|
|
21887
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
21888
|
+
const timeout = RegionUrlProvider.settingsTimeouts.get(url.hostname);
|
|
21889
|
+
clearTimeout(timeout);
|
|
21890
|
+
RegionUrlProvider.settingsTimeouts.set(url.hostname, setTimeout(() => __awaiter(this, void 0, void 0, function* () {
|
|
21891
|
+
try {
|
|
21892
|
+
const newSettings = yield RegionUrlProvider.fetchRegionSettings(url, token);
|
|
21893
|
+
RegionUrlProvider.updateCachedRegionSettings(url, token, newSettings);
|
|
21894
|
+
} catch (error) {
|
|
21895
|
+
if (error instanceof ConnectionError && error.reason === ConnectionErrorReason.NotAllowed) {
|
|
21896
|
+
livekitLogger.debug('token is not valid, cancelling auto region refresh');
|
|
21897
|
+
return;
|
|
21898
|
+
}
|
|
21899
|
+
livekitLogger.debug('auto refetching of region settings failed', {
|
|
21900
|
+
error
|
|
21901
|
+
});
|
|
21902
|
+
// continue retrying with the same max age
|
|
21903
|
+
RegionUrlProvider.scheduleRefetch(url, token, maxAgeInMs);
|
|
21904
|
+
}
|
|
21905
|
+
}), maxAgeInMs));
|
|
21906
|
+
});
|
|
21907
|
+
}
|
|
21908
|
+
static updateCachedRegionSettings(url, token, settings) {
|
|
21909
|
+
RegionUrlProvider.cache.set(url.hostname, settings);
|
|
21910
|
+
RegionUrlProvider.scheduleRefetch(url, token, settings.maxAgeInMs);
|
|
21911
|
+
}
|
|
21912
|
+
static stopRefetch(hostname) {
|
|
21913
|
+
const timeout = RegionUrlProvider.settingsTimeouts.get(hostname);
|
|
21914
|
+
if (timeout) {
|
|
21915
|
+
clearTimeout(timeout);
|
|
21916
|
+
RegionUrlProvider.settingsTimeouts.delete(hostname);
|
|
21917
|
+
}
|
|
21918
|
+
}
|
|
21919
|
+
static scheduleCleanup(hostname) {
|
|
21920
|
+
let tracker = RegionUrlProvider.connectionTrackers.get(hostname);
|
|
21921
|
+
if (!tracker) {
|
|
21922
|
+
return;
|
|
21923
|
+
}
|
|
21924
|
+
// Cancel any existing cleanup timeout
|
|
21925
|
+
if (tracker.cleanupTimeout) {
|
|
21926
|
+
clearTimeout(tracker.cleanupTimeout);
|
|
21927
|
+
}
|
|
21928
|
+
// Schedule cleanup to stop refetch after delay
|
|
21929
|
+
tracker.cleanupTimeout = setTimeout(() => {
|
|
21930
|
+
const currentTracker = RegionUrlProvider.connectionTrackers.get(hostname);
|
|
21931
|
+
if (currentTracker && currentTracker.connectionCount === 0) {
|
|
21932
|
+
livekitLogger.debug('stopping region refetch after disconnect delay', {
|
|
21933
|
+
hostname
|
|
21934
|
+
});
|
|
21935
|
+
RegionUrlProvider.stopRefetch(hostname);
|
|
21936
|
+
}
|
|
21937
|
+
if (currentTracker) {
|
|
21938
|
+
currentTracker.cleanupTimeout = undefined;
|
|
21939
|
+
}
|
|
21940
|
+
}, STOP_REFETCH_DELAY_MS);
|
|
21941
|
+
}
|
|
21942
|
+
static cancelCleanup(hostname) {
|
|
21943
|
+
const tracker = RegionUrlProvider.connectionTrackers.get(hostname);
|
|
21944
|
+
if (tracker === null || tracker === void 0 ? void 0 : tracker.cleanupTimeout) {
|
|
21945
|
+
clearTimeout(tracker.cleanupTimeout);
|
|
21946
|
+
tracker.cleanupTimeout = undefined;
|
|
21947
|
+
}
|
|
21948
|
+
}
|
|
21949
|
+
notifyConnected() {
|
|
21950
|
+
const hostname = this.serverUrl.hostname;
|
|
21951
|
+
let tracker = RegionUrlProvider.connectionTrackers.get(hostname);
|
|
21952
|
+
if (!tracker) {
|
|
21953
|
+
tracker = {
|
|
21954
|
+
connectionCount: 0
|
|
21955
|
+
};
|
|
21956
|
+
RegionUrlProvider.connectionTrackers.set(hostname, tracker);
|
|
21957
|
+
}
|
|
21958
|
+
tracker.connectionCount++;
|
|
21959
|
+
// Cancel any scheduled cleanup since we have an active connection
|
|
21960
|
+
RegionUrlProvider.cancelCleanup(hostname);
|
|
21961
|
+
}
|
|
21962
|
+
notifyDisconnected() {
|
|
21963
|
+
const hostname = this.serverUrl.hostname;
|
|
21964
|
+
const tracker = RegionUrlProvider.connectionTrackers.get(hostname);
|
|
21965
|
+
if (!tracker) {
|
|
21966
|
+
return;
|
|
21967
|
+
}
|
|
21968
|
+
tracker.connectionCount = Math.max(0, tracker.connectionCount - 1);
|
|
21969
|
+
// If no more connections, schedule cleanup
|
|
21970
|
+
if (tracker.connectionCount === 0) {
|
|
21971
|
+
RegionUrlProvider.scheduleCleanup(hostname);
|
|
21972
|
+
}
|
|
21973
|
+
}
|
|
21974
|
+
constructor(url, token) {
|
|
21975
|
+
this.attemptedRegions = [];
|
|
21976
|
+
this.serverUrl = new URL(url);
|
|
21977
|
+
this.token = token;
|
|
21978
|
+
}
|
|
21979
|
+
updateToken(token) {
|
|
21980
|
+
var _a;
|
|
21981
|
+
this.token = token;
|
|
21982
|
+
const url = this.getServerUrl();
|
|
21983
|
+
const settings = RegionUrlProvider.cache.get(url.hostname);
|
|
21984
|
+
RegionUrlProvider.scheduleRefetch(this.serverUrl, this.token, (_a = settings === null || settings === void 0 ? void 0 : settings.maxAgeInMs) !== null && _a !== void 0 ? _a : DEFAULT_MAX_AGE_MS);
|
|
21985
|
+
}
|
|
21986
|
+
isCloud() {
|
|
21987
|
+
return isCloud(this.serverUrl);
|
|
21988
|
+
}
|
|
21989
|
+
getServerUrl() {
|
|
21990
|
+
return this.serverUrl;
|
|
21991
|
+
}
|
|
21992
|
+
/** @internal */
|
|
21993
|
+
fetchRegionSettings(abortSignal) {
|
|
21994
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
21995
|
+
return RegionUrlProvider.fetchRegionSettings(this.serverUrl, this.token, abortSignal);
|
|
21996
|
+
});
|
|
21997
|
+
}
|
|
21998
|
+
getNextBestRegionUrl(abortSignal) {
|
|
21999
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
22000
|
+
if (!this.isCloud()) {
|
|
22001
|
+
throw Error('region availability is only supported for LiveKit Cloud domains');
|
|
22002
|
+
}
|
|
22003
|
+
let cachedSettings = RegionUrlProvider.cache.get(this.serverUrl.hostname);
|
|
22004
|
+
if (!cachedSettings || Date.now() - cachedSettings.updatedAtInMs > cachedSettings.maxAgeInMs) {
|
|
22005
|
+
cachedSettings = yield this.fetchRegionSettings(abortSignal);
|
|
22006
|
+
RegionUrlProvider.updateCachedRegionSettings(this.serverUrl, this.token, cachedSettings);
|
|
22007
|
+
}
|
|
22008
|
+
const regionsLeft = cachedSettings.regionSettings.regions.filter(region => !this.attemptedRegions.find(attempted => attempted.url === region.url));
|
|
22009
|
+
if (regionsLeft.length > 0) {
|
|
22010
|
+
const nextRegion = regionsLeft[0];
|
|
22011
|
+
this.attemptedRegions.push(nextRegion);
|
|
22012
|
+
livekitLogger.debug("next region: ".concat(nextRegion.region));
|
|
22013
|
+
return nextRegion.url;
|
|
22014
|
+
} else {
|
|
22015
|
+
return null;
|
|
22016
|
+
}
|
|
22017
|
+
});
|
|
22018
|
+
}
|
|
22019
|
+
resetAttempts() {
|
|
22020
|
+
this.attemptedRegions = [];
|
|
22021
|
+
}
|
|
22022
|
+
setServerReportedRegions(settings) {
|
|
22023
|
+
RegionUrlProvider.updateCachedRegionSettings(this.serverUrl, this.token, settings);
|
|
22024
|
+
}
|
|
22025
|
+
}
|
|
22026
|
+
RegionUrlProvider.cache = new Map();
|
|
22027
|
+
RegionUrlProvider.settingsTimeouts = new Map();
|
|
22028
|
+
RegionUrlProvider.connectionTrackers = new Map();
|
|
22029
|
+
RegionUrlProvider.fetchLock = new _();
|
|
22030
|
+
function getCloudConfigUrl(serverUrl) {
|
|
22031
|
+
return "".concat(serverUrl.protocol.replace('ws', 'http'), "//").concat(serverUrl.host, "/settings");
|
|
21845
22032
|
}class BaseStreamReader {
|
|
21846
22033
|
get info() {
|
|
21847
22034
|
return this._info;
|
|
@@ -23540,55 +23727,82 @@ class IncomingDataTrackManager extends eventsExports.EventEmitter {
|
|
|
23540
23727
|
let bufferSize = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : READABLE_STREAM_DEFAULT_BUFFER_SIZE;
|
|
23541
23728
|
let streamController = null;
|
|
23542
23729
|
const sfuSubscriptionComplete = new Future();
|
|
23730
|
+
const detachSignal = () => {
|
|
23731
|
+
signal === null || signal === void 0 ? void 0 : signal.removeEventListener('abort', onAbort);
|
|
23732
|
+
};
|
|
23733
|
+
const cleanup = () => {
|
|
23734
|
+
detachSignal();
|
|
23735
|
+
if (!streamController) {
|
|
23736
|
+
log$1.warn("ReadableStream subscribed to ".concat(sid, " was not started."));
|
|
23737
|
+
return;
|
|
23738
|
+
}
|
|
23739
|
+
const descriptor = this.descriptors.get(sid);
|
|
23740
|
+
if (!descriptor) {
|
|
23741
|
+
log$1.warn("Unknown track ".concat(sid, ", skipping cancel..."));
|
|
23742
|
+
return;
|
|
23743
|
+
}
|
|
23744
|
+
if (descriptor.subscription.type !== 'active') {
|
|
23745
|
+
log$1.warn("Subscription for track ".concat(sid, " is not active, skipping cancel..."));
|
|
23746
|
+
return;
|
|
23747
|
+
}
|
|
23748
|
+
descriptor.subscription.streamControllers.delete(streamController);
|
|
23749
|
+
// If no active stream controllers are left, also unsubscribe on the SFU end.
|
|
23750
|
+
if (descriptor.subscription.streamControllers.size === 0) {
|
|
23751
|
+
this.unSubscribeRequest(descriptor.info.sid);
|
|
23752
|
+
}
|
|
23753
|
+
};
|
|
23754
|
+
const onAbort = () => {
|
|
23755
|
+
var _a;
|
|
23756
|
+
if (!streamController) {
|
|
23757
|
+
return;
|
|
23758
|
+
}
|
|
23759
|
+
const currentDescriptor = this.descriptors.get(sid);
|
|
23760
|
+
if ((currentDescriptor === null || currentDescriptor === void 0 ? void 0 : currentDescriptor.subscription.type) === 'active') {
|
|
23761
|
+
currentDescriptor.subscription.streamControllers.delete(streamController);
|
|
23762
|
+
}
|
|
23763
|
+
streamController.error(DataTrackSubscribeError.cancelled());
|
|
23764
|
+
(_a = sfuSubscriptionComplete.reject) === null || _a === void 0 ? void 0 : _a.call(sfuSubscriptionComplete, DataTrackSubscribeError.cancelled());
|
|
23765
|
+
cleanup();
|
|
23766
|
+
};
|
|
23543
23767
|
const stream = new ReadableStream({
|
|
23544
23768
|
start: controller => {
|
|
23545
23769
|
streamController = controller;
|
|
23546
|
-
const onAbort = () => {
|
|
23547
|
-
var _a;
|
|
23548
|
-
controller.error(DataTrackSubscribeError.cancelled());
|
|
23549
|
-
(_a = sfuSubscriptionComplete.reject) === null || _a === void 0 ? void 0 : _a.call(sfuSubscriptionComplete, DataTrackSubscribeError.cancelled());
|
|
23550
|
-
};
|
|
23551
23770
|
this.subscribeRequest(sid, signal).then(() => __awaiter(this, void 0, void 0, function* () {
|
|
23552
|
-
var _a;
|
|
23553
|
-
signal === null || signal === void 0 ? void 0 : signal.addEventListener('abort', onAbort);
|
|
23771
|
+
var _a, _b, _c;
|
|
23554
23772
|
const descriptor = this.descriptors.get(sid);
|
|
23555
23773
|
if (!descriptor) {
|
|
23556
23774
|
log$1.error("Unknown track ".concat(sid));
|
|
23775
|
+
const err = DataTrackSubscribeError.disconnected();
|
|
23776
|
+
controller.error(err);
|
|
23777
|
+
(_a = sfuSubscriptionComplete.reject) === null || _a === void 0 ? void 0 : _a.call(sfuSubscriptionComplete, err);
|
|
23557
23778
|
return;
|
|
23558
23779
|
}
|
|
23559
23780
|
if (descriptor.subscription.type !== 'active') {
|
|
23560
23781
|
log$1.error("Subscription for track ".concat(sid, " is not active"));
|
|
23782
|
+
const err = DataTrackSubscribeError.disconnected();
|
|
23783
|
+
controller.error(err);
|
|
23784
|
+
(_b = sfuSubscriptionComplete.reject) === null || _b === void 0 ? void 0 : _b.call(sfuSubscriptionComplete, err);
|
|
23561
23785
|
return;
|
|
23562
23786
|
}
|
|
23563
|
-
|
|
23564
|
-
|
|
23787
|
+
// Attach the abort signal, aborting immediately if the abort signal was fired while
|
|
23788
|
+
// subscribeRequest was in flight.
|
|
23789
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
23790
|
+
onAbort();
|
|
23791
|
+
return;
|
|
23792
|
+
}
|
|
23793
|
+
signal === null || signal === void 0 ? void 0 : signal.addEventListener('abort', onAbort);
|
|
23794
|
+
descriptor.subscription.streamControllers.set(controller, detachSignal);
|
|
23795
|
+
(_c = sfuSubscriptionComplete.resolve) === null || _c === void 0 ? void 0 : _c.call(sfuSubscriptionComplete);
|
|
23565
23796
|
})).catch(err => {
|
|
23566
23797
|
var _a;
|
|
23798
|
+
// subscribeRequest rejected (cancelled, timed out, disconnected). The signal
|
|
23799
|
+
// listener was never attached in this path, so nothing to detach.
|
|
23567
23800
|
controller.error(err);
|
|
23568
23801
|
(_a = sfuSubscriptionComplete.reject) === null || _a === void 0 ? void 0 : _a.call(sfuSubscriptionComplete, err);
|
|
23569
|
-
}).finally(() => {
|
|
23570
|
-
signal === null || signal === void 0 ? void 0 : signal.removeEventListener('abort', onAbort);
|
|
23571
23802
|
});
|
|
23572
23803
|
},
|
|
23573
23804
|
cancel: () => {
|
|
23574
|
-
|
|
23575
|
-
log$1.warn("ReadableStream subscribed to ".concat(sid, " was not started."));
|
|
23576
|
-
return;
|
|
23577
|
-
}
|
|
23578
|
-
const descriptor = this.descriptors.get(sid);
|
|
23579
|
-
if (!descriptor) {
|
|
23580
|
-
log$1.warn("Unknown track ".concat(sid, ", skipping cancel..."));
|
|
23581
|
-
return;
|
|
23582
|
-
}
|
|
23583
|
-
if (descriptor.subscription.type !== 'active') {
|
|
23584
|
-
log$1.warn("Subscription for track ".concat(sid, " is not active, skipping cancel..."));
|
|
23585
|
-
return;
|
|
23586
|
-
}
|
|
23587
|
-
descriptor.subscription.streamControllers.delete(streamController);
|
|
23588
|
-
// If no active stream controllers are left, also unsubscribe on the SFU end.
|
|
23589
|
-
if (descriptor.subscription.streamControllers.size === 0) {
|
|
23590
|
-
this.unSubscribeRequest(descriptor.info.sid);
|
|
23591
|
-
}
|
|
23805
|
+
cleanup();
|
|
23592
23806
|
}
|
|
23593
23807
|
}, new CountQueuingStrategy({
|
|
23594
23808
|
highWaterMark: bufferSize
|
|
@@ -23726,9 +23940,7 @@ class IncomingDataTrackManager extends eventsExports.EventEmitter {
|
|
|
23726
23940
|
log$1.warn("Unexpected descriptor state in unSubscribeRequest, expected active, found ".concat((_a = descriptor.subscription) === null || _a === void 0 ? void 0 : _a.type));
|
|
23727
23941
|
return;
|
|
23728
23942
|
}
|
|
23729
|
-
|
|
23730
|
-
controller.close();
|
|
23731
|
-
}
|
|
23943
|
+
this.closeStreamControllers(descriptor.subscription.streamControllers, sid);
|
|
23732
23944
|
// FIXME: this might be wrong? Shouldn't this only occur if it is the last subscription to
|
|
23733
23945
|
// terminate?
|
|
23734
23946
|
const previousDescriptorSubscription = descriptor.subscription;
|
|
@@ -23741,6 +23953,23 @@ class IncomingDataTrackManager extends eventsExports.EventEmitter {
|
|
|
23741
23953
|
subscribe: false
|
|
23742
23954
|
});
|
|
23743
23955
|
}
|
|
23956
|
+
/** Detach abort-signal listeners and close all downstream stream controllers for an active
|
|
23957
|
+
* subscription. Used when the subscription is being torn down by the manager (unsubscribe,
|
|
23958
|
+
* unpublish, or shutdown). */
|
|
23959
|
+
closeStreamControllers(streamControllers, sid) {
|
|
23960
|
+
for (const [controller, detachSignal] of streamControllers) {
|
|
23961
|
+
// Detach before close so we don't leak a listener on the user's AbortSignal.
|
|
23962
|
+
detachSignal();
|
|
23963
|
+
try {
|
|
23964
|
+
controller.close();
|
|
23965
|
+
} catch (err) {
|
|
23966
|
+
// Defensive: if the controller has already been errored (e.g. by a racing abort whose
|
|
23967
|
+
// listener removed itself before we got here), close() throws. There's nothing
|
|
23968
|
+
// meaningful to do other than log — the stream is already terminal.
|
|
23969
|
+
log$1.warn("Failed to close readable stream for track ".concat(sid, ": ").concat(err));
|
|
23970
|
+
}
|
|
23971
|
+
}
|
|
23972
|
+
}
|
|
23744
23973
|
/** SFU notification that track publications have changed.
|
|
23745
23974
|
*
|
|
23746
23975
|
* This event is produced from both {@link JoinResponse} and {@link ParticipantUpdate}
|
|
@@ -23822,9 +24051,7 @@ class IncomingDataTrackManager extends eventsExports.EventEmitter {
|
|
|
23822
24051
|
}
|
|
23823
24052
|
this.descriptors.delete(sid);
|
|
23824
24053
|
if (descriptor.subscription.type === 'active') {
|
|
23825
|
-
descriptor.subscription.streamControllers
|
|
23826
|
-
controller.close();
|
|
23827
|
-
});
|
|
24054
|
+
this.closeStreamControllers(descriptor.subscription.streamControllers, sid);
|
|
23828
24055
|
this.subscriptionHandles.delete(descriptor.subscription.subcriptionHandle);
|
|
23829
24056
|
}
|
|
23830
24057
|
this.emit('trackUnpublished', {
|
|
@@ -23874,7 +24101,7 @@ class IncomingDataTrackManager extends eventsExports.EventEmitter {
|
|
|
23874
24101
|
type: 'active',
|
|
23875
24102
|
subcriptionHandle: assignedHandle,
|
|
23876
24103
|
pipeline,
|
|
23877
|
-
streamControllers: new
|
|
24104
|
+
streamControllers: new Map()
|
|
23878
24105
|
};
|
|
23879
24106
|
this.subscriptionHandles.set(assignedHandle, sid);
|
|
23880
24107
|
(_b = (_a = previousDescriptorSubscription.completionFuture).resolve) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
@@ -23911,7 +24138,7 @@ class IncomingDataTrackManager extends eventsExports.EventEmitter {
|
|
|
23911
24138
|
return;
|
|
23912
24139
|
}
|
|
23913
24140
|
// Broadcast to all downstream subscribers
|
|
23914
|
-
for (const controller of descriptor.subscription.streamControllers) {
|
|
24141
|
+
for (const controller of descriptor.subscription.streamControllers.keys()) {
|
|
23915
24142
|
if (controller.desiredSize !== null && controller.desiredSize <= 0) {
|
|
23916
24143
|
log$1.warn("Cannot send frame to subscribers: readable stream is full (desiredSize is ".concat(controller.desiredSize, "). To increase this threshold, set a higher 'options.highWaterMark' when calling .subscribe()."));
|
|
23917
24144
|
continue;
|
|
@@ -23969,7 +24196,7 @@ class IncomingDataTrackManager extends eventsExports.EventEmitter {
|
|
|
23969
24196
|
(_b = (_a = descriptor.subscription.completionFuture).reject) === null || _b === void 0 ? void 0 : _b.call(_a, DataTrackSubscribeError.disconnected());
|
|
23970
24197
|
}
|
|
23971
24198
|
if (descriptor.subscription.type === 'active') {
|
|
23972
|
-
descriptor.subscription.streamControllers
|
|
24199
|
+
this.closeStreamControllers(descriptor.subscription.streamControllers, descriptor.info.sid);
|
|
23973
24200
|
}
|
|
23974
24201
|
}
|
|
23975
24202
|
this.descriptors.clear();
|
|
@@ -25175,10 +25402,17 @@ class HTMLElementInfo {
|
|
|
25175
25402
|
}
|
|
25176
25403
|
};
|
|
25177
25404
|
this.onEnterPiP = () => {
|
|
25178
|
-
var _a, _b
|
|
25405
|
+
var _a, _b;
|
|
25179
25406
|
(_b = (_a = window.documentPictureInPicture) === null || _a === void 0 ? void 0 : _a.window) === null || _b === void 0 ? void 0 : _b.addEventListener('pagehide', this.onLeavePiP);
|
|
25180
|
-
|
|
25181
|
-
|
|
25407
|
+
// Document PiP: the browser may fire 'enter' before the app has appended its subtree into
|
|
25408
|
+
// documentPictureInPicture.window. Defer so pipWin.document.contains(video) is reliable.
|
|
25409
|
+
queueMicrotask(() => {
|
|
25410
|
+
requestAnimationFrame(() => {
|
|
25411
|
+
var _a;
|
|
25412
|
+
this.isPiP = isElementInPiP(this.element);
|
|
25413
|
+
(_a = this.handleVisibilityChanged) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
25414
|
+
});
|
|
25415
|
+
});
|
|
25182
25416
|
};
|
|
25183
25417
|
this.onLeavePiP = () => {
|
|
25184
25418
|
var _a;
|
|
@@ -26451,11 +26685,12 @@ class Participant extends eventsExports.EventEmitter {
|
|
|
26451
26685
|
if (track && track.track) {
|
|
26452
26686
|
// screenshare cannot be muted, unpublish instead
|
|
26453
26687
|
if (source === Track.Source.ScreenShare) {
|
|
26454
|
-
|
|
26688
|
+
const unpublishPromises = [this.unpublishTrack(track.track)];
|
|
26455
26689
|
const screenAudioTrack = this.getTrackPublication(Track.Source.ScreenShareAudio);
|
|
26456
26690
|
if (screenAudioTrack && screenAudioTrack.track) {
|
|
26457
|
-
this.unpublishTrack(screenAudioTrack.track);
|
|
26691
|
+
unpublishPromises.push(this.unpublishTrack(screenAudioTrack.track));
|
|
26458
26692
|
}
|
|
26693
|
+
[track] = yield Promise.all(unpublishPromises);
|
|
26459
26694
|
} else {
|
|
26460
26695
|
yield track.mute();
|
|
26461
26696
|
}
|
|
@@ -26586,10 +26821,42 @@ class Participant extends eventsExports.EventEmitter {
|
|
|
26586
26821
|
return this.publishOrRepublishTrack(track, options);
|
|
26587
26822
|
});
|
|
26588
26823
|
}
|
|
26824
|
+
/**
|
|
26825
|
+
* Waits for the engine's next `Restarted` event. Unlike `engine.waitForRestarted`, this does
|
|
26826
|
+
* not short-circuit when `pcState === Connected` — at the point this is called (right after a
|
|
26827
|
+
* `NegotiationError`) the PC transport is still connected, but `fullReconnectOnNext` has been
|
|
26828
|
+
* set and `attemptReconnect` is queued via setTimeout. We need to wait for that restart to
|
|
26829
|
+
* actually complete (which clears `pendingTrackResolvers` via `cleanupClient`) before retrying.
|
|
26830
|
+
*/
|
|
26831
|
+
waitForNextEngineRestart() {
|
|
26832
|
+
let timeoutMs = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 15000;
|
|
26833
|
+
return new Promise((resolve, reject) => {
|
|
26834
|
+
const cleanup = () => {
|
|
26835
|
+
clearTimeout(timeout);
|
|
26836
|
+
this.engine.off(EngineEvent.Restarted, onRestarted);
|
|
26837
|
+
this.engine.off(EngineEvent.Closing, onClosing);
|
|
26838
|
+
};
|
|
26839
|
+
const onRestarted = () => {
|
|
26840
|
+
cleanup();
|
|
26841
|
+
resolve();
|
|
26842
|
+
};
|
|
26843
|
+
const onClosing = () => {
|
|
26844
|
+
cleanup();
|
|
26845
|
+
reject(new Error('engine closed before restart completed'));
|
|
26846
|
+
};
|
|
26847
|
+
const timeout = setTimeout(() => {
|
|
26848
|
+
cleanup();
|
|
26849
|
+
reject(new Error('timed out waiting for engine restart'));
|
|
26850
|
+
}, timeoutMs);
|
|
26851
|
+
this.engine.once(EngineEvent.Restarted, onRestarted);
|
|
26852
|
+
this.engine.once(EngineEvent.Closing, onClosing);
|
|
26853
|
+
});
|
|
26854
|
+
}
|
|
26589
26855
|
publishOrRepublishTrack(track_1, options_1) {
|
|
26590
26856
|
return __awaiter(this, arguments, void 0, function (track, options) {
|
|
26591
26857
|
var _this2 = this;
|
|
26592
26858
|
let isRepublish = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
26859
|
+
let hasRetriedAfterNegotiationError = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
|
|
26593
26860
|
return function* () {
|
|
26594
26861
|
var _a, _b, _c, _d;
|
|
26595
26862
|
if (isLocalAudioTrack(track)) {
|
|
@@ -26720,6 +26987,14 @@ class Participant extends eventsExports.EventEmitter {
|
|
|
26720
26987
|
const publication = yield publishPromise;
|
|
26721
26988
|
return publication;
|
|
26722
26989
|
} catch (e) {
|
|
26990
|
+
if (!hasRetriedAfterNegotiationError && e instanceof NegotiationError) {
|
|
26991
|
+
_this2.log.warn('negotiation due to track publish failed, retrying after reconnect', Object.assign(Object.assign({}, _this2.logContext), {
|
|
26992
|
+
error: e
|
|
26993
|
+
}));
|
|
26994
|
+
_this2.pendingPublishPromises.delete(track);
|
|
26995
|
+
yield _this2.waitForNextEngineRestart();
|
|
26996
|
+
return yield _this2.publishOrRepublishTrack(track, options, isRepublish, true);
|
|
26997
|
+
}
|
|
26723
26998
|
throw e;
|
|
26724
26999
|
} finally {
|
|
26725
27000
|
_this2.pendingPublishPromises.delete(track);
|
|
@@ -26959,7 +27234,11 @@ class Participant extends eventsExports.EventEmitter {
|
|
|
26959
27234
|
resolve(ti);
|
|
26960
27235
|
} catch (err) {
|
|
26961
27236
|
if (track.sender && ((_a = this.engine.pcManager) === null || _a === void 0 ? void 0 : _a.publisher)) {
|
|
26962
|
-
|
|
27237
|
+
try {
|
|
27238
|
+
this.engine.pcManager.publisher.removeTrack(track.sender);
|
|
27239
|
+
} catch (e) {
|
|
27240
|
+
this.log.error(e, this.logContext);
|
|
27241
|
+
}
|
|
26963
27242
|
yield this.engine.negotiate().catch(negotiateErr => {
|
|
26964
27243
|
this.log.error('failed to negotiate after removing track due to failed add track request', Object.assign(Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)), {
|
|
26965
27244
|
error: negotiateErr
|
|
@@ -27003,6 +27282,19 @@ class Participant extends eventsExports.EventEmitter {
|
|
|
27003
27282
|
// save options for when it needs to be republished again
|
|
27004
27283
|
publication.options = opts;
|
|
27005
27284
|
track.sid = ti.sid;
|
|
27285
|
+
// keep publish options on the video track so that it can recompute encoding
|
|
27286
|
+
// parameters when the MediaStreamTrack is restarted (e.g. after switching cameras).
|
|
27287
|
+
// Seed the dimensions we encoded at publish time so the first no-op restart
|
|
27288
|
+
// (e.g. unmute with unchanged constraints) can skip the recompute.
|
|
27289
|
+
if (isLocalVideoTrack(track)) {
|
|
27290
|
+
track.publishOptions = opts;
|
|
27291
|
+
if (req.width && req.height) {
|
|
27292
|
+
track.lastEncodedDimensions = {
|
|
27293
|
+
width: req.width,
|
|
27294
|
+
height: req.height
|
|
27295
|
+
};
|
|
27296
|
+
}
|
|
27297
|
+
}
|
|
27006
27298
|
this.log.debug("publishing ".concat(track.kind, " with encodings"), Object.assign(Object.assign({}, this.logContext), {
|
|
27007
27299
|
encodings,
|
|
27008
27300
|
trackInfo: ti
|
|
@@ -27202,13 +27494,19 @@ class Participant extends eventsExports.EventEmitter {
|
|
|
27202
27494
|
negotiationNeeded = true;
|
|
27203
27495
|
}
|
|
27204
27496
|
}
|
|
27205
|
-
|
|
27497
|
+
try {
|
|
27498
|
+
negotiationNeeded = this.engine.removeTrack(trackSender);
|
|
27499
|
+
} catch (e) {
|
|
27500
|
+
this.log.warn(e, this.logContext);
|
|
27206
27501
|
negotiationNeeded = true;
|
|
27207
27502
|
}
|
|
27208
27503
|
if (isLocalVideoTrack(track)) {
|
|
27209
27504
|
for (const [, trackInfo] of track.simulcastCodecs) {
|
|
27210
27505
|
if (trackInfo.sender) {
|
|
27211
|
-
|
|
27506
|
+
try {
|
|
27507
|
+
negotiationNeeded = this.engine.removeTrack(trackInfo.sender);
|
|
27508
|
+
} catch (e) {
|
|
27509
|
+
this.log.warn(e, this.logContext);
|
|
27212
27510
|
negotiationNeeded = true;
|
|
27213
27511
|
}
|
|
27214
27512
|
trackInfo.sender = undefined;
|
|
@@ -27718,14 +28016,14 @@ class DeferrableMap extends Map {
|
|
|
27718
28016
|
this.pending = new Map();
|
|
27719
28017
|
}
|
|
27720
28018
|
set(key, value) {
|
|
27721
|
-
var _a;
|
|
28019
|
+
var _a, _b;
|
|
27722
28020
|
super.set(key, value);
|
|
27723
28021
|
// Resolve any futures waiting on this key.
|
|
27724
|
-
const futures = this.pending.get(key);
|
|
28022
|
+
const futures = (_a = this.pending) === null || _a === void 0 ? void 0 : _a.get(key);
|
|
27725
28023
|
if (futures) {
|
|
27726
28024
|
for (const future of futures) {
|
|
27727
28025
|
if (!future.isResolved) {
|
|
27728
|
-
(
|
|
28026
|
+
(_b = future.resolve) === null || _b === void 0 ? void 0 : _b.call(future, value);
|
|
27729
28027
|
}
|
|
27730
28028
|
}
|
|
27731
28029
|
this.pending.delete(key);
|
|
@@ -27733,7 +28031,7 @@ class DeferrableMap extends Map {
|
|
|
27733
28031
|
return this;
|
|
27734
28032
|
}
|
|
27735
28033
|
get [Symbol.toStringTag]() {
|
|
27736
|
-
return '
|
|
28034
|
+
return 'DeferrableMap';
|
|
27737
28035
|
}
|
|
27738
28036
|
getDeferred(key, signal) {
|
|
27739
28037
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -28056,8 +28354,13 @@ class DeferrableMap extends Map {
|
|
|
28056
28354
|
}
|
|
28057
28355
|
}class RemoteParticipant extends Participant {
|
|
28058
28356
|
/** @internal */
|
|
28059
|
-
static fromParticipantInfo(signalClient, pi, loggerOptions) {
|
|
28060
|
-
return new RemoteParticipant(signalClient, pi.sid, pi.identity, pi.name, pi.metadata, pi.attributes, loggerOptions, pi.kind
|
|
28357
|
+
static fromParticipantInfo(signalClient, pi, loggerOptions, manager) {
|
|
28358
|
+
return new RemoteParticipant(signalClient, pi.sid, pi.identity, pi.name, pi.metadata, pi.attributes, loggerOptions, pi.kind, pi.dataTracks.map(dti => {
|
|
28359
|
+
const info = DataTrackInfo.from(dti);
|
|
28360
|
+
return new RemoteDataTrack(info, manager, {
|
|
28361
|
+
publisherIdentity: pi.identity
|
|
28362
|
+
});
|
|
28363
|
+
}));
|
|
28061
28364
|
}
|
|
28062
28365
|
get logContext() {
|
|
28063
28366
|
return Object.assign(Object.assign({}, super.logContext), {
|
|
@@ -28068,12 +28371,15 @@ class DeferrableMap extends Map {
|
|
|
28068
28371
|
/** @internal */
|
|
28069
28372
|
constructor(signalClient, sid, identity, name, metadata, attributes, loggerOptions) {
|
|
28070
28373
|
let kind = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : ParticipantInfo_Kind.STANDARD;
|
|
28374
|
+
let remoteDataTracks = arguments.length > 8 && arguments[8] !== undefined ? arguments[8] : [];
|
|
28071
28375
|
super(sid, identity || '', name, metadata, attributes, loggerOptions, kind);
|
|
28072
28376
|
this.signalClient = signalClient;
|
|
28073
28377
|
this.trackPublications = new Map();
|
|
28074
28378
|
this.audioTrackPublications = new Map();
|
|
28075
28379
|
this.videoTrackPublications = new Map();
|
|
28076
|
-
this.dataTracks = new DeferrableMap(
|
|
28380
|
+
this.dataTracks = new DeferrableMap(remoteDataTracks.map(remoteDataTrack => {
|
|
28381
|
+
return [remoteDataTrack.info.name, remoteDataTrack];
|
|
28382
|
+
}));
|
|
28077
28383
|
this.volumeMap = new Map();
|
|
28078
28384
|
}
|
|
28079
28385
|
addTrackPublication(publication) {
|
|
@@ -28538,7 +28844,7 @@ class Room extends eventsExports.EventEmitter {
|
|
|
28538
28844
|
this.maybeCreateEngine();
|
|
28539
28845
|
}
|
|
28540
28846
|
if ((_b = this.regionUrlProvider) === null || _b === void 0 ? void 0 : _b.isCloud()) {
|
|
28541
|
-
this.engine.
|
|
28847
|
+
this.engine.setRegionStrategy(this.createRegionStrategy());
|
|
28542
28848
|
}
|
|
28543
28849
|
this.acquireAudioContext();
|
|
28544
28850
|
this.connOptions = Object.assign(Object.assign({}, roomConnectOptionDefaults), opts);
|
|
@@ -29367,18 +29673,7 @@ class Room extends eventsExports.EventEmitter {
|
|
|
29367
29673
|
}).on(EngineEvent.DCBufferStatusChanged, (status, kind) => {
|
|
29368
29674
|
this.emit(RoomEvent.DCBufferStatusChanged, status, kind);
|
|
29369
29675
|
}).on(EngineEvent.LocalTrackSubscribed, subscribedSid => {
|
|
29370
|
-
|
|
29371
|
-
let {
|
|
29372
|
-
trackSid
|
|
29373
|
-
} = _ref2;
|
|
29374
|
-
return trackSid === subscribedSid;
|
|
29375
|
-
});
|
|
29376
|
-
if (!trackPublication) {
|
|
29377
|
-
this.log.warn('could not find local track subscription for subscribed event', this.logContext);
|
|
29378
|
-
return;
|
|
29379
|
-
}
|
|
29380
|
-
this.localParticipant.emit(ParticipantEvent.LocalTrackSubscribed, trackPublication);
|
|
29381
|
-
this.emitWhenConnected(RoomEvent.LocalTrackSubscribed, trackPublication, this.localParticipant);
|
|
29676
|
+
this.handleLocalTrackSubscribed(subscribedSid);
|
|
29382
29677
|
}).on(EngineEvent.RoomMoved, roomMoved => {
|
|
29383
29678
|
this.log.debug('room moved', roomMoved);
|
|
29384
29679
|
if (roomMoved.room) {
|
|
@@ -29414,8 +29709,8 @@ class Room extends eventsExports.EventEmitter {
|
|
|
29414
29709
|
}
|
|
29415
29710
|
this.outgoingDataTrackManager.receivedSfuUnpublishResponse(event.info.pubHandle);
|
|
29416
29711
|
}).on(EngineEvent.DataTrackSubscriberHandles, event => {
|
|
29417
|
-
const handleToSidMapping = new Map(Object.entries(event.subHandles).map(
|
|
29418
|
-
let [key, value] =
|
|
29712
|
+
const handleToSidMapping = new Map(Object.entries(event.subHandles).map(_ref2 => {
|
|
29713
|
+
let [key, value] = _ref2;
|
|
29419
29714
|
return [parseInt(key, 10), value.trackSid];
|
|
29420
29715
|
}));
|
|
29421
29716
|
this.incomingDataTrackManager.receivedSfuSubscriberHandles(handleToSidMapping);
|
|
@@ -29433,6 +29728,16 @@ class Room extends eventsExports.EventEmitter {
|
|
|
29433
29728
|
return [participant.identity, participant.dataTracks.map(info => DataTrackInfo.from(info))];
|
|
29434
29729
|
}));
|
|
29435
29730
|
this.incomingDataTrackManager.receiveSfuPublicationUpdates(mapped);
|
|
29731
|
+
}).on(EngineEvent.TokenRefreshed, token => {
|
|
29732
|
+
var _a;
|
|
29733
|
+
(_a = this.regionUrlProvider) === null || _a === void 0 ? void 0 : _a.updateToken(token);
|
|
29734
|
+
}).on(EngineEvent.ServerRegionsReported, regions => {
|
|
29735
|
+
var _a;
|
|
29736
|
+
(_a = this.regionUrlProvider) === null || _a === void 0 ? void 0 : _a.setServerReportedRegions({
|
|
29737
|
+
regionSettings: regions,
|
|
29738
|
+
updatedAtInMs: Date.now(),
|
|
29739
|
+
maxAgeInMs: DEFAULT_MAX_AGE_MS
|
|
29740
|
+
});
|
|
29436
29741
|
});
|
|
29437
29742
|
if (this.localParticipant) {
|
|
29438
29743
|
this.localParticipant.setupEngine(this.engine);
|
|
@@ -29444,6 +29749,17 @@ class Room extends eventsExports.EventEmitter {
|
|
|
29444
29749
|
this.outgoingDataStreamManager.setupEngine(this.engine);
|
|
29445
29750
|
}
|
|
29446
29751
|
}
|
|
29752
|
+
createRegionStrategy() {
|
|
29753
|
+
return {
|
|
29754
|
+
getNextUrl: signal => __awaiter(this, void 0, void 0, function* () {
|
|
29755
|
+
return this.regionUrlProvider ? this.regionUrlProvider.getNextBestRegionUrl(signal) : null;
|
|
29756
|
+
}),
|
|
29757
|
+
resetAttempts: () => {
|
|
29758
|
+
var _a;
|
|
29759
|
+
return (_a = this.regionUrlProvider) === null || _a === void 0 ? void 0 : _a.resetAttempts();
|
|
29760
|
+
}
|
|
29761
|
+
};
|
|
29762
|
+
}
|
|
29447
29763
|
/**
|
|
29448
29764
|
* getLocalDevices abstracts navigator.mediaDevices.enumerateDevices.
|
|
29449
29765
|
* In particular, it requests device permissions by default if needed
|
|
@@ -29521,6 +29837,9 @@ class Room extends eventsExports.EventEmitter {
|
|
|
29521
29837
|
// @ts-expect-error function is private
|
|
29522
29838
|
yield this.engine.client.handleOnClose('simulate disconnect');
|
|
29523
29839
|
break;
|
|
29840
|
+
case 'fail-on-v1-path':
|
|
29841
|
+
this.engine.failNextV1Path();
|
|
29842
|
+
break;
|
|
29524
29843
|
case 'speaker':
|
|
29525
29844
|
req = new SimulateScenario({
|
|
29526
29845
|
scenario: {
|
|
@@ -29844,6 +30163,56 @@ class Room extends eventsExports.EventEmitter {
|
|
|
29844
30163
|
this.emit(RoomEvent.EncryptionError, new Error("Encrypted ".concat(publication.source, " track received from participant ").concat(participant.sid, ", but room does not have encryption enabled!")));
|
|
29845
30164
|
}
|
|
29846
30165
|
}
|
|
30166
|
+
handleLocalTrackSubscribed(subscribedSid) {
|
|
30167
|
+
const findPublication = () => this.localParticipant.getTrackPublications().find(_ref3 => {
|
|
30168
|
+
let {
|
|
30169
|
+
trackSid
|
|
30170
|
+
} = _ref3;
|
|
30171
|
+
return trackSid === subscribedSid;
|
|
30172
|
+
});
|
|
30173
|
+
const trackPublication = findPublication();
|
|
30174
|
+
if (trackPublication) {
|
|
30175
|
+
this.emitLocalTrackSubscribed(trackPublication);
|
|
30176
|
+
return;
|
|
30177
|
+
}
|
|
30178
|
+
// the track publication may not be registered yet if the server signals
|
|
30179
|
+
// the subscription before publishTrack has finished adding the publication.
|
|
30180
|
+
// defer with a timeout until LocalTrackPublished fires for the matching trackSid
|
|
30181
|
+
this.log.debug('deferring LocalTrackSubscribed, publication not yet available', Object.assign(Object.assign({}, this.logContext), {
|
|
30182
|
+
subscribedSid
|
|
30183
|
+
}));
|
|
30184
|
+
const TIMEOUT_MS = 10000;
|
|
30185
|
+
let timer;
|
|
30186
|
+
const onPublished = pub => {
|
|
30187
|
+
if (pub.trackSid === subscribedSid) {
|
|
30188
|
+
cleanup();
|
|
30189
|
+
this.emitLocalTrackSubscribed(pub);
|
|
30190
|
+
}
|
|
30191
|
+
};
|
|
30192
|
+
const cleanup = () => {
|
|
30193
|
+
clearTimeout(timer);
|
|
30194
|
+
this.localParticipant.off(ParticipantEvent.LocalTrackPublished, onPublished);
|
|
30195
|
+
this.off(RoomEvent.Disconnected, cleanup);
|
|
30196
|
+
};
|
|
30197
|
+
this.localParticipant.on(ParticipantEvent.LocalTrackPublished, onPublished);
|
|
30198
|
+
this.once(RoomEvent.Disconnected, cleanup);
|
|
30199
|
+
timer = setTimeout(() => {
|
|
30200
|
+
cleanup();
|
|
30201
|
+
// final attempt in case the publication was added without emitting the event
|
|
30202
|
+
const pub = findPublication();
|
|
30203
|
+
if (pub) {
|
|
30204
|
+
this.emitLocalTrackSubscribed(pub);
|
|
30205
|
+
} else {
|
|
30206
|
+
this.log.warn('could not find local track publication for LocalTrackSubscribed event after timeout', Object.assign(Object.assign({}, this.logContext), {
|
|
30207
|
+
subscribedSid
|
|
30208
|
+
}));
|
|
30209
|
+
}
|
|
30210
|
+
}, TIMEOUT_MS);
|
|
30211
|
+
}
|
|
30212
|
+
emitLocalTrackSubscribed(trackPublication) {
|
|
30213
|
+
this.localParticipant.emit(ParticipantEvent.LocalTrackSubscribed, trackPublication);
|
|
30214
|
+
this.emitWhenConnected(RoomEvent.LocalTrackSubscribed, trackPublication, this.localParticipant);
|
|
30215
|
+
}
|
|
29847
30216
|
handleDisconnect() {
|
|
29848
30217
|
let shouldStopTracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
|
|
29849
30218
|
let reason = arguments.length > 1 ? arguments[1] : undefined;
|
|
@@ -30048,7 +30417,7 @@ class Room extends eventsExports.EventEmitter {
|
|
|
30048
30417
|
participant = RemoteParticipant.fromParticipantInfo(this.engine.client, info, {
|
|
30049
30418
|
loggerContextCb: () => this.logContext,
|
|
30050
30419
|
loggerName: this.options.loggerName
|
|
30051
|
-
});
|
|
30420
|
+
}, this.incomingDataTrackManager);
|
|
30052
30421
|
} else {
|
|
30053
30422
|
participant = new RemoteParticipant(this.engine.client, '', identity, undefined, undefined, undefined, {
|
|
30054
30423
|
loggerContextCb: () => this.logContext,
|