livekit-client 1.12.1 → 1.12.3
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 +7 -3
- package/dist/livekit-client.e2ee.worker.js +1 -1
- package/dist/livekit-client.e2ee.worker.js.map +1 -1
- package/dist/livekit-client.e2ee.worker.mjs +389 -301
- package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
- package/dist/livekit-client.esm.mjs +11279 -13498
- 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 +2 -2
- package/dist/src/api/SignalClient.d.ts.map +1 -1
- package/dist/src/connectionHelper/ConnectionCheck.d.ts +3 -2
- package/dist/src/connectionHelper/ConnectionCheck.d.ts.map +1 -1
- package/dist/src/connectionHelper/checks/Checker.d.ts +3 -2
- package/dist/src/connectionHelper/checks/Checker.d.ts.map +1 -1
- package/dist/src/connectionHelper/checks/webrtc.d.ts.map +1 -1
- package/dist/src/connectionHelper/checks/websocket.d.ts.map +1 -1
- package/dist/src/e2ee/E2eeManager.d.ts +4 -2
- package/dist/src/e2ee/E2eeManager.d.ts.map +1 -1
- package/dist/src/e2ee/KeyProvider.d.ts +4 -2
- package/dist/src/e2ee/KeyProvider.d.ts.map +1 -1
- package/dist/src/e2ee/constants.d.ts +1 -0
- package/dist/src/e2ee/constants.d.ts.map +1 -1
- package/dist/src/e2ee/types.d.ts +1 -0
- package/dist/src/e2ee/types.d.ts.map +1 -1
- package/dist/src/e2ee/worker/FrameCryptor.d.ts +4 -2
- package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
- package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts +14 -3
- package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/proto/livekit_models_pb.d.ts +1264 -0
- package/dist/src/proto/livekit_models_pb.d.ts.map +1 -0
- package/dist/src/proto/livekit_rtc_pb.d.ts +1373 -0
- package/dist/src/proto/livekit_rtc_pb.d.ts.map +1 -0
- package/dist/src/room/PCTransport.d.ts +2 -1
- package/dist/src/room/PCTransport.d.ts.map +1 -1
- package/dist/src/room/RTCEngine.d.ts +9 -5
- package/dist/src/room/RTCEngine.d.ts.map +1 -1
- package/dist/src/room/RegionUrlProvider.d.ts +4 -1
- package/dist/src/room/RegionUrlProvider.d.ts.map +1 -1
- package/dist/src/room/Room.d.ts +15 -11
- package/dist/src/room/Room.d.ts.map +1 -1
- package/dist/src/room/participant/LocalParticipant.d.ts +2 -2
- package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/Participant.d.ts +5 -3
- package/dist/src/room/participant/Participant.d.ts.map +1 -1
- package/dist/src/room/participant/ParticipantTrackPermission.d.ts +1 -1
- package/dist/src/room/participant/ParticipantTrackPermission.d.ts.map +1 -1
- package/dist/src/room/participant/RemoteParticipant.d.ts +2 -3
- package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
- package/dist/src/room/participant/publishUtils.d.ts +1 -1
- package/dist/src/room/participant/publishUtils.d.ts.map +1 -1
- package/dist/src/room/timers.d.ts +5 -4
- package/dist/src/room/timers.d.ts.map +1 -1
- package/dist/src/room/track/LocalTrack.d.ts +3 -0
- package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
- package/dist/src/room/track/LocalTrackPublication.d.ts +1 -1
- package/dist/src/room/track/LocalTrackPublication.d.ts.map +1 -1
- package/dist/src/room/track/LocalVideoTrack.d.ts +2 -2
- package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
- package/dist/src/room/track/RemoteTrackPublication.d.ts +1 -1
- package/dist/src/room/track/RemoteTrackPublication.d.ts.map +1 -1
- package/dist/src/room/track/Track.d.ts +6 -4
- package/dist/src/room/track/Track.d.ts.map +1 -1
- package/dist/src/room/track/TrackPublication.d.ts +7 -5
- package/dist/src/room/track/TrackPublication.d.ts.map +1 -1
- package/dist/src/room/track/create.d.ts.map +1 -1
- package/dist/src/room/track/options.d.ts +8 -0
- package/dist/src/room/track/options.d.ts.map +1 -1
- package/dist/src/room/track/utils.d.ts +5 -1
- package/dist/src/room/track/utils.d.ts.map +1 -1
- package/dist/src/room/utils.d.ts +3 -1
- package/dist/src/room/utils.d.ts.map +1 -1
- package/dist/src/test/mocks.d.ts +4 -3
- package/dist/src/test/mocks.d.ts.map +1 -1
- package/dist/ts4.2/src/api/SignalClient.d.ts +2 -2
- package/dist/ts4.2/src/connectionHelper/ConnectionCheck.d.ts +3 -2
- package/dist/ts4.2/src/connectionHelper/checks/Checker.d.ts +3 -2
- package/dist/ts4.2/src/e2ee/E2eeManager.d.ts +4 -2
- package/dist/ts4.2/src/e2ee/KeyProvider.d.ts +4 -2
- package/dist/ts4.2/src/e2ee/constants.d.ts +1 -0
- package/dist/ts4.2/src/e2ee/types.d.ts +1 -0
- package/dist/ts4.2/src/e2ee/worker/FrameCryptor.d.ts +4 -2
- package/dist/ts4.2/src/e2ee/worker/ParticipantKeyHandler.d.ts +14 -3
- package/dist/ts4.2/src/index.d.ts +1 -1
- package/dist/ts4.2/src/proto/livekit_models_pb.d.ts +1264 -0
- package/dist/ts4.2/src/proto/livekit_rtc_pb.d.ts +1373 -0
- package/dist/ts4.2/src/room/PCTransport.d.ts +2 -1
- package/dist/ts4.2/src/room/RTCEngine.d.ts +9 -5
- package/dist/ts4.2/src/room/RegionUrlProvider.d.ts +4 -1
- package/dist/ts4.2/src/room/Room.d.ts +15 -11
- package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +2 -2
- package/dist/ts4.2/src/room/participant/Participant.d.ts +5 -3
- package/dist/ts4.2/src/room/participant/ParticipantTrackPermission.d.ts +1 -1
- package/dist/ts4.2/src/room/participant/RemoteParticipant.d.ts +2 -3
- package/dist/ts4.2/src/room/participant/publishUtils.d.ts +1 -1
- package/dist/ts4.2/src/room/timers.d.ts +5 -4
- package/dist/ts4.2/src/room/track/LocalTrack.d.ts +3 -0
- package/dist/ts4.2/src/room/track/LocalTrackPublication.d.ts +1 -1
- package/dist/ts4.2/src/room/track/LocalVideoTrack.d.ts +2 -2
- package/dist/ts4.2/src/room/track/RemoteTrackPublication.d.ts +1 -1
- package/dist/ts4.2/src/room/track/Track.d.ts +6 -4
- package/dist/ts4.2/src/room/track/TrackPublication.d.ts +7 -5
- package/dist/ts4.2/src/room/track/options.d.ts +8 -0
- package/dist/ts4.2/src/room/track/utils.d.ts +5 -1
- package/dist/ts4.2/src/room/utils.d.ts +3 -1
- package/dist/ts4.2/src/test/mocks.d.ts +4 -3
- package/package.json +10 -10
- package/src/api/SignalClient.ts +104 -101
- package/src/connectionHelper/ConnectionCheck.ts +3 -2
- package/src/connectionHelper/checks/Checker.ts +3 -3
- package/src/connectionHelper/checks/webrtc.ts +66 -2
- package/src/connectionHelper/checks/websocket.ts +4 -0
- package/src/e2ee/E2eeManager.ts +4 -3
- package/src/e2ee/KeyProvider.ts +3 -2
- package/src/e2ee/constants.ts +4 -0
- package/src/e2ee/types.ts +1 -0
- package/src/e2ee/worker/FrameCryptor.test.ts +1 -3
- package/src/e2ee/worker/FrameCryptor.ts +5 -5
- package/src/e2ee/worker/ParticipantKeyHandler.ts +37 -6
- package/src/e2ee/worker/e2ee.worker.ts +1 -1
- package/src/index.ts +1 -1
- package/src/proto/livekit_models_pb.ts +2096 -0
- package/src/proto/livekit_rtc_pb.ts +2332 -0
- package/src/room/PCTransport.ts +1 -1
- package/src/room/RTCEngine.ts +28 -22
- package/src/room/RegionUrlProvider.ts +11 -2
- package/src/room/Room.test.ts +1 -0
- package/src/room/Room.ts +158 -79
- package/src/room/participant/LocalParticipant.ts +43 -59
- package/src/room/participant/Participant.ts +6 -4
- package/src/room/participant/ParticipantTrackPermission.ts +3 -3
- package/src/room/participant/RemoteParticipant.ts +5 -6
- package/src/room/participant/publishUtils.test.ts +1 -0
- package/src/room/participant/publishUtils.ts +4 -2
- package/src/room/track/LocalTrack.ts +24 -9
- package/src/room/track/LocalTrackPublication.ts +1 -1
- package/src/room/track/LocalVideoTrack.test.ts +2 -1
- package/src/room/track/LocalVideoTrack.ts +28 -26
- package/src/room/track/RemoteTrackPublication.ts +12 -7
- package/src/room/track/RemoteVideoTrack.test.ts +5 -4
- package/src/room/track/Track.ts +9 -6
- package/src/room/track/TrackPublication.ts +7 -5
- package/src/room/track/create.ts +9 -17
- package/src/room/track/facingMode.test.ts +1 -0
- package/src/room/track/options.ts +23 -16
- package/src/room/track/utils.test.ts +1 -0
- package/src/room/track/utils.ts +44 -2
- package/src/room/utils.test.ts +16 -0
- package/src/room/utils.ts +20 -4
- package/src/test/mocks.ts +7 -5
- package/src/utils/AsyncQueue.test.ts +1 -0
- package/src/utils/browserParser.test.ts +33 -3
- package/src/utils/browserParser.ts +1 -1
- package/dist/src/proto/google/protobuf/timestamp.d.ts +0 -146
- package/dist/src/proto/google/protobuf/timestamp.d.ts.map +0 -1
- package/dist/src/proto/livekit_models.d.ts +0 -2399
- package/dist/src/proto/livekit_models.d.ts.map +0 -1
- package/dist/src/proto/livekit_rtc.d.ts +0 -14352
- package/dist/src/proto/livekit_rtc.d.ts.map +0 -1
- package/dist/ts4.2/src/proto/google/protobuf/timestamp.d.ts +0 -150
- package/dist/ts4.2/src/proto/livekit_models.d.ts +0 -2659
- package/dist/ts4.2/src/proto/livekit_rtc.d.ts +0 -15764
- package/src/proto/google/protobuf/timestamp.ts +0 -230
- package/src/proto/livekit_models.ts +0 -4006
- package/src/proto/livekit_rtc.ts +0 -4672
package/src/room/PCTransport.ts
CHANGED
package/src/room/RTCEngine.ts
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
-
import EventEmitter from '
|
1
|
+
import { EventEmitter } from 'events';
|
2
2
|
import type { MediaAttributes } from 'sdp-transform';
|
3
|
-
import
|
3
|
+
import type TypedEventEmitter from 'typed-emitter';
|
4
4
|
import type { SignalOptions } from '../api/SignalClient';
|
5
|
+
import { SignalClient } from '../api/SignalClient';
|
5
6
|
import log from '../logger';
|
6
7
|
import type { InternalRoomOptions } from '../options';
|
7
8
|
import {
|
@@ -16,7 +17,7 @@ import {
|
|
16
17
|
SpeakerInfo,
|
17
18
|
TrackInfo,
|
18
19
|
UserPacket,
|
19
|
-
} from '../proto/
|
20
|
+
} from '../proto/livekit_models_pb';
|
20
21
|
import {
|
21
22
|
AddTrackRequest,
|
22
23
|
ConnectionQualityUpdate,
|
@@ -28,10 +29,10 @@ import {
|
|
28
29
|
SubscriptionPermissionUpdate,
|
29
30
|
SubscriptionResponse,
|
30
31
|
TrackPublishedResponse,
|
31
|
-
} from '../proto/
|
32
|
+
} from '../proto/livekit_rtc_pb';
|
32
33
|
import PCTransport, { PCEvents } from './PCTransport';
|
33
34
|
import type { ReconnectContext, ReconnectPolicy } from './ReconnectPolicy';
|
34
|
-
import { RegionUrlProvider } from './RegionUrlProvider';
|
35
|
+
import type { RegionUrlProvider } from './RegionUrlProvider';
|
35
36
|
import { roomConnectOptionDefaults } from './defaults';
|
36
37
|
import {
|
37
38
|
ConnectionError,
|
@@ -49,7 +50,6 @@ import { Track } from './track/Track';
|
|
49
50
|
import type { TrackPublishOptions, VideoCodec } from './track/options';
|
50
51
|
import {
|
51
52
|
Mutex,
|
52
|
-
isCloud,
|
53
53
|
isVideoCodec,
|
54
54
|
isWeb,
|
55
55
|
sleep,
|
@@ -72,7 +72,7 @@ enum PCState {
|
|
72
72
|
}
|
73
73
|
|
74
74
|
/** @internal */
|
75
|
-
export default class RTCEngine extends EventEmitter<EngineEventCallbacks> {
|
75
|
+
export default class RTCEngine extends (EventEmitter as new () => TypedEventEmitter<EngineEventCallbacks>) {
|
76
76
|
publisher?: PCTransport;
|
77
77
|
|
78
78
|
subscriber?: PCTransport;
|
@@ -122,7 +122,7 @@ export default class RTCEngine extends EventEmitter<EngineEventCallbacks> {
|
|
122
122
|
// this is helpful to know if we need to restart ICE on the publisher connection
|
123
123
|
private hasPublished: boolean = false;
|
124
124
|
|
125
|
-
// keep join info around for reconnect
|
125
|
+
// keep join info around for reconnect, this could be a region url
|
126
126
|
private url?: string;
|
127
127
|
|
128
128
|
private token?: string;
|
@@ -358,6 +358,11 @@ export default class RTCEngine extends EventEmitter<EngineEventCallbacks> {
|
|
358
358
|
return getConnectedAddress(this.primaryPC);
|
359
359
|
}
|
360
360
|
|
361
|
+
/* @internal */
|
362
|
+
setRegionUrlProvider(provider: RegionUrlProvider) {
|
363
|
+
this.regionUrlProvider = provider;
|
364
|
+
}
|
365
|
+
|
361
366
|
private configure(joinResponse: JoinResponse) {
|
362
367
|
// already configured
|
363
368
|
if (this.publisher || this.subscriber) {
|
@@ -492,11 +497,11 @@ export default class RTCEngine extends EventEmitter<EngineEventCallbacks> {
|
|
492
497
|
|
493
498
|
this.client.onLocalTrackPublished = (res: TrackPublishedResponse) => {
|
494
499
|
log.debug('received trackPublishedResponse', res);
|
495
|
-
|
496
|
-
if (!resolve) {
|
500
|
+
if (!this.pendingTrackResolvers[res.cid]) {
|
497
501
|
log.error(`missing track resolver for ${res.cid}`);
|
498
502
|
return;
|
499
503
|
}
|
504
|
+
const { resolve } = this.pendingTrackResolvers[res.cid];
|
500
505
|
delete this.pendingTrackResolvers[res.cid];
|
501
506
|
resolve(res.track!);
|
502
507
|
};
|
@@ -628,12 +633,12 @@ export default class RTCEngine extends EventEmitter<EngineEventCallbacks> {
|
|
628
633
|
log.error('unsupported data type', message.data);
|
629
634
|
return;
|
630
635
|
}
|
631
|
-
const dp = DataPacket.
|
632
|
-
if (dp.value
|
636
|
+
const dp = DataPacket.fromBinary(new Uint8Array(buffer));
|
637
|
+
if (dp.value?.case === 'speaker') {
|
633
638
|
// dispatch speaker updates
|
634
|
-
this.emit(EngineEvent.ActiveSpeakersUpdate, dp.value.
|
635
|
-
} else if (dp.value
|
636
|
-
this.emit(EngineEvent.DataPacketReceived, dp.value.
|
639
|
+
this.emit(EngineEvent.ActiveSpeakersUpdate, dp.value.value.speakers);
|
640
|
+
} else if (dp.value?.case === 'user') {
|
641
|
+
this.emit(EngineEvent.DataPacketReceived, dp.value.value, dp.kind);
|
637
642
|
}
|
638
643
|
} finally {
|
639
644
|
unlock();
|
@@ -644,11 +649,11 @@ export default class RTCEngine extends EventEmitter<EngineEventCallbacks> {
|
|
644
649
|
const channel = event.currentTarget as RTCDataChannel;
|
645
650
|
const channelKind = channel.maxRetransmits === 0 ? 'lossy' : 'reliable';
|
646
651
|
|
647
|
-
if (event instanceof ErrorEvent) {
|
652
|
+
if (event instanceof ErrorEvent && event.error) {
|
648
653
|
const { error } = event.error;
|
649
654
|
log.error(`DataChannel error on ${channelKind}: ${event.message}`, error);
|
650
655
|
} else {
|
651
|
-
log.error(`Unknown DataChannel
|
656
|
+
log.error(`Unknown DataChannel error on ${channelKind}`, event);
|
652
657
|
}
|
653
658
|
};
|
654
659
|
|
@@ -843,8 +848,10 @@ export default class RTCEngine extends EventEmitter<EngineEventCallbacks> {
|
|
843
848
|
log.debug(`reconnecting in ${delay}ms`);
|
844
849
|
|
845
850
|
this.clearReconnectTimeout();
|
846
|
-
if (this.
|
847
|
-
|
851
|
+
if (this.token && this.regionUrlProvider) {
|
852
|
+
// token may have been refreshed, we do not want to recreate the regionUrlProvider
|
853
|
+
// since the current engine may have inherited a regional url
|
854
|
+
this.regionUrlProvider.updateToken(this.token);
|
848
855
|
}
|
849
856
|
this.reconnectTimeout = CriticalTimers.setTimeout(
|
850
857
|
() => this.attemptReconnect(disconnectReason),
|
@@ -1114,13 +1121,12 @@ export default class RTCEngine extends EventEmitter<EngineEventCallbacks> {
|
|
1114
1121
|
};
|
1115
1122
|
this.once(EngineEvent.Restarted, onRestarted);
|
1116
1123
|
this.once(EngineEvent.Disconnected, onDisconnected);
|
1117
|
-
this.once(EngineEvent.Closing, onDisconnected);
|
1118
1124
|
});
|
1119
1125
|
};
|
1120
1126
|
|
1121
1127
|
/* @internal */
|
1122
1128
|
async sendDataPacket(packet: DataPacket, kind: DataPacket_Kind) {
|
1123
|
-
const msg =
|
1129
|
+
const msg = packet.toBinary();
|
1124
1130
|
|
1125
1131
|
// make sure we do have a data connection
|
1126
1132
|
await this.ensurePublisherConnected(kind);
|
@@ -1241,7 +1247,7 @@ export default class RTCEngine extends EventEmitter<EngineEventCallbacks> {
|
|
1241
1247
|
this.hasPublished = true;
|
1242
1248
|
|
1243
1249
|
const handleClosed = () => {
|
1244
|
-
log.
|
1250
|
+
log.debug('engine disconnected while negotiation was ongoing');
|
1245
1251
|
cleanup();
|
1246
1252
|
resolve();
|
1247
1253
|
return;
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import log from '../logger';
|
2
|
-
import type { RegionInfo, RegionSettings } from '../proto/
|
2
|
+
import type { RegionInfo, RegionSettings } from '../proto/livekit_rtc_pb';
|
3
3
|
import { ConnectionError, ConnectionErrorReason } from './errors';
|
4
4
|
import { isCloud } from './utils';
|
5
5
|
|
@@ -21,10 +21,18 @@ export class RegionUrlProvider {
|
|
21
21
|
this.token = token;
|
22
22
|
}
|
23
23
|
|
24
|
+
updateToken(token: string) {
|
25
|
+
this.token = token;
|
26
|
+
}
|
27
|
+
|
24
28
|
isCloud() {
|
25
29
|
return isCloud(this.serverUrl);
|
26
30
|
}
|
27
31
|
|
32
|
+
getServerUrl() {
|
33
|
+
return this.serverUrl;
|
34
|
+
}
|
35
|
+
|
28
36
|
async getNextBestRegionUrl(abortSignal?: AbortSignal) {
|
29
37
|
if (!this.isCloud()) {
|
30
38
|
throw Error('region availability is only supported for LiveKit Cloud domains');
|
@@ -49,7 +57,8 @@ export class RegionUrlProvider {
|
|
49
57
|
this.attemptedRegions = [];
|
50
58
|
}
|
51
59
|
|
52
|
-
|
60
|
+
/* @internal */
|
61
|
+
async fetchRegionSettings(signal?: AbortSignal) {
|
53
62
|
const regionSettingsResponse = await fetch(`${getCloudConfigUrl(this.serverUrl)}/regions`, {
|
54
63
|
headers: { authorization: `Bearer ${this.token}` },
|
55
64
|
signal,
|
package/src/room/Room.test.ts
CHANGED
package/src/room/Room.ts
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
import
|
1
|
+
import { protoInt64 } from '@bufbuild/protobuf';
|
2
|
+
import { EventEmitter } from 'events';
|
3
|
+
import type TypedEmitter from 'typed-emitter';
|
2
4
|
import 'webrtc-adapter';
|
3
5
|
import { toProtoSessionDescription } from '../api/SignalClient';
|
4
6
|
import { EncryptionEvent } from '../e2ee';
|
@@ -24,15 +26,18 @@ import {
|
|
24
26
|
TrackSource,
|
25
27
|
TrackType,
|
26
28
|
UserPacket,
|
27
|
-
} from '../proto/
|
29
|
+
} from '../proto/livekit_models_pb';
|
28
30
|
import {
|
29
31
|
ConnectionQualityUpdate,
|
30
32
|
JoinResponse,
|
33
|
+
LeaveRequest,
|
31
34
|
SimulateScenario,
|
32
35
|
StreamStateUpdate,
|
33
36
|
SubscriptionPermissionUpdate,
|
34
37
|
SubscriptionResponse,
|
35
|
-
|
38
|
+
SyncState,
|
39
|
+
UpdateSubscription,
|
40
|
+
} from '../proto/livekit_rtc_pb';
|
36
41
|
import { getBrowser } from '../utils/browserParser';
|
37
42
|
import DeviceManager from './DeviceManager';
|
38
43
|
import RTCEngine from './RTCEngine';
|
@@ -69,6 +74,7 @@ import {
|
|
69
74
|
isCloud,
|
70
75
|
isWeb,
|
71
76
|
supportsSetSinkId,
|
77
|
+
toHttpUrl,
|
72
78
|
unpackStreamId,
|
73
79
|
unwrapConstraint,
|
74
80
|
} from './utils';
|
@@ -93,7 +99,7 @@ export const RoomState = ConnectionState;
|
|
93
99
|
*
|
94
100
|
* @noInheritDoc
|
95
101
|
*/
|
96
|
-
class Room extends EventEmitter<RoomEventCallbacks> {
|
102
|
+
class Room extends (EventEmitter as new () => TypedEmitter<RoomEventCallbacks>) {
|
97
103
|
state: ConnectionState = ConnectionState.Disconnected;
|
98
104
|
|
99
105
|
/** map of sid: [[RemoteParticipant]] */
|
@@ -142,12 +148,17 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
142
148
|
|
143
149
|
private connectionReconcileInterval?: ReturnType<typeof setInterval>;
|
144
150
|
|
151
|
+
private regionUrlProvider?: RegionUrlProvider;
|
152
|
+
|
153
|
+
private regionUrl?: string;
|
154
|
+
|
145
155
|
/**
|
146
156
|
* Creates a new Room, the primary construct for a LiveKit session.
|
147
157
|
* @param options
|
148
158
|
*/
|
149
159
|
constructor(options?: RoomOptions) {
|
150
160
|
super();
|
161
|
+
this.setMaxListeners(100);
|
151
162
|
this.participants = new Map();
|
152
163
|
this.cachedParticipantSids = [];
|
153
164
|
this.identityToSid = new Map();
|
@@ -337,15 +348,36 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
337
348
|
}
|
338
349
|
|
339
350
|
/**
|
340
|
-
*
|
341
|
-
*
|
342
|
-
*
|
343
|
-
*
|
344
|
-
*
|
345
|
-
*
|
351
|
+
* prepareConnection should be called as soon as the page is loaded, in order
|
352
|
+
* to speed up the connection attempt. This function will
|
353
|
+
* - perform DNS resolution and pre-warm the DNS cache
|
354
|
+
* - establish TLS connection and cache TLS keys
|
355
|
+
*
|
356
|
+
* With LiveKit Cloud, it will also determine the best edge data center for
|
357
|
+
* the current client to connect to if a token is provided.
|
346
358
|
*/
|
347
|
-
async prepareConnection(url: string) {
|
348
|
-
|
359
|
+
async prepareConnection(url: string, token?: string) {
|
360
|
+
if (this.state !== ConnectionState.Disconnected) {
|
361
|
+
return;
|
362
|
+
}
|
363
|
+
log.debug(`prepareConnection to ${url}`);
|
364
|
+
try {
|
365
|
+
if (isCloud(new URL(url)) && token) {
|
366
|
+
this.regionUrlProvider = new RegionUrlProvider(url, token);
|
367
|
+
const regionUrl = await this.regionUrlProvider.getNextBestRegionUrl();
|
368
|
+
// we will not replace the regionUrl if an attempt had already started
|
369
|
+
// to avoid overriding regionUrl after a new connection attempt had started
|
370
|
+
if (regionUrl && this.state === ConnectionState.Disconnected) {
|
371
|
+
this.regionUrl = regionUrl;
|
372
|
+
await fetch(toHttpUrl(regionUrl), { method: 'HEAD' });
|
373
|
+
log.debug(`prepared connection to ${regionUrl}`);
|
374
|
+
}
|
375
|
+
} else {
|
376
|
+
await fetch(toHttpUrl(url), { method: 'HEAD' });
|
377
|
+
}
|
378
|
+
} catch (e) {
|
379
|
+
log.warn('could not prepare connection', { error: e });
|
380
|
+
}
|
349
381
|
}
|
350
382
|
|
351
383
|
connect = async (url: string, token: string, opts?: RoomConnectOptions): Promise<void> => {
|
@@ -365,8 +397,23 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
365
397
|
}
|
366
398
|
|
367
399
|
this.setAndEmitConnectionState(ConnectionState.Connecting);
|
368
|
-
|
369
|
-
|
400
|
+
if (this.regionUrlProvider?.getServerUrl().toString() !== url) {
|
401
|
+
this.regionUrl = undefined;
|
402
|
+
this.regionUrlProvider = undefined;
|
403
|
+
}
|
404
|
+
if (isCloud(new URL(url))) {
|
405
|
+
if (this.regionUrlProvider === undefined) {
|
406
|
+
this.regionUrlProvider = new RegionUrlProvider(url, token);
|
407
|
+
} else {
|
408
|
+
this.regionUrlProvider.updateToken(token);
|
409
|
+
}
|
410
|
+
// trigger the first fetch without waiting for a response
|
411
|
+
// if initial connection fails, this will speed up picking regional url
|
412
|
+
// on subsequent runs
|
413
|
+
this.regionUrlProvider.fetchRegionSettings().catch((e) => {
|
414
|
+
log.warn('could not fetch region settings', { error: e });
|
415
|
+
});
|
416
|
+
}
|
370
417
|
|
371
418
|
const connectFn = async (
|
372
419
|
resolve: () => void,
|
@@ -376,6 +423,7 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
376
423
|
if (this.abortController) {
|
377
424
|
this.abortController.abort();
|
378
425
|
}
|
426
|
+
|
379
427
|
this.abortController = new AbortController();
|
380
428
|
|
381
429
|
// at this point the intention to connect has been signalled so we can allow cancelling of the connection via disconnect() again
|
@@ -387,13 +435,16 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
387
435
|
resolve();
|
388
436
|
} catch (e) {
|
389
437
|
if (
|
390
|
-
|
438
|
+
this.regionUrlProvider &&
|
391
439
|
e instanceof ConnectionError &&
|
392
|
-
e.reason !== ConnectionErrorReason.Cancelled
|
440
|
+
e.reason !== ConnectionErrorReason.Cancelled &&
|
441
|
+
e.reason !== ConnectionErrorReason.NotAllowed
|
393
442
|
) {
|
394
443
|
let nextUrl: string | null = null;
|
395
444
|
try {
|
396
|
-
nextUrl = await
|
445
|
+
nextUrl = await this.regionUrlProvider.getNextBestRegionUrl(
|
446
|
+
this.abortController?.signal,
|
447
|
+
);
|
397
448
|
} catch (error) {
|
398
449
|
if (
|
399
450
|
error instanceof ConnectionError &&
|
@@ -404,7 +455,9 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
404
455
|
}
|
405
456
|
}
|
406
457
|
if (nextUrl) {
|
407
|
-
log.
|
458
|
+
log.info('initial connection failed, retrying with another region', {
|
459
|
+
nextUrl,
|
460
|
+
});
|
408
461
|
await connectFn(resolve, reject, nextUrl);
|
409
462
|
} else {
|
410
463
|
reject(e);
|
@@ -414,9 +467,17 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
414
467
|
}
|
415
468
|
}
|
416
469
|
};
|
417
|
-
|
418
|
-
|
419
|
-
|
470
|
+
|
471
|
+
const regionUrl = this.regionUrl;
|
472
|
+
this.regionUrl = undefined;
|
473
|
+
this.connectFuture = new Future(
|
474
|
+
(resolve, reject) => {
|
475
|
+
connectFn(resolve, reject, regionUrl);
|
476
|
+
},
|
477
|
+
() => {
|
478
|
+
this.clearConnectionFutures();
|
479
|
+
},
|
480
|
+
);
|
420
481
|
|
421
482
|
return this.connectFuture.promise;
|
422
483
|
};
|
@@ -495,6 +556,9 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
495
556
|
// create engine if previously disconnected
|
496
557
|
this.maybeCreateEngine();
|
497
558
|
}
|
559
|
+
if (this.regionUrlProvider?.isCloud()) {
|
560
|
+
this.engine.setRegionUrlProvider(this.regionUrlProvider);
|
561
|
+
}
|
498
562
|
|
499
563
|
this.acquireAudioContext();
|
500
564
|
|
@@ -637,34 +701,34 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
637
701
|
await this.engine.client.handleOnClose('simulate disconnect');
|
638
702
|
break;
|
639
703
|
case 'speaker':
|
640
|
-
req = SimulateScenario
|
704
|
+
req = new SimulateScenario({
|
641
705
|
scenario: {
|
642
|
-
|
643
|
-
|
706
|
+
case: 'speakerUpdate',
|
707
|
+
value: 3,
|
644
708
|
},
|
645
709
|
});
|
646
710
|
break;
|
647
711
|
case 'node-failure':
|
648
|
-
req = SimulateScenario
|
712
|
+
req = new SimulateScenario({
|
649
713
|
scenario: {
|
650
|
-
|
651
|
-
|
714
|
+
case: 'nodeFailure',
|
715
|
+
value: true,
|
652
716
|
},
|
653
717
|
});
|
654
718
|
break;
|
655
719
|
case 'server-leave':
|
656
|
-
req = SimulateScenario
|
720
|
+
req = new SimulateScenario({
|
657
721
|
scenario: {
|
658
|
-
|
659
|
-
|
722
|
+
case: 'serverLeave',
|
723
|
+
value: true,
|
660
724
|
},
|
661
725
|
});
|
662
726
|
break;
|
663
727
|
case 'migration':
|
664
|
-
req = SimulateScenario
|
728
|
+
req = new SimulateScenario({
|
665
729
|
scenario: {
|
666
|
-
|
667
|
-
|
730
|
+
case: 'migration',
|
731
|
+
value: true,
|
668
732
|
},
|
669
733
|
});
|
670
734
|
break;
|
@@ -680,19 +744,21 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
680
744
|
break;
|
681
745
|
case 'force-tcp':
|
682
746
|
case 'force-tls':
|
683
|
-
req = SimulateScenario
|
747
|
+
req = new SimulateScenario({
|
684
748
|
scenario: {
|
685
|
-
|
686
|
-
|
749
|
+
case: 'switchCandidateProtocol',
|
750
|
+
value: scenario === 'force-tls' ? 2 : 1,
|
687
751
|
},
|
688
752
|
});
|
689
753
|
postAction = async () => {
|
690
754
|
const onLeave = this.engine.client.onLeave;
|
691
755
|
if (onLeave) {
|
692
|
-
onLeave(
|
693
|
-
|
694
|
-
|
695
|
-
|
756
|
+
onLeave(
|
757
|
+
new LeaveRequest({
|
758
|
+
reason: DisconnectReason.CLIENT_INITIATED,
|
759
|
+
canReconnect: true,
|
760
|
+
}),
|
761
|
+
);
|
696
762
|
}
|
697
763
|
};
|
698
764
|
break;
|
@@ -737,8 +803,19 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
737
803
|
dummyAudioEl.hidden = true;
|
738
804
|
const track = getEmptyAudioStreamTrack();
|
739
805
|
track.enabled = true;
|
740
|
-
|
806
|
+
const stream = new MediaStream([track]);
|
807
|
+
dummyAudioEl.srcObject = stream;
|
808
|
+
document.addEventListener('visibilitychange', () => {
|
809
|
+
if (!dummyAudioEl) {
|
810
|
+
return;
|
811
|
+
}
|
812
|
+
// set the srcObject to null on page hide in order to prevent lock screen controls to show up for it
|
813
|
+
dummyAudioEl.srcObject = document.hidden ? null : stream;
|
814
|
+
});
|
741
815
|
document.body.append(dummyAudioEl);
|
816
|
+
this.once(RoomEvent.Disconnected, () => {
|
817
|
+
dummyAudioEl?.remove();
|
818
|
+
});
|
742
819
|
}
|
743
820
|
elements.push(dummyAudioEl);
|
744
821
|
}
|
@@ -985,6 +1062,8 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
985
1062
|
if (!track.isMuted) {
|
986
1063
|
if (
|
987
1064
|
(track instanceof LocalAudioTrack || track instanceof LocalVideoTrack) &&
|
1065
|
+
track.source !== Track.Source.ScreenShare &&
|
1066
|
+
track.source !== Track.Source.ScreenShareAudio &&
|
988
1067
|
!track.isUserProvided
|
989
1068
|
) {
|
990
1069
|
// we need to restart the track before publishing, often a full reconnect
|
@@ -1030,6 +1109,8 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
1030
1109
|
return;
|
1031
1110
|
}
|
1032
1111
|
|
1112
|
+
this.regionUrl = undefined;
|
1113
|
+
|
1033
1114
|
try {
|
1034
1115
|
this.participants.forEach((p) => {
|
1035
1116
|
p.tracks.forEach((pub) => {
|
@@ -1461,25 +1542,27 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
1461
1542
|
});
|
1462
1543
|
});
|
1463
1544
|
|
1464
|
-
this.engine.client.sendSyncState(
|
1465
|
-
|
1466
|
-
|
1467
|
-
|
1545
|
+
this.engine.client.sendSyncState(
|
1546
|
+
new SyncState({
|
1547
|
+
answer: toProtoSessionDescription({
|
1548
|
+
sdp: previousAnswer.sdp,
|
1549
|
+
type: previousAnswer.type,
|
1550
|
+
}),
|
1551
|
+
offer: previousOffer
|
1552
|
+
? toProtoSessionDescription({
|
1553
|
+
sdp: previousOffer.sdp,
|
1554
|
+
type: previousOffer.type,
|
1555
|
+
})
|
1556
|
+
: undefined,
|
1557
|
+
subscription: new UpdateSubscription({
|
1558
|
+
trackSids,
|
1559
|
+
subscribe: !autoSubscribe,
|
1560
|
+
participantTracks: [],
|
1561
|
+
}),
|
1562
|
+
publishTracks: this.localParticipant.publishedTracksInfo(),
|
1563
|
+
dataChannels: this.localParticipant.dataChannelsInfo(),
|
1468
1564
|
}),
|
1469
|
-
|
1470
|
-
? toProtoSessionDescription({
|
1471
|
-
sdp: previousOffer.sdp,
|
1472
|
-
type: previousOffer.type,
|
1473
|
-
})
|
1474
|
-
: undefined,
|
1475
|
-
subscription: {
|
1476
|
-
trackSids,
|
1477
|
-
subscribe: !autoSubscribe,
|
1478
|
-
participantTracks: [],
|
1479
|
-
},
|
1480
|
-
publishTracks: this.localParticipant.publishedTracksInfo(),
|
1481
|
-
dataChannels: this.localParticipant.dataChannelsInfo(),
|
1482
|
-
});
|
1565
|
+
);
|
1483
1566
|
}
|
1484
1567
|
|
1485
1568
|
/**
|
@@ -1539,9 +1622,9 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
1539
1622
|
return true;
|
1540
1623
|
}
|
1541
1624
|
|
1542
|
-
private emitWhenConnected<
|
1543
|
-
event:
|
1544
|
-
...args:
|
1625
|
+
private emitWhenConnected<E extends keyof RoomEventCallbacks>(
|
1626
|
+
event: E,
|
1627
|
+
...args: Parameters<RoomEventCallbacks[E]>
|
1545
1628
|
): boolean {
|
1546
1629
|
if (this.state === ConnectionState.Connected) {
|
1547
1630
|
return this.emit(event, ...args);
|
@@ -1621,22 +1704,22 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
1621
1704
|
...options.participants,
|
1622
1705
|
};
|
1623
1706
|
this.handleDisconnect();
|
1624
|
-
this.roomInfo = {
|
1707
|
+
this.roomInfo = new RoomModel({
|
1625
1708
|
sid: 'RM_SIMULATED',
|
1626
1709
|
name: 'simulated-room',
|
1627
1710
|
emptyTimeout: 0,
|
1628
1711
|
maxParticipants: 0,
|
1629
|
-
creationTime: new Date().getTime(),
|
1712
|
+
creationTime: protoInt64.parse(new Date().getTime()),
|
1630
1713
|
metadata: '',
|
1631
1714
|
numParticipants: 1,
|
1632
1715
|
numPublishers: 1,
|
1633
1716
|
turnPassword: '',
|
1634
1717
|
enabledCodecs: [],
|
1635
1718
|
activeRecording: false,
|
1636
|
-
};
|
1719
|
+
});
|
1637
1720
|
|
1638
1721
|
this.localParticipant.updateInfo(
|
1639
|
-
ParticipantInfo
|
1722
|
+
new ParticipantInfo({
|
1640
1723
|
identity: 'simulated-local',
|
1641
1724
|
name: 'local-name',
|
1642
1725
|
}),
|
@@ -1648,7 +1731,7 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
1648
1731
|
if (publishOptions.video) {
|
1649
1732
|
const camPub = new LocalTrackPublication(
|
1650
1733
|
Track.Kind.Video,
|
1651
|
-
TrackInfo
|
1734
|
+
new TrackInfo({
|
1652
1735
|
source: TrackSource.CAMERA,
|
1653
1736
|
sid: Math.floor(Math.random() * 10_000).toString(),
|
1654
1737
|
type: TrackType.AUDIO,
|
@@ -1674,7 +1757,7 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
1674
1757
|
if (publishOptions.audio) {
|
1675
1758
|
const audioPub = new LocalTrackPublication(
|
1676
1759
|
Track.Kind.Audio,
|
1677
|
-
TrackInfo
|
1760
|
+
new TrackInfo({
|
1678
1761
|
source: TrackSource.MICROPHONE,
|
1679
1762
|
sid: Math.floor(Math.random() * 10_000).toString(),
|
1680
1763
|
type: TrackType.AUDIO,
|
@@ -1691,12 +1774,12 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
1691
1774
|
}
|
1692
1775
|
|
1693
1776
|
for (let i = 0; i < participantOptions.count - 1; i += 1) {
|
1694
|
-
let info: ParticipantInfo = ParticipantInfo
|
1777
|
+
let info: ParticipantInfo = new ParticipantInfo({
|
1695
1778
|
sid: Math.floor(Math.random() * 10_000).toString(),
|
1696
1779
|
identity: `simulated-${i}`,
|
1697
1780
|
state: ParticipantInfo_State.ACTIVE,
|
1698
1781
|
tracks: [],
|
1699
|
-
joinedAt: Date.now(),
|
1782
|
+
joinedAt: protoInt64.parse(Date.now()),
|
1700
1783
|
});
|
1701
1784
|
const p = this.getOrCreateParticipant(info.identity, info);
|
1702
1785
|
if (participantOptions.video) {
|
@@ -1706,7 +1789,7 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
1706
1789
|
false,
|
1707
1790
|
true,
|
1708
1791
|
);
|
1709
|
-
const videoTrack = TrackInfo
|
1792
|
+
const videoTrack = new TrackInfo({
|
1710
1793
|
source: TrackSource.CAMERA,
|
1711
1794
|
sid: Math.floor(Math.random() * 10_000).toString(),
|
1712
1795
|
type: TrackType.AUDIO,
|
@@ -1716,7 +1799,7 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
1716
1799
|
}
|
1717
1800
|
if (participantOptions.audio) {
|
1718
1801
|
const dummyTrack = getEmptyAudioStreamTrack();
|
1719
|
-
const audioTrack = TrackInfo
|
1802
|
+
const audioTrack = new TrackInfo({
|
1720
1803
|
source: TrackSource.MICROPHONE,
|
1721
1804
|
sid: Math.floor(Math.random() * 10_000).toString(),
|
1722
1805
|
type: TrackType.AUDIO,
|
@@ -1730,14 +1813,10 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
1730
1813
|
}
|
1731
1814
|
|
1732
1815
|
// /** @internal */
|
1733
|
-
emit<
|
1734
|
-
event:
|
1735
|
-
...args:
|
1816
|
+
emit<E extends keyof RoomEventCallbacks>(
|
1817
|
+
event: E,
|
1818
|
+
...args: Parameters<RoomEventCallbacks[E]>
|
1736
1819
|
): boolean {
|
1737
|
-
// emit<E extends keyof RoomEventCallbacks>(
|
1738
|
-
// event: E,
|
1739
|
-
// ...args: Parameters<RoomEventCallbacks[E]>
|
1740
|
-
// ): boolean {
|
1741
1820
|
// active speaker updates are too spammy
|
1742
1821
|
if (event !== RoomEvent.ActiveSpeakersChanged) {
|
1743
1822
|
log.debug(`room event ${event}`, { event, args });
|