livekit-client 2.7.5 → 2.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. package/dist/livekit-client.e2ee.worker.js.map +1 -1
  2. package/dist/livekit-client.e2ee.worker.mjs +14 -14
  3. package/dist/livekit-client.e2ee.worker.mjs.map +1 -1
  4. package/dist/livekit-client.esm.mjs +151 -88
  5. package/dist/livekit-client.esm.mjs.map +1 -1
  6. package/dist/livekit-client.umd.js +1 -1
  7. package/dist/livekit-client.umd.js.map +1 -1
  8. package/dist/src/connectionHelper/ConnectionCheck.d.ts.map +1 -1
  9. package/dist/src/connectionHelper/checks/Checker.d.ts.map +1 -1
  10. package/dist/src/e2ee/E2eeManager.d.ts.map +1 -1
  11. package/dist/src/e2ee/KeyProvider.d.ts.map +1 -1
  12. package/dist/src/e2ee/worker/FrameCryptor.d.ts.map +1 -1
  13. package/dist/src/e2ee/worker/ParticipantKeyHandler.d.ts.map +1 -1
  14. package/dist/src/room/DeviceManager.d.ts +2 -0
  15. package/dist/src/room/DeviceManager.d.ts.map +1 -1
  16. package/dist/src/room/RTCEngine.d.ts.map +1 -1
  17. package/dist/src/room/Room.d.ts +1 -0
  18. package/dist/src/room/Room.d.ts.map +1 -1
  19. package/dist/src/room/defaults.d.ts.map +1 -1
  20. package/dist/src/room/participant/LocalParticipant.d.ts.map +1 -1
  21. package/dist/src/room/participant/Participant.d.ts.map +1 -1
  22. package/dist/src/room/participant/publishUtils.d.ts.map +1 -1
  23. package/dist/src/room/track/LocalAudioTrack.d.ts +0 -1
  24. package/dist/src/room/track/LocalAudioTrack.d.ts.map +1 -1
  25. package/dist/src/room/track/LocalTrack.d.ts +2 -0
  26. package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
  27. package/dist/src/room/track/LocalVideoTrack.d.ts +0 -1
  28. package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
  29. package/dist/src/room/track/Track.d.ts.map +1 -1
  30. package/dist/src/room/track/TrackPublication.d.ts.map +1 -1
  31. package/dist/src/room/track/utils.d.ts.map +1 -1
  32. package/dist/ts4.2/src/room/DeviceManager.d.ts +2 -0
  33. package/dist/ts4.2/src/room/Room.d.ts +1 -0
  34. package/dist/ts4.2/src/room/track/LocalAudioTrack.d.ts +0 -1
  35. package/dist/ts4.2/src/room/track/LocalTrack.d.ts +2 -0
  36. package/dist/ts4.2/src/room/track/LocalVideoTrack.d.ts +0 -1
  37. package/package.json +16 -15
  38. package/src/e2ee/worker/tsconfig.json +1 -1
  39. package/src/room/DeviceManager.ts +9 -2
  40. package/src/room/Room.ts +70 -9
  41. package/src/room/defaults.ts +2 -0
  42. package/src/room/participant/LocalParticipant.ts +15 -4
  43. package/src/room/participant/publishUtils.ts +14 -4
  44. package/src/room/track/LocalAudioTrack.ts +0 -16
  45. package/src/room/track/LocalTrack.ts +23 -1
  46. package/src/room/track/LocalVideoTrack.ts +1 -19
  47. package/src/room/track/create.ts +2 -2
  48. package/src/room/track/utils.test.ts +4 -12
  49. package/src/room/track/utils.ts +6 -2
@@ -13,18 +13,18 @@ function _mergeNamespaces(n, m) {
13
13
  return Object.freeze(n);
14
14
  }
15
15
 
