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.
Files changed (166) hide show
  1. package/README.md +19 -1
  2. package/dist/livekit-client.e2ee.worker.js +1 -1
  3. package/dist/livekit-client.e2ee.worker.js.map +1 -1
  4. package/dist/livekit-client.e2ee.worker.mjs +442 -334
  5. package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
  6. package/dist/livekit-client.esm.mjs +12303 -14499
  7. package/dist/livekit-client.esm.mjs.map +1 -1
  8. package/dist/livekit-client.umd.js +1 -1
  9. package/dist/livekit-client.umd.js.map +1 -1
  10. package/dist/src/api/SignalClient.d.ts +2 -2
  11. package/dist/src/api/SignalClient.d.ts.map +1 -1
  12. package/dist/src/connectionHelper/ConnectionCheck.d.ts +3 -2
  13. package/dist/src/connectionHelper/ConnectionCheck.d.ts.map +1 -1
  14. package/dist/src/connectionHelper/checks/Checker.d.ts +3 -2
  15. package/dist/src/connectionHelper/checks/Checker.d.ts.map +1 -1
  16. package/dist/src/connectionHelper/checks/webrtc.d.ts.map +1 -1
  17. package/dist/src/connectionHelper/checks/websocket.d.ts.map +1 -1
  18. package/dist/src/e2ee/E2eeManager.d.ts +4 -2
  19. package/dist/src/e2ee/E2eeManager.d.ts.map +1 -1
  20. package/dist/src/e2ee/KeyProvider.d.ts +4 -2
  21. package/dist/src/e2ee/KeyProvider.d.ts.map +1 -1
  22. package/dist/src/e2ee/constants.d.ts +1 -0
  23. package/dist/src/e2ee/constants.d.ts.map +1 -1
  24. package/dist/src/e2ee/types.d.ts +1 -0
  25. package/dist/src/e2ee/types.d.ts.map +1 -1
  26. package/dist/src/e2ee/worker/FrameCryptor.d.ts +4 -3
  27. package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
  28. package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts +21 -2
  29. package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts.map +1 -1
  30. package/dist/src/index.d.ts +1 -1
  31. package/dist/src/index.d.ts.map +1 -1
  32. package/dist/src/proto/livekit_models_pb.d.ts +1264 -0
  33. package/dist/src/proto/livekit_models_pb.d.ts.map +1 -0
  34. package/dist/src/proto/livekit_rtc_pb.d.ts +1373 -0
  35. package/dist/src/proto/livekit_rtc_pb.d.ts.map +1 -0
  36. package/dist/src/room/PCTransport.d.ts +2 -1
  37. package/dist/src/room/PCTransport.d.ts.map +1 -1
  38. package/dist/src/room/RTCEngine.d.ts +9 -5
  39. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  40. package/dist/src/room/RegionUrlProvider.d.ts +4 -1
  41. package/dist/src/room/RegionUrlProvider.d.ts.map +1 -1
  42. package/dist/src/room/Room.d.ts +15 -11
  43. package/dist/src/room/Room.d.ts.map +1 -1
  44. package/dist/src/room/participant/LocalParticipant.d.ts +2 -2
  45. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  46. package/dist/src/room/participant/Participant.d.ts +5 -3
  47. package/dist/src/room/participant/Participant.d.ts.map +1 -1
  48. package/dist/src/room/participant/ParticipantTrackPermission.d.ts +1 -1
  49. package/dist/src/room/participant/ParticipantTrackPermission.d.ts.map +1 -1
  50. package/dist/src/room/participant/RemoteParticipant.d.ts +8 -7
  51. package/dist/src/room/participant/RemoteParticipant.d.ts.map +1 -1
  52. package/dist/src/room/timers.d.ts +5 -4
  53. package/dist/src/room/timers.d.ts.map +1 -1
  54. package/dist/src/room/track/LocalTrack.d.ts +3 -0
  55. package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
  56. package/dist/src/room/track/LocalTrackPublication.d.ts +1 -1
  57. package/dist/src/room/track/LocalTrackPublication.d.ts.map +1 -1
  58. package/dist/src/room/track/LocalVideoTrack.d.ts +2 -2
  59. package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
  60. package/dist/src/room/track/RemoteTrackPublication.d.ts +1 -1
  61. package/dist/src/room/track/RemoteTrackPublication.d.ts.map +1 -1
  62. package/dist/src/room/track/Track.d.ts +6 -4
  63. package/dist/src/room/track/Track.d.ts.map +1 -1
  64. package/dist/src/room/track/TrackPublication.d.ts +7 -5
  65. package/dist/src/room/track/TrackPublication.d.ts.map +1 -1
  66. package/dist/src/room/track/create.d.ts.map +1 -1
  67. package/dist/src/room/track/options.d.ts +7 -0
  68. package/dist/src/room/track/options.d.ts.map +1 -1
  69. package/dist/src/room/track/utils.d.ts +5 -1
  70. package/dist/src/room/track/utils.d.ts.map +1 -1
  71. package/dist/src/room/utils.d.ts +3 -1
  72. package/dist/src/room/utils.d.ts.map +1 -1
  73. package/dist/src/test/mocks.d.ts +4 -3
  74. package/dist/src/test/mocks.d.ts.map +1 -1
  75. package/dist/src/utils/browserParser.d.ts +2 -0
  76. package/dist/src/utils/browserParser.d.ts.map +1 -1
  77. package/dist/ts4.2/src/api/SignalClient.d.ts +2 -2
  78. package/dist/ts4.2/src/connectionHelper/ConnectionCheck.d.ts +3 -2
  79. package/dist/ts4.2/src/connectionHelper/checks/Checker.d.ts +3 -2
  80. package/dist/ts4.2/src/e2ee/E2eeManager.d.ts +4 -2
  81. package/dist/ts4.2/src/e2ee/KeyProvider.d.ts +4 -2
  82. package/dist/ts4.2/src/e2ee/constants.d.ts +1 -0
  83. package/dist/ts4.2/src/e2ee/types.d.ts +1 -0
  84. package/dist/ts4.2/src/e2ee/worker/FrameCryptor.d.ts +4 -3
  85. package/dist/ts4.2/src/e2ee/worker/ParticipantKeyHandler.d.ts +21 -2
  86. package/dist/ts4.2/src/index.d.ts +1 -1
  87. package/dist/ts4.2/src/proto/livekit_models_pb.d.ts +1264 -0
  88. package/dist/ts4.2/src/proto/livekit_rtc_pb.d.ts +1373 -0
  89. package/dist/ts4.2/src/room/PCTransport.d.ts +2 -1
  90. package/dist/ts4.2/src/room/RTCEngine.d.ts +9 -5
  91. package/dist/ts4.2/src/room/RegionUrlProvider.d.ts +4 -1
  92. package/dist/ts4.2/src/room/Room.d.ts +15 -11
  93. package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +2 -2
  94. package/dist/ts4.2/src/room/participant/Participant.d.ts +5 -3
  95. package/dist/ts4.2/src/room/participant/ParticipantTrackPermission.d.ts +1 -1
  96. package/dist/ts4.2/src/room/participant/RemoteParticipant.d.ts +8 -7
  97. package/dist/ts4.2/src/room/timers.d.ts +5 -4
  98. package/dist/ts4.2/src/room/track/LocalTrack.d.ts +3 -0
  99. package/dist/ts4.2/src/room/track/LocalTrackPublication.d.ts +1 -1
  100. package/dist/ts4.2/src/room/track/LocalVideoTrack.d.ts +2 -2
  101. package/dist/ts4.2/src/room/track/RemoteTrackPublication.d.ts +1 -1
  102. package/dist/ts4.2/src/room/track/Track.d.ts +6 -4
  103. package/dist/ts4.2/src/room/track/TrackPublication.d.ts +7 -5
  104. package/dist/ts4.2/src/room/track/options.d.ts +7 -0
  105. package/dist/ts4.2/src/room/track/utils.d.ts +5 -1
  106. package/dist/ts4.2/src/room/utils.d.ts +3 -1
  107. package/dist/ts4.2/src/test/mocks.d.ts +4 -3
  108. package/dist/ts4.2/src/utils/browserParser.d.ts +2 -0
  109. package/package.json +10 -10
  110. package/src/api/SignalClient.ts +104 -101
  111. package/src/connectionHelper/ConnectionCheck.ts +3 -2
  112. package/src/connectionHelper/checks/Checker.ts +3 -3
  113. package/src/connectionHelper/checks/webrtc.ts +66 -2
  114. package/src/connectionHelper/checks/websocket.ts +4 -0
  115. package/src/e2ee/E2eeManager.ts +4 -3
  116. package/src/e2ee/KeyProvider.ts +3 -2
  117. package/src/e2ee/constants.ts +4 -0
  118. package/src/e2ee/types.ts +1 -0
  119. package/src/e2ee/worker/FrameCryptor.test.ts +1 -3
  120. package/src/e2ee/worker/FrameCryptor.ts +14 -16
  121. package/src/e2ee/worker/ParticipantKeyHandler.ts +48 -2
  122. package/src/e2ee/worker/e2ee.worker.ts +12 -6
  123. package/src/index.ts +1 -1
  124. package/src/proto/livekit_models_pb.ts +2096 -0
  125. package/src/proto/livekit_rtc_pb.ts +2332 -0
  126. package/src/room/PCTransport.ts +1 -1
  127. package/src/room/RTCEngine.ts +24 -18
  128. package/src/room/RegionUrlProvider.ts +11 -2
  129. package/src/room/Room.test.ts +1 -0
  130. package/src/room/Room.ts +175 -86
  131. package/src/room/participant/LocalParticipant.ts +43 -59
  132. package/src/room/participant/Participant.ts +6 -4
  133. package/src/room/participant/ParticipantTrackPermission.ts +3 -3
  134. package/src/room/participant/RemoteParticipant.ts +24 -21
  135. package/src/room/participant/publishUtils.test.ts +1 -0
  136. package/src/room/track/LocalTrack.ts +24 -9
  137. package/src/room/track/LocalTrackPublication.ts +1 -1
  138. package/src/room/track/LocalVideoTrack.test.ts +2 -1
  139. package/src/room/track/LocalVideoTrack.ts +22 -22
  140. package/src/room/track/RemoteTrackPublication.ts +12 -7
  141. package/src/room/track/RemoteVideoTrack.test.ts +5 -4
  142. package/src/room/track/Track.ts +9 -6
  143. package/src/room/track/TrackPublication.ts +7 -5
  144. package/src/room/track/create.ts +18 -17
  145. package/src/room/track/facingMode.test.ts +1 -0
  146. package/src/room/track/options.ts +6 -0
  147. package/src/room/track/utils.test.ts +1 -0
  148. package/src/room/track/utils.ts +44 -2
  149. package/src/room/utils.test.ts +16 -0
  150. package/src/room/utils.ts +20 -4
  151. package/src/test/mocks.ts +7 -5
  152. package/src/utils/AsyncQueue.test.ts +1 -0
  153. package/src/utils/browserParser.test.ts +33 -3
  154. package/src/utils/browserParser.ts +5 -0
  155. package/dist/src/proto/google/protobuf/timestamp.d.ts +0 -146
  156. package/dist/src/proto/google/protobuf/timestamp.d.ts.map +0 -1
  157. package/dist/src/proto/livekit_models.d.ts +0 -2399
  158. package/dist/src/proto/livekit_models.d.ts.map +0 -1
  159. package/dist/src/proto/livekit_rtc.d.ts +0 -14352
  160. package/dist/src/proto/livekit_rtc.d.ts.map +0 -1
  161. package/dist/ts4.2/src/proto/google/protobuf/timestamp.d.ts +0 -150
  162. package/dist/ts4.2/src/proto/livekit_models.d.ts +0 -2659
  163. package/dist/ts4.2/src/proto/livekit_rtc.d.ts +0 -15764
  164. package/src/proto/google/protobuf/timestamp.ts +0 -230
  165. package/src/proto/livekit_models.ts +0 -4006
  166. package/src/proto/livekit_rtc.ts +0 -4672
package/src/room/Room.ts CHANGED
@@ -1,4 +1,6 @@
1
- import EventEmitter from 'eventemitter3';
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/livekit_models';
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
- } from '../proto/livekit_rtc';
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
- * prepares the connection to the livekit server by sending a HEAD request in order to
341
- * 1. speed up DNS resolution
342
- * 2. speed up TLS setup
343
- * on the actual connection request
344
- * throws an error if server is not reachable after the request timeout
345
- * @experimental
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
- await fetch(`http${url.substring(2)}`, { method: 'HEAD' });
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
- const urlProvider = new RegionUrlProvider(url, token);
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
- isCloud(new URL(url)) &&
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 urlProvider.getNextBestRegionUrl(this.abortController?.signal);
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.debug('initial connection failed, retrying with another region');
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
- this.connectFuture = new Future(connectFn, () => {
418
- this.clearConnectionFutures();
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.fromPartial({
704
+ req = new SimulateScenario({
641
705
  scenario: {
642
- $case: 'speakerUpdate',
643
- speakerUpdate: 3,
706
+ case: 'speakerUpdate',
707
+ value: 3,
644
708
  },
645
709
  });
646
710
  break;
647
711
  case 'node-failure':
648
- req = SimulateScenario.fromPartial({
712
+ req = new SimulateScenario({
649
713
  scenario: {
650
- $case: 'nodeFailure',
651
- nodeFailure: true,
714
+ case: 'nodeFailure',
715
+ value: true,
652
716
  },
653
717
  });
654
718
  break;
655
719
  case 'server-leave':
656
- req = SimulateScenario.fromPartial({
720
+ req = new SimulateScenario({
657
721
  scenario: {
658
- $case: 'serverLeave',
659
- serverLeave: true,
722
+ case: 'serverLeave',
723
+ value: true,
660
724
  },
661
725
  });
662
726
  break;
663
727
  case 'migration':
664
- req = SimulateScenario.fromPartial({
728
+ req = new SimulateScenario({
665
729
  scenario: {
666
- $case: 'migration',
667
- migration: true,
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.fromPartial({
747
+ req = new SimulateScenario({
684
748
  scenario: {
685
- $case: 'switchCandidateProtocol',
686
- switchCandidateProtocol: scenario === 'force-tls' ? 2 : 1,
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
- reason: DisconnectReason.CLIENT_INITIATED,
694
- canReconnect: true,
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 (isSafari()) {
787
+ const browser = getBrowser();
788
+ if (browser && browser.os === 'iOS') {
723
789
  /**
724
- * iOS Safari blocks audio element playback if
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
- dummyAudioEl.srcObject = new MediaStream([track]);
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
- await this.audioContext.resume();
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
- answer: toProtoSessionDescription({
1456
- sdp: previousAnswer.sdp,
1457
- type: previousAnswer.type,
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
- offer: previousOffer
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<T extends EventEmitter.EventNames<RoomEventCallbacks>>(
1533
- event: T,
1534
- ...args: EventEmitter.EventArgs<RoomEventCallbacks, T>
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.fromPartial({
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.fromPartial({
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.fromPartial({
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.fromPartial({
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.fromPartial({
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.fromPartial({
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<T extends EventEmitter.EventNames<RoomEventCallbacks>>(
1724
- event: T,
1725
- ...args: EventEmitter.EventArgs<RoomEventCallbacks, T>
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 });