livekit-client 1.9.1 → 1.9.3
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/livekit-client.esm.mjs +3338 -3037
- 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 +98 -97
- package/dist/src/api/SignalClient.d.ts.map +1 -1
- package/dist/src/connectionHelper/ConnectionCheck.d.ts +25 -24
- package/dist/src/connectionHelper/ConnectionCheck.d.ts.map +1 -1
- package/dist/src/connectionHelper/checks/Checker.d.ts +58 -58
- package/dist/src/connectionHelper/checks/publishAudio.d.ts +5 -5
- package/dist/src/connectionHelper/checks/publishVideo.d.ts +5 -5
- package/dist/src/connectionHelper/checks/reconnect.d.ts +5 -5
- package/dist/src/connectionHelper/checks/turn.d.ts +5 -5
- package/dist/src/connectionHelper/checks/webrtc.d.ts +5 -5
- package/dist/src/connectionHelper/checks/websocket.d.ts +5 -5
- package/dist/src/index.d.ts +34 -31
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/logger.d.ts +25 -25
- package/dist/src/options.d.ts +98 -98
- package/dist/src/proto/google/protobuf/timestamp.d.ts +145 -145
- package/dist/src/proto/livekit_models.d.ts +2300 -2300
- package/dist/src/proto/livekit_rtc.d.ts +14032 -14032
- package/dist/src/room/DefaultReconnectPolicy.d.ts +7 -7
- package/dist/src/room/DeviceManager.d.ts +8 -8
- package/dist/src/room/PCTransport.d.ts +37 -37
- package/dist/src/room/PCTransport.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +126 -120
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/ReconnectPolicy.d.ts +23 -23
- package/dist/src/room/RegionUrlProvider.d.ts +13 -13
- package/dist/src/room/Room.d.ts +232 -229
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/defaults.d.ts +7 -7
- package/dist/src/room/defaults.d.ts.map +1 -1
- package/dist/src/room/errors.d.ts +42 -42
- package/dist/src/room/events.d.ts +455 -455
- package/dist/src/room/participant/LocalParticipant.d.ts +170 -170
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/Participant.d.ts +93 -93
- package/dist/src/room/participant/ParticipantTrackPermission.d.ts +25 -25
- package/dist/src/room/participant/RemoteParticipant.d.ts +52 -51
- package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/publishUtils.d.ts +19 -18
- package/dist/src/room/participant/publishUtils.d.ts.map +1 -1
- package/dist/src/room/stats.d.ts +66 -66
- package/dist/src/room/timers.d.ts +12 -12
- package/dist/src/room/track/LocalAudioTrack.d.ts +24 -24
- package/dist/src/room/track/LocalAudioTrack.d.ts.map +1 -1
- package/dist/src/room/track/LocalTrack.d.ts +43 -43
- package/dist/src/room/track/LocalTrackPublication.d.ts +37 -37
- package/dist/src/room/track/LocalVideoTrack.d.ts +53 -53
- package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
- package/dist/src/room/track/RemoteAudioTrack.d.ts +52 -52
- package/dist/src/room/track/RemoteAudioTrack.d.ts.map +1 -1
- package/dist/src/room/track/RemoteTrack.d.ts +14 -14
- package/dist/src/room/track/RemoteTrackPublication.d.ts +60 -60
- package/dist/src/room/track/RemoteVideoTrack.d.ts +52 -52
- package/dist/src/room/track/RemoteVideoTrack.d.ts.map +1 -1
- package/dist/src/room/track/Track.d.ts +124 -124
- package/dist/src/room/track/TrackPublication.d.ts +67 -67
- package/dist/src/room/track/create.d.ts +23 -23
- package/dist/src/room/track/create.d.ts.map +1 -1
- package/dist/src/room/track/options.d.ts +255 -247
- package/dist/src/room/track/options.d.ts.map +1 -1
- package/dist/src/room/track/types.d.ts +22 -22
- package/dist/src/room/track/utils.d.ts +13 -13
- package/dist/src/room/types.d.ts +25 -25
- package/dist/src/room/utils.d.ts +86 -85
- package/dist/src/room/utils.d.ts.map +1 -1
- package/dist/src/test/MockMediaStreamTrack.d.ts +25 -25
- package/dist/src/test/mocks.d.ts +10 -10
- package/dist/src/version.d.ts +2 -2
- package/dist/ts4.2/src/api/SignalClient.d.ts +1 -0
- package/dist/ts4.2/src/connectionHelper/ConnectionCheck.d.ts +2 -1
- package/dist/ts4.2/src/index.d.ts +8 -6
- package/dist/ts4.2/src/room/PCTransport.d.ts +1 -1
- package/dist/ts4.2/src/room/RTCEngine.d.ts +7 -1
- package/dist/ts4.2/src/room/Room.d.ts +3 -0
- package/dist/ts4.2/src/room/defaults.d.ts +1 -1
- package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +4 -4
- package/dist/ts4.2/src/room/participant/RemoteParticipant.d.ts +2 -1
- package/dist/ts4.2/src/room/participant/publishUtils.d.ts +2 -1
- package/dist/ts4.2/src/room/track/LocalAudioTrack.d.ts +1 -1
- package/dist/ts4.2/src/room/track/LocalVideoTrack.d.ts +1 -1
- package/dist/ts4.2/src/room/track/RemoteAudioTrack.d.ts +1 -1
- package/dist/ts4.2/src/room/track/create.d.ts +1 -1
- package/dist/ts4.2/src/room/track/options.d.ts +10 -2
- package/dist/ts4.2/src/room/utils.d.ts +1 -0
- package/package.json +12 -12
- package/src/api/SignalClient.ts +19 -16
- package/src/connectionHelper/ConnectionCheck.ts +2 -1
- package/src/index.ts +13 -8
- package/src/room/PCTransport.ts +9 -5
- package/src/room/RTCEngine.ts +44 -4
- package/src/room/Room.ts +87 -42
- package/src/room/defaults.ts +6 -4
- package/src/room/participant/LocalParticipant.ts +10 -9
- package/src/room/participant/RemoteParticipant.ts +2 -1
- package/src/room/participant/publishUtils.ts +7 -5
- package/src/room/track/LocalAudioTrack.ts +2 -1
- package/src/room/track/LocalVideoTrack.ts +3 -2
- package/src/room/track/RemoteAudioTrack.ts +2 -1
- package/src/room/track/RemoteVideoTrack.ts +4 -8
- package/src/room/track/create.ts +2 -2
- package/src/room/track/options.ts +23 -7
- package/src/room/utils.ts +15 -2
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "livekit-client",
|
3
|
-
"version": "1.9.
|
3
|
+
"version": "1.9.3",
|
4
4
|
"description": "JavaScript/TypeScript client SDK for LiveKit",
|
5
5
|
"main": "./dist/livekit-client.umd.js",
|
6
6
|
"unpkg": "./dist/livekit-client.umd.js",
|
@@ -51,8 +51,8 @@
|
|
51
51
|
"webrtc-adapter": "^8.1.1"
|
52
52
|
},
|
53
53
|
"devDependencies": {
|
54
|
-
"@babel/core": "7.21.
|
55
|
-
"@babel/preset-env": "7.21.
|
54
|
+
"@babel/core": "7.21.8",
|
55
|
+
"@babel/preset-env": "7.21.5",
|
56
56
|
"@changesets/cli": "2.26.1",
|
57
57
|
"@livekit/changesets-changelog-github": "^0.0.4",
|
58
58
|
"@rollup/plugin-babel": "6.0.3",
|
@@ -61,31 +61,31 @@
|
|
61
61
|
"@rollup/plugin-node-resolve": "15.0.2",
|
62
62
|
"@rollup/plugin-terser": "^0.4.0",
|
63
63
|
"@trivago/prettier-plugin-sort-imports": "^4.1.1",
|
64
|
-
"@types/jest": "29.5.
|
64
|
+
"@types/jest": "29.5.1",
|
65
65
|
"@types/sdp-transform": "2.4.6",
|
66
66
|
"@types/ua-parser-js": "0.7.36",
|
67
67
|
"@types/ws": "8.5.4",
|
68
|
-
"@typescript-eslint/eslint-plugin": "5.
|
69
|
-
"@typescript-eslint/parser": "5.
|
68
|
+
"@typescript-eslint/eslint-plugin": "5.59.2",
|
69
|
+
"@typescript-eslint/parser": "5.59.2",
|
70
70
|
"downlevel-dts": "^0.11.0",
|
71
|
-
"eslint": "8.
|
71
|
+
"eslint": "8.39.0",
|
72
72
|
"eslint-config-airbnb-typescript": "17.0.0",
|
73
73
|
"eslint-config-prettier": "8.8.0",
|
74
74
|
"eslint-plugin-import": "2.27.5",
|
75
75
|
"gh-pages": "5.0.0",
|
76
76
|
"jest": "29.5.0",
|
77
77
|
"prettier": "^2.8.8",
|
78
|
-
"rollup": "3.
|
78
|
+
"rollup": "3.21.3",
|
79
79
|
"rollup-plugin-delete": "^2.0.0",
|
80
80
|
"rollup-plugin-filesize": "10.0.0",
|
81
81
|
"rollup-plugin-re": "1.0.7",
|
82
82
|
"rollup-plugin-typescript2": "0.34.1",
|
83
83
|
"ts-jest": "29.1.0",
|
84
|
-
"ts-proto": "1.
|
85
|
-
"typedoc": "0.24.
|
84
|
+
"ts-proto": "1.147.1",
|
85
|
+
"typedoc": "0.24.6",
|
86
86
|
"typedoc-plugin-no-inherit": "1.4.0",
|
87
|
-
"typescript": "
|
88
|
-
"vite": "4.
|
87
|
+
"typescript": "5.0.4",
|
88
|
+
"vite": "4.3.4"
|
89
89
|
},
|
90
90
|
"browserslist": [
|
91
91
|
"safari >= 11",
|
package/src/api/SignalClient.ts
CHANGED
@@ -321,14 +321,8 @@ export class SignalClient {
|
|
321
321
|
};
|
322
322
|
|
323
323
|
this.ws.onclose = (ev: CloseEvent) => {
|
324
|
-
|
325
|
-
|
326
|
-
log.debug(`websocket connection closed: ${ev.reason}`);
|
327
|
-
this.isConnected = false;
|
328
|
-
if (this.onClose) {
|
329
|
-
this.onClose(ev.reason);
|
330
|
-
}
|
331
|
-
this.ws = undefined;
|
324
|
+
log.warn(`websocket closed`, { ev });
|
325
|
+
this.handleOnClose(ev.reason);
|
332
326
|
};
|
333
327
|
});
|
334
328
|
}
|
@@ -351,12 +345,14 @@ export class SignalClient {
|
|
351
345
|
}
|
352
346
|
});
|
353
347
|
|
354
|
-
this.ws.
|
355
|
-
|
356
|
-
|
348
|
+
if (this.ws.readyState < this.ws.CLOSING) {
|
349
|
+
this.ws.close();
|
350
|
+
// 250ms grace period for ws to close gracefully
|
351
|
+
await Promise.race([closePromise, sleep(250)]);
|
352
|
+
}
|
353
|
+
this.ws = undefined;
|
354
|
+
this.clearPingInterval();
|
357
355
|
}
|
358
|
-
this.ws = undefined;
|
359
|
-
this.clearPingInterval();
|
360
356
|
} finally {
|
361
357
|
unlock();
|
362
358
|
}
|
@@ -618,6 +614,15 @@ export class SignalClient {
|
|
618
614
|
this.isReconnecting = false;
|
619
615
|
}
|
620
616
|
|
617
|
+
private async handleOnClose(reason: string) {
|
618
|
+
if (!this.isConnected) return;
|
619
|
+
await this.close();
|
620
|
+
log.debug(`websocket connection closed: ${reason}`);
|
621
|
+
if (this.onClose) {
|
622
|
+
this.onClose(reason);
|
623
|
+
}
|
624
|
+
}
|
625
|
+
|
621
626
|
private handleWSError(ev: Event) {
|
622
627
|
log.error('websocket error', ev);
|
623
628
|
}
|
@@ -638,9 +643,7 @@ export class SignalClient {
|
|
638
643
|
Date.now() - this.pingTimeoutDuration! * 1000,
|
639
644
|
).toUTCString()}`,
|
640
645
|
);
|
641
|
-
|
642
|
-
this.onClose('ping timeout');
|
643
|
-
}
|
646
|
+
this.handleOnClose('ping timeout');
|
644
647
|
}, this.pingTimeoutDuration * 1000);
|
645
648
|
}
|
646
649
|
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import EventEmitter from 'events';
|
2
2
|
import type TypedEmitter from 'typed-emitter';
|
3
|
-
import {
|
3
|
+
import { CheckStatus, Checker } from './checks/Checker';
|
4
|
+
import type { CheckInfo, InstantiableCheck } from './checks/Checker';
|
4
5
|
import { PublishAudioCheck } from './checks/publishAudio';
|
5
6
|
import { PublishVideoCheck } from './checks/publishVideo';
|
6
7
|
import { ReconnectCheck } from './checks/reconnect';
|
package/src/index.ts
CHANGED
@@ -14,11 +14,12 @@ import LocalVideoTrack from './room/track/LocalVideoTrack';
|
|
14
14
|
import RemoteAudioTrack from './room/track/RemoteAudioTrack';
|
15
15
|
import RemoteTrack from './room/track/RemoteTrack';
|
16
16
|
import RemoteTrackPublication from './room/track/RemoteTrackPublication';
|
17
|
-
import
|
17
|
+
import type { ElementInfo } from './room/track/RemoteVideoTrack';
|
18
|
+
import RemoteVideoTrack from './room/track/RemoteVideoTrack';
|
18
19
|
import { TrackPublication } from './room/track/TrackPublication';
|
19
20
|
import type { LiveKitReactNativeInfo } from './room/types';
|
21
|
+
import type { AudioAnalyserOptions } from './room/utils';
|
20
22
|
import {
|
21
|
-
type AudioAnalyserOptions,
|
22
23
|
createAudioAnalyser,
|
23
24
|
getEmptyAudioStreamTrack,
|
24
25
|
getEmptyVideoStreamTrack,
|
@@ -26,18 +27,19 @@ import {
|
|
26
27
|
supportsAV1,
|
27
28
|
supportsAdaptiveStream,
|
28
29
|
supportsDynacast,
|
30
|
+
supportsVP9,
|
29
31
|
} from './room/utils';
|
30
32
|
|
33
|
+
export * from './connectionHelper/ConnectionCheck';
|
31
34
|
export * from './options';
|
32
35
|
export * from './room/errors';
|
33
36
|
export * from './room/events';
|
34
|
-
export
|
37
|
+
export * from './room/track/Track';
|
35
38
|
export * from './room/track/create';
|
36
39
|
export * from './room/track/options';
|
37
|
-
export * from './room/track/Track';
|
38
40
|
export * from './room/track/types';
|
41
|
+
export type { DataPublishOptions, SimulationScenario } from './room/types';
|
39
42
|
export * from './version';
|
40
|
-
export * from './connectionHelper/ConnectionCheck';
|
41
43
|
export {
|
42
44
|
setLogLevel,
|
43
45
|
setLogExtension,
|
@@ -47,8 +49,8 @@ export {
|
|
47
49
|
supportsAdaptiveStream,
|
48
50
|
supportsDynacast,
|
49
51
|
supportsAV1,
|
52
|
+
supportsVP9,
|
50
53
|
createAudioAnalyser,
|
51
|
-
AudioAnalyserOptions,
|
52
54
|
LogLevel,
|
53
55
|
Room,
|
54
56
|
ConnectionState,
|
@@ -66,12 +68,15 @@ export {
|
|
66
68
|
RemoteAudioTrack,
|
67
69
|
RemoteVideoTrack,
|
68
70
|
RemoteTrackPublication,
|
69
|
-
ParticipantTrackPermission,
|
70
71
|
TrackPublication,
|
71
72
|
VideoQuality,
|
72
73
|
ConnectionQuality,
|
73
|
-
ElementInfo,
|
74
74
|
DefaultReconnectPolicy,
|
75
75
|
CriticalTimers,
|
76
|
+
};
|
77
|
+
export type {
|
78
|
+
ElementInfo,
|
79
|
+
ParticipantTrackPermission,
|
80
|
+
AudioAnalyserOptions,
|
76
81
|
LiveKitReactNativeInfo,
|
77
82
|
};
|
package/src/room/PCTransport.ts
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
import EventEmitter from 'events';
|
2
|
-
import {
|
2
|
+
import { parse, write } from 'sdp-transform';
|
3
|
+
import type { MediaDescription } from 'sdp-transform';
|
3
4
|
import { debounce } from 'ts-debounce';
|
4
5
|
import log from '../logger';
|
5
6
|
import { NegotiationError } from './errors';
|
6
|
-
import { ddExtensionURI, isSVCCodec } from './utils';
|
7
|
+
import { ddExtensionURI, isChromiumBased, isSVCCodec } from './utils';
|
7
8
|
|
8
9
|
/** @internal */
|
9
10
|
interface TrackBitrateInfo {
|
@@ -35,9 +36,12 @@ export default class PCTransport extends EventEmitter {
|
|
35
36
|
|
36
37
|
onOffer?: (offer: RTCSessionDescriptionInit) => void;
|
37
38
|
|
38
|
-
constructor(config?: RTCConfiguration) {
|
39
|
+
constructor(config?: RTCConfiguration, mediaConstraints: Record<string, unknown> = {}) {
|
39
40
|
super();
|
40
|
-
this.pc =
|
41
|
+
this.pc = isChromiumBased()
|
42
|
+
? // @ts-expect-error chrome allows additional media constraints to be passed into the RTCPeerConnection constructor
|
43
|
+
new RTCPeerConnection(config, mediaConstraints)
|
44
|
+
: new RTCPeerConnection(config);
|
41
45
|
}
|
42
46
|
|
43
47
|
get isICEConnected(): boolean {
|
@@ -289,7 +293,7 @@ function ensureVideoDDExtensionForSVC(
|
|
289
293
|
payloads?: string | undefined;
|
290
294
|
} & MediaDescription,
|
291
295
|
) {
|
292
|
-
const codec = media.rtp
|
296
|
+
const codec = media.rtp[0]?.codec?.toLowerCase();
|
293
297
|
if (!isSVCCodec(codec)) {
|
294
298
|
return;
|
295
299
|
}
|
package/src/room/RTCEngine.ts
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
import { EventEmitter } from 'events';
|
2
2
|
import type TypedEventEmitter from 'typed-emitter';
|
3
|
-
import { SignalClient
|
3
|
+
import { SignalClient } from '../api/SignalClient';
|
4
|
+
import type { SignalOptions } from '../api/SignalClient';
|
4
5
|
import log from '../logger';
|
5
6
|
import type { InternalRoomOptions } from '../options';
|
6
7
|
import {
|
@@ -77,6 +78,11 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
77
78
|
|
78
79
|
fullReconnectOnNext: boolean = false;
|
79
80
|
|
81
|
+
/**
|
82
|
+
* @internal
|
83
|
+
*/
|
84
|
+
latestJoinResponse?: JoinResponse;
|
85
|
+
|
80
86
|
get isClosed() {
|
81
87
|
return this._isClosed;
|
82
88
|
}
|
@@ -171,6 +177,7 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
171
177
|
this.joinAttempts += 1;
|
172
178
|
const joinResponse = await this.client.join(url, token, opts, abortSignal);
|
173
179
|
this._isClosed = false;
|
180
|
+
this.latestJoinResponse = joinResponse;
|
174
181
|
|
175
182
|
this.subscriberPrimary = joinResponse.subscriberPrimary;
|
176
183
|
if (!this.publisher) {
|
@@ -307,8 +314,8 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
307
314
|
this.participantSid = joinResponse.participant?.sid;
|
308
315
|
|
309
316
|
const rtcConfig = this.makeRTCConfiguration(joinResponse);
|
310
|
-
|
311
|
-
this.publisher = new PCTransport(rtcConfig);
|
317
|
+
const googConstraints = { optional: [{ googDscp: true }] };
|
318
|
+
this.publisher = new PCTransport(rtcConfig, googConstraints);
|
312
319
|
this.subscriber = new PCTransport(rtcConfig);
|
313
320
|
|
314
321
|
this.emit(EngineEvent.TransportsCreated, this.publisher, this.subscriber);
|
@@ -821,7 +828,7 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
821
828
|
}ms. giving up`,
|
822
829
|
);
|
823
830
|
this.emit(EngineEvent.Disconnected);
|
824
|
-
this.close();
|
831
|
+
await this.close();
|
825
832
|
}
|
826
833
|
} finally {
|
827
834
|
this.attemptingReconnect = false;
|
@@ -1120,6 +1127,39 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
|
|
1120
1127
|
await this.ensureDataTransportConnected(kind, false);
|
1121
1128
|
}
|
1122
1129
|
|
1130
|
+
/* @internal */
|
1131
|
+
verifyTransport(): boolean {
|
1132
|
+
// primary connection
|
1133
|
+
if (!this.primaryPC) {
|
1134
|
+
return false;
|
1135
|
+
}
|
1136
|
+
if (
|
1137
|
+
this.primaryPC.connectionState === 'closed' ||
|
1138
|
+
this.primaryPC.connectionState === 'failed'
|
1139
|
+
) {
|
1140
|
+
return false;
|
1141
|
+
}
|
1142
|
+
|
1143
|
+
// also verify publisher connection if it's needed or different
|
1144
|
+
if (this.hasPublished && this.subscriberPrimary) {
|
1145
|
+
if (!this.publisher) {
|
1146
|
+
return false;
|
1147
|
+
}
|
1148
|
+
if (
|
1149
|
+
this.publisher.pc.connectionState === 'closed' ||
|
1150
|
+
this.publisher.pc.connectionState === 'failed'
|
1151
|
+
) {
|
1152
|
+
return false;
|
1153
|
+
}
|
1154
|
+
}
|
1155
|
+
|
1156
|
+
// ensure signal is connected
|
1157
|
+
if (!this.client.ws || this.client.ws.readyState === WebSocket.CLOSED) {
|
1158
|
+
return false;
|
1159
|
+
}
|
1160
|
+
return true;
|
1161
|
+
}
|
1162
|
+
|
1123
1163
|
/** @internal */
|
1124
1164
|
negotiate(): Promise<void> {
|
1125
1165
|
// observe signal state
|
package/src/room/Room.ts
CHANGED
@@ -45,6 +45,7 @@ import LocalParticipant from './participant/LocalParticipant';
|
|
45
45
|
import type Participant from './participant/Participant';
|
46
46
|
import type { ConnectionQuality } from './participant/Participant';
|
47
47
|
import RemoteParticipant from './participant/RemoteParticipant';
|
48
|
+
import CriticalTimers from './timers';
|
48
49
|
import LocalAudioTrack from './track/LocalAudioTrack';
|
49
50
|
import LocalTrackPublication from './track/LocalTrackPublication';
|
50
51
|
import LocalVideoTrack from './track/LocalVideoTrack';
|
@@ -73,6 +74,8 @@ export enum ConnectionState {
|
|
73
74
|
Reconnecting = 'reconnecting',
|
74
75
|
}
|
75
76
|
|
77
|
+
const connectionReconcileFrequency = 2 * 1000;
|
78
|
+
|
76
79
|
/** @deprecated RoomState has been renamed to [[ConnectionState]] */
|
77
80
|
export const RoomState = ConnectionState;
|
78
81
|
|
@@ -126,6 +129,8 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
126
129
|
|
127
130
|
private cachedParticipantSids: Array<string>;
|
128
131
|
|
132
|
+
private connectionReconcileInterval?: ReturnType<typeof setInterval>;
|
133
|
+
|
129
134
|
/**
|
130
135
|
* Creates a new Room, the primary construct for a LiveKit session.
|
131
136
|
* @param options
|
@@ -189,7 +194,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
189
194
|
}
|
190
195
|
|
191
196
|
private maybeCreateEngine() {
|
192
|
-
if (this.engine) {
|
197
|
+
if (this.engine && !this.engine.isClosed) {
|
193
198
|
return;
|
194
199
|
}
|
195
200
|
|
@@ -215,6 +220,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
215
220
|
.on(EngineEvent.ActiveSpeakersUpdate, this.handleActiveSpeakersUpdate)
|
216
221
|
.on(EngineEvent.DataPacketReceived, this.handleDataPacket)
|
217
222
|
.on(EngineEvent.Resuming, () => {
|
223
|
+
this.clearConnectionReconcile();
|
218
224
|
if (this.setAndEmitConnectionState(ConnectionState.Reconnecting)) {
|
219
225
|
this.emit(RoomEvent.Reconnecting);
|
220
226
|
}
|
@@ -223,6 +229,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
223
229
|
.on(EngineEvent.Resumed, () => {
|
224
230
|
this.setAndEmitConnectionState(ConnectionState.Connected);
|
225
231
|
this.emit(RoomEvent.Reconnected);
|
232
|
+
this.registerConnectionReconcile();
|
226
233
|
this.updateSubscriptions();
|
227
234
|
|
228
235
|
// once reconnected, figure out if any participants connected during reconnect and emit events for it
|
@@ -488,6 +495,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
488
495
|
}
|
489
496
|
this.setAndEmitConnectionState(ConnectionState.Connected);
|
490
497
|
this.emit(RoomEvent.Connected);
|
498
|
+
this.registerConnectionReconcile();
|
491
499
|
};
|
492
500
|
|
493
501
|
/**
|
@@ -829,6 +837,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
829
837
|
}
|
830
838
|
|
831
839
|
private handleRestarting = () => {
|
840
|
+
this.clearConnectionReconcile();
|
832
841
|
// also unwind existing participants & existing subscriptions
|
833
842
|
for (const p of this.participants.values()) {
|
834
843
|
this.handleParticipantDisconnected(p.sid, p);
|
@@ -894,6 +903,7 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
894
903
|
}
|
895
904
|
this.setAndEmitConnectionState(ConnectionState.Connected);
|
896
905
|
this.emit(RoomEvent.Reconnected);
|
906
|
+
this.registerConnectionReconcile();
|
897
907
|
|
898
908
|
// emit participant connected events after connection has been re-established
|
899
909
|
this.participants.forEach((participant) => {
|
@@ -902,57 +912,61 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
902
912
|
};
|
903
913
|
|
904
914
|
private handleDisconnect(shouldStopTracks = true, reason?: DisconnectReason) {
|
915
|
+
this.clearConnectionReconcile();
|
905
916
|
if (this.state === ConnectionState.Disconnected) {
|
906
917
|
return;
|
907
918
|
}
|
908
919
|
|
909
|
-
|
910
|
-
|
911
|
-
p.
|
920
|
+
try {
|
921
|
+
this.participants.forEach((p) => {
|
922
|
+
p.tracks.forEach((pub) => {
|
923
|
+
p.unpublishTrack(pub.trackSid);
|
924
|
+
});
|
912
925
|
});
|
913
|
-
});
|
914
926
|
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
927
|
+
this.localParticipant.tracks.forEach((pub) => {
|
928
|
+
if (pub.track) {
|
929
|
+
this.localParticipant.unpublishTrack(pub.track, shouldStopTracks);
|
930
|
+
}
|
931
|
+
if (shouldStopTracks) {
|
932
|
+
pub.track?.detach();
|
933
|
+
pub.track?.stop();
|
934
|
+
}
|
935
|
+
});
|
924
936
|
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
937
|
+
this.localParticipant
|
938
|
+
.off(ParticipantEvent.ParticipantMetadataChanged, this.onLocalParticipantMetadataChanged)
|
939
|
+
.off(ParticipantEvent.ParticipantNameChanged, this.onLocalParticipantNameChanged)
|
940
|
+
.off(ParticipantEvent.TrackMuted, this.onLocalTrackMuted)
|
941
|
+
.off(ParticipantEvent.TrackUnmuted, this.onLocalTrackUnmuted)
|
942
|
+
.off(ParticipantEvent.LocalTrackPublished, this.onLocalTrackPublished)
|
943
|
+
.off(ParticipantEvent.LocalTrackUnpublished, this.onLocalTrackUnpublished)
|
944
|
+
.off(ParticipantEvent.ConnectionQualityChanged, this.onLocalConnectionQualityChanged)
|
945
|
+
.off(ParticipantEvent.MediaDevicesError, this.onMediaDevicesError)
|
946
|
+
.off(
|
947
|
+
ParticipantEvent.ParticipantPermissionsChanged,
|
948
|
+
this.onLocalParticipantPermissionsChanged,
|
949
|
+
);
|
938
950
|
|
939
|
-
|
940
|
-
|
941
|
-
|
951
|
+
this.localParticipant.tracks.clear();
|
952
|
+
this.localParticipant.videoTracks.clear();
|
953
|
+
this.localParticipant.audioTracks.clear();
|
942
954
|
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
955
|
+
this.participants.clear();
|
956
|
+
this.activeSpeakers = [];
|
957
|
+
if (this.audioContext && typeof this.options.expWebAudioMix === 'boolean') {
|
958
|
+
this.audioContext.close();
|
959
|
+
this.audioContext = undefined;
|
960
|
+
}
|
961
|
+
if (isWeb()) {
|
962
|
+
window.removeEventListener('beforeunload', this.onPageLeave);
|
963
|
+
window.removeEventListener('pagehide', this.onPageLeave);
|
964
|
+
navigator.mediaDevices?.removeEventListener('devicechange', this.handleDeviceChange);
|
965
|
+
}
|
966
|
+
} finally {
|
967
|
+
this.setAndEmitConnectionState(ConnectionState.Disconnected);
|
968
|
+
this.emit(RoomEvent.Disconnected, reason);
|
953
969
|
}
|
954
|
-
this.setAndEmitConnectionState(ConnectionState.Disconnected);
|
955
|
-
this.emit(RoomEvent.Disconnected, reason);
|
956
970
|
}
|
957
971
|
|
958
972
|
private handleParticipantUpdates = (participantInfos: ParticipantInfo[]) => {
|
@@ -1341,6 +1355,37 @@ class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>)
|
|
1341
1355
|
}
|
1342
1356
|
}
|
1343
1357
|
|
1358
|
+
private registerConnectionReconcile() {
|
1359
|
+
this.clearConnectionReconcile();
|
1360
|
+
let consecutiveFailures = 0;
|
1361
|
+
this.connectionReconcileInterval = CriticalTimers.setInterval(() => {
|
1362
|
+
if (
|
1363
|
+
// ensure we didn't tear it down
|
1364
|
+
!this.engine ||
|
1365
|
+
// engine detected close, but Room missed it
|
1366
|
+
this.engine.isClosed ||
|
1367
|
+
// transports failed without notifying engine
|
1368
|
+
!this.engine.verifyTransport()
|
1369
|
+
) {
|
1370
|
+
consecutiveFailures++;
|
1371
|
+
log.warn('detected connection state mismatch', { numFailures: consecutiveFailures });
|
1372
|
+
if (consecutiveFailures >= 3)
|
1373
|
+
this.handleDisconnect(
|
1374
|
+
this.options.stopLocalTrackOnUnpublish,
|
1375
|
+
DisconnectReason.UNKNOWN_REASON,
|
1376
|
+
);
|
1377
|
+
} else {
|
1378
|
+
consecutiveFailures = 0;
|
1379
|
+
}
|
1380
|
+
}, connectionReconcileFrequency);
|
1381
|
+
}
|
1382
|
+
|
1383
|
+
private clearConnectionReconcile() {
|
1384
|
+
if (this.connectionReconcileInterval) {
|
1385
|
+
CriticalTimers.clearInterval(this.connectionReconcileInterval);
|
1386
|
+
}
|
1387
|
+
}
|
1388
|
+
|
1344
1389
|
private setAndEmitConnectionState(state: ConnectionState): boolean {
|
1345
1390
|
if (state === this.state) {
|
1346
1391
|
// unchanged
|
package/src/room/defaults.ts
CHANGED
@@ -1,16 +1,18 @@
|
|
1
1
|
import type { InternalRoomConnectOptions, InternalRoomOptions } from '../options';
|
2
2
|
import DefaultReconnectPolicy from './DefaultReconnectPolicy';
|
3
|
-
import {
|
3
|
+
import { AudioPresets, ScreenSharePresets, VideoPresets } from './track/options';
|
4
|
+
import type {
|
4
5
|
AudioCaptureOptions,
|
5
|
-
AudioPresets,
|
6
|
-
ScreenSharePresets,
|
7
6
|
TrackPublishDefaults,
|
8
7
|
VideoCaptureOptions,
|
9
|
-
VideoPresets,
|
10
8
|
} from './track/options';
|
11
9
|
|
12
10
|
export const publishDefaults: TrackPublishDefaults = {
|
11
|
+
/**
|
12
|
+
* @deprecated
|
13
|
+
*/
|
13
14
|
audioBitrate: AudioPresets.music.maxBitrate,
|
15
|
+
audioPreset: AudioPresets.music,
|
14
16
|
dtx: true,
|
15
17
|
red: true,
|
16
18
|
forceStereo: false,
|
@@ -18,22 +18,21 @@ import LocalTrack from '../track/LocalTrack';
|
|
18
18
|
import LocalTrackPublication from '../track/LocalTrackPublication';
|
19
19
|
import LocalVideoTrack, { videoLayersFromEncodings } from '../track/LocalVideoTrack';
|
20
20
|
import { Track } from '../track/Track';
|
21
|
-
import {
|
21
|
+
import { ScreenSharePresets, isBackupCodec, isCodecEqual } from '../track/options';
|
22
|
+
import type {
|
22
23
|
AudioCaptureOptions,
|
23
24
|
BackupVideoCodec,
|
24
25
|
CreateLocalTracksOptions,
|
25
26
|
ScreenShareCaptureOptions,
|
26
|
-
ScreenSharePresets,
|
27
27
|
TrackPublishOptions,
|
28
28
|
VideoCaptureOptions,
|
29
|
-
isBackupCodec,
|
30
|
-
isCodecEqual,
|
31
29
|
} from '../track/options';
|
32
30
|
import { constraintsForOptions, mergeDefaultOptions } from '../track/utils';
|
33
31
|
import type { DataPublishOptions } from '../types';
|
34
32
|
import { Future, isFireFox, isSVCCodec, isSafari, isWeb, supportsAV1, supportsVP9 } from '../utils';
|
35
33
|
import Participant from './Participant';
|
36
|
-
import {
|
34
|
+
import { trackPermissionToProto } from './ParticipantTrackPermission';
|
35
|
+
import type { ParticipantTrackPermission } from './ParticipantTrackPermission';
|
37
36
|
import RemoteParticipant from './RemoteParticipant';
|
38
37
|
import {
|
39
38
|
computeTrackBackupEncodings,
|
@@ -151,7 +150,7 @@ export default class LocalParticipant extends Participant {
|
|
151
150
|
|
152
151
|
/**
|
153
152
|
* Sets and updates the metadata of the local participant.
|
154
|
-
* Note: this requires `
|
153
|
+
* Note: this requires `canUpdateOwnMetadata` permission encoded in the token.
|
155
154
|
* @param metadata
|
156
155
|
*/
|
157
156
|
setMetadata(metadata: string): void {
|
@@ -161,7 +160,7 @@ export default class LocalParticipant extends Participant {
|
|
161
160
|
|
162
161
|
/**
|
163
162
|
* Sets and updates the name of the local participant.
|
164
|
-
* Note: this requires `
|
163
|
+
* Note: this requires `canUpdateOwnMetadata` permission encoded in the token.
|
165
164
|
* @param metadata
|
166
165
|
*/
|
167
166
|
setName(name: string): void {
|
@@ -651,10 +650,12 @@ export default class LocalParticipant extends Participant {
|
|
651
650
|
opts,
|
652
651
|
);
|
653
652
|
req.layers = videoLayersFromEncodings(req.width, req.height, simEncodings ?? encodings);
|
654
|
-
} else if (track.kind === Track.Kind.Audio
|
653
|
+
} else if (track.kind === Track.Kind.Audio) {
|
655
654
|
encodings = [
|
656
655
|
{
|
657
|
-
maxBitrate: opts.audioBitrate,
|
656
|
+
maxBitrate: opts.audioPreset?.maxBitrate ?? opts.audioBitrate,
|
657
|
+
priority: opts.audioPreset?.priority ?? 'high',
|
658
|
+
networkPriority: opts.audioPreset?.priority ?? 'high',
|
658
659
|
},
|
659
660
|
];
|
660
661
|
}
|
@@ -11,7 +11,8 @@ import { Track } from '../track/Track';
|
|
11
11
|
import type { TrackPublication } from '../track/TrackPublication';
|
12
12
|
import type { AudioOutputOptions } from '../track/options';
|
13
13
|
import type { AdaptiveStreamSettings } from '../track/types';
|
14
|
-
import Participant
|
14
|
+
import Participant from './Participant';
|
15
|
+
import type { ParticipantEventCallbacks } from './Participant';
|
15
16
|
|
16
17
|
export default class RemoteParticipant extends Participant {
|
17
18
|
audioTracks: Map<string, RemoteTrackPublication>;
|
@@ -3,15 +3,12 @@ import { TrackInvalidError } from '../errors';
|
|
3
3
|
import LocalAudioTrack from '../track/LocalAudioTrack';
|
4
4
|
import LocalVideoTrack from '../track/LocalVideoTrack';
|
5
5
|
import { Track } from '../track/Track';
|
6
|
-
import {
|
6
|
+
import { ScreenSharePresets, VideoPreset, VideoPresets, VideoPresets43 } from '../track/options';
|
7
|
+
import type {
|
7
8
|
BackupVideoCodec,
|
8
|
-
ScreenSharePresets,
|
9
9
|
TrackPublishOptions,
|
10
10
|
VideoCodec,
|
11
11
|
VideoEncoding,
|
12
|
-
VideoPreset,
|
13
|
-
VideoPresets,
|
14
|
-
VideoPresets43,
|
15
12
|
} from '../track/options';
|
16
13
|
import { isSVCCodec } from '../utils';
|
17
14
|
|
@@ -61,6 +58,7 @@ export const computeDefaultScreenShareSimulcastPresets = (fromPreset: VideoPrese
|
|
61
58
|
),
|
62
59
|
),
|
63
60
|
t.fps,
|
61
|
+
fromPreset.encoding.priority,
|
64
62
|
),
|
65
63
|
);
|
66
64
|
};
|
@@ -317,6 +315,10 @@ function encodingsFromPresets(
|
|
317
315
|
if (preset.encoding.maxFramerate) {
|
318
316
|
encoding.maxFramerate = preset.encoding.maxFramerate;
|
319
317
|
}
|
318
|
+
if (preset.encoding.priority) {
|
319
|
+
encoding.priority = preset.encoding.priority;
|
320
|
+
encoding.networkPriority = preset.encoding.priority;
|
321
|
+
}
|
320
322
|
encodings.push(encoding);
|
321
323
|
});
|
322
324
|
return encodings;
|