livekit-client 1.9.0 → 1.9.2
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 +6 -5
- package/dist/livekit-client.esm.mjs +200 -58
- 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 +1 -0
- 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 +1 -0
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +4 -0
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/publishUtils.d.ts.map +1 -1
- package/dist/src/room/track/options.d.ts +7 -1
- package/dist/src/room/track/options.d.ts.map +1 -1
- package/dist/src/room/utils.d.ts +3 -0
- package/dist/src/room/utils.d.ts.map +1 -1
- package/dist/ts4.2/src/api/SignalClient.d.ts +1 -0
- package/dist/ts4.2/src/room/RTCEngine.d.ts +1 -0
- package/dist/ts4.2/src/room/Room.d.ts +4 -0
- package/dist/ts4.2/src/room/track/options.d.ts +7 -0
- package/dist/ts4.2/src/room/utils.d.ts +3 -0
- package/package.json +1 -1
- package/src/api/SignalClient.ts +15 -11
- package/src/room/PCTransport.ts +40 -1
- package/src/room/RTCEngine.ts +34 -1
- package/src/room/Room.ts +99 -42
- package/src/room/participant/LocalParticipant.ts +35 -4
- package/src/room/participant/publishUtils.ts +13 -5
- package/src/room/track/options.ts +14 -1
- package/src/room/utils.ts +18 -8
package/README.md
CHANGED
@@ -45,13 +45,12 @@ await room.connect(...);
|
|
45
45
|
|
46
46
|
```typescript
|
47
47
|
import {
|
48
|
-
|
49
|
-
Room,
|
50
|
-
RoomEvent,
|
48
|
+
Participant,
|
51
49
|
RemoteParticipant,
|
52
|
-
RemoteTrackPublication,
|
53
50
|
RemoteTrack,
|
54
|
-
|
51
|
+
RemoteTrackPublication,
|
52
|
+
Room,
|
53
|
+
RoomEvent,
|
55
54
|
} from 'livekit-client';
|
56
55
|
|
57
56
|
// creates a new room with options
|
@@ -296,7 +295,9 @@ You can have a look at the `"browerslist"` section of `package.json` for more de
|
|
296
295
|
If you are targeting legacy browsers, but still want adaptiveStream functionality you'll likely need to use polyfills for [ResizeObserver](https://www.npmjs.com/package/resize-observer-polyfill) and [IntersectionObserver](https://www.npmjs.com/package/intersection-observer).
|
297
296
|
|
298
297
|
<!--BEGIN_REPO_NAV-->
|
298
|
+
|
299
299
|
<br/><table>
|
300
|
+
|
300
301
|
<thead><tr><th colspan="2">LiveKit Ecosystem</th></tr></thead>
|
301
302
|
<tbody>
|
302
303
|
<tr><td>Client SDKs</td><td><a href="https://github.com/livekit/components-js">Components</a> · <b>JavaScript</b> · <a href="https://github.com/livekit/client-sdk-rust">Rust</a> · <a href="https://github.com/livekit/client-sdk-swift">iOS/macOS</a> · <a href="https://github.com/livekit/client-sdk-android">Android</a> · <a href="https://github.com/livekit/client-sdk-flutter">Flutter</a> · <a href="https://github.com/livekit/client-sdk-unity-web">Unity (web)</a> · <a href="https://github.com/livekit/client-sdk-react-native">React Native (beta)</a></td></tr><tr></tr>
|
@@ -14940,7 +14940,7 @@ var uaParser = {exports: {}};
|
|
14940
14940
|
var uaParserExports = uaParser.exports;
|
14941
14941
|
var UAParser = /*@__PURE__*/getDefaultExportFromCjs(uaParserExports);
|
14942
14942
|
|
14943
|
-
var version$1 = "1.9.
|
14943
|
+
var version$1 = "1.9.2";
|
14944
14944
|
|
14945
14945
|
const version = version$1;
|
14946
14946
|
const protocolVersion = 9;
|
@@ -15037,6 +15037,7 @@ function getNewAudioContext() {
|
|
15037
15037
|
}
|
15038
15038
|
|
15039
15039
|
const separator = '|';
|
15040
|
+
const ddExtensionURI = 'https://aomediacodec.github.io/av1-rtp-spec/#dependency-descriptor-rtp-header-extension';
|
15040
15041
|
function unpackStreamId(packed) {
|
15041
15042
|
const parts = packed.split(separator);
|
15042
15043
|
if (parts.length > 1) {
|
@@ -15064,7 +15065,6 @@ function supportsDynacast() {
|
|
15064
15065
|
function supportsAV1() {
|
15065
15066
|
const capabilities = RTCRtpReceiver.getCapabilities('video');
|
15066
15067
|
let hasAV1 = false;
|
15067
|
-
let hasDDExt = false;
|
15068
15068
|
if (capabilities) {
|
15069
15069
|
for (const codec of capabilities.codecs) {
|
15070
15070
|
if (codec.mimeType === 'video/AV1') {
|
@@ -15072,14 +15072,24 @@ function supportsAV1() {
|
|
15072
15072
|
break;
|
15073
15073
|
}
|
15074
15074
|
}
|
15075
|
-
|
15076
|
-
|
15077
|
-
|
15075
|
+
}
|
15076
|
+
return hasAV1;
|
15077
|
+
}
|
15078
|
+
function supportsVP9() {
|
15079
|
+
const capabilities = RTCRtpReceiver.getCapabilities('video');
|
15080
|
+
let hasVP9 = false;
|
15081
|
+
if (capabilities) {
|
15082
|
+
for (const codec of capabilities.codecs) {
|
15083
|
+
if (codec.mimeType === 'video/VP9') {
|
15084
|
+
hasVP9 = true;
|
15078
15085
|
break;
|
15079
15086
|
}
|
15080
15087
|
}
|
15081
15088
|
}
|
15082
|
-
return
|
15089
|
+
return hasVP9;
|
15090
|
+
}
|
15091
|
+
function isSVCCodec(codec) {
|
15092
|
+
return codec === 'av1' || codec === 'vp9';
|
15083
15093
|
}
|
15084
15094
|
function supportsSetSinkId(elm) {
|
15085
15095
|
if (!document) {
|
@@ -15499,13 +15509,7 @@ class SignalClient {
|
|
15499
15509
|
this.handleSignalResponse(resp);
|
15500
15510
|
};
|
15501
15511
|
this.ws.onclose = ev => {
|
15502
|
-
|
15503
|
-
livekitLogger.debug("websocket connection closed: ".concat(ev.reason));
|
15504
|
-
this.isConnected = false;
|
15505
|
-
if (this.onClose) {
|
15506
|
-
this.onClose(ev.reason);
|
15507
|
-
}
|
15508
|
-
this.ws = undefined;
|
15512
|
+
this.handleOnClose(ev.reason);
|
15509
15513
|
};
|
15510
15514
|
});
|
15511
15515
|
}
|
@@ -15774,6 +15778,17 @@ class SignalClient {
|
|
15774
15778
|
}
|
15775
15779
|
this.isReconnecting = false;
|
15776
15780
|
}
|
15781
|
+
handleOnClose(reason) {
|
15782
|
+
if (!this.isConnected) return;
|
15783
|
+
this.clearPingInterval();
|
15784
|
+
this.clearPingTimeout();
|
15785
|
+
livekitLogger.debug("websocket connection closed: ".concat(reason));
|
15786
|
+
this.isConnected = false;
|
15787
|
+
if (this.onClose) {
|
15788
|
+
this.onClose(reason);
|
15789
|
+
}
|
15790
|
+
this.ws = undefined;
|
15791
|
+
}
|
15777
15792
|
handleWSError(ev) {
|
15778
15793
|
livekitLogger.error('websocket error', ev);
|
15779
15794
|
}
|
@@ -15789,9 +15804,7 @@ class SignalClient {
|
|
15789
15804
|
}
|
15790
15805
|
this.pingTimeout = CriticalTimers.setTimeout(() => {
|
15791
15806
|
livekitLogger.warn("ping timeout triggered. last pong received at: ".concat(new Date(Date.now() - this.pingTimeoutDuration * 1000).toUTCString()));
|
15792
|
-
|
15793
|
-
this.onClose('ping timeout');
|
15794
|
-
}
|
15807
|
+
this.handleOnClose('ping timeout');
|
15795
15808
|
}, this.pingTimeoutDuration * 1000);
|
15796
15809
|
}
|
15797
15810
|
/**
|
@@ -16763,6 +16776,7 @@ class PCTransport extends EventEmitter$1 {
|
|
16763
16776
|
if (media.type === 'audio') {
|
16764
16777
|
ensureAudioNackAndStereo(media, [], []);
|
16765
16778
|
} else if (media.type === 'video') {
|
16779
|
+
ensureVideoDDExtensionForSVC(media);
|
16766
16780
|
// mung sdp for codec bitrate setting that can't apply by sendEncoding
|
16767
16781
|
this.trackBitrates.some(trackbr => {
|
16768
16782
|
if (!media.msid || !media.msid.includes(trackbr.sid)) {
|
@@ -16780,6 +16794,9 @@ class PCTransport extends EventEmitter$1 {
|
|
16780
16794
|
if (codecPayload > 0) {
|
16781
16795
|
if (!media.fmtp.some(fmtp => {
|
16782
16796
|
if (fmtp.payload === codecPayload) {
|
16797
|
+
if (!fmtp.config.includes('x-google-start-bitrate')) {
|
16798
|
+
fmtp.config += ";x-google-start-bitrate=".concat(trackbr.maxbr * 0.7);
|
16799
|
+
}
|
16783
16800
|
if (!fmtp.config.includes('x-google-max-bitrate')) {
|
16784
16801
|
fmtp.config += ";x-google-max-bitrate=".concat(trackbr.maxbr);
|
16785
16802
|
}
|
@@ -16789,7 +16806,7 @@ class PCTransport extends EventEmitter$1 {
|
|
16789
16806
|
})) {
|
16790
16807
|
media.fmtp.push({
|
16791
16808
|
payload: codecPayload,
|
16792
|
-
config: "x-google-max-bitrate=".concat(trackbr.maxbr)
|
16809
|
+
config: "x-google-start-bitrate=".concat(trackbr.maxbr * 0.7, ";x-google-max-bitrate=").concat(trackbr.maxbr)
|
16793
16810
|
});
|
16794
16811
|
}
|
16795
16812
|
}
|
@@ -16887,6 +16904,29 @@ function ensureAudioNackAndStereo(media, stereoMids, nackMids) {
|
|
16887
16904
|
}
|
16888
16905
|
}
|
16889
16906
|
}
|
16907
|
+
function ensureVideoDDExtensionForSVC(media) {
|
16908
|
+
var _a, _b, _c, _d;
|
16909
|
+
const codec = (_b = (_a = media.rtp.at(0)) === null || _a === void 0 ? void 0 : _a.codec) === null || _b === void 0 ? void 0 : _b.toLowerCase();
|
16910
|
+
if (!isSVCCodec(codec)) {
|
16911
|
+
return;
|
16912
|
+
}
|
16913
|
+
let maxID = 0;
|
16914
|
+
const ddFound = (_c = media.ext) === null || _c === void 0 ? void 0 : _c.some(ext => {
|
16915
|
+
if (ext.uri === ddExtensionURI) {
|
16916
|
+
return true;
|
16917
|
+
}
|
16918
|
+
if (ext.value > maxID) {
|
16919
|
+
maxID = ext.value;
|
16920
|
+
}
|
16921
|
+
return false;
|
16922
|
+
});
|
16923
|
+
if (!ddFound) {
|
16924
|
+
(_d = media.ext) === null || _d === void 0 ? void 0 : _d.push({
|
16925
|
+
value: maxID + 1,
|
16926
|
+
uri: ddExtensionURI
|
16927
|
+
});
|
16928
|
+
}
|
16929
|
+
}
|
16890
16930
|
function extractStereoAndNackAudioFromOffer(offer) {
|
16891
16931
|
var _a;
|
16892
16932
|
const stereoMids = [];
|
@@ -16996,6 +17036,9 @@ const backupCodecs = ['vp8', 'h264'];
|
|
16996
17036
|
function isBackupCodec(codec) {
|
16997
17037
|
return !!backupCodecs.find(backup => backup === codec);
|
16998
17038
|
}
|
17039
|
+
function isCodecEqual(c1, c2) {
|
17040
|
+
return (c1 === null || c1 === void 0 ? void 0 : c1.toLowerCase().replace(/audio\/|video\//y, '')) === (c2 === null || c2 === void 0 ? void 0 : c2.toLowerCase().replace(/audio\/|video\//y, ''));
|
17041
|
+
}
|
16999
17042
|
var AudioPresets;
|
17000
17043
|
(function (AudioPresets) {
|
17001
17044
|
AudioPresets.telephone = {
|
@@ -18531,7 +18574,7 @@ class RTCEngine extends eventsExports.EventEmitter {
|
|
18531
18574
|
} else {
|
18532
18575
|
livekitLogger.info("could not recover connection after ".concat(this.reconnectAttempts, " attempts, ").concat(Date.now() - this.reconnectStart, "ms. giving up"));
|
18533
18576
|
this.emit(EngineEvent.Disconnected);
|
18534
|
-
this.close();
|
18577
|
+
await this.close();
|
18535
18578
|
}
|
18536
18579
|
} finally {
|
18537
18580
|
this.attemptingReconnect = false;
|
@@ -18752,6 +18795,30 @@ class RTCEngine extends eventsExports.EventEmitter {
|
|
18752
18795
|
async ensurePublisherConnected(kind) {
|
18753
18796
|
await this.ensureDataTransportConnected(kind, false);
|
18754
18797
|
}
|
18798
|
+
/* @internal */
|
18799
|
+
verifyTransport() {
|
18800
|
+
// primary connection
|
18801
|
+
if (!this.primaryPC) {
|
18802
|
+
return false;
|
18803
|
+
}
|
18804
|
+
if (this.primaryPC.connectionState === 'closed' || this.primaryPC.connectionState === 'failed') {
|
18805
|
+
return false;
|
18806
|
+
}
|
18807
|
+
// also verify publisher connection if it's needed or different
|
18808
|
+
if (this.hasPublished && this.subscriberPrimary) {
|
18809
|
+
if (!this.publisher) {
|
18810
|
+
return false;
|
18811
|
+
}
|
18812
|
+
if (this.publisher.pc.connectionState === 'closed' || this.publisher.pc.connectionState === 'failed') {
|
18813
|
+
return false;
|
18814
|
+
}
|
18815
|
+
}
|
18816
|
+
// ensure signal is connected
|
18817
|
+
if (!this.client.ws || this.client.ws.readyState === WebSocket.CLOSED) {
|
18818
|
+
return false;
|
18819
|
+
}
|
18820
|
+
return true;
|
18821
|
+
}
|
18755
18822
|
/** @internal */
|
18756
18823
|
negotiate() {
|
18757
18824
|
// observe signal state
|
@@ -21064,7 +21131,7 @@ function computeVideoEncodings(isScreenShare, width, height, options) {
|
|
21064
21131
|
livekitLogger.debug('using video encoding', videoEncoding);
|
21065
21132
|
}
|
21066
21133
|
const original = new VideoPreset(width, height, videoEncoding.maxBitrate, videoEncoding.maxFramerate);
|
21067
|
-
if (scalabilityMode && videoCodec
|
21134
|
+
if (scalabilityMode && isSVCCodec(videoCodec)) {
|
21068
21135
|
livekitLogger.debug("using svc with scalabilityMode ".concat(scalabilityMode));
|
21069
21136
|
const encodings = [];
|
21070
21137
|
// svc use first encoding as the original, so we sort encoding from high to low
|
@@ -21164,8 +21231,13 @@ function determineAppropriateEncoding(isScreenShare, width, height, codec) {
|
|
21164
21231
|
if (codec) {
|
21165
21232
|
switch (codec) {
|
21166
21233
|
case 'av1':
|
21234
|
+
encoding = _objectSpread2({}, encoding);
|
21167
21235
|
encoding.maxBitrate = encoding.maxBitrate * 0.7;
|
21168
21236
|
break;
|
21237
|
+
case 'vp9':
|
21238
|
+
encoding = _objectSpread2({}, encoding);
|
21239
|
+
encoding.maxBitrate = encoding.maxBitrate * 0.85;
|
21240
|
+
break;
|
21169
21241
|
}
|
21170
21242
|
}
|
21171
21243
|
return encoding;
|
@@ -21205,13 +21277,15 @@ function encodingsFromPresets(width, height, presets) {
|
|
21205
21277
|
}
|
21206
21278
|
const size = Math.min(width, height);
|
21207
21279
|
const rid = videoRids[idx];
|
21208
|
-
|
21280
|
+
const encoding = {
|
21209
21281
|
rid,
|
21210
21282
|
scaleResolutionDownBy: Math.max(1, size / Math.min(preset.width, preset.height)),
|
21211
|
-
maxBitrate: preset.encoding.maxBitrate
|
21212
|
-
|
21213
|
-
|
21214
|
-
|
21283
|
+
maxBitrate: preset.encoding.maxBitrate
|
21284
|
+
};
|
21285
|
+
if (preset.encoding.maxFramerate) {
|
21286
|
+
encoding.maxFramerate = preset.encoding.maxFramerate;
|
21287
|
+
}
|
21288
|
+
encodings.push(encoding);
|
21215
21289
|
});
|
21216
21290
|
return encodings;
|
21217
21291
|
}
|
@@ -21784,10 +21858,13 @@ class LocalParticipant extends Participant {
|
|
21784
21858
|
// we frequently get no data on layer 0 when enabled
|
21785
21859
|
opts.simulcast = false;
|
21786
21860
|
}
|
21787
|
-
// require full AV1 SVC support prior to using it
|
21861
|
+
// require full AV1/VP9 SVC support prior to using it
|
21788
21862
|
if (opts.videoCodec === 'av1' && !supportsAV1()) {
|
21789
21863
|
opts.videoCodec = undefined;
|
21790
21864
|
}
|
21865
|
+
if (opts.videoCodec === 'vp9' && !supportsVP9()) {
|
21866
|
+
opts.videoCodec = undefined;
|
21867
|
+
}
|
21791
21868
|
// handle track actions
|
21792
21869
|
track.on(TrackEvent.Muted, this.onTrackMuted);
|
21793
21870
|
track.on(TrackEvent.Unmuted, this.onTrackUnmuted);
|
@@ -21825,7 +21902,7 @@ class LocalParticipant extends Participant {
|
|
21825
21902
|
req.height = dims.height;
|
21826
21903
|
// for svc codecs, disable simulcast and use vp8 for backup codec
|
21827
21904
|
if (track instanceof LocalVideoTrack) {
|
21828
|
-
if ((opts
|
21905
|
+
if (isSVCCodec(opts.videoCodec)) {
|
21829
21906
|
// set scalabilityMode to 'L3T3' by default
|
21830
21907
|
opts.scalabilityMode = (_c = opts.scalabilityMode) !== null && _c !== void 0 ? _c : 'L3T3';
|
21831
21908
|
}
|
@@ -21856,6 +21933,28 @@ class LocalParticipant extends Participant {
|
|
21856
21933
|
throw new UnexpectedConnectionState('cannot publish track when not connected');
|
21857
21934
|
}
|
21858
21935
|
const ti = await this.engine.addTrack(req);
|
21936
|
+
let primaryCodecSupported = false;
|
21937
|
+
let backupCodecSupported = false;
|
21938
|
+
ti.codecs.forEach(c => {
|
21939
|
+
if (isCodecEqual(c.mimeType, opts.videoCodec)) {
|
21940
|
+
primaryCodecSupported = true;
|
21941
|
+
} else if (opts.backupCodec && isCodecEqual(c.mimeType, opts.backupCodec.codec)) {
|
21942
|
+
backupCodecSupported = true;
|
21943
|
+
}
|
21944
|
+
});
|
21945
|
+
if (req.simulcastCodecs.length > 0) {
|
21946
|
+
if (!primaryCodecSupported && !backupCodecSupported) {
|
21947
|
+
throw Error('cannot publish track, codec not supported by server');
|
21948
|
+
}
|
21949
|
+
if (!primaryCodecSupported && opts.backupCodec) {
|
21950
|
+
const backupCodec = opts.backupCodec;
|
21951
|
+
opts = _objectSpread2({}, opts);
|
21952
|
+
livekitLogger.debug("primary codec ".concat(opts.videoCodec, " not supported, fallback to ").concat(backupCodec.codec));
|
21953
|
+
opts.videoCodec = backupCodec.codec;
|
21954
|
+
opts.videoEncoding = backupCodec.encoding;
|
21955
|
+
encodings = simEncodings;
|
21956
|
+
}
|
21957
|
+
}
|
21859
21958
|
const publication = new LocalTrackPublication(track.kind, ti, track);
|
21860
21959
|
// save options for when it needs to be republished again
|
21861
21960
|
publication.options = opts;
|
@@ -21869,7 +21968,7 @@ class LocalParticipant extends Participant {
|
|
21869
21968
|
});
|
21870
21969
|
// store RTPSender
|
21871
21970
|
track.sender = await this.engine.createSender(track, opts, encodings);
|
21872
|
-
if (track.codec
|
21971
|
+
if (track.codec && isSVCCodec(track.codec) && encodings && ((_d = encodings[0]) === null || _d === void 0 ? void 0 : _d.maxBitrate)) {
|
21873
21972
|
this.engine.publisher.setTrackCodecBitrate(req.cid, track.codec, encodings[0].maxBitrate / 1000);
|
21874
21973
|
}
|
21875
21974
|
this.engine.negotiate();
|
@@ -22170,6 +22269,7 @@ var ConnectionState;
|
|
22170
22269
|
ConnectionState["Connected"] = "connected";
|
22171
22270
|
ConnectionState["Reconnecting"] = "reconnecting";
|
22172
22271
|
})(ConnectionState || (ConnectionState = {}));
|
22272
|
+
const connectionReconcileFrequency = 2 * 1000;
|
22173
22273
|
/** @deprecated RoomState has been renamed to [[ConnectionState]] */
|
22174
22274
|
const RoomState = ConnectionState;
|
22175
22275
|
/**
|
@@ -22349,6 +22449,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22349
22449
|
}
|
22350
22450
|
this.setAndEmitConnectionState(ConnectionState.Connected);
|
22351
22451
|
this.emit(RoomEvent.Connected);
|
22452
|
+
this.registerConnectionReconcile();
|
22352
22453
|
};
|
22353
22454
|
/**
|
22354
22455
|
* disconnects the room, emits [[RoomEvent.Disconnected]]
|
@@ -22392,6 +22493,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22392
22493
|
await this.disconnect();
|
22393
22494
|
};
|
22394
22495
|
this.handleRestarting = () => {
|
22496
|
+
this.clearConnectionReconcile();
|
22395
22497
|
// also unwind existing participants & existing subscriptions
|
22396
22498
|
for (const p of this.participants.values()) {
|
22397
22499
|
this.handleParticipantDisconnected(p.sid, p);
|
@@ -22404,6 +22506,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22404
22506
|
livekitLogger.debug("signal reconnected to server", {
|
22405
22507
|
region: joinResponse.serverRegion
|
22406
22508
|
});
|
22509
|
+
this.cachedParticipantSids = [];
|
22407
22510
|
this.applyJoinResponse(joinResponse);
|
22408
22511
|
try {
|
22409
22512
|
// unpublish & republish tracks
|
@@ -22447,6 +22550,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22447
22550
|
}
|
22448
22551
|
this.setAndEmitConnectionState(ConnectionState.Connected);
|
22449
22552
|
this.emit(RoomEvent.Reconnected);
|
22553
|
+
this.registerConnectionReconcile();
|
22450
22554
|
// emit participant connected events after connection has been re-established
|
22451
22555
|
this.participants.forEach(participant => {
|
22452
22556
|
this.emit(RoomEvent.ParticipantConnected, participant);
|
@@ -22647,6 +22751,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22647
22751
|
};
|
22648
22752
|
this.setMaxListeners(100);
|
22649
22753
|
this.participants = new Map();
|
22754
|
+
this.cachedParticipantSids = [];
|
22650
22755
|
this.identityToSid = new Map();
|
22651
22756
|
this.options = _objectSpread2(_objectSpread2({}, roomOptionDefaults), options);
|
22652
22757
|
this.options.audioCaptureDefaults = _objectSpread2(_objectSpread2({}, audioDefaults), options === null || options === void 0 ? void 0 : options.audioCaptureDefaults);
|
@@ -22687,7 +22792,7 @@ class Room extends eventsExports.EventEmitter {
|
|
22687
22792
|
return (_b = (_a = this.roomInfo) === null || _a === void 0 ? void 0 : _a.numPublishers) !== null && _b !== void 0 ? _b : 0;
|
22688
22793
|
}
|
22689
22794
|
maybeCreateEngine() {
|
22690
|
-
if (this.engine) {
|
22795
|
+
if (this.engine && !this.engine.isClosed) {
|
22691
22796
|
return;
|
22692
22797
|
}
|
22693
22798
|
this.engine = new RTCEngine(this.options);
|
@@ -22702,13 +22807,20 @@ class Room extends eventsExports.EventEmitter {
|
|
22702
22807
|
}).on(EngineEvent.Disconnected, reason => {
|
22703
22808
|
this.handleDisconnect(this.options.stopLocalTrackOnUnpublish, reason);
|
22704
22809
|
}).on(EngineEvent.ActiveSpeakersUpdate, this.handleActiveSpeakersUpdate).on(EngineEvent.DataPacketReceived, this.handleDataPacket).on(EngineEvent.Resuming, () => {
|
22810
|
+
this.clearConnectionReconcile();
|
22705
22811
|
if (this.setAndEmitConnectionState(ConnectionState.Reconnecting)) {
|
22706
22812
|
this.emit(RoomEvent.Reconnecting);
|
22707
22813
|
}
|
22814
|
+
this.cachedParticipantSids = Array.from(this.participants.keys());
|
22708
22815
|
}).on(EngineEvent.Resumed, () => {
|
22709
22816
|
this.setAndEmitConnectionState(ConnectionState.Connected);
|
22710
22817
|
this.emit(RoomEvent.Reconnected);
|
22818
|
+
this.registerConnectionReconcile();
|
22711
22819
|
this.updateSubscriptions();
|
22820
|
+
// once reconnected, figure out if any participants connected during reconnect and emit events for it
|
22821
|
+
const diffParticipants = Array.from(this.participants.values()).filter(p => !this.cachedParticipantSids.includes(p.sid));
|
22822
|
+
diffParticipants.forEach(p => this.emit(RoomEvent.ParticipantConnected, p));
|
22823
|
+
this.cachedParticipantSids = [];
|
22712
22824
|
}).on(EngineEvent.SignalResumed, () => {
|
22713
22825
|
if (this.state === ConnectionState.Reconnecting) {
|
22714
22826
|
this.sendSyncState();
|
@@ -23014,41 +23126,45 @@ class Room extends eventsExports.EventEmitter {
|
|
23014
23126
|
let shouldStopTracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
|
23015
23127
|
let reason = arguments.length > 1 ? arguments[1] : undefined;
|
23016
23128
|
var _a;
|
23129
|
+
this.clearConnectionReconcile();
|
23017
23130
|
if (this.state === ConnectionState.Disconnected) {
|
23018
23131
|
return;
|
23019
23132
|
}
|
23020
|
-
|
23021
|
-
|
23022
|
-
p.
|
23133
|
+
try {
|
23134
|
+
this.participants.forEach(p => {
|
23135
|
+
p.tracks.forEach(pub => {
|
23136
|
+
p.unpublishTrack(pub.trackSid);
|
23137
|
+
});
|
23023
23138
|
});
|
23024
|
-
|
23025
|
-
|
23026
|
-
|
23027
|
-
|
23028
|
-
|
23029
|
-
|
23030
|
-
|
23031
|
-
|
23032
|
-
|
23139
|
+
this.localParticipant.tracks.forEach(pub => {
|
23140
|
+
var _a, _b;
|
23141
|
+
if (pub.track) {
|
23142
|
+
this.localParticipant.unpublishTrack(pub.track, shouldStopTracks);
|
23143
|
+
}
|
23144
|
+
if (shouldStopTracks) {
|
23145
|
+
(_a = pub.track) === null || _a === void 0 ? void 0 : _a.detach();
|
23146
|
+
(_b = pub.track) === null || _b === void 0 ? void 0 : _b.stop();
|
23147
|
+
}
|
23148
|
+
});
|
23149
|
+
this.localParticipant.off(ParticipantEvent.ParticipantMetadataChanged, this.onLocalParticipantMetadataChanged).off(ParticipantEvent.ParticipantNameChanged, this.onLocalParticipantNameChanged).off(ParticipantEvent.TrackMuted, this.onLocalTrackMuted).off(ParticipantEvent.TrackUnmuted, this.onLocalTrackUnmuted).off(ParticipantEvent.LocalTrackPublished, this.onLocalTrackPublished).off(ParticipantEvent.LocalTrackUnpublished, this.onLocalTrackUnpublished).off(ParticipantEvent.ConnectionQualityChanged, this.onLocalConnectionQualityChanged).off(ParticipantEvent.MediaDevicesError, this.onMediaDevicesError).off(ParticipantEvent.ParticipantPermissionsChanged, this.onLocalParticipantPermissionsChanged);
|
23150
|
+
this.localParticipant.tracks.clear();
|
23151
|
+
this.localParticipant.videoTracks.clear();
|
23152
|
+
this.localParticipant.audioTracks.clear();
|
23153
|
+
this.participants.clear();
|
23154
|
+
this.activeSpeakers = [];
|
23155
|
+
if (this.audioContext && typeof this.options.expWebAudioMix === 'boolean') {
|
23156
|
+
this.audioContext.close();
|
23157
|
+
this.audioContext = undefined;
|
23158
|
+
}
|
23159
|
+
if (isWeb()) {
|
23160
|
+
window.removeEventListener('beforeunload', this.onPageLeave);
|
23161
|
+
window.removeEventListener('pagehide', this.onPageLeave);
|
23162
|
+
(_a = navigator.mediaDevices) === null || _a === void 0 ? void 0 : _a.removeEventListener('devicechange', this.handleDeviceChange);
|
23033
23163
|
}
|
23034
|
-
}
|
23035
|
-
|
23036
|
-
|
23037
|
-
this.localParticipant.videoTracks.clear();
|
23038
|
-
this.localParticipant.audioTracks.clear();
|
23039
|
-
this.participants.clear();
|
23040
|
-
this.activeSpeakers = [];
|
23041
|
-
if (this.audioContext && typeof this.options.expWebAudioMix === 'boolean') {
|
23042
|
-
this.audioContext.close();
|
23043
|
-
this.audioContext = undefined;
|
23044
|
-
}
|
23045
|
-
if (isWeb()) {
|
23046
|
-
window.removeEventListener('beforeunload', this.onPageLeave);
|
23047
|
-
window.removeEventListener('pagehide', this.onPageLeave);
|
23048
|
-
(_a = navigator.mediaDevices) === null || _a === void 0 ? void 0 : _a.removeEventListener('devicechange', this.handleDeviceChange);
|
23164
|
+
} finally {
|
23165
|
+
this.setAndEmitConnectionState(ConnectionState.Disconnected);
|
23166
|
+
this.emit(RoomEvent.Disconnected, reason);
|
23049
23167
|
}
|
23050
|
-
this.setAndEmitConnectionState(ConnectionState.Disconnected);
|
23051
|
-
this.emit(RoomEvent.Disconnected, reason);
|
23052
23168
|
}
|
23053
23169
|
handleParticipantDisconnected(sid, participant) {
|
23054
23170
|
// remove and send event
|
@@ -23201,6 +23317,32 @@ class Room extends eventsExports.EventEmitter {
|
|
23201
23317
|
}
|
23202
23318
|
}
|
23203
23319
|
}
|
23320
|
+
registerConnectionReconcile() {
|
23321
|
+
this.clearConnectionReconcile();
|
23322
|
+
let consecutiveFailures = 0;
|
23323
|
+
this.connectionReconcileInterval = CriticalTimers.setInterval(() => {
|
23324
|
+
if (
|
23325
|
+
// ensure we didn't tear it down
|
23326
|
+
!this.engine ||
|
23327
|
+
// engine detected close, but Room missed it
|
23328
|
+
this.engine.isClosed ||
|
23329
|
+
// transports failed without notifying engine
|
23330
|
+
!this.engine.verifyTransport()) {
|
23331
|
+
consecutiveFailures++;
|
23332
|
+
livekitLogger.warn('detected connection state mismatch', {
|
23333
|
+
numFailures: consecutiveFailures
|
23334
|
+
});
|
23335
|
+
if (consecutiveFailures >= 3) this.handleDisconnect(this.options.stopLocalTrackOnUnpublish, DisconnectReason.UNKNOWN_REASON);
|
23336
|
+
} else {
|
23337
|
+
consecutiveFailures = 0;
|
23338
|
+
}
|
23339
|
+
}, connectionReconcileFrequency);
|
23340
|
+
}
|
23341
|
+
clearConnectionReconcile() {
|
23342
|
+
if (this.connectionReconcileInterval) {
|
23343
|
+
CriticalTimers.clearInterval(this.connectionReconcileInterval);
|
23344
|
+
}
|
23345
|
+
}
|
23204
23346
|
setAndEmitConnectionState(state) {
|
23205
23347
|
if (state === this.state) {
|
23206
23348
|
// unchanged
|
@@ -23784,5 +23926,5 @@ class ConnectionCheck extends EventEmitter$1 {
|
|
23784
23926
|
}
|
23785
23927
|
}
|
23786
23928
|
|
23787
|
-
export { AudioPresets, ConnectionCheck, ConnectionError, ConnectionQuality, ConnectionState, CriticalTimers, DataPacket_Kind, DefaultReconnectPolicy, DeviceUnsupportedError, DisconnectReason, EngineEvent, LivekitError, LocalAudioTrack, LocalParticipant, LocalTrack, LocalTrackPublication, LocalVideoTrack, LogLevel, MediaDeviceFailure, NegotiationError, Participant, ParticipantEvent, PublishDataError, RemoteAudioTrack, RemoteParticipant, RemoteTrack, RemoteTrackPublication, RemoteVideoTrack, Room, RoomEvent, RoomState, ScreenSharePresets, Track, TrackEvent, TrackInvalidError, TrackPublication, UnexpectedConnectionState, UnsupportedServer, VideoPreset, VideoPresets, VideoPresets43, VideoQuality, attachToElement, createAudioAnalyser, createLocalAudioTrack, createLocalScreenTracks, createLocalTracks, createLocalVideoTrack, detachTrack, getEmptyAudioStreamTrack, getEmptyVideoStreamTrack, isBackupCodec, isBrowserSupported, protocolVersion, setLogExtension, setLogLevel, supportsAV1, supportsAdaptiveStream, supportsDynacast, version };
|
23929
|
+
export { AudioPresets, ConnectionCheck, ConnectionError, ConnectionQuality, ConnectionState, CriticalTimers, DataPacket_Kind, DefaultReconnectPolicy, DeviceUnsupportedError, DisconnectReason, EngineEvent, LivekitError, LocalAudioTrack, LocalParticipant, LocalTrack, LocalTrackPublication, LocalVideoTrack, LogLevel, MediaDeviceFailure, NegotiationError, Participant, ParticipantEvent, PublishDataError, RemoteAudioTrack, RemoteParticipant, RemoteTrack, RemoteTrackPublication, RemoteVideoTrack, Room, RoomEvent, RoomState, ScreenSharePresets, Track, TrackEvent, TrackInvalidError, TrackPublication, UnexpectedConnectionState, UnsupportedServer, VideoPreset, VideoPresets, VideoPresets43, VideoQuality, attachToElement, createAudioAnalyser, createLocalAudioTrack, createLocalScreenTracks, createLocalTracks, createLocalVideoTrack, detachTrack, getEmptyAudioStreamTrack, getEmptyVideoStreamTrack, isBackupCodec, isBrowserSupported, isCodecEqual, protocolVersion, setLogExtension, setLogLevel, supportsAV1, supportsAdaptiveStream, supportsDynacast, version };
|
23788
23930
|
//# sourceMappingURL=livekit-client.esm.mjs.map
|