livekit-client 1.12.1 → 1.12.2
Sign up to get free protection for your applications and to get access to all the features.
- 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 +12735 -14958
- 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/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 +7 -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/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 +7 -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 +24 -18
- 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/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 +22 -22
- 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 +6 -0
- 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/api/SignalClient.ts
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import { protoInt64 } from '@bufbuild/protobuf';
|
1
2
|
import log from '../logger';
|
2
3
|
import {
|
3
4
|
ClientInfo,
|
@@ -7,12 +8,14 @@ import {
|
|
7
8
|
Room,
|
8
9
|
SpeakerInfo,
|
9
10
|
VideoLayer,
|
10
|
-
} from '../proto/
|
11
|
+
} from '../proto/livekit_models_pb';
|
11
12
|
import {
|
12
13
|
AddTrackRequest,
|
13
14
|
ConnectionQualityUpdate,
|
14
15
|
JoinResponse,
|
15
16
|
LeaveRequest,
|
17
|
+
MuteTrackRequest,
|
18
|
+
Ping,
|
16
19
|
ReconnectResponse,
|
17
20
|
SessionDescription,
|
18
21
|
SignalRequest,
|
@@ -21,18 +24,22 @@ import {
|
|
21
24
|
SimulateScenario,
|
22
25
|
StreamStateUpdate,
|
23
26
|
SubscribedQualityUpdate,
|
27
|
+
SubscriptionPermission,
|
24
28
|
SubscriptionPermissionUpdate,
|
25
29
|
SubscriptionResponse,
|
26
30
|
SyncState,
|
27
31
|
TrackPermission,
|
28
32
|
TrackPublishedResponse,
|
29
33
|
TrackUnpublishedResponse,
|
34
|
+
TrickleRequest,
|
35
|
+
UpdateParticipantMetadata,
|
30
36
|
UpdateSubscription,
|
31
37
|
UpdateTrackSettings,
|
32
|
-
|
38
|
+
UpdateVideoLayers,
|
39
|
+
} from '../proto/livekit_rtc_pb';
|
33
40
|
import { ConnectionError, ConnectionErrorReason } from '../room/errors';
|
34
41
|
import CriticalTimers from '../room/timers';
|
35
|
-
import { Mutex, getClientInfo, isReactNative, sleep } from '../room/utils';
|
42
|
+
import { Mutex, getClientInfo, isReactNative, sleep, toWebsocketUrl } from '../room/utils';
|
36
43
|
import { AsyncQueue } from '../utils/AsyncQueue';
|
37
44
|
|
38
45
|
// internal options
|
@@ -65,7 +72,7 @@ export interface SignalOptions {
|
|
65
72
|
|
66
73
|
type SignalMessage = SignalRequest['message'];
|
67
74
|
|
68
|
-
type SignalKind = NonNullable<SignalMessage>['
|
75
|
+
type SignalKind = NonNullable<SignalMessage>['case'];
|
69
76
|
|
70
77
|
const passThroughQueueSignals: Array<SignalKind> = [
|
71
78
|
'syncState',
|
@@ -77,7 +84,7 @@ const passThroughQueueSignals: Array<SignalKind> = [
|
|
77
84
|
];
|
78
85
|
|
79
86
|
function canPassThroughQueue(req: SignalMessage): boolean {
|
80
|
-
const canPass = passThroughQueueSignals.indexOf(req
|
87
|
+
const canPass = passThroughQueueSignals.indexOf(req!.case) >= 0;
|
81
88
|
log.trace('request allowed to bypass queue:', { canPass, req });
|
82
89
|
return canPass;
|
83
90
|
}
|
@@ -206,9 +213,7 @@ export class SignalClient {
|
|
206
213
|
abortSignal?: AbortSignal,
|
207
214
|
): Promise<JoinResponse | ReconnectResponse | void> {
|
208
215
|
this.connectOptions = opts;
|
209
|
-
|
210
|
-
url = url.replace('http', 'ws');
|
211
|
-
}
|
216
|
+
url = toWebsocketUrl(url);
|
212
217
|
// strip trailing slash
|
213
218
|
url = url.replace(/\/$/, '');
|
214
219
|
url += '/rtc';
|
@@ -268,9 +273,9 @@ export class SignalClient {
|
|
268
273
|
let resp: SignalResponse;
|
269
274
|
if (typeof ev.data === 'string') {
|
270
275
|
const json = JSON.parse(ev.data);
|
271
|
-
resp = SignalResponse.
|
276
|
+
resp = SignalResponse.fromJson(json);
|
272
277
|
} else if (ev.data instanceof ArrayBuffer) {
|
273
|
-
resp = SignalResponse.
|
278
|
+
resp = SignalResponse.fromBinary(new Uint8Array(ev.data));
|
274
279
|
} else {
|
275
280
|
log.error(`could not decode websocket message: ${typeof ev.data}`);
|
276
281
|
return;
|
@@ -279,11 +284,11 @@ export class SignalClient {
|
|
279
284
|
if (!this.isConnected) {
|
280
285
|
let shouldProcessMessage = false;
|
281
286
|
// handle join message only
|
282
|
-
if (resp.message
|
287
|
+
if (resp.message?.case === 'join') {
|
283
288
|
this.isConnected = true;
|
284
289
|
abortSignal?.removeEventListener('abort', abortHandler);
|
285
|
-
this.pingTimeoutDuration = resp.message.
|
286
|
-
this.pingIntervalDuration = resp.message.
|
290
|
+
this.pingTimeoutDuration = resp.message.value.pingTimeout;
|
291
|
+
this.pingIntervalDuration = resp.message.value.pingInterval;
|
287
292
|
|
288
293
|
if (this.pingTimeoutDuration && this.pingTimeoutDuration > 0) {
|
289
294
|
log.debug('ping config', {
|
@@ -292,14 +297,14 @@ export class SignalClient {
|
|
292
297
|
});
|
293
298
|
this.startPingInterval();
|
294
299
|
}
|
295
|
-
resolve(resp.message.
|
300
|
+
resolve(resp.message.value);
|
296
301
|
} else if (opts.reconnect) {
|
297
302
|
// in reconnecting, any message received means signal reconnected
|
298
303
|
this.isConnected = true;
|
299
304
|
abortSignal?.removeEventListener('abort', abortHandler);
|
300
305
|
this.startPingInterval();
|
301
|
-
if (resp.message
|
302
|
-
resolve(resp.message?.
|
306
|
+
if (resp.message?.case === 'reconnect') {
|
307
|
+
resolve(resp.message?.value);
|
303
308
|
} else {
|
304
309
|
resolve();
|
305
310
|
shouldProcessMessage = true;
|
@@ -308,7 +313,7 @@ export class SignalClient {
|
|
308
313
|
// non-reconnect case, should receive join response first
|
309
314
|
reject(
|
310
315
|
new ConnectionError(
|
311
|
-
`did not receive join response, got ${resp.message
|
316
|
+
`did not receive join response, got ${resp.message?.case} instead`,
|
312
317
|
),
|
313
318
|
);
|
314
319
|
}
|
@@ -382,8 +387,8 @@ export class SignalClient {
|
|
382
387
|
sendOffer(offer: RTCSessionDescriptionInit) {
|
383
388
|
log.debug('sending offer', offer);
|
384
389
|
this.sendRequest({
|
385
|
-
|
386
|
-
|
390
|
+
case: 'offer',
|
391
|
+
value: toProtoSessionDescription(offer),
|
387
392
|
});
|
388
393
|
}
|
389
394
|
|
@@ -391,94 +396,94 @@ export class SignalClient {
|
|
391
396
|
sendAnswer(answer: RTCSessionDescriptionInit) {
|
392
397
|
log.debug('sending answer');
|
393
398
|
return this.sendRequest({
|
394
|
-
|
395
|
-
|
399
|
+
case: 'answer',
|
400
|
+
value: toProtoSessionDescription(answer),
|
396
401
|
});
|
397
402
|
}
|
398
403
|
|
399
404
|
sendIceCandidate(candidate: RTCIceCandidateInit, target: SignalTarget) {
|
400
405
|
log.trace('sending ice candidate', candidate);
|
401
406
|
return this.sendRequest({
|
402
|
-
|
403
|
-
|
407
|
+
case: 'trickle',
|
408
|
+
value: new TrickleRequest({
|
404
409
|
candidateInit: JSON.stringify(candidate),
|
405
410
|
target,
|
406
|
-
},
|
411
|
+
}),
|
407
412
|
});
|
408
413
|
}
|
409
414
|
|
410
415
|
sendMuteTrack(trackSid: string, muted: boolean) {
|
411
416
|
return this.sendRequest({
|
412
|
-
|
413
|
-
|
417
|
+
case: 'mute',
|
418
|
+
value: new MuteTrackRequest({
|
414
419
|
sid: trackSid,
|
415
420
|
muted,
|
416
|
-
},
|
421
|
+
}),
|
417
422
|
});
|
418
423
|
}
|
419
424
|
|
420
425
|
sendAddTrack(req: AddTrackRequest) {
|
421
426
|
return this.sendRequest({
|
422
|
-
|
423
|
-
|
427
|
+
case: 'addTrack',
|
428
|
+
value: req,
|
424
429
|
});
|
425
430
|
}
|
426
431
|
|
427
432
|
sendUpdateLocalMetadata(metadata: string, name: string) {
|
428
433
|
return this.sendRequest({
|
429
|
-
|
430
|
-
|
434
|
+
case: 'updateMetadata',
|
435
|
+
value: new UpdateParticipantMetadata({
|
431
436
|
metadata,
|
432
437
|
name,
|
433
|
-
},
|
438
|
+
}),
|
434
439
|
});
|
435
440
|
}
|
436
441
|
|
437
442
|
sendUpdateTrackSettings(settings: UpdateTrackSettings) {
|
438
443
|
this.sendRequest({
|
439
|
-
|
440
|
-
|
444
|
+
case: 'trackSetting',
|
445
|
+
value: settings,
|
441
446
|
});
|
442
447
|
}
|
443
448
|
|
444
449
|
sendUpdateSubscription(sub: UpdateSubscription) {
|
445
450
|
return this.sendRequest({
|
446
|
-
|
447
|
-
|
451
|
+
case: 'subscription',
|
452
|
+
value: sub,
|
448
453
|
});
|
449
454
|
}
|
450
455
|
|
451
456
|
sendSyncState(sync: SyncState) {
|
452
457
|
return this.sendRequest({
|
453
|
-
|
454
|
-
|
458
|
+
case: 'syncState',
|
459
|
+
value: sync,
|
455
460
|
});
|
456
461
|
}
|
457
462
|
|
458
463
|
sendUpdateVideoLayers(trackSid: string, layers: VideoLayer[]) {
|
459
464
|
return this.sendRequest({
|
460
|
-
|
461
|
-
|
465
|
+
case: 'updateLayers',
|
466
|
+
value: new UpdateVideoLayers({
|
462
467
|
trackSid,
|
463
468
|
layers,
|
464
|
-
},
|
469
|
+
}),
|
465
470
|
});
|
466
471
|
}
|
467
472
|
|
468
473
|
sendUpdateSubscriptionPermissions(allParticipants: boolean, trackPermissions: TrackPermission[]) {
|
469
474
|
return this.sendRequest({
|
470
|
-
|
471
|
-
|
475
|
+
case: 'subscriptionPermission',
|
476
|
+
value: new SubscriptionPermission({
|
472
477
|
allParticipants,
|
473
478
|
trackPermissions,
|
474
|
-
},
|
479
|
+
}),
|
475
480
|
});
|
476
481
|
}
|
477
482
|
|
478
483
|
sendSimulateScenario(scenario: SimulateScenario) {
|
479
484
|
return this.sendRequest({
|
480
|
-
|
481
|
-
|
485
|
+
case: 'simulate',
|
486
|
+
value: scenario,
|
482
487
|
});
|
483
488
|
}
|
484
489
|
|
@@ -486,26 +491,26 @@ export class SignalClient {
|
|
486
491
|
/** send both of ping and pingReq for compatibility to old and new server */
|
487
492
|
return Promise.all([
|
488
493
|
this.sendRequest({
|
489
|
-
|
490
|
-
|
494
|
+
case: 'ping',
|
495
|
+
value: protoInt64.parse(Date.now()),
|
491
496
|
}),
|
492
497
|
this.sendRequest({
|
493
|
-
|
494
|
-
|
495
|
-
timestamp: Date.now(),
|
496
|
-
rtt: this.rtt,
|
497
|
-
},
|
498
|
+
case: 'pingReq',
|
499
|
+
value: new Ping({
|
500
|
+
timestamp: protoInt64.parse(Date.now()),
|
501
|
+
rtt: protoInt64.parse(this.rtt),
|
502
|
+
}),
|
498
503
|
}),
|
499
504
|
]);
|
500
505
|
}
|
501
506
|
|
502
507
|
sendLeave() {
|
503
508
|
return this.sendRequest({
|
504
|
-
|
505
|
-
|
509
|
+
case: 'leave',
|
510
|
+
value: new LeaveRequest({
|
506
511
|
canReconnect: false,
|
507
512
|
reason: DisconnectReason.CLIENT_INITIATED,
|
508
|
-
},
|
513
|
+
}),
|
509
514
|
});
|
510
515
|
}
|
511
516
|
|
@@ -527,18 +532,16 @@ export class SignalClient {
|
|
527
532
|
await sleep(this.signalLatency);
|
528
533
|
}
|
529
534
|
if (!this.ws || this.ws.readyState !== this.ws.OPEN) {
|
530
|
-
log.error(`cannot send signal request before connected, type: ${message
|
535
|
+
log.error(`cannot send signal request before connected, type: ${message?.case}`);
|
531
536
|
return;
|
532
537
|
}
|
538
|
+
const req = new SignalRequest({ message });
|
533
539
|
|
534
|
-
const req = {
|
535
|
-
message,
|
536
|
-
};
|
537
540
|
try {
|
538
541
|
if (this.useJSON) {
|
539
|
-
this.ws.send(
|
542
|
+
this.ws.send(req.toJsonString());
|
540
543
|
} else {
|
541
|
-
this.ws.send(
|
544
|
+
this.ws.send(req.toBinary());
|
542
545
|
}
|
543
546
|
} catch (e) {
|
544
547
|
log.error('error sending signal message', { error: e });
|
@@ -551,77 +554,77 @@ export class SignalClient {
|
|
551
554
|
log.debug('received unsupported message');
|
552
555
|
return;
|
553
556
|
}
|
554
|
-
if (msg
|
555
|
-
const sd = fromProtoSessionDescription(msg.
|
557
|
+
if (msg.case === 'answer') {
|
558
|
+
const sd = fromProtoSessionDescription(msg.value);
|
556
559
|
if (this.onAnswer) {
|
557
560
|
this.onAnswer(sd);
|
558
561
|
}
|
559
|
-
} else if (msg
|
560
|
-
const sd = fromProtoSessionDescription(msg.
|
562
|
+
} else if (msg.case === 'offer') {
|
563
|
+
const sd = fromProtoSessionDescription(msg.value);
|
561
564
|
if (this.onOffer) {
|
562
565
|
this.onOffer(sd);
|
563
566
|
}
|
564
|
-
} else if (msg
|
565
|
-
const candidate: RTCIceCandidateInit = JSON.parse(msg.
|
567
|
+
} else if (msg.case === 'trickle') {
|
568
|
+
const candidate: RTCIceCandidateInit = JSON.parse(msg.value.candidateInit!);
|
566
569
|
if (this.onTrickle) {
|
567
|
-
this.onTrickle(candidate, msg.
|
570
|
+
this.onTrickle(candidate, msg.value.target);
|
568
571
|
}
|
569
|
-
} else if (msg
|
572
|
+
} else if (msg.case === 'update') {
|
570
573
|
if (this.onParticipantUpdate) {
|
571
|
-
this.onParticipantUpdate(msg.
|
574
|
+
this.onParticipantUpdate(msg.value.participants ?? []);
|
572
575
|
}
|
573
|
-
} else if (msg
|
576
|
+
} else if (msg.case === 'trackPublished') {
|
574
577
|
if (this.onLocalTrackPublished) {
|
575
|
-
this.onLocalTrackPublished(msg.
|
578
|
+
this.onLocalTrackPublished(msg.value);
|
576
579
|
}
|
577
|
-
} else if (msg
|
580
|
+
} else if (msg.case === 'speakersChanged') {
|
578
581
|
if (this.onSpeakersChanged) {
|
579
|
-
this.onSpeakersChanged(msg.
|
582
|
+
this.onSpeakersChanged(msg.value.speakers ?? []);
|
580
583
|
}
|
581
|
-
} else if (msg
|
584
|
+
} else if (msg.case === 'leave') {
|
582
585
|
if (this.onLeave) {
|
583
|
-
this.onLeave(msg.
|
586
|
+
this.onLeave(msg.value);
|
584
587
|
}
|
585
|
-
} else if (msg
|
588
|
+
} else if (msg.case === 'mute') {
|
586
589
|
if (this.onRemoteMuteChanged) {
|
587
|
-
this.onRemoteMuteChanged(msg.
|
590
|
+
this.onRemoteMuteChanged(msg.value.sid, msg.value.muted);
|
588
591
|
}
|
589
|
-
} else if (msg
|
590
|
-
if (this.onRoomUpdate && msg.
|
591
|
-
this.onRoomUpdate(msg.
|
592
|
+
} else if (msg.case === 'roomUpdate') {
|
593
|
+
if (this.onRoomUpdate && msg.value.room) {
|
594
|
+
this.onRoomUpdate(msg.value.room);
|
592
595
|
}
|
593
|
-
} else if (msg
|
596
|
+
} else if (msg.case === 'connectionQuality') {
|
594
597
|
if (this.onConnectionQuality) {
|
595
|
-
this.onConnectionQuality(msg.
|
598
|
+
this.onConnectionQuality(msg.value);
|
596
599
|
}
|
597
|
-
} else if (msg
|
600
|
+
} else if (msg.case === 'streamStateUpdate') {
|
598
601
|
if (this.onStreamStateUpdate) {
|
599
|
-
this.onStreamStateUpdate(msg.
|
602
|
+
this.onStreamStateUpdate(msg.value);
|
600
603
|
}
|
601
|
-
} else if (msg
|
604
|
+
} else if (msg.case === 'subscribedQualityUpdate') {
|
602
605
|
if (this.onSubscribedQualityUpdate) {
|
603
|
-
this.onSubscribedQualityUpdate(msg.
|
606
|
+
this.onSubscribedQualityUpdate(msg.value);
|
604
607
|
}
|
605
|
-
} else if (msg
|
608
|
+
} else if (msg.case === 'subscriptionPermissionUpdate') {
|
606
609
|
if (this.onSubscriptionPermissionUpdate) {
|
607
|
-
this.onSubscriptionPermissionUpdate(msg.
|
610
|
+
this.onSubscriptionPermissionUpdate(msg.value);
|
608
611
|
}
|
609
|
-
} else if (msg
|
612
|
+
} else if (msg.case === 'refreshToken') {
|
610
613
|
if (this.onTokenRefresh) {
|
611
|
-
this.onTokenRefresh(msg.
|
614
|
+
this.onTokenRefresh(msg.value);
|
612
615
|
}
|
613
|
-
} else if (msg
|
616
|
+
} else if (msg.case === 'trackUnpublished') {
|
614
617
|
if (this.onLocalTrackUnpublished) {
|
615
|
-
this.onLocalTrackUnpublished(msg.
|
618
|
+
this.onLocalTrackUnpublished(msg.value);
|
616
619
|
}
|
617
|
-
} else if (msg
|
620
|
+
} else if (msg.case === 'subscriptionResponse') {
|
618
621
|
if (this.onSubscriptionError) {
|
619
|
-
this.onSubscriptionError(msg.
|
622
|
+
this.onSubscriptionError(msg.value);
|
620
623
|
}
|
621
|
-
} else if (msg
|
624
|
+
} else if (msg.case === 'pong') {
|
622
625
|
this.resetPingTimeout();
|
623
|
-
} else if (msg
|
624
|
-
this.rtt = Date.now() - msg.
|
626
|
+
} else if (msg.case === 'pongResp') {
|
627
|
+
this.rtt = Date.now() - Number.parseInt(msg.value.lastPingTimestamp.toString());
|
625
628
|
this.resetPingTimeout();
|
626
629
|
} else {
|
627
630
|
log.debug('unsupported message', msg);
|
@@ -724,10 +727,10 @@ function fromProtoSessionDescription(sd: SessionDescription): RTCSessionDescript
|
|
724
727
|
export function toProtoSessionDescription(
|
725
728
|
rsd: RTCSessionDescription | RTCSessionDescriptionInit,
|
726
729
|
): SessionDescription {
|
727
|
-
const sd
|
730
|
+
const sd = new SessionDescription({
|
728
731
|
sdp: rsd.sdp!,
|
729
732
|
type: rsd.type!,
|
730
|
-
};
|
733
|
+
});
|
731
734
|
return sd;
|
732
735
|
}
|
733
736
|
|
@@ -1,4 +1,5 @@
|
|
1
|
-
import EventEmitter from '
|
1
|
+
import { EventEmitter } from 'events';
|
2
|
+
import type TypedEmitter from 'typed-emitter';
|
2
3
|
import { CheckStatus, Checker } from './checks/Checker';
|
3
4
|
import type { CheckInfo, InstantiableCheck } from './checks/Checker';
|
4
5
|
import { PublishAudioCheck } from './checks/publishAudio';
|
@@ -10,7 +11,7 @@ import { WebSocketCheck } from './checks/websocket';
|
|
10
11
|
|
11
12
|
export type { CheckInfo, CheckStatus };
|
12
13
|
|
13
|
-
export class ConnectionCheck extends EventEmitter<ConnectionCheckCallbacks> {
|
14
|
+
export class ConnectionCheck extends (EventEmitter as new () => TypedEmitter<ConnectionCheckCallbacks>) {
|
14
15
|
token: string;
|
15
16
|
|
16
17
|
url: string;
|
@@ -1,4 +1,5 @@
|
|
1
|
-
import EventEmitter from '
|
1
|
+
import { EventEmitter } from 'events';
|
2
|
+
import type TypedEmitter from 'typed-emitter';
|
2
3
|
import type { RoomConnectOptions, RoomOptions } from '../../options';
|
3
4
|
import type RTCEngine from '../../room/RTCEngine';
|
4
5
|
import Room, { ConnectionState } from '../../room/Room';
|
@@ -29,7 +30,7 @@ export interface CheckerOptions {
|
|
29
30
|
connectOptions?: RoomConnectOptions;
|
30
31
|
}
|
31
32
|
|
32
|
-
export abstract class Checker extends EventEmitter<CheckerCallbacks> {
|
33
|
+
export abstract class Checker extends (EventEmitter as new () => TypedEmitter<CheckerCallbacks>) {
|
33
34
|
protected url: string;
|
34
35
|
|
35
36
|
protected token: string;
|
@@ -67,7 +68,6 @@ export abstract class Checker extends EventEmitter<CheckerCallbacks> {
|
|
67
68
|
throw Error('check is running already');
|
68
69
|
}
|
69
70
|
this.setStatus(CheckStatus.RUNNING);
|
70
|
-
this.appendMessage(`${this.name} started.`);
|
71
71
|
|
72
72
|
try {
|
73
73
|
await this.perform();
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import { RoomEvent } from '../../room/events';
|
1
2
|
import { Checker } from './Checker';
|
2
3
|
|
3
4
|
export class WebRTCCheck extends Checker {
|
@@ -6,13 +7,76 @@ export class WebRTCCheck extends Checker {
|
|
6
7
|
}
|
7
8
|
|
8
9
|
protected async perform(): Promise<void> {
|
10
|
+
let hasTcp = false;
|
11
|
+
let hasIpv4Udp = false;
|
12
|
+
this.room.on(RoomEvent.SignalConnected, () => {
|
13
|
+
const prevTrickle = this.room.engine.client.onTrickle;
|
14
|
+
|
15
|
+
const candidates: RTCIceCandidate[] = [];
|
16
|
+
this.room.engine.client.onTrickle = (sd, target) => {
|
17
|
+
console.log('got candidate', sd);
|
18
|
+
if (sd.candidate) {
|
19
|
+
const candidate = new RTCIceCandidate(sd);
|
20
|
+
candidates.push(candidate);
|
21
|
+
let str = `${candidate.protocol} ${candidate.address}:${candidate.port} ${candidate.type}`;
|
22
|
+
if (candidate.protocol === 'tcp' && candidate.tcpType === 'passive') {
|
23
|
+
hasTcp = true;
|
24
|
+
str += ' (active)';
|
25
|
+
} else if (candidate.protocol === 'udp' && candidate.address) {
|
26
|
+
if (isIPPrivate(candidate.address)) {
|
27
|
+
str += ' (private)';
|
28
|
+
} else {
|
29
|
+
hasIpv4Udp = true;
|
30
|
+
}
|
31
|
+
}
|
32
|
+
this.appendMessage(str);
|
33
|
+
}
|
34
|
+
if (prevTrickle) {
|
35
|
+
prevTrickle(sd, target);
|
36
|
+
}
|
37
|
+
};
|
38
|
+
|
39
|
+
if (this.room.engine.subscriber) {
|
40
|
+
this.room.engine.subscriber.pc.onicecandidateerror = (ev) => {
|
41
|
+
if (ev instanceof RTCPeerConnectionIceErrorEvent) {
|
42
|
+
this.appendWarning(
|
43
|
+
`error with ICE candidate: ${ev.errorCode} ${ev.errorText} ${ev.url}`,
|
44
|
+
);
|
45
|
+
}
|
46
|
+
};
|
47
|
+
}
|
48
|
+
});
|
9
49
|
try {
|
10
|
-
|
11
|
-
this.room = await this.connect();
|
50
|
+
await this.connect();
|
12
51
|
console.log('now the room is connected');
|
13
52
|
} catch (err) {
|
14
53
|
this.appendWarning('ports need to be open on firewall in order to connect.');
|
15
54
|
throw err;
|
16
55
|
}
|
56
|
+
if (!hasTcp) {
|
57
|
+
this.appendWarning('Server is not configured for ICE/TCP');
|
58
|
+
}
|
59
|
+
if (!hasIpv4Udp) {
|
60
|
+
this.appendWarning(
|
61
|
+
'No public IPv4 UDP candidates were found. Your server is likely not configured correctly',
|
62
|
+
);
|
63
|
+
}
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
function isIPPrivate(address: string): boolean {
|
68
|
+
const parts = address.split('.');
|
69
|
+
if (parts.length === 4) {
|
70
|
+
if (parts[0] === '10') {
|
71
|
+
return true;
|
72
|
+
} else if (parts[0] === '192' && parts[1] === '168') {
|
73
|
+
return true;
|
74
|
+
} else if (parts[0] === '172') {
|
75
|
+
const second = parseInt(parts[1], 10);
|
76
|
+
if (second >= 16 && second <= 31) {
|
77
|
+
return true;
|
78
|
+
}
|
79
|
+
}
|
17
80
|
}
|
81
|
+
return false;
|
18
82
|
}
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import { SignalClient } from '../../api/SignalClient';
|
2
|
+
import { ServerInfo_Edition } from '../../proto/livekit_models_pb';
|
2
3
|
import { Checker } from './Checker';
|
3
4
|
|
4
5
|
export class WebSocketCheck extends Checker {
|
@@ -18,6 +19,9 @@ export class WebSocketCheck extends Checker {
|
|
18
19
|
e2eeEnabled: false,
|
19
20
|
});
|
20
21
|
this.appendMessage(`Connected to server, version ${joinRes.serverVersion}.`);
|
22
|
+
if (joinRes.serverInfo?.edition === ServerInfo_Edition.Cloud && joinRes.serverInfo?.region) {
|
23
|
+
this.appendMessage(`LiveKit Cloud: ${joinRes.serverInfo?.region}`);
|
24
|
+
}
|
21
25
|
await signalClient.close();
|
22
26
|
}
|
23
27
|
}
|
package/src/e2ee/E2eeManager.ts
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
-
import EventEmitter from '
|
1
|
+
import { EventEmitter } from 'events';
|
2
|
+
import type TypedEventEmitter from 'typed-emitter';
|
2
3
|
import log from '../logger';
|
3
|
-
import { Encryption_Type, TrackInfo } from '../proto/
|
4
|
+
import { Encryption_Type, TrackInfo } from '../proto/livekit_models_pb';
|
4
5
|
import type RTCEngine from '../room/RTCEngine';
|
5
6
|
import type Room from '../room/Room';
|
6
7
|
import { ConnectionState } from '../room/Room';
|
@@ -32,7 +33,7 @@ import { isE2EESupported, isScriptTransformSupported, mimeTypeToVideoCodecString
|
|
32
33
|
/**
|
33
34
|
* @experimental
|
34
35
|
*/
|
35
|
-
export class E2EEManager extends EventEmitter<E2EEManagerCallbacks> {
|
36
|
+
export class E2EEManager extends (EventEmitter as new () => TypedEventEmitter<E2EEManagerCallbacks>) {
|
36
37
|
protected worker: Worker;
|
37
38
|
|
38
39
|
protected room?: Room;
|
package/src/e2ee/KeyProvider.ts
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
import EventEmitter from '
|
1
|
+
import { EventEmitter } from 'events';
|
2
|
+
import type TypedEventEmitter from 'typed-emitter';
|
2
3
|
import { KEY_PROVIDER_DEFAULTS } from './constants';
|
3
4
|
import type { KeyInfo, KeyProviderCallbacks, KeyProviderOptions } from './types';
|
4
5
|
import { createKeyMaterialFromString } from './utils';
|
@@ -6,7 +7,7 @@ import { createKeyMaterialFromString } from './utils';
|
|
6
7
|
/**
|
7
8
|
* @experimental
|
8
9
|
*/
|
9
|
-
export class BaseKeyProvider extends EventEmitter<KeyProviderCallbacks> {
|
10
|
+
export class BaseKeyProvider extends (EventEmitter as new () => TypedEventEmitter<KeyProviderCallbacks>) {
|
10
11
|
private keyInfoMap: Map<string, KeyInfo>;
|
11
12
|
|
12
13
|
private options: KeyProviderOptions;
|
package/src/e2ee/constants.ts
CHANGED
@@ -7,6 +7,9 @@ export const ENCRYPTION_ALGORITHM = 'AES-GCM';
|
|
7
7
|
// in the frame trailer.
|
8
8
|
export const KEYRING_SIZE = 16;
|
9
9
|
|
10
|
+
// How many consecutive frames can fail decrypting before a particular key gets marked as invalid
|
11
|
+
export const DECRYPTION_FAILURE_TOLERANCE = 10;
|
12
|
+
|
10
13
|
// We copy the first bytes of the VP8 payload unencrypted.
|
11
14
|
// For keyframes this is 10 bytes, for non-keyframes (delta) 3. See
|
12
15
|
// https://tools.ietf.org/html/rfc6386#section-9.1
|
@@ -37,4 +40,5 @@ export const KEY_PROVIDER_DEFAULTS: KeyProviderOptions = {
|
|
37
40
|
sharedKey: false,
|
38
41
|
ratchetSalt: SALT,
|
39
42
|
ratchetWindowSize: 8,
|
43
|
+
failureTolerance: DECRYPTION_FAILURE_TOLERANCE,
|
40
44
|
} as const;
|
package/src/e2ee/types.ts
CHANGED