16
- var k = Object.defineProperty;
17
- var n = (s, o, c) => o in s ? k(s, o, {
16
+ var e = Object.defineProperty;
17
+ var h = (i, s, t) => s in i ? e(i, s, {
18
18
  enumerable: !0,
19
19
  configurable: !0,
20
20
  writable: !0,
21
- value: c
22
- }) : s[o] = c;
23
- var l = (s, o, c) => n(s, typeof o != "symbol" ? o + "" : o, c);
24
- class h {
21
+ value: t
22
+ }) : i[s] = t;
23
+ var o = (i, s, t) => h(i, typeof s != "symbol" ? s + "" : s, t);
24
+ class _ {
25
25
  constructor() {
26
- l(this, "_locking");
27
- l(this, "_locks");
26
+ o(this, "_locking");
27
+ o(this, "_locks");
28
28
  this._locking = Promise.resolve(), this._locks = 0;
29
29
  }
30
30
  isLocked() {
@@ -32,12 +32,12 @@ class h {
32
32
  }
33
33
  lock() {
34
34
  this._locks += 1;
35
- let o;
36
- const c = new Promise(i => o = () => {
37
- this._locks -= 1, i();
35
+ let s;
36
+ const t = new Promise(l => s = () => {
37
+ this._locks -= 1, l();
38
38
  }),
39
- t = this._locking.then(() => o);
40
- return this._locking = this._locking.then(() => c), t;
39
+ c = this._locking.then(() => s);
40
+ return this._locking = this._locking.then(() => t), c;
41
41
  }
42
42
  }
43
43
 
@@ -5020,12 +5020,6 @@ const DataStream_Header = /* @__PURE__ */proto3.makeMessageType("livekit.DataStr
5020
5020
  kind: "scalar",
5021
5021
  T: 4,
5022
5022
  opt: true
5023
- }, {
5024
- no: 6,
5025
- name: "total_chunks",
5026
- kind: "scalar",
5027
- T: 4,
5028
- opt: true
5029
5023
  }, {
5030
5024
  no: 7,
5031
5025
  name: "encryption_type",
@@ -11119,7 +11113,7 @@ function getOSVersion(ua) {
11119
11113
  return ua.includes('mac os') ? getMatch(/\(.+?(\d+_\d+(:?_\d+)?)/, ua, 1).replace(/_/g, '.') : undefined;
11120
11114
  }
11121
11115
 
11122
- var version$1 = "2.7.5";
11116
+ var version$1 = "2.8.0";
11123
11117
 
11124
11118
  const version = version$1;
11125
11119
  const protocolVersion = 15;
@@ -11627,10 +11621,11 @@ function detachTrack(track, element) {
11627
11621
  }
11628
11622
  }
11629
11623
  Track.streamStateFromProto = streamStateFromProto;
11630
- })(Track || (Track = {}));
11624
+ })(Track);
11631
11625
 
11632
11626
  function mergeDefaultOptions(options, audioDefaults, videoDefaults) {
11633
- var _a;
11627
+ var _a, _b, _c;
11628
+ var _d, _e;
11634
11629
  const {
11635
11630
  optionsWithoutProcessor,
11636
11631
  audioProcessor,
@@ -11642,12 +11637,14 @@ function mergeDefaultOptions(options, audioDefaults, videoDefaults) {
11642
11637
  // use defaults
11643
11638
  if (clonedOptions.audio) {
11644
11639
  mergeObjectWithoutOverwriting(clonedOptions.audio, audioDefaults);
11640
+ (_b = (_d = clonedOptions.audio).deviceId) !== null && _b !== void 0 ? _b : _d.deviceId = 'default';
11645
11641
  if (audioProcessor) {
11646
11642
  clonedOptions.audio.processor = audioProcessor;
11647
11643
  }
11648
11644
  }
11649
11645
  if (clonedOptions.video) {
11650
11646
  mergeObjectWithoutOverwriting(clonedOptions.video, videoDefaults);
11647
+ (_c = (_e = clonedOptions.video).deviceId) !== null && _c !== void 0 ? _c : _e.deviceId = 'default';
11651
11648
  if (videoProcessor) {
11652
11649
  clonedOptions.video.processor = videoProcessor;
11653
11650
  }
@@ -11661,6 +11658,8 @@ function mergeObjectWithoutOverwriting(mainObject, objectToMerge) {
11661
11658
  return mainObject;
11662
11659
  }
11663
11660
  function constraintsForOptions(options) {
11661
+ var _a, _b;
11662
+ var _c, _d;
11664
11663
  const constraints = {};
11665
11664
  if (options.video) {
11666
11665
  // default video options
@@ -11679,8 +11678,11 @@ function constraintsForOptions(options) {
11679
11678
  }
11680
11679
  });
11681
11680
  constraints.video = videoOptions;
11681
+ (_a = (_c = constraints.video).deviceId) !== null && _a !== void 0 ? _a : _c.deviceId = 'default';
11682
11682
  } else {
11683
- constraints.video = options.video;
11683
+ constraints.video = options.video ? {
11684
+ deviceId: 'default'
11685
+ } : false;
11684
11686
  }
11685
11687
  } else {
11686
11688
  constraints.video = false;
@@ -11688,8 +11690,11 @@ function constraintsForOptions(options) {
11688
11690
  if (options.audio) {
11689
11691
  if (typeof options.audio === 'object') {
11690
11692
  constraints.audio = options.audio;
11693
+ (_b = (_d = constraints.audio).deviceId) !== null && _b !== void 0 ? _b : _d.deviceId = 'default';
11691
11694
  } else {
11692
- constraints.audio = true;
11695
+ constraints.audio = {
11696
+ deviceId: 'default'
11697
+ };
11693
11698
  }
11694
11699
  } else {
11695
11700
  constraints.audio = false;
@@ -12315,12 +12320,18 @@ function getDisconnectReasonFromConnectionError(e) {
12315
12320
 
12316
12321
  const defaultId = 'default';
12317
12322
  class DeviceManager {
12323
+ constructor() {
12324
+ this._previousDevices = [];
12325
+ }
12318
12326
  static getInstance() {
12319
12327
  if (this.instance === undefined) {
12320
12328
  this.instance = new DeviceManager();
12321
12329
  }
12322
12330
  return this.instance;
12323
12331
  }
12332
+ get previousDevices() {
12333
+ return this._previousDevices;
12334
+ }
12324
12335
  getDevices(kind_1) {
12325
12336
  return __awaiter(this, arguments, void 0, function (kind) {
12326
12337
  var _this = this;
@@ -12351,7 +12362,9 @@ class DeviceManager {
12351
12362
  if (isDummyDeviceOrEmpty) {
12352
12363
  const permissionsToAcquire = {
12353
12364
  video: kind !== 'audioinput' && kind !== 'audiooutput',
12354
- audio: kind !== 'videoinput'
12365
+ audio: kind !== 'videoinput' && {
12366
+ deviceId: 'default'
12367
+ }
12355
12368
  };
12356
12369
  const stream = yield navigator.mediaDevices.getUserMedia(permissionsToAcquire);
12357
12370
  devices = yield navigator.mediaDevices.enumerateDevices();
@@ -12360,6 +12373,7 @@ class DeviceManager {
12360
12373
  });
12361
12374
  }
12362
12375
  }
12376
+ _this._previousDevices = devices;
12363
12377
  if (kind) {
12364
12378
  devices = devices.filter(device => device.kind === kind);
12365
12379
  }
@@ -12439,10 +12453,10 @@ class LocalTrack extends Track {
12439
12453
  };
12440
12454
  this.reacquireTrack = false;
12441
12455
  this.providedByUser = userProvidedTrack;
12442
- this.muteLock = new h();
12443
- this.pauseUpstreamLock = new h();
12444
- this.processorLock = new h();
12445
- this.restartLock = new h();
12456
+ this.muteLock = new _();
12457
+ this.pauseUpstreamLock = new _();
12458
+ this.processorLock = new _();
12459
+ this.restartLock = new _();
12446
12460
  this.setMediaStreamTrack(mediaTrack, true);
12447
12461
  // added to satisfy TS compiler, constraints are synced with MediaStreamTrack
12448
12462
  this._constraints = mediaTrack.getConstraints();
@@ -12582,6 +12596,21 @@ class LocalTrack extends Track {
12582
12596
  }();
12583
12597
  });
12584
12598
  }
12599
+ setDeviceId(deviceId) {
12600
+ return __awaiter(this, void 0, void 0, function* () {
12601
+ if (this._constraints.deviceId === deviceId && this._mediaStreamTrack.getSettings().deviceId === unwrapConstraint(deviceId)) {
12602
+ return true;
12603
+ }
12604
+ this._constraints.deviceId = deviceId;
12605
+ // when track is muted, underlying media stream track is stopped and
12606
+ // will be restarted later
12607
+ if (this.isMuted) {
12608
+ return true;
12609
+ }
12610
+ yield this.restartTrack();
12611
+ return unwrapConstraint(deviceId) === this._mediaStreamTrack.getSettings().deviceId;
12612
+ });
12613
+ }
12585
12614
  /**
12586
12615
  * @returns DeviceID of the device that is currently being used for this track
12587
12616
  */
@@ -13236,7 +13265,7 @@ var QueueTaskStatus;
13236
13265
  class AsyncQueue {
13237
13266
  constructor() {
13238
13267
  this.pendingTasks = new Map();
13239
- this.taskMutex = new h();
13268
+ this.taskMutex = new _();
13240
13269
  this.nextTaskIndex = 0;
13241
13270
  }
13242
13271
  run(task) {
@@ -13329,8 +13358,8 @@ class SignalClient {
13329
13358
  this.useJSON = useJSON;
13330
13359
  this.requestQueue = new AsyncQueue();
13331
13360
  this.queuedRequests = [];
13332
- this.closingLock = new h();
13333
- this.connectionLock = new h();
13361
+ this.closingLock = new _();
13362
+ this.connectionLock = new _();
13334
13363
  this.state = SignalConnectionState.DISCONNECTED;
13335
13364
  }
13336
13365
  get logContext() {
@@ -14628,6 +14657,8 @@ function requireLib() {
14628
14657
  hasRequiredLib = 1;
14629
14658
  var parser = requireParser();
14630
14659
  var writer = requireWriter();
14660
+ var grammar = requireGrammar();
14661
+ lib.grammar = grammar;
14631
14662
  lib.write = writer;
14632
14663
  lib.parse = parser.parse;
14633
14664
  lib.parseParams = parser.parseParams;
@@ -15205,12 +15236,14 @@ const publishDefaults = {
15205
15236
  backupCodec: true
15206
15237
  };
15207
15238
  const audioDefaults = {
15239
+ deviceId: 'default',
15208
15240
  autoGainControl: true,
15209
15241
  echoCancellation: true,
15210
15242
  noiseSuppression: true,
15211
15243
  voiceIsolation: true
15212
15244
  };
15213
15245
  const videoDefaults = {
15246
+ deviceId: 'default',
15214
15247
  resolution: VideoPresets.h720.resolution
15215
15248
  };
15216
15249
  const roomOptionDefaults = {
@@ -15307,8 +15340,8 @@ class PCTransportManager {
15307
15340
  (_a = this.onPublisherOffer) === null || _a === void 0 ? void 0 : _a.call(this, offer);
15308
15341
  };
15309
15342
  this.state = PCTransportState.NEW;
15310
- this.connectionLock = new h();
15311
- this.remoteOfferLock = new h();
15343
+ this.connectionLock = new _();
15344
+ this.remoteOfferLock = new _();
15312
15345
  }
15313
15346
  get logContext() {
15314
15347
  var _a, _b;
@@ -15583,18 +15616,6 @@ class LocalAudioTrack extends LocalTrack {
15583
15616
  this.audioContext = audioContext;
15584
15617
  this.checkForSilence();
15585
15618
  }
15586
- setDeviceId(deviceId) {
15587
- return __awaiter(this, void 0, void 0, function* () {
15588
- if (this._constraints.deviceId === deviceId && this._mediaStreamTrack.getSettings().deviceId === unwrapConstraint(deviceId)) {
15589
- return true;
15590
- }
15591
- this._constraints.deviceId = deviceId;
15592
- if (!this.isMuted) {
15593
- yield this.restartTrack();
15594
- }
15595
- return this.isMuted || unwrapConstraint(deviceId) === this._mediaStreamTrack.getSettings().deviceId;
15596
- });
15597
- }
15598
15619
  mute() {
15599
15620
  const _super = Object.create(null, {
15600
15621
  mute: {
@@ -15831,6 +15852,7 @@ function computeVideoEncodings(isScreenShare, width, height, options) {
15831
15852
  videoEncoding = determineAppropriateEncoding(isScreenShare, width, height, videoCodec);
15832
15853
  livekitLogger.debug('using video encoding', videoEncoding);
15833
15854
  }
15855
+ const sourceFramerate = videoEncoding.maxFramerate;
15834
15856
  const original = new VideoPreset(width, height, videoEncoding.maxBitrate, videoEncoding.maxFramerate, videoEncoding.priority);
15835
15857
  if (scalabilityMode && isSVCCodec(videoCodec)) {
15836
15858
  const sm = new ScalabilityMode(scalabilityMode);
@@ -15904,10 +15926,10 @@ function computeVideoEncodings(isScreenShare, width, height, options) {
15904
15926
  // based on other conditions.
15905
15927
  const size = Math.max(width, height);
15906
15928
  if (size >= 960 && midPreset) {
15907
- return encodingsFromPresets(width, height, [lowPreset, midPreset, original]);
15929
+ return encodingsFromPresets(width, height, [lowPreset, midPreset, original], sourceFramerate);
15908
15930
  }
15909
15931
  if (size >= 480) {
15910
- return encodingsFromPresets(width, height, [lowPreset, original]);
15932
+ return encodingsFromPresets(width, height, [lowPreset, original], sourceFramerate);
15911
15933
  }
15912
15934
  }
15913
15935
  return encodingsFromPresets(width, height, [original]);
@@ -15995,7 +16017,7 @@ function defaultSimulcastLayers(isScreenShare, original) {
15995
16017
  return defaultSimulcastPresets43;
15996
16018
  }
15997
16019
  // presets should be ordered by low, medium, high
15998
- function encodingsFromPresets(width, height, presets) {
16020
+ function encodingsFromPresets(width, height, presets, sourceFramerate) {
15999
16021
  const encodings = [];
16000
16022
  presets.forEach((preset, idx) => {
16001
16023
  if (idx >= videoRids.length) {
@@ -16008,8 +16030,11 @@ function encodingsFromPresets(width, height, presets) {
16008
16030
  scaleResolutionDownBy: Math.max(1, size / Math.min(preset.width, preset.height)),
16009
16031
  maxBitrate: preset.encoding.maxBitrate
16010
16032
  };
16011
- if (preset.encoding.maxFramerate) {
16012
- encoding.maxFramerate = preset.encoding.maxFramerate;
16033
+ // ensure that the sourceFramerate is the highest framerate applied across all layers so that the
16034
+ // original encoding doesn't get bumped unintentionally by any of the other layers
16035
+ const maxFramerate = sourceFramerate && preset.encoding.maxFramerate ? Math.min(sourceFramerate, preset.encoding.maxFramerate) : preset.encoding.maxFramerate;
16036
+ if (maxFramerate) {
16037
+ encoding.maxFramerate = maxFramerate;
16013
16038
  }
16014
16039
  const canSetPriority = isFireFox() || idx === 0;
16015
16040
  if (preset.encoding.priority && canSetPriority) {
@@ -16147,7 +16172,7 @@ class LocalVideoTrack extends LocalTrack {
16147
16172
  }
16148
16173
  this.prevStats = statsMap;
16149
16174
  });
16150
- this.senderLock = new h();
16175
+ this.senderLock = new _();
16151
16176
  }
16152
16177
  get isSimulcast() {
16153
16178
  if (this.sender && this.sender.getParameters().encodings.length > 1) {
@@ -16357,20 +16382,6 @@ class LocalVideoTrack extends LocalTrack {
16357
16382
  this.log.debug("setting publishing quality. max quality ".concat(maxQuality), this.logContext);
16358
16383
  this.setPublishingLayers(qualities);
16359
16384
  }
16360
- setDeviceId(deviceId) {
16361
- return __awaiter(this, void 0, void 0, function* () {
16362
- if (this._constraints.deviceId === deviceId && this._mediaStreamTrack.getSettings().deviceId === unwrapConstraint(deviceId)) {
16363
- return true;
16364
- }
16365
- this._constraints.deviceId = deviceId;
16366
- // when video is muted, underlying media stream track is stopped and
16367
- // will be restarted later
16368
- if (!this.isMuted) {
16369
- yield this.restartTrack();
16370
- }
16371
- return this.isMuted || unwrapConstraint(deviceId) === this._mediaStreamTrack.getSettings().deviceId;
16372
- });
16373
- }
16374
16385
  restartTrack(options) {
16375
16386
  return __awaiter(this, void 0, void 0, function* () {
16376
16387
  var _a, e_3, _b, _c;
@@ -16907,8 +16918,8 @@ class RTCEngine extends eventsExports.EventEmitter {
16907
16918
  this.client.signalLatency = this.options.expSignalLatency;
16908
16919
  this.reconnectPolicy = this.options.reconnectPolicy;
16909
16920
  this.registerOnLineListener();
16910
- this.closingLock = new h();
16911
- this.dataProcessLock = new h();
16921
+ this.closingLock = new _();
16922
+ this.dataProcessLock = new _();
16912
16923
  this.dcBufferStatus = new Map([[DataPacket_Kind.LOSSY, true], [DataPacket_Kind.RELIABLE, true]]);
16913
16924
  this.client.onParticipantUpdate = updates => this.emit(EngineEvent.ParticipantUpdate, updates);
16914
16925
  this.client.onConnectionQuality = update => this.emit(EngineEvent.ConnectionQualityUpdate, update);
@@ -18748,7 +18759,7 @@ class TrackPublication extends eventsExports.EventEmitter {
18748
18759
  PermissionStatus["Allowed"] = "allowed";
18749
18760
  PermissionStatus["NotAllowed"] = "not_allowed";
18750
18761
  })(TrackPublication.PermissionStatus || (TrackPublication.PermissionStatus = {}));
18751
- })(TrackPublication || (TrackPublication = {}));
18762
+ })(TrackPublication);
18752
18763
 
18753
18764
  class LocalTrackPublication extends TrackPublication {
18754
18765
  get isUpstreamPaused() {
@@ -19322,7 +19333,7 @@ class LocalParticipant extends Participant {
19322
19333
  this.engine = engine;
19323
19334
  this.roomOptions = options;
19324
19335
  this.setupEngine(engine);
19325
- this.activeDeviceMap = new Map();
19336
+ this.activeDeviceMap = new Map([['audioinput', 'default'], ['videoinput', 'default'], ['audiooutput', 'default']]);
19326
19337
  this.pendingSignalRequests = new Map();
19327
19338
  }
19328
19339
  get lastCameraError() {
@@ -19535,6 +19546,16 @@ class LocalParticipant extends Participant {
19535
19546
  default:
19536
19547
  throw new TrackInvalidError(source);
19537
19548
  }
19549
+ } catch (e) {
19550
+ localTracks === null || localTracks === void 0 ? void 0 : localTracks.forEach(tr => {
19551
+ tr.stop();
19552
+ });
19553
+ if (e instanceof Error) {
19554
+ this.emit(ParticipantEvent.MediaDevicesError, e);
19555
+ }
19556
+ throw e;
19557
+ }
19558
+ try {
19538
19559
  const publishPromises = [];
19539
19560
  for (const localTrack of localTracks) {
19540
19561
  this.log.info('publishing track', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(localTrack)));
@@ -19548,9 +19569,6 @@ class LocalParticipant extends Participant {
19548
19569
  localTracks === null || localTracks === void 0 ? void 0 : localTracks.forEach(tr => {
19549
19570
  tr.stop();
19550
19571
  });
19551
- if (e instanceof Error && !(e instanceof TrackInvalidError)) {
19552
- this.emit(ParticipantEvent.MediaDevicesError, e);
19553
- }
19554
19572
  throw e;
19555
19573
  } finally {
19556
19574
  this.pendingPublishing.delete(source);
@@ -21215,7 +21233,7 @@ class Room extends eventsExports.EventEmitter {
21215
21233
  */
21216
21234
  constructor(options) {
21217
21235
  var _this;
21218
- var _a, _b;
21236
+ var _a, _b, _c;
21219
21237
  super();
21220
21238
  _this = this;
21221
21239
  this.state = ConnectionState.Disconnected;
@@ -21384,7 +21402,7 @@ class Room extends eventsExports.EventEmitter {
21384
21402
  }
21385
21403
  };
21386
21404
  this.attemptConnection = (url, token, opts, abortController) => __awaiter(this, void 0, void 0, function* () {
21387
- var _a, _b, _c;
21405
+ var _a, _b;
21388
21406
  if (this.state === ConnectionState.Reconnecting || this.isResuming || ((_a = this.engine) === null || _a === void 0 ? void 0 : _a.pendingReconnect)) {
21389
21407
  this.log.info('Reconnection attempt replaced by new connection attempt', this.logContext);
21390
21408
  // make sure we close and recreate the existing engine in order to get rid of any potentially ongoing reconnection attempts
@@ -21446,7 +21464,6 @@ class Room extends eventsExports.EventEmitter {
21446
21464
  }
21447
21465
  if (isWeb()) {
21448
21466
  document.addEventListener('freeze', this.onPageLeave);
21449
- (_c = navigator.mediaDevices) === null || _c === void 0 ? void 0 : _c.addEventListener('devicechange', this.handleDeviceChange);
21450
21467
  }
21451
21468
  this.setAndEmitConnectionState(ConnectionState.Connected);
21452
21469
  this.emit(RoomEvent.Connected);
@@ -21829,13 +21846,41 @@ class Room extends eventsExports.EventEmitter {
21829
21846
  }
21830
21847
  };
21831
21848
  this.handleDeviceChange = () => __awaiter(this, void 0, void 0, function* () {
21849
+ var _a, _b;
21850
+ const previousDevices = DeviceManager.getInstance().previousDevices;
21832
21851
  // check for available devices, but don't request permissions in order to avoid prompts for kinds that haven't been used before
21833
21852
  const availableDevices = yield DeviceManager.getInstance().getDevices(undefined, false);
21853
+ const browser = getBrowser();
21854
+ if ((browser === null || browser === void 0 ? void 0 : browser.name) === 'Chrome' && browser.os !== 'iOS') {
21855
+ for (let availableDevice of availableDevices) {
21856
+ const previousDevice = previousDevices.find(info => info.deviceId === availableDevice.deviceId);
21857
+ if (previousDevice && previousDevice.label !== '' && previousDevice.kind === availableDevice.kind && previousDevice.label !== availableDevice.label) {
21858
+ // label has changed on device the same deviceId, indicating that the default device has changed on the OS level
21859
+ if (this.getActiveDevice(availableDevice.kind) === 'default') {
21860
+ // emit an active device change event only if the selected output device is actually on `default`
21861
+ this.emit(RoomEvent.ActiveDeviceChanged, availableDevice.kind, availableDevice.deviceId);
21862
+ }
21863
+ }
21864
+ }
21865
+ }
21834
21866
  // inputs are automatically handled via TrackEvent.Ended causing a TrackEvent.Restarted. Here we only need to worry about audiooutputs changing
21835
- const kinds = ['audiooutput'];
21867
+ const kinds = ['audiooutput', 'audioinput', 'videoinput'];
21836
21868
  for (let kind of kinds) {
21837
- // switch to first available device if previously active device is not available any more
21838
21869
  const devicesOfKind = availableDevices.filter(d => d.kind === kind);
21870
+ const activeDevice = this.getActiveDevice(kind);
21871
+ if (activeDevice === ((_a = previousDevices.filter(info => info.kind === kind)[0]) === null || _a === void 0 ? void 0 : _a.deviceId)) {
21872
+ // 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
21873
+ // 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
21874
+ if (devicesOfKind.length > 0 && ((_b = devicesOfKind[0]) === null || _b === void 0 ? void 0 : _b.deviceId) !== activeDevice) {
21875
+ yield this.switchActiveDevice(kind, devicesOfKind[0].deviceId);
21876
+ continue;
21877
+ }
21878
+ }
21879
+ if (kind === 'audioinput' && !isSafari() || kind === 'videoinput') {
21880
+ // airpods on Safari need special handling for audioinput as the track doesn't end as soon as you take them out
21881
+ continue;
21882
+ }
21883
+ // switch to first available device if previously active device is not available any more
21839
21884
  if (devicesOfKind.length > 0 && !devicesOfKind.find(deviceInfo => deviceInfo.deviceId === this.getActiveDevice(kind))) {
21840
21885
  yield this.switchActiveDevice(kind, devicesOfKind[0].deviceId);
21841
21886
  }
@@ -21895,7 +21940,7 @@ class Room extends eventsExports.EventEmitter {
21895
21940
  this.emit(RoomEvent.LocalAudioSilenceDetected, pub);
21896
21941
  }
21897
21942
  }
21898
- const deviceId = yield (_f = pub.track) === null || _f === void 0 ? void 0 : _f.getDeviceId();
21943
+ const deviceId = yield (_f = pub.track) === null || _f === void 0 ? void 0 : _f.getDeviceId(false);
21899
21944
  const deviceKind = sourceToKind(pub.source);
21900
21945
  if (deviceKind && deviceId && deviceId !== this.localParticipant.activeDeviceMap.get(deviceKind)) {
21901
21946
  this.localParticipant.activeDeviceMap.set(deviceKind, deviceId);
@@ -21939,7 +21984,7 @@ class Room extends eventsExports.EventEmitter {
21939
21984
  this.options.videoCaptureDefaults = Object.assign(Object.assign({}, videoDefaults), options === null || options === void 0 ? void 0 : options.videoCaptureDefaults);
21940
21985
  this.options.publishDefaults = Object.assign(Object.assign({}, publishDefaults), options === null || options === void 0 ? void 0 : options.publishDefaults);
21941
21986
  this.maybeCreateEngine();
21942
- this.disconnectLock = new h();
21987
+ this.disconnectLock = new _();
21943
21988
  this.localParticipant = new LocalParticipant('', '', this.engine, this.options);
21944
21989
  if (this.options.videoCaptureDefaults.deviceId) {
21945
21990
  this.localParticipant.activeDeviceMap.set('videoinput', unwrapConstraint(this.options.videoCaptureDefaults.deviceId));
@@ -21953,6 +21998,18 @@ class Room extends eventsExports.EventEmitter {
21953
21998
  if (this.options.e2ee) {
21954
21999
  this.setupE2EE();
21955
22000
  }
22001
+ if (isWeb()) {
22002
+ const abortController = new AbortController();
22003
+ // in order to catch device changes prior to room connection we need to register the event in the constructor
22004
+ (_c = navigator.mediaDevices) === null || _c === void 0 ? void 0 : _c.addEventListener('devicechange', this.handleDeviceChange, {
22005
+ signal: abortController.signal
22006
+ });
22007
+ if (Room.cleanupRegistry) {
22008
+ Room.cleanupRegistry.register(this, () => {
22009
+ abortController.abort();
22010
+ });
22011
+ }
22012
+ }
21956
22013
  }
21957
22014
  /**
21958
22015
  * @experimental
@@ -22323,15 +22380,15 @@ class Room extends eventsExports.EventEmitter {
22323
22380
  return function* () {
22324
22381
  var _a, _b, _c, _d, _e, _f;
22325
22382
  var _g;
22326
- let deviceHasChanged = false;
22327
22383
  let success = true;
22384
+ let needsUpdateWithoutTracks = false;
22328
22385
  const deviceConstraint = exact ? {
22329
22386
  exact: deviceId
22330
22387
  } : deviceId;
22331
22388
  if (kind === 'audioinput') {
22389
+ needsUpdateWithoutTracks = _this3.localParticipant.audioTrackPublications.size === 0;
22332
22390
  const prevDeviceId = (_a = _this3.getActiveDevice(kind)) !== null && _a !== void 0 ? _a : _this3.options.audioCaptureDefaults.deviceId;
22333
22391
  _this3.options.audioCaptureDefaults.deviceId = deviceConstraint;
22334
- deviceHasChanged = prevDeviceId !== deviceConstraint;
22335
22392
  const tracks = Array.from(_this3.localParticipant.audioTrackPublications.values()).filter(track => track.source === Track.Source.Microphone);
22336
22393
  try {
22337
22394
  success = (yield Promise.all(tracks.map(t => {
@@ -22343,9 +22400,9 @@ class Room extends eventsExports.EventEmitter {
22343
22400
  throw e;
22344
22401
  }
22345
22402
  } else if (kind === 'videoinput') {
22403
+ needsUpdateWithoutTracks = _this3.localParticipant.videoTrackPublications.size === 0;
22346
22404
  const prevDeviceId = (_b = _this3.getActiveDevice(kind)) !== null && _b !== void 0 ? _b : _this3.options.videoCaptureDefaults.deviceId;
22347
22405
  _this3.options.videoCaptureDefaults.deviceId = deviceConstraint;
22348
- deviceHasChanged = prevDeviceId !== deviceConstraint;
22349
22406
  const tracks = Array.from(_this3.localParticipant.videoTrackPublications.values()).filter(track => track.source === Track.Source.Camera);
22350
22407
  try {
22351
22408
  success = (yield Promise.all(tracks.map(t => {
@@ -22367,7 +22424,6 @@ class Room extends eventsExports.EventEmitter {
22367
22424
  (_d = (_g = _this3.options).audioOutput) !== null && _d !== void 0 ? _d : _g.audioOutput = {};
22368
22425
  const prevDeviceId = (_e = _this3.getActiveDevice(kind)) !== null && _e !== void 0 ? _e : _this3.options.audioOutput.deviceId;
22369
22426
  _this3.options.audioOutput.deviceId = deviceId;
22370
- deviceHasChanged = prevDeviceId !== deviceConstraint;
22371
22427
  try {
22372
22428
  if (_this3.options.webAudioMix) {
22373
22429
  // @ts-expect-error setSinkId is not yet in the typescript type of AudioContext
@@ -22383,7 +22439,7 @@ class Room extends eventsExports.EventEmitter {
22383
22439
  throw e;
22384
22440
  }
22385
22441
  }
22386
- if (deviceHasChanged && success) {
22442
+ if (needsUpdateWithoutTracks) {
22387
22443
  _this3.localParticipant.activeDeviceMap.set(kind, deviceId);
22388
22444
  _this3.emit(RoomEvent.ActiveDeviceChanged, kind, deviceId);
22389
22445
  }
@@ -22859,6 +22915,9 @@ class Room extends eventsExports.EventEmitter {
22859
22915
  return super.emit(event, ...args);
22860
22916
  }
22861
22917
  }
22918
+ Room.cleanupRegistry = typeof FinalizationRegistry !== 'undefined' && new FinalizationRegistry(cleanup => {
22919
+ cleanup();
22920
+ });
22862
22921
  function mapArgs(args) {
22863
22922
  return args.map(arg => {
22864
22923
  if (!arg) {
@@ -23002,8 +23061,12 @@ function createLocalTracks(options) {
23002
23061
  var _a, _b;
23003
23062
  // set default options to true
23004
23063
  options !== null && options !== void 0 ? options : options = {};
23005
- (_a = options.audio) !== null && _a !== void 0 ? _a : options.audio = true;
23006
- (_b = options.video) !== null && _b !== void 0 ? _b : options.video = true;
23064
+ (_a = options.audio) !== null && _a !== void 0 ? _a : options.audio = {
23065
+ deviceId: 'default'
23066
+ };
23067
+ (_b = options.video) !== null && _b !== void 0 ? _b : options.video = {
23068
+ deviceId: 'default'
23069
+ };
23007
23070
  const {
23008
23071
  audioProcessor,
23009
23072
  videoProcessor
@@ -23519,5 +23582,5 @@ function isFacingModeValue(item) {
23519
23582
  return item === undefined || allowedValues.includes(item);
23520
23583
  }
23521
23584
 
23522
- export { AudioPresets, 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, h as Mutex, NegotiationError, Participant, ParticipantEvent, ParticipantInfo_Kind as ParticipantKind, PublishDataError, RemoteAudioTrack, RemoteParticipant, RemoteTrack, RemoteTrackPublication, RemoteVideoTrack, Room, RoomEvent, RpcError, ScreenSharePresets, SignalRequestError, SubscriptionError, Track, TrackEvent, TrackInvalidError, TrackPublication, 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, isBackupCodec, isBrowserSupported, isE2EESupported, isInsertableStreamSupported, isScriptTransformSupported, isVideoFrame, needsRbspUnescaping, parseRbsp, protocolVersion, ratchet, setLogExtension, setLogLevel, supportsAV1, supportsAdaptiveStream, supportsDynacast, supportsVP9, version, videoCodecs, writeRbsp };
23585
+ export { AudioPresets, 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, RemoteAudioTrack, RemoteParticipant, RemoteTrack, RemoteTrackPublication, RemoteVideoTrack, Room, RoomEvent, RpcError, ScreenSharePresets, SignalRequestError, SubscriptionError, Track, TrackEvent, TrackInvalidError, TrackPublication, 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, isBackupCodec, isBrowserSupported, isE2EESupported, isInsertableStreamSupported, isScriptTransformSupported, isVideoFrame, needsRbspUnescaping, parseRbsp, protocolVersion, ratchet, setLogExtension, setLogLevel, supportsAV1, supportsAdaptiveStream, supportsDynacast, supportsVP9, version, videoCodecs, writeRbsp };
23523
23586
  //# sourceMappingURL=livekit-client.esm.mjs.map