livekit-client 2.13.0 → 2.13.2

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.
Files changed (66) hide show
  1. package/dist/livekit-client.e2ee.worker.js +1 -1
  2. package/dist/livekit-client.e2ee.worker.js.map +1 -1
  3. package/dist/livekit-client.e2ee.worker.mjs +1 -0
  4. package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
  5. package/dist/livekit-client.esm.mjs +383 -117
  6. package/dist/livekit-client.esm.mjs.map +1 -1
  7. package/dist/livekit-client.umd.js +1 -1
  8. package/dist/livekit-client.umd.js.map +1 -1
  9. package/dist/src/index.d.ts +1 -0
  10. package/dist/src/index.d.ts.map +1 -1
  11. package/dist/src/room/RTCEngine.d.ts +1 -1
  12. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  13. package/dist/src/room/Room.d.ts +6 -2
  14. package/dist/src/room/Room.d.ts.map +1 -1
  15. package/dist/src/room/defaults.d.ts.map +1 -1
  16. package/dist/src/room/errors.d.ts +2 -1
  17. package/dist/src/room/errors.d.ts.map +1 -1
  18. package/dist/src/room/events.d.ts +5 -1
  19. package/dist/src/room/events.d.ts.map +1 -1
  20. package/dist/src/room/participant/LocalParticipant.d.ts +9 -0
  21. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  22. package/dist/src/room/participant/Participant.d.ts +1 -1
  23. package/dist/src/room/participant/Participant.d.ts.map +1 -1
  24. package/dist/src/room/participant/publishUtils.d.ts.map +1 -1
  25. package/dist/src/room/track/LocalTrack.d.ts +10 -0
  26. package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
  27. package/dist/src/room/track/LocalVideoTrack.d.ts +1 -1
  28. package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
  29. package/dist/src/room/track/Track.d.ts +1 -0
  30. package/dist/src/room/track/Track.d.ts.map +1 -1
  31. package/dist/src/room/track/options.d.ts +8 -0
  32. package/dist/src/room/track/options.d.ts.map +1 -1
  33. package/dist/src/room/track/record.d.ts +6 -0
  34. package/dist/src/room/track/record.d.ts.map +1 -0
  35. package/dist/src/room/utils.d.ts +3 -0
  36. package/dist/src/room/utils.d.ts.map +1 -1
  37. package/dist/ts4.2/src/index.d.ts +1 -0
  38. package/dist/ts4.2/src/room/RTCEngine.d.ts +1 -1
  39. package/dist/ts4.2/src/room/Room.d.ts +5 -1
  40. package/dist/ts4.2/src/room/errors.d.ts +2 -1
  41. package/dist/ts4.2/src/room/events.d.ts +5 -1
  42. package/dist/ts4.2/src/room/participant/LocalParticipant.d.ts +9 -0
  43. package/dist/ts4.2/src/room/participant/Participant.d.ts +1 -1
  44. package/dist/ts4.2/src/room/track/LocalTrack.d.ts +10 -0
  45. package/dist/ts4.2/src/room/track/LocalVideoTrack.d.ts +1 -1
  46. package/dist/ts4.2/src/room/track/Track.d.ts +1 -0
  47. package/dist/ts4.2/src/room/track/options.d.ts +8 -0
  48. package/dist/ts4.2/src/room/track/record.d.ts +6 -0
  49. package/dist/ts4.2/src/room/utils.d.ts +3 -0
  50. package/package.json +13 -12
  51. package/src/e2ee/worker/tsconfig.json +9 -1
  52. package/src/index.ts +2 -0
  53. package/src/room/RTCEngine.ts +7 -7
  54. package/src/room/Room.ts +23 -8
  55. package/src/room/defaults.ts +1 -0
  56. package/src/room/errors.ts +1 -0
  57. package/src/room/events.ts +5 -0
  58. package/src/room/participant/LocalParticipant.ts +215 -34
  59. package/src/room/participant/Participant.ts +1 -1
  60. package/src/room/participant/publishUtils.ts +4 -0
  61. package/src/room/track/LocalTrack.ts +47 -2
  62. package/src/room/track/LocalVideoTrack.ts +14 -5
  63. package/src/room/track/Track.ts +1 -0
  64. package/src/room/track/options.ts +9 -0
  65. package/src/room/track/record.ts +51 -0
  66. package/src/room/utils.ts +14 -1
@@ -7182,7 +7182,7 @@ let deprecationWarnings_ = true;
7182
7182
  */
7183
7183
  function extractVersion(uastring, expr, pos) {
7184
7184
  const match = uastring.match(expr);
7185
- return match && match.length >= pos && parseInt(match[pos], 10);
7185
+ return match && match.length >= pos && parseFloat(match[pos], 10);
7186
7186
  }
7187
7187
 
7188
7188
  // Wraps the peerconnection event eventNameToWrap in a function
@@ -7327,19 +7327,21 @@ function detectBrowser(window) {
7327
7327
  if (navigator.mozGetUserMedia) {
7328
7328
  // Firefox.
7329
7329
  result.browser = 'firefox';
7330
- result.version = extractVersion(navigator.userAgent, /Firefox\/(\d+)\./, 1);
7330
+ result.version = parseInt(extractVersion(navigator.userAgent, /Firefox\/(\d+)\./, 1));
7331
7331
  } else if (navigator.webkitGetUserMedia || window.isSecureContext === false && window.webkitRTCPeerConnection) {
7332
7332
  // Chrome, Chromium, Webview, Opera.
7333
7333
  // Version matches Chrome/WebRTC version.
7334
7334
  // Chrome 74 removed webkitGetUserMedia on http as well so we need the
7335
7335
  // more complicated fallback to webkitRTCPeerConnection.
7336
7336
  result.browser = 'chrome';
7337
- result.version = extractVersion(navigator.userAgent, /Chrom(e|ium)\/(\d+)\./, 2);
7337
+ result.version = parseInt(extractVersion(navigator.userAgent, /Chrom(e|ium)\/(\d+)\./, 2));
7338
7338
  } else if (window.RTCPeerConnection && navigator.userAgent.match(/AppleWebKit\/(\d+)\./)) {
7339
7339
  // Safari.
7340
7340
  result.browser = 'safari';
7341
- result.version = extractVersion(navigator.userAgent, /AppleWebKit\/(\d+)\./, 1);
7341
+ result.version = parseInt(extractVersion(navigator.userAgent, /AppleWebKit\/(\d+)\./, 1));
7342
7342
  result.supportsUnifiedPlan = window.RTCRtpTransceiver && 'currentDirection' in window.RTCRtpTransceiver.prototype;
7343
+ // Only for internal usage.
7344
+ result._safariVersion = extractVersion(navigator.userAgent, /Version\/(\d+(\.?\d+))/, 1);
7343
7345
  } else {
7344
7346
  // Default fallthrough: not supported.
7345
7347
  result.browser = 'Not a supported browser.';
@@ -9940,7 +9942,7 @@ function removeExtmapAllowMixed(window, browserDetails) {
9940
9942
  if (browserDetails.browser === 'chrome' && browserDetails.version >= 71) {
9941
9943
  return;
9942
9944
  }
9943
- if (browserDetails.browser === 'safari' && browserDetails.version >= 605) {
9945
+ if (browserDetails.browser === 'safari' && browserDetails._safariVersion >= 13.1) {
9944
9946
  return;
9945
9947
  }
9946
9948
  const nativeSRD = window.RTCPeerConnection.prototype.setRemoteDescription;
@@ -10477,6 +10479,7 @@ var ConnectionErrorReason;
10477
10479
  ConnectionErrorReason[ConnectionErrorReason["InternalError"] = 2] = "InternalError";
10478
10480
  ConnectionErrorReason[ConnectionErrorReason["Cancelled"] = 3] = "Cancelled";
10479
10481
  ConnectionErrorReason[ConnectionErrorReason["LeaveRequest"] = 4] = "LeaveRequest";
10482
+ ConnectionErrorReason[ConnectionErrorReason["Timeout"] = 5] = "Timeout";
10480
10483
  })(ConnectionErrorReason || (ConnectionErrorReason = {}));
10481
10484
  class ConnectionError extends LivekitError {
10482
10485
  constructor(message, reason, status, context) {
@@ -11178,6 +11181,10 @@ var TrackEvent;
11178
11181
  * @experimental
11179
11182
  */
11180
11183
  TrackEvent["TimeSyncUpdate"] = "timeSyncUpdate";
11184
+ /**
11185
+ * @internal
11186
+ */
11187
+ TrackEvent["PreConnectBufferFlushed"] = "preConnectBufferFlushed";
11181
11188
  })(TrackEvent || (TrackEvent = {}));
11182
11189
 
11183
11190
  function cloneDeep(value) {
@@ -11259,7 +11266,7 @@ function getOSVersion(ua) {
11259
11266
  return ua.includes('mac os') ? getMatch(/\(.+?(\d+_\d+(:?_\d+)?)/, ua, 1).replace(/_/g, '.') : undefined;
11260
11267
  }
11261
11268
 
11262
- var version$1 = "2.13.0";
11269
+ var version$1 = "2.13.2";
11263
11270
 
11264
11271
  const version = version$1;
11265
11272
  const protocolVersion = 16;
@@ -11869,10 +11876,21 @@ function isSafari() {
11869
11876
  var _a;
11870
11877
  return ((_a = getBrowser()) === null || _a === void 0 ? void 0 : _a.name) === 'Safari';
11871
11878
  }
11879
+ function isSafariBased() {
11880
+ const b = getBrowser();
11881
+ return (b === null || b === void 0 ? void 0 : b.name) === 'Safari' || (b === null || b === void 0 ? void 0 : b.os) === 'iOS';
11882
+ }
11872
11883
  function isSafari17() {
11873
11884
  const b = getBrowser();
11874
11885
  return (b === null || b === void 0 ? void 0 : b.name) === 'Safari' && b.version.startsWith('17.');
11875
11886
  }
11887
+ function isSafariSvcApi(browser) {
11888
+ if (!browser) {
11889
+ browser = getBrowser();
11890
+ }
11891
+ // Safari 18.4 requires legacy svc api and scaleResolutionDown to be set
11892
+ return (browser === null || browser === void 0 ? void 0 : browser.name) === 'Safari' && compareVersions(browser.version, '18.3') > 0;
11893
+ }
11876
11894
  function isMobile() {
11877
11895
  var _a, _b;
11878
11896
  if (!isWeb()) return false;
@@ -15066,7 +15084,8 @@ const publishDefaults = {
15066
15084
  screenShareEncoding: ScreenSharePresets.h1080fps15.encoding,
15067
15085
  stopMicTrackOnMute: false,
15068
15086
  videoCodec: defaultVideoCodec,
15069
- backupCodec: true
15087
+ backupCodec: true,
15088
+ preConnectBuffer: false
15070
15089
  };
15071
15090
  const audioDefaults = {
15072
15091
  deviceId: {
@@ -15514,7 +15533,49 @@ function computeBitrate(currentStats, prevStats) {
15514
15533
  return (bytesNow - bytesPrev) * 8 * 1000 / (currentStats.timestamp - prevStats.timestamp);
15515
15534
  }
15516
15535
 
15517
- const defaultDimensionsTimeout = 1000;
15536
+ class LocalTrackRecorder extends MediaRecorder {
15537
+ constructor(track, options) {
15538
+ super(new MediaStream([track.mediaStreamTrack]), options);
15539
+ let dataListener;
15540
+ let streamController;
15541
+ const isClosed = () => streamController === undefined;
15542
+ const onStop = () => {
15543
+ this.removeEventListener('dataavailable', dataListener);
15544
+ this.removeEventListener('stop', onStop);
15545
+ this.removeEventListener('error', onError);
15546
+ streamController === null || streamController === void 0 ? void 0 : streamController.close();
15547
+ streamController = undefined;
15548
+ };
15549
+ const onError = event => {
15550
+ streamController === null || streamController === void 0 ? void 0 : streamController.error(event);
15551
+ this.removeEventListener('dataavailable', dataListener);
15552
+ this.removeEventListener('stop', onStop);
15553
+ this.removeEventListener('error', onError);
15554
+ streamController = undefined;
15555
+ };
15556
+ this.byteStream = new ReadableStream({
15557
+ start: controller => {
15558
+ streamController = controller;
15559
+ dataListener = event => __awaiter(this, void 0, void 0, function* () {
15560
+ const arrayBuffer = yield event.data.arrayBuffer();
15561
+ if (isClosed()) {
15562
+ return;
15563
+ }
15564
+ controller.enqueue(new Uint8Array(arrayBuffer));
15565
+ });
15566
+ this.addEventListener('dataavailable', dataListener);
15567
+ },
15568
+ cancel: () => {
15569
+ onStop();
15570
+ }
15571
+ });
15572
+ this.addEventListener('stop', onStop);
15573
+ this.addEventListener('error', onError);
15574
+ }
15575
+ }
15576
+
15577
+ const DEFAULT_DIMENSIONS_TIMEOUT = 1000;
15578
+ const PRE_CONNECT_BUFFER_TIMEOUT = 10000;
15518
15579
  class LocalTrack extends Track {
15519
15580
  /** @internal */
15520
15581
  get sender() {
@@ -15527,6 +15588,9 @@ class LocalTrack extends Track {
15527
15588
  get constraints() {
15528
15589
  return this._constraints;
15529
15590
  }
15591
+ get hasPreConnectBuffer() {
15592
+ return !!this.localTrackRecorder;
15593
+ }
15530
15594
  /**
15531
15595
  *
15532
15596
  * @param mediaTrack
@@ -15682,7 +15746,7 @@ class LocalTrack extends Track {
15682
15746
  waitForDimensions() {
15683
15747
  return __awaiter(this, arguments, void 0, function () {
15684
15748
  var _this = this;
15685
- let timeout = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : defaultDimensionsTimeout;
15749
+ let timeout = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : DEFAULT_DIMENSIONS_TIMEOUT;
15686
15750
  return function* () {
15687
15751
  var _a;
15688
15752
  if (_this.kind === Track.Kind.Audio) {
@@ -16037,6 +16101,36 @@ class LocalTrack extends Track {
16037
16101
  }();
16038
16102
  });
16039
16103
  }
16104
+ /** @internal */
16105
+ startPreConnectBuffer() {
16106
+ let timeslice = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 100;
16107
+ if (!this.localTrackRecorder) {
16108
+ this.localTrackRecorder = new LocalTrackRecorder(this, {
16109
+ mimeType: 'audio/webm;codecs=opus'
16110
+ });
16111
+ } else {
16112
+ this.log.warn('preconnect buffer already started');
16113
+ return;
16114
+ }
16115
+ this.localTrackRecorder.start(timeslice);
16116
+ this.autoStopPreConnectBuffer = setTimeout(() => {
16117
+ this.log.warn('preconnect buffer timed out, stopping recording automatically', this.logContext);
16118
+ this.stopPreConnectBuffer();
16119
+ }, PRE_CONNECT_BUFFER_TIMEOUT);
16120
+ }
16121
+ /** @internal */
16122
+ stopPreConnectBuffer() {
16123
+ clearTimeout(this.autoStopPreConnectBuffer);
16124
+ if (this.localTrackRecorder) {
16125
+ this.localTrackRecorder.stop();
16126
+ this.localTrackRecorder = undefined;
16127
+ }
16128
+ }
16129
+ /** @internal */
16130
+ getPreConnectBuffer() {
16131
+ var _a;
16132
+ return (_a = this.localTrackRecorder) === null || _a === void 0 ? void 0 : _a.byteStream;
16133
+ }
16040
16134
  }
16041
16135
 
16042
16136
  class LocalAudioTrack extends LocalTrack {
@@ -16349,12 +16443,15 @@ function computeVideoEncodings(isScreenShare, width, height, options) {
16349
16443
  // TODO: when we upstream libwebrtc, this will need additional verification
16350
16444
  isReactNative() || (browser === null || browser === void 0 ? void 0 : browser.name) === 'Chrome' && compareVersions(browser === null || browser === void 0 ? void 0 : browser.version, '113') < 0) {
16351
16445
  const bitratesRatio = sm.suffix == 'h' ? 2 : 3;
16446
+ // safari 18.4 uses a different svc API that requires scaleResolutionDownBy to be set.
16447
+ const requireScale = isSafariSvcApi(browser);
16352
16448
  for (let i = 0; i < sm.spatial; i += 1) {
16353
16449
  // in legacy SVC, scaleResolutionDownBy cannot be set
16354
16450
  encodings.push({
16355
16451
  rid: videoRids[2 - i],
16356
16452
  maxBitrate: videoEncoding.maxBitrate / Math.pow(bitratesRatio, i),
16357
- maxFramerate: original.encoding.maxFramerate
16453
+ maxFramerate: original.encoding.maxFramerate,
16454
+ scaleResolutionDownBy: requireScale ? Math.pow(2, i) : undefined
16358
16455
  });
16359
16456
  }
16360
16457
  // legacy SVC, scalabilityMode is set only on the first encoding
@@ -16861,7 +16958,7 @@ class LocalVideoTrack extends LocalTrack {
16861
16958
  }));
16862
16959
  }
16863
16960
  this.log.debug("setting publishing quality. max quality ".concat(maxQuality), this.logContext);
16864
- this.setPublishingLayers(qualities);
16961
+ this.setPublishingLayers(isSVCCodec(this.codec), qualities);
16865
16962
  }
16866
16963
  restartTrack(options) {
16867
16964
  return __awaiter(this, void 0, void 0, function* () {
@@ -16996,7 +17093,7 @@ class LocalVideoTrack extends LocalTrack {
16996
17093
  }));
16997
17094
  // only enable simulcast codec for preference codec setted
16998
17095
  if (!this.codec && codecs.length > 0) {
16999
- yield this.setPublishingLayers(codecs[0].qualities);
17096
+ yield this.setPublishingLayers(isSVCCodec(codecs[0].codec), codecs[0].qualities);
17000
17097
  return [];
17001
17098
  }
17002
17099
  this.subscribedCodecs = codecs;
@@ -17007,7 +17104,7 @@ class LocalVideoTrack extends LocalTrack {
17007
17104
  _a = false;
17008
17105
  const codec = _d;
17009
17106
  if (!this.codec || this.codec === codec.codec) {
17010
- yield this.setPublishingLayers(codec.qualities);
17107
+ yield this.setPublishingLayers(isSVCCodec(codec.codec), codec.qualities);
17011
17108
  } else {
17012
17109
  const simulcastCodecInfo = this.simulcastCodecs.get(codec.codec);
17013
17110
  this.log.debug("try setPublishingCodec for ".concat(codec.codec), Object.assign(Object.assign({}, this.logContext), {
@@ -17022,7 +17119,7 @@ class LocalVideoTrack extends LocalTrack {
17022
17119
  }
17023
17120
  } else if (simulcastCodecInfo.encodings) {
17024
17121
  this.log.debug("try setPublishingLayersForSender ".concat(codec.codec), this.logContext);
17025
- yield setPublishingLayersForSender(simulcastCodecInfo.sender, simulcastCodecInfo.encodings, codec.qualities, this.senderLock, this.log, this.logContext);
17122
+ yield setPublishingLayersForSender(simulcastCodecInfo.sender, simulcastCodecInfo.encodings, codec.qualities, this.senderLock, isSVCCodec(codec.codec), this.log, this.logContext);
17026
17123
  }
17027
17124
  }
17028
17125
  }
@@ -17044,7 +17141,7 @@ class LocalVideoTrack extends LocalTrack {
17044
17141
  * @internal
17045
17142
  * Sets layers that should be publishing
17046
17143
  */
17047
- setPublishingLayers(qualities) {
17144
+ setPublishingLayers(isSvc, qualities) {
17048
17145
  return __awaiter(this, void 0, void 0, function* () {
17049
17146
  this.log.debug('setting publishing layers', Object.assign(Object.assign({}, this.logContext), {
17050
17147
  qualities
@@ -17052,7 +17149,7 @@ class LocalVideoTrack extends LocalTrack {
17052
17149
  if (!this.sender || !this.encodings) {
17053
17150
  return;
17054
17151
  }
17055
- yield setPublishingLayersForSender(this.sender, this.encodings, qualities, this.senderLock, this.log, this.logContext);
17152
+ yield setPublishingLayersForSender(this.sender, this.encodings, qualities, this.senderLock, isSvc, this.log, this.logContext);
17056
17153
  });
17057
17154
  }
17058
17155
  handleAppVisibilityChanged() {
@@ -17070,7 +17167,7 @@ class LocalVideoTrack extends LocalTrack {
17070
17167
  });
17071
17168
  }
17072
17169
  }
17073
- function setPublishingLayersForSender(sender, senderEncodings, qualities, senderLock, log, logContext) {
17170
+ function setPublishingLayersForSender(sender, senderEncodings, qualities, senderLock, isSVC, log, logContext) {
17074
17171
  return __awaiter(this, void 0, void 0, function* () {
17075
17172
  const unlock = yield senderLock.lock();
17076
17173
  log.debug('setPublishingLayersForSender', Object.assign(Object.assign({}, logContext), {
@@ -17132,6 +17229,12 @@ function setPublishingLayersForSender(sender, senderEncodings, qualities, sender
17132
17229
  }
17133
17230
  }
17134
17231
  } else {
17232
+ if (isSVC) {
17233
+ const hasEnabledEncoding = qualities.some(q => q.enabled);
17234
+ if (hasEnabledEncoding) {
17235
+ qualities.forEach(q => q.enabled = true);
17236
+ }
17237
+ }
17135
17238
  // simulcast dynacast encodings
17136
17239
  encodings.forEach((encoding, idx) => {
17137
17240
  var _a;
@@ -17445,12 +17548,12 @@ class RTCEngine extends eventsExports.EventEmitter {
17445
17548
  }
17446
17549
  /** @internal */
17447
17550
  get logContext() {
17448
- var _a, _b, _c, _d, _e, _f, _g, _h;
17551
+ var _a, _b, _c, _d, _e, _f;
17449
17552
  return {
17450
17553
  room: (_b = (_a = this.latestJoinResponse) === null || _a === void 0 ? void 0 : _a.room) === null || _b === void 0 ? void 0 : _b.name,
17451
17554
  roomID: (_d = (_c = this.latestJoinResponse) === null || _c === void 0 ? void 0 : _c.room) === null || _d === void 0 ? void 0 : _d.sid,
17452
17555
  participant: (_f = (_e = this.latestJoinResponse) === null || _e === void 0 ? void 0 : _e.participant) === null || _f === void 0 ? void 0 : _f.identity,
17453
- pID: (_h = (_g = this.latestJoinResponse) === null || _g === void 0 ? void 0 : _g.participant) === null || _h === void 0 ? void 0 : _h.sid
17556
+ pID: this.participantSid
17454
17557
  };
17455
17558
  }
17456
17559
  join(url, token, opts, abortSignal) {
@@ -17474,10 +17577,7 @@ class RTCEngine extends eventsExports.EventEmitter {
17474
17577
  this.negotiate();
17475
17578
  }
17476
17579
  this.clientConfiguration = joinResponse.clientConfiguration;
17477
- // emit signal connected event after a short delay to allow for join response to be processed on room
17478
- setTimeout(() => {
17479
- this.emit(EngineEvent.SignalConnected);
17480
- }, 10);
17580
+ this.emit(EngineEvent.SignalConnected, joinResponse);
17481
17581
  return joinResponse;
17482
17582
  } catch (e) {
17483
17583
  if (e instanceof ConnectionError) {
@@ -17551,7 +17651,7 @@ class RTCEngine extends eventsExports.EventEmitter {
17551
17651
  return new Promise((resolve, reject) => {
17552
17652
  const publicationTimeout = setTimeout(() => {
17553
17653
  delete this.pendingTrackResolvers[req.cid];
17554
- reject(new ConnectionError('publication of local track timed out, no response from server', ConnectionErrorReason.InternalError));
17654
+ reject(new ConnectionError('publication of local track timed out, no response from server', ConnectionErrorReason.Timeout));
17555
17655
  }, 10000);
17556
17656
  this.pendingTrackResolvers[req.cid] = {
17557
17657
  resolve: info => {
@@ -17727,6 +17827,9 @@ class RTCEngine extends eventsExports.EventEmitter {
17727
17827
  this.client.onRoomMoved = res => {
17728
17828
  var _a;
17729
17829
  this.participantSid = (_a = res.participant) === null || _a === void 0 ? void 0 : _a.sid;
17830
+ if (this.latestJoinResponse) {
17831
+ this.latestJoinResponse.room = res.room;
17832
+ }
17730
17833
  this.emit(EngineEvent.RoomMoved, res);
17731
17834
  };
17732
17835
  this.client.onClose = () => {
@@ -20027,12 +20130,29 @@ class LocalParticipant extends Participant {
20027
20130
  this.updateTrackSubscriptionPermissions();
20028
20131
  };
20029
20132
  this.handleDisconnected = () => {
20030
- var _a, _b;
20133
+ var _a, _b, _c, _d, _e, _f;
20031
20134
  if (this.reconnectFuture) {
20032
20135
  this.reconnectFuture.promise.catch(e => this.log.warn(e.message, this.logContext));
20033
20136
  (_b = (_a = this.reconnectFuture) === null || _a === void 0 ? void 0 : _a.reject) === null || _b === void 0 ? void 0 : _b.call(_a, 'Got disconnected during reconnection attempt');
20034
20137
  this.reconnectFuture = undefined;
20035
20138
  }
20139
+ if (this.signalConnectedFuture) {
20140
+ (_d = (_c = this.signalConnectedFuture).reject) === null || _d === void 0 ? void 0 : _d.call(_c, 'Got disconnected without signal connected');
20141
+ this.signalConnectedFuture = undefined;
20142
+ }
20143
+ (_f = (_e = this.activeAgentFuture) === null || _e === void 0 ? void 0 : _e.reject) === null || _f === void 0 ? void 0 : _f.call(_e, 'Got disconnected without active agent present');
20144
+ this.activeAgentFuture = undefined;
20145
+ this.firstActiveAgent = undefined;
20146
+ };
20147
+ this.handleSignalConnected = joinResponse => {
20148
+ var _a, _b;
20149
+ if (joinResponse.participant) {
20150
+ this.updateInfo(joinResponse.participant);
20151
+ }
20152
+ if (!this.signalConnectedFuture) {
20153
+ this.signalConnectedFuture = new Future();
20154
+ }
20155
+ (_b = (_a = this.signalConnectedFuture).resolve) === null || _b === void 0 ? void 0 : _b.call(_a);
20036
20156
  };
20037
20157
  this.handleSignalRequestResponse = response => {
20038
20158
  const {
@@ -20108,7 +20228,7 @@ class LocalParticipant extends Participant {
20108
20228
  };
20109
20229
  this.handleSubscribedQualityUpdate = update => __awaiter(this, void 0, void 0, function* () {
20110
20230
  var _a, e_1, _b, _c;
20111
- var _d, _e;
20231
+ var _d;
20112
20232
  if (!((_d = this.roomOptions) === null || _d === void 0 ? void 0 : _d.dynacast)) {
20113
20233
  return;
20114
20234
  }
@@ -20119,34 +20239,30 @@ class LocalParticipant extends Participant {
20119
20239
  }));
20120
20240
  return;
20121
20241
  }
20122
- if (update.subscribedCodecs.length > 0) {
20123
- if (!pub.videoTrack) {
20124
- return;
20242
+ if (!pub.videoTrack) {
20243
+ return;
20244
+ }
20245
+ const newCodecs = yield pub.videoTrack.setPublishingCodecs(update.subscribedCodecs);
20246
+ try {
20247
+ for (var _e = true, newCodecs_1 = __asyncValues(newCodecs), newCodecs_1_1; newCodecs_1_1 = yield newCodecs_1.next(), _a = newCodecs_1_1.done, !_a; _e = true) {
20248
+ _c = newCodecs_1_1.value;
20249
+ _e = false;
20250
+ const codec = _c;
20251
+ if (isBackupCodec(codec)) {
20252
+ this.log.debug("publish ".concat(codec, " for ").concat(pub.videoTrack.sid), Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(pub)));
20253
+ yield this.publishAdditionalCodecForTrack(pub.videoTrack, codec, pub.options);
20254
+ }
20125
20255
  }
20126
- const newCodecs = yield pub.videoTrack.setPublishingCodecs(update.subscribedCodecs);
20256
+ } catch (e_1_1) {
20257
+ e_1 = {
20258
+ error: e_1_1
20259
+ };
20260
+ } finally {
20127
20261
  try {
20128
- for (var _f = true, newCodecs_1 = __asyncValues(newCodecs), newCodecs_1_1; newCodecs_1_1 = yield newCodecs_1.next(), _a = newCodecs_1_1.done, !_a; _f = true) {
20129
- _c = newCodecs_1_1.value;
20130
- _f = false;
20131
- const codec = _c;
20132
- if (isBackupCodec(codec)) {
20133
- this.log.debug("publish ".concat(codec, " for ").concat(pub.videoTrack.sid), Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(pub)));
20134
- yield this.publishAdditionalCodecForTrack(pub.videoTrack, codec, pub.options);
20135
- }
20136
- }
20137
- } catch (e_1_1) {
20138
- e_1 = {
20139
- error: e_1_1
20140
- };
20262
+ if (!_e && !_a && (_b = newCodecs_1.return)) yield _b.call(newCodecs_1);
20141
20263
  } finally {
20142
- try {
20143
- if (!_f && !_a && (_b = newCodecs_1.return)) yield _b.call(newCodecs_1);
20144
- } finally {
20145
- if (e_1) throw e_1.error;
20146
- }
20264
+ if (e_1) throw e_1.error;
20147
20265
  }
20148
- } else if (update.subscribedQualities.length > 0) {
20149
- yield (_e = pub.videoTrack) === null || _e === void 0 ? void 0 : _e.setPublishingLayers(update.subscribedQualities);
20150
20266
  }
20151
20267
  });
20152
20268
  this.handleLocalTrackUnpublished = unpublished => {
@@ -20255,7 +20371,7 @@ class LocalParticipant extends Participant {
20255
20371
  pub.unmute();
20256
20372
  }
20257
20373
  });
20258
- this.engine.on(EngineEvent.Connected, this.handleReconnected).on(EngineEvent.SignalRestarted, this.handleReconnected).on(EngineEvent.SignalResumed, this.handleReconnected).on(EngineEvent.Restarting, this.handleReconnecting).on(EngineEvent.Resuming, this.handleReconnecting).on(EngineEvent.LocalTrackUnpublished, this.handleLocalTrackUnpublished).on(EngineEvent.SubscribedQualityUpdate, this.handleSubscribedQualityUpdate).on(EngineEvent.Disconnected, this.handleDisconnected).on(EngineEvent.SignalRequestResponse, this.handleSignalRequestResponse).on(EngineEvent.DataPacketReceived, this.handleDataPacket);
20374
+ this.engine.on(EngineEvent.Connected, this.handleReconnected).on(EngineEvent.SignalConnected, this.handleSignalConnected).on(EngineEvent.SignalRestarted, this.handleReconnected).on(EngineEvent.SignalResumed, this.handleReconnected).on(EngineEvent.Restarting, this.handleReconnecting).on(EngineEvent.Resuming, this.handleReconnecting).on(EngineEvent.LocalTrackUnpublished, this.handleLocalTrackUnpublished).on(EngineEvent.SubscribedQualityUpdate, this.handleSubscribedQualityUpdate).on(EngineEvent.Disconnected, this.handleDisconnected).on(EngineEvent.SignalRequestResponse, this.handleSignalRequestResponse).on(EngineEvent.DataPacketReceived, this.handleDataPacket);
20259
20375
  }
20260
20376
  /**
20261
20377
  * Sets and updates the metadata of the local participant.
@@ -20433,11 +20549,17 @@ class LocalParticipant extends Participant {
20433
20549
  tr.stop();
20434
20550
  });
20435
20551
  if (e instanceof Error) {
20436
- this.emit(ParticipantEvent.MediaDevicesError, e);
20552
+ this.emit(ParticipantEvent.MediaDevicesError, e, sourceToKind(source));
20437
20553
  }
20438
20554
  this.pendingPublishing.delete(source);
20439
20555
  throw e;
20440
20556
  }
20557
+ for (const localTrack of localTracks) {
20558
+ if (source === Track.Source.Microphone && isAudioTrack(localTrack) && (publishOptions === null || publishOptions === void 0 ? void 0 : publishOptions.preConnectBuffer)) {
20559
+ this.log.info('starting preconnect buffer for microphone', Object.assign({}, this.logContext));
20560
+ localTrack.startPreConnectBuffer();
20561
+ }
20562
+ }
20441
20563
  try {
20442
20564
  const publishPromises = [];
20443
20565
  for (const localTrack of localTracks) {
@@ -20712,23 +20834,13 @@ class LocalParticipant extends Participant {
20712
20834
  this.log.debug('deferring track publication until signal is connected', Object.assign(Object.assign({}, this.logContext), {
20713
20835
  track: getLogContextFromTrack(track)
20714
20836
  }));
20715
- const onSignalConnected = () => __awaiter(this, void 0, void 0, function* () {
20716
- try {
20717
- const publication = yield this.publish(track, opts, isStereo);
20718
- resolve(publication);
20719
- } catch (e) {
20720
- reject(e);
20721
- }
20722
- });
20723
- setTimeout(() => {
20724
- this.engine.off(EngineEvent.SignalConnected, onSignalConnected);
20837
+ const timeout = setTimeout(() => {
20725
20838
  reject(new PublishTrackError('publishing rejected as engine not connected within timeout', 408));
20726
20839
  }, 15000);
20727
- this.engine.once(EngineEvent.SignalConnected, onSignalConnected);
20728
- this.engine.on(EngineEvent.Closing, () => {
20729
- this.engine.off(EngineEvent.SignalConnected, onSignalConnected);
20730
- reject(new PublishTrackError('publishing rejected as engine closed', 499));
20731
- });
20840
+ yield this.waitUntilEngineConnected();
20841
+ clearTimeout(timeout);
20842
+ const publication = yield this.publish(track, opts, isStereo);
20843
+ resolve(publication);
20732
20844
  } else {
20733
20845
  try {
20734
20846
  const publication = yield this.publish(track, opts, isStereo);
@@ -20753,6 +20865,12 @@ class LocalParticipant extends Participant {
20753
20865
  }();
20754
20866
  });
20755
20867
  }
20868
+ waitUntilEngineConnected() {
20869
+ if (!this.signalConnectedFuture) {
20870
+ this.signalConnectedFuture = new Future();
20871
+ }
20872
+ return this.signalConnectedFuture.promise;
20873
+ }
20756
20874
  hasPermissionsToPublish(track) {
20757
20875
  if (!this.permissions) {
20758
20876
  this.log.warn('no permissions present for publishing track', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
@@ -20810,6 +20928,27 @@ class LocalParticipant extends Participant {
20810
20928
  track.on(TrackEvent.UpstreamPaused, this.onTrackUpstreamPaused);
20811
20929
  track.on(TrackEvent.UpstreamResumed, this.onTrackUpstreamResumed);
20812
20930
  track.on(TrackEvent.AudioTrackFeatureUpdate, this.onTrackFeatureUpdate);
20931
+ const audioFeatures = [];
20932
+ const disableDtx = !((_a = opts.dtx) !== null && _a !== void 0 ? _a : true);
20933
+ const settings = track.getSourceTrackSettings();
20934
+ if (settings.autoGainControl) {
20935
+ audioFeatures.push(AudioTrackFeature.TF_AUTO_GAIN_CONTROL);
20936
+ }
20937
+ if (settings.echoCancellation) {
20938
+ audioFeatures.push(AudioTrackFeature.TF_ECHO_CANCELLATION);
20939
+ }
20940
+ if (settings.noiseSuppression) {
20941
+ audioFeatures.push(AudioTrackFeature.TF_NOISE_SUPPRESSION);
20942
+ }
20943
+ if (settings.channelCount && settings.channelCount > 1) {
20944
+ audioFeatures.push(AudioTrackFeature.TF_STEREO);
20945
+ }
20946
+ if (disableDtx) {
20947
+ audioFeatures.push(AudioTrackFeature.TF_NO_DTX);
20948
+ }
20949
+ if (isLocalAudioTrack(track) && track.hasPreConnectBuffer) {
20950
+ audioFeatures.push(AudioTrackFeature.TF_PRECONNECT_BUFFER);
20951
+ }
20813
20952
  // create track publication from track
20814
20953
  const req = new AddTrackRequest({
20815
20954
  // get local track id for use during publishing
@@ -20818,12 +20957,13 @@ class LocalParticipant extends Participant {
20818
20957
  type: Track.kindToProto(track.kind),
20819
20958
  muted: track.isMuted,
20820
20959
  source: Track.sourceToProto(track.source),
20821
- disableDtx: !((_a = opts.dtx) !== null && _a !== void 0 ? _a : true),
20960
+ disableDtx,
20822
20961
  encryption: this.encryptionType,
20823
20962
  stereo: isStereo,
20824
20963
  disableRed: this.isE2EEEnabled || !((_b = opts.red) !== null && _b !== void 0 ? _b : true),
20825
20964
  stream: opts === null || opts === void 0 ? void 0 : opts.stream,
20826
- backupCodecPolicy: opts === null || opts === void 0 ? void 0 : opts.backupCodecPolicy
20965
+ backupCodecPolicy: opts === null || opts === void 0 ? void 0 : opts.backupCodecPolicy,
20966
+ audioFeatures
20827
20967
  });
20828
20968
  // compute encodings and layers for video
20829
20969
  let encodings;
@@ -20949,11 +21089,28 @@ class LocalParticipant extends Participant {
20949
21089
  yield this.engine.negotiate();
20950
21090
  });
20951
21091
  let ti;
21092
+ const addTrackPromise = new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
21093
+ var _a;
21094
+ try {
21095
+ ti = yield this.engine.addTrack(req);
21096
+ resolve(ti);
21097
+ } catch (err) {
21098
+ if (track.sender && ((_a = this.engine.pcManager) === null || _a === void 0 ? void 0 : _a.publisher)) {
21099
+ this.engine.pcManager.publisher.removeTrack(track.sender);
21100
+ yield this.engine.negotiate().catch(negotiateErr => {
21101
+ this.log.error('failed to negotiate after removing track due to failed add track request', Object.assign(Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)), {
21102
+ error: negotiateErr
21103
+ }));
21104
+ });
21105
+ }
21106
+ reject(err);
21107
+ }
21108
+ }));
20952
21109
  if (this.enabledPublishVideoCodecs.length > 0) {
20953
- const rets = yield Promise.all([this.engine.addTrack(req), negotiate()]);
21110
+ const rets = yield Promise.all([addTrackPromise, negotiate()]);
20954
21111
  ti = rets[0];
20955
21112
  } else {
20956
- ti = yield this.engine.addTrack(req);
21113
+ ti = yield addTrackPromise;
20957
21114
  // server might not support the codec the client has requested, in that case, fallback
20958
21115
  // to a supported codec
20959
21116
  let primaryCodecMime;
@@ -20994,6 +21151,75 @@ class LocalParticipant extends Participant {
20994
21151
  this.addTrackPublication(publication);
20995
21152
  // send event for publication
20996
21153
  this.emit(ParticipantEvent.LocalTrackPublished, publication);
21154
+ if (isLocalAudioTrack(track) && ti.audioFeatures.includes(AudioTrackFeature.TF_PRECONNECT_BUFFER)) {
21155
+ const stream = track.getPreConnectBuffer();
21156
+ // TODO: we're registering the listener after negotiation, so there might be a race
21157
+ this.on(ParticipantEvent.LocalTrackSubscribed, pub => {
21158
+ if (pub.trackSid === ti.sid) {
21159
+ if (!track.hasPreConnectBuffer) {
21160
+ this.log.warn('subscribe event came to late, buffer already closed', this.logContext);
21161
+ return;
21162
+ }
21163
+ this.log.debug('finished recording preconnect buffer', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
21164
+ track.stopPreConnectBuffer();
21165
+ }
21166
+ });
21167
+ if (stream) {
21168
+ const bufferStreamPromise = new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
21169
+ var _a, e_2, _b, _c;
21170
+ var _d, _e;
21171
+ try {
21172
+ this.log.debug('waiting for agent', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
21173
+ const agentActiveTimeout = setTimeout(() => {
21174
+ reject(new Error('agent not active within 10 seconds'));
21175
+ }, 10000);
21176
+ const agent = yield this.waitUntilActiveAgentPresent();
21177
+ clearTimeout(agentActiveTimeout);
21178
+ this.log.debug('sending preconnect buffer', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
21179
+ const writer = yield this.streamBytes({
21180
+ name: 'preconnect-buffer',
21181
+ mimeType: 'audio/opus',
21182
+ topic: 'lk.agent.pre-connect-audio-buffer',
21183
+ destinationIdentities: [agent.identity],
21184
+ attributes: {
21185
+ trackId: publication.trackSid,
21186
+ sampleRate: String((_d = settings.sampleRate) !== null && _d !== void 0 ? _d : '48000'),
21187
+ channels: String((_e = settings.channelCount) !== null && _e !== void 0 ? _e : '1')
21188
+ }
21189
+ });
21190
+ try {
21191
+ for (var _f = true, stream_1 = __asyncValues(stream), stream_1_1; stream_1_1 = yield stream_1.next(), _a = stream_1_1.done, !_a; _f = true) {
21192
+ _c = stream_1_1.value;
21193
+ _f = false;
21194
+ const chunk = _c;
21195
+ yield writer.write(chunk);
21196
+ }
21197
+ } catch (e_2_1) {
21198
+ e_2 = {
21199
+ error: e_2_1
21200
+ };
21201
+ } finally {
21202
+ try {
21203
+ if (!_f && !_a && (_b = stream_1.return)) yield _b.call(stream_1);
21204
+ } finally {
21205
+ if (e_2) throw e_2.error;
21206
+ }
21207
+ }
21208
+ yield writer.close();
21209
+ resolve();
21210
+ } catch (e) {
21211
+ reject(e);
21212
+ }
21213
+ }));
21214
+ bufferStreamPromise.then(() => {
21215
+ this.log.debug('preconnect buffer sent successfully', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)));
21216
+ }).catch(e => {
21217
+ this.log.error('error sending preconnect buffer', Object.assign(Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(track)), {
21218
+ error: e
21219
+ }));
21220
+ });
21221
+ }
21222
+ }
20997
21223
  return publication;
20998
21224
  });
20999
21225
  }
@@ -21742,6 +21968,29 @@ class LocalParticipant extends Participant {
21742
21968
  });
21743
21969
  return true;
21744
21970
  }
21971
+ /** @internal */
21972
+ setActiveAgent(agent) {
21973
+ var _a, _b, _c, _d;
21974
+ this.firstActiveAgent = agent;
21975
+ if (agent && !this.firstActiveAgent) {
21976
+ this.firstActiveAgent = agent;
21977
+ }
21978
+ if (agent) {
21979
+ (_b = (_a = this.activeAgentFuture) === null || _a === void 0 ? void 0 : _a.resolve) === null || _b === void 0 ? void 0 : _b.call(_a, agent);
21980
+ } else {
21981
+ (_d = (_c = this.activeAgentFuture) === null || _c === void 0 ? void 0 : _c.reject) === null || _d === void 0 ? void 0 : _d.call(_c, 'Agent disconnected');
21982
+ }
21983
+ this.activeAgentFuture = undefined;
21984
+ }
21985
+ waitUntilActiveAgentPresent() {
21986
+ if (this.firstActiveAgent) {
21987
+ return Promise.resolve(this.firstActiveAgent);
21988
+ }
21989
+ if (!this.activeAgentFuture) {
21990
+ this.activeAgentFuture = new Future();
21991
+ }
21992
+ return this.activeAgentFuture.promise;
21993
+ }
21745
21994
  getPublicationForTrack(track) {
21746
21995
  let publication;
21747
21996
  this.trackPublications.forEach(pub => {
@@ -22935,49 +23184,10 @@ class Room extends eventsExports.EventEmitter {
22935
23184
  }
22936
23185
  };
22937
23186
  this.handleDeviceChange = () => __awaiter(this, void 0, void 0, function* () {
22938
- var _a, _b, _c;
22939
- const previousDevices = DeviceManager.getInstance().previousDevices;
22940
- // check for available devices, but don't request permissions in order to avoid prompts for kinds that haven't been used before
22941
- const availableDevices = yield DeviceManager.getInstance().getDevices(undefined, false);
22942
- const browser = getBrowser();
22943
- if ((browser === null || browser === void 0 ? void 0 : browser.name) === 'Chrome' && browser.os !== 'iOS') {
22944
- for (let availableDevice of availableDevices) {
22945
- const previousDevice = previousDevices.find(info => info.deviceId === availableDevice.deviceId);
22946
- if (previousDevice && previousDevice.label !== '' && previousDevice.kind === availableDevice.kind && previousDevice.label !== availableDevice.label) {
22947
- // label has changed on device the same deviceId, indicating that the default device has changed on the OS level
22948
- if (this.getActiveDevice(availableDevice.kind) === 'default') {
22949
- // emit an active device change event only if the selected output device is actually on `default`
22950
- this.emit(RoomEvent.ActiveDeviceChanged, availableDevice.kind, availableDevice.deviceId);
22951
- }
22952
- }
22953
- }
22954
- }
22955
- const kinds = ['audiooutput', 'audioinput', 'videoinput'];
22956
- for (let kind of kinds) {
22957
- const targetSource = kindToSource(kind);
22958
- const targetPublication = this.localParticipant.getTrackPublication(targetSource);
22959
- if (targetPublication && ((_a = targetPublication.track) === null || _a === void 0 ? void 0 : _a.isUserProvided)) {
22960
- // if the track is user provided, we don't want to switch devices on behalf of the user
22961
- continue;
22962
- }
22963
- const devicesOfKind = availableDevices.filter(d => d.kind === kind);
22964
- const activeDevice = this.getActiveDevice(kind);
22965
- if (activeDevice === ((_b = previousDevices.filter(info => info.kind === kind)[0]) === null || _b === void 0 ? void 0 : _b.deviceId)) {
22966
- // in Safari the first device is always the default, so we assume a user on the default device would like to switch to the default once it changes
22967
- // FF doesn't emit an event when the default device changes, so we perform the same best effort and switch to the new device once connected and if it's the first in the array
22968
- if (devicesOfKind.length > 0 && ((_c = devicesOfKind[0]) === null || _c === void 0 ? void 0 : _c.deviceId) !== activeDevice) {
22969
- yield this.switchActiveDevice(kind, devicesOfKind[0].deviceId);
22970
- continue;
22971
- }
22972
- }
22973
- if (kind === 'audioinput' && !isSafari() || kind === 'videoinput') {
22974
- // airpods on Safari need special handling for audioinput as the track doesn't end as soon as you take them out
22975
- continue;
22976
- }
22977
- // switch to first available device if previously active device is not available any more
22978
- if (devicesOfKind.length > 0 && !devicesOfKind.find(deviceInfo => deviceInfo.deviceId === this.getActiveDevice(kind))) {
22979
- yield this.switchActiveDevice(kind, devicesOfKind[0].deviceId);
22980
- }
23187
+ var _a;
23188
+ if (((_a = getBrowser()) === null || _a === void 0 ? void 0 : _a.os) !== 'iOS') {
23189
+ // default devices are non deterministic on iOS, so we don't attempt to select them here
23190
+ yield this.selectDefaultDevices();
22981
23191
  }
22982
23192
  this.emit(RoomEvent.MediaDevicesChanged);
22983
23193
  });
@@ -23059,8 +23269,8 @@ class Room extends eventsExports.EventEmitter {
23059
23269
  this.onLocalConnectionQualityChanged = quality => {
23060
23270
  this.emit(RoomEvent.ConnectionQualityChanged, quality, this.localParticipant);
23061
23271
  };
23062
- this.onMediaDevicesError = e => {
23063
- this.emit(RoomEvent.MediaDevicesError, e);
23272
+ this.onMediaDevicesError = (e, kind) => {
23273
+ this.emit(RoomEvent.MediaDevicesError, e, kind);
23064
23274
  };
23065
23275
  this.onLocalParticipantPermissionsChanged = prevPermissions => {
23066
23276
  this.emit(RoomEvent.ParticipantPermissionsChanged, prevPermissions, this.localParticipant);
@@ -23882,6 +24092,59 @@ class Room extends eventsExports.EventEmitter {
23882
24092
  }
23883
24093
  }
23884
24094
  }
24095
+ /**
24096
+ * attempt to select the default devices if the previously selected devices are no longer available after a device change event
24097
+ */
24098
+ selectDefaultDevices() {
24099
+ return __awaiter(this, void 0, void 0, function* () {
24100
+ var _a, _b, _c;
24101
+ const previousDevices = DeviceManager.getInstance().previousDevices;
24102
+ // check for available devices, but don't request permissions in order to avoid prompts for kinds that haven't been used before
24103
+ const availableDevices = yield DeviceManager.getInstance().getDevices(undefined, false);
24104
+ const browser = getBrowser();
24105
+ if ((browser === null || browser === void 0 ? void 0 : browser.name) === 'Chrome' && browser.os !== 'iOS') {
24106
+ for (let availableDevice of availableDevices) {
24107
+ const previousDevice = previousDevices.find(info => info.deviceId === availableDevice.deviceId);
24108
+ if (previousDevice && previousDevice.label !== '' && previousDevice.kind === availableDevice.kind && previousDevice.label !== availableDevice.label) {
24109
+ // label has changed on device the same deviceId, indicating that the default device has changed on the OS level
24110
+ if (this.getActiveDevice(availableDevice.kind) === 'default') {
24111
+ // emit an active device change event only if the selected output device is actually on `default`
24112
+ this.emit(RoomEvent.ActiveDeviceChanged, availableDevice.kind, availableDevice.deviceId);
24113
+ }
24114
+ }
24115
+ }
24116
+ }
24117
+ const kinds = ['audiooutput', 'audioinput', 'videoinput'];
24118
+ for (let kind of kinds) {
24119
+ const targetSource = kindToSource(kind);
24120
+ const targetPublication = this.localParticipant.getTrackPublication(targetSource);
24121
+ if (targetPublication && ((_a = targetPublication.track) === null || _a === void 0 ? void 0 : _a.isUserProvided)) {
24122
+ // if the track is user provided, we don't want to switch devices on behalf of the user
24123
+ continue;
24124
+ }
24125
+ const devicesOfKind = availableDevices.filter(d => d.kind === kind);
24126
+ const activeDevice = this.getActiveDevice(kind);
24127
+ if (activeDevice === ((_b = previousDevices.filter(info => info.kind === kind)[0]) === null || _b === void 0 ? void 0 : _b.deviceId)) {
24128
+ // in Safari the first device is always the default, so we assume a user on the default device would like to switch to the default once it changes
24129
+ // FF doesn't emit an event when the default device changes, so we perform the same best effort and switch to the new device once connected and if it's the first in the array
24130
+ if (devicesOfKind.length > 0 && ((_c = devicesOfKind[0]) === null || _c === void 0 ? void 0 : _c.deviceId) !== activeDevice) {
24131
+ yield this.switchActiveDevice(kind, devicesOfKind[0].deviceId);
24132
+ continue;
24133
+ }
24134
+ }
24135
+ if (kind === 'audioinput' && !isSafariBased() || kind === 'videoinput') {
24136
+ // airpods on Safari need special handling for audioinput as the track doesn't end as soon as you take them out
24137
+ continue;
24138
+ }
24139
+ // switch to first available device if previously active device is not available any more
24140
+ if (devicesOfKind.length > 0 && !devicesOfKind.find(deviceInfo => deviceInfo.deviceId === this.getActiveDevice(kind)) && (
24141
+ // avoid switching audio output on safari without explicit user action as it leads to slowed down audio playback
24142
+ kind !== 'audiooutput' || !isSafariBased())) {
24143
+ yield this.switchActiveDevice(kind, devicesOfKind[0].deviceId);
24144
+ }
24145
+ }
24146
+ });
24147
+ }
23885
24148
  acquireAudioContext() {
23886
24149
  return __awaiter(this, void 0, void 0, function* () {
23887
24150
  var _a, _b;
@@ -23995,6 +24258,9 @@ class Room extends eventsExports.EventEmitter {
23995
24258
  this.emitWhenConnected(RoomEvent.TrackSubscriptionPermissionChanged, pub, status, participant);
23996
24259
  }).on(ParticipantEvent.Active, () => {
23997
24260
  this.emitWhenConnected(RoomEvent.ParticipantActive, participant);
24261
+ if (participant.kind === ParticipantInfo_Kind.AGENT) {
24262
+ this.localParticipant.setActiveAgent(participant);
24263
+ }
23998
24264
  });
23999
24265
  // update info at the end after callbacks have been set up
24000
24266
  if (info) {
@@ -25050,5 +25316,5 @@ function isFacingModeValue(item) {
25050
25316
  return item === undefined || allowedValues.includes(item);
25051
25317
  }
25052
25318
 
25053
- export { AudioPresets, BackupCodecPolicy, BaseKeyProvider, CheckStatus, Checker, ConnectionCheck, ConnectionError, ConnectionErrorReason, ConnectionQuality, ConnectionState, CriticalTimers, CryptorError, CryptorErrorReason, CryptorEvent, DataPacket_Kind, DefaultReconnectPolicy, DeviceUnsupportedError, DisconnectReason, EncryptionEvent, EngineEvent, ExternalE2EEKeyProvider, KeyHandlerEvent, KeyProviderEvent, LivekitError, LocalAudioTrack, LocalParticipant, LocalTrack, LocalTrackPublication, LocalVideoTrack, LogLevel, LoggerNames, MediaDeviceFailure, _ as Mutex, NegotiationError, Participant, ParticipantEvent, ParticipantInfo_Kind as ParticipantKind, PublishDataError, PublishTrackError, RemoteAudioTrack, RemoteParticipant, RemoteTrack, RemoteTrackPublication, RemoteVideoTrack, Room, RoomEvent, RpcError, ScreenSharePresets, SignalRequestError, SubscriptionError, Track, TrackEvent, TrackInvalidError, TrackPublication, TrackType, UnexpectedConnectionState, UnsupportedServer, VideoPreset, VideoPresets, VideoPresets43, VideoQuality, attachToElement, compareVersions, createAudioAnalyser, createE2EEKey, createKeyMaterialFromBuffer, createKeyMaterialFromString, createLocalAudioTrack, createLocalScreenTracks, createLocalTracks, createLocalVideoTrack, deriveKeys, detachTrack, facingModeFromDeviceLabel, facingModeFromLocalTrack, getBrowser, getEmptyAudioStreamTrack, getEmptyVideoStreamTrack, getLogger, importKey, isAudioTrack, isBackupCodec, isBrowserSupported, isE2EESupported, isInsertableStreamSupported, isLocalParticipant, isLocalTrack, isRemoteParticipant, isRemoteTrack, isScriptTransformSupported, isVideoFrame, isVideoTrack, needsRbspUnescaping, parseRbsp, protocolVersion, ratchet, setLogExtension, setLogLevel, supportsAV1, supportsAdaptiveStream, supportsDynacast, supportsVP9, version, videoCodecs, writeRbsp };
25319
+ export { AudioPresets, BackupCodecPolicy, BaseKeyProvider, CheckStatus, Checker, ConnectionCheck, ConnectionError, ConnectionErrorReason, ConnectionQuality, ConnectionState, CriticalTimers, CryptorError, CryptorErrorReason, CryptorEvent, DataPacket_Kind, DefaultReconnectPolicy, DeviceUnsupportedError, DisconnectReason, EncryptionEvent, EngineEvent, ExternalE2EEKeyProvider, KeyHandlerEvent, KeyProviderEvent, LivekitError, LocalAudioTrack, LocalParticipant, LocalTrack, LocalTrackPublication, LocalTrackRecorder, LocalVideoTrack, LogLevel, LoggerNames, MediaDeviceFailure, _ as Mutex, NegotiationError, Participant, ParticipantEvent, ParticipantInfo_Kind as ParticipantKind, PublishDataError, PublishTrackError, RemoteAudioTrack, RemoteParticipant, RemoteTrack, RemoteTrackPublication, RemoteVideoTrack, Room, RoomEvent, RpcError, ScreenSharePresets, SignalRequestError, SubscriptionError, Track, TrackEvent, TrackInvalidError, TrackPublication, TrackType, UnexpectedConnectionState, UnsupportedServer, VideoPreset, VideoPresets, VideoPresets43, VideoQuality, attachToElement, compareVersions, createAudioAnalyser, createE2EEKey, createKeyMaterialFromBuffer, createKeyMaterialFromString, createLocalAudioTrack, createLocalScreenTracks, createLocalTracks, createLocalVideoTrack, deriveKeys, detachTrack, facingModeFromDeviceLabel, facingModeFromLocalTrack, getBrowser, getEmptyAudioStreamTrack, getEmptyVideoStreamTrack, getLogger, importKey, isAudioTrack, isBackupCodec, isBrowserSupported, isE2EESupported, isInsertableStreamSupported, isLocalParticipant, isLocalTrack, isRemoteParticipant, isRemoteTrack, isScriptTransformSupported, isVideoFrame, isVideoTrack, needsRbspUnescaping, parseRbsp, protocolVersion, ratchet, setLogExtension, setLogLevel, supportsAV1, supportsAdaptiveStream, supportsDynacast, supportsVP9, version, videoCodecs, writeRbsp };
25054
25320
  //# sourceMappingURL=livekit-client.esm.mjs.map