livekit-client 1.12.0 → 1.12.2
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +19 -1
- 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 +442 -334
- package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
- package/dist/livekit-client.esm.mjs +12303 -14499
- 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 -3
- package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
- package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts +21 -2
- 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 +8 -7
- 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/src/utils/browserParser.d.ts +2 -0
- package/dist/src/utils/browserParser.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 -3
- package/dist/ts4.2/src/e2ee/worker/ParticipantKeyHandler.d.ts +21 -2
- 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 +8 -7
- 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/dist/ts4.2/src/utils/browserParser.d.ts +2 -0
- 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 +14 -16
- package/src/e2ee/worker/ParticipantKeyHandler.ts +48 -2
- package/src/e2ee/worker/e2ee.worker.ts +12 -6
- 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 +175 -86
- 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 +24 -21
- 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 +18 -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 +5 -0
- 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/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,19 @@ 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';
|
41
|
+
import { getBrowser } from '../utils/browserParser';
|
36
42
|
import DeviceManager from './DeviceManager';
|
37
43
|
import RTCEngine from './RTCEngine';
|
38
44
|
import { RegionUrlProvider } from './RegionUrlProvider';
|
@@ -66,9 +72,9 @@ import {
|
|
66
72
|
createDummyVideoStreamTrack,
|
67
73
|
getEmptyAudioStreamTrack,
|
68
74
|
isCloud,
|
69
|
-
isSafari,
|
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;
|
@@ -718,10 +784,10 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
718
784
|
async startAudio() {
|
719
785
|
await this.acquireAudioContext();
|
720
786
|
const elements: Array<HTMLMediaElement> = [];
|
721
|
-
|
722
|
-
if (
|
787
|
+
const browser = getBrowser();
|
788
|
+
if (browser && browser.os === 'iOS') {
|
723
789
|
/**
|
724
|
-
* iOS
|
790
|
+
* iOS blocks audio element playback if
|
725
791
|
* - user is not publishing audio themselves and
|
726
792
|
* - no other audio source is playing
|
727
793
|
*
|
@@ -732,12 +798,24 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
732
798
|
let dummyAudioEl = document.getElementById(audioId) as HTMLAudioElement | null;
|
733
799
|
if (!dummyAudioEl) {
|
734
800
|
dummyAudioEl = document.createElement('audio');
|
801
|
+
dummyAudioEl.id = audioId;
|
735
802
|
dummyAudioEl.autoplay = true;
|
736
803
|
dummyAudioEl.hidden = true;
|
737
804
|
const track = getEmptyAudioStreamTrack();
|
738
805
|
track.enabled = true;
|
739
|
-
|
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
|
+
});
|
740
815
|
document.body.append(dummyAudioEl);
|
816
|
+
this.once(RoomEvent.Disconnected, () => {
|
817
|
+
dummyAudioEl?.remove();
|
818
|
+
});
|
741
819
|
}
|
742
820
|
elements.push(dummyAudioEl);
|
743
821
|
}
|
@@ -833,7 +911,7 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
833
911
|
} else if (kind === 'audiooutput') {
|
834
912
|
if (
|
835
913
|
(!supportsSetSinkId() && !this.options.expWebAudioMix) ||
|
836
|
-
(this.audioContext && !('setSinkId' in this.audioContext))
|
914
|
+
(this.options.expWebAudioMix && this.audioContext && !('setSinkId' in this.audioContext))
|
837
915
|
) {
|
838
916
|
throw new Error('cannot switch audio output, setSinkId not supported');
|
839
917
|
}
|
@@ -984,6 +1062,8 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
984
1062
|
if (!track.isMuted) {
|
985
1063
|
if (
|
986
1064
|
(track instanceof LocalAudioTrack || track instanceof LocalVideoTrack) &&
|
1065
|
+
track.source !== Track.Source.ScreenShare &&
|
1066
|
+
track.source !== Track.Source.ScreenShareAudio &&
|
987
1067
|
!track.isUserProvided
|
988
1068
|
) {
|
989
1069
|
// we need to restart the track before publishing, often a full reconnect
|
@@ -1029,6 +1109,8 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
1029
1109
|
return;
|
1030
1110
|
}
|
1031
1111
|
|
1112
|
+
this.regionUrl = undefined;
|
1113
|
+
|
1032
1114
|
try {
|
1033
1115
|
this.participants.forEach((p) => {
|
1034
1116
|
p.tracks.forEach((pub) => {
|
@@ -1303,13 +1385,22 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
1303
1385
|
) {
|
1304
1386
|
// override audio context with custom audio context if supplied by user
|
1305
1387
|
this.audioContext = this.options.expWebAudioMix.audioContext;
|
1306
|
-
|
1307
|
-
} else {
|
1388
|
+
} else if (!this.audioContext || this.audioContext.state === 'closed') {
|
1308
1389
|
// by using an AudioContext, it reduces lag on audio elements
|
1309
1390
|
// https://stackoverflow.com/questions/9811429/html5-audio-tag-on-safari-has-a-delay/54119854#54119854
|
1310
1391
|
this.audioContext = getNewAudioContext() ?? undefined;
|
1311
1392
|
}
|
1312
1393
|
|
1394
|
+
if (this.audioContext && this.audioContext.state === 'suspended') {
|
1395
|
+
// for iOS a newly created AudioContext is always in `suspended` state.
|
1396
|
+
// we try our best to resume the context here, if that doesn't work, we just continue with regular processing
|
1397
|
+
try {
|
1398
|
+
await this.audioContext.resume();
|
1399
|
+
} catch (e: any) {
|
1400
|
+
log.warn(e);
|
1401
|
+
}
|
1402
|
+
}
|
1403
|
+
|
1313
1404
|
if (this.options.expWebAudioMix) {
|
1314
1405
|
this.participants.forEach((participant) => participant.setAudioContext(this.audioContext));
|
1315
1406
|
}
|
@@ -1451,25 +1542,27 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
1451
1542
|
});
|
1452
1543
|
});
|
1453
1544
|
|
1454
|
-
this.engine.client.sendSyncState(
|
1455
|
-
|
1456
|
-
|
1457
|
-
|
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(),
|
1458
1564
|
}),
|
1459
|
-
|
1460
|
-
? toProtoSessionDescription({
|
1461
|
-
sdp: previousOffer.sdp,
|
1462
|
-
type: previousOffer.type,
|
1463
|
-
})
|
1464
|
-
: undefined,
|
1465
|
-
subscription: {
|
1466
|
-
trackSids,
|
1467
|
-
subscribe: !autoSubscribe,
|
1468
|
-
participantTracks: [],
|
1469
|
-
},
|
1470
|
-
publishTracks: this.localParticipant.publishedTracksInfo(),
|
1471
|
-
dataChannels: this.localParticipant.dataChannelsInfo(),
|
1472
|
-
});
|
1565
|
+
);
|
1473
1566
|
}
|
1474
1567
|
|
1475
1568
|
/**
|
@@ -1529,9 +1622,9 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
1529
1622
|
return true;
|
1530
1623
|
}
|
1531
1624
|
|
1532
|
-
private emitWhenConnected<
|
1533
|
-
event:
|
1534
|
-
...args:
|
1625
|
+
private emitWhenConnected<E extends keyof RoomEventCallbacks>(
|
1626
|
+
event: E,
|
1627
|
+
...args: Parameters<RoomEventCallbacks[E]>
|
1535
1628
|
): boolean {
|
1536
1629
|
if (this.state === ConnectionState.Connected) {
|
1537
1630
|
return this.emit(event, ...args);
|
@@ -1611,22 +1704,22 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
1611
1704
|
...options.participants,
|
1612
1705
|
};
|
1613
1706
|
this.handleDisconnect();
|
1614
|
-
this.roomInfo = {
|
1707
|
+
this.roomInfo = new RoomModel({
|
1615
1708
|
sid: 'RM_SIMULATED',
|
1616
1709
|
name: 'simulated-room',
|
1617
1710
|
emptyTimeout: 0,
|
1618
1711
|
maxParticipants: 0,
|
1619
|
-
creationTime: new Date().getTime(),
|
1712
|
+
creationTime: protoInt64.parse(new Date().getTime()),
|
1620
1713
|
metadata: '',
|
1621
1714
|
numParticipants: 1,
|
1622
1715
|
numPublishers: 1,
|
1623
1716
|
turnPassword: '',
|
1624
1717
|
enabledCodecs: [],
|
1625
1718
|
activeRecording: false,
|
1626
|
-
};
|
1719
|
+
});
|
1627
1720
|
|
1628
1721
|
this.localParticipant.updateInfo(
|
1629
|
-
ParticipantInfo
|
1722
|
+
new ParticipantInfo({
|
1630
1723
|
identity: 'simulated-local',
|
1631
1724
|
name: 'local-name',
|
1632
1725
|
}),
|
@@ -1638,7 +1731,7 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
1638
1731
|
if (publishOptions.video) {
|
1639
1732
|
const camPub = new LocalTrackPublication(
|
1640
1733
|
Track.Kind.Video,
|
1641
|
-
TrackInfo
|
1734
|
+
new TrackInfo({
|
1642
1735
|
source: TrackSource.CAMERA,
|
1643
1736
|
sid: Math.floor(Math.random() * 10_000).toString(),
|
1644
1737
|
type: TrackType.AUDIO,
|
@@ -1664,7 +1757,7 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
1664
1757
|
if (publishOptions.audio) {
|
1665
1758
|
const audioPub = new LocalTrackPublication(
|
1666
1759
|
Track.Kind.Audio,
|
1667
|
-
TrackInfo
|
1760
|
+
new TrackInfo({
|
1668
1761
|
source: TrackSource.MICROPHONE,
|
1669
1762
|
sid: Math.floor(Math.random() * 10_000).toString(),
|
1670
1763
|
type: TrackType.AUDIO,
|
@@ -1681,12 +1774,12 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
1681
1774
|
}
|
1682
1775
|
|
1683
1776
|
for (let i = 0; i < participantOptions.count - 1; i += 1) {
|
1684
|
-
let info: ParticipantInfo = ParticipantInfo
|
1777
|
+
let info: ParticipantInfo = new ParticipantInfo({
|
1685
1778
|
sid: Math.floor(Math.random() * 10_000).toString(),
|
1686
1779
|
identity: `simulated-${i}`,
|
1687
1780
|
state: ParticipantInfo_State.ACTIVE,
|
1688
1781
|
tracks: [],
|
1689
|
-
joinedAt: Date.now(),
|
1782
|
+
joinedAt: protoInt64.parse(Date.now()),
|
1690
1783
|
});
|
1691
1784
|
const p = this.getOrCreateParticipant(info.identity, info);
|
1692
1785
|
if (participantOptions.video) {
|
@@ -1696,7 +1789,7 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
1696
1789
|
false,
|
1697
1790
|
true,
|
1698
1791
|
);
|
1699
|
-
const videoTrack = TrackInfo
|
1792
|
+
const videoTrack = new TrackInfo({
|
1700
1793
|
source: TrackSource.CAMERA,
|
1701
1794
|
sid: Math.floor(Math.random() * 10_000).toString(),
|
1702
1795
|
type: TrackType.AUDIO,
|
@@ -1706,7 +1799,7 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
1706
1799
|
}
|
1707
1800
|
if (participantOptions.audio) {
|
1708
1801
|
const dummyTrack = getEmptyAudioStreamTrack();
|
1709
|
-
const audioTrack = TrackInfo
|
1802
|
+
const audioTrack = new TrackInfo({
|
1710
1803
|
source: TrackSource.MICROPHONE,
|
1711
1804
|
sid: Math.floor(Math.random() * 10_000).toString(),
|
1712
1805
|
type: TrackType.AUDIO,
|
@@ -1720,14 +1813,10 @@ class Room extends EventEmitter<RoomEventCallbacks> {
|
|
1720
1813
|
}
|
1721
1814
|
|
1722
1815
|
// /** @internal */
|
1723
|
-
emit<
|
1724
|
-
event:
|
1725
|
-
...args:
|
1816
|
+
emit<E extends keyof RoomEventCallbacks>(
|
1817
|
+
event: E,
|
1818
|
+
...args: Parameters<RoomEventCallbacks[E]>
|
1726
1819
|
): boolean {
|
1727
|
-
// emit<E extends keyof RoomEventCallbacks>(
|
1728
|
-
// event: E,
|
1729
|
-
// ...args: Parameters<RoomEventCallbacks[E]>
|
1730
|
-
// ): boolean {
|
1731
1820
|
// active speaker updates are too spammy
|
1732
1821
|
if (event !== RoomEvent.ActiveSpeakersChanged) {
|
1733
1822
|
log.debug(`room event ${event}`, { event, args });
|