livekit-client 2.7.5 → 2.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) 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 +158 -92
  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/RemoteAudioTrack.d.ts.map +1 -1
  30. package/dist/src/room/track/RemoteVideoTrack.d.ts.map +1 -1
  31. package/dist/src/room/track/Track.d.ts.map +1 -1
  32. package/dist/src/room/track/TrackPublication.d.ts.map +1 -1
  33. package/dist/src/room/track/utils.d.ts.map +1 -1
  34. package/dist/ts4.2/src/room/DeviceManager.d.ts +2 -0
  35. package/dist/ts4.2/src/room/Room.d.ts +1 -0
  36. package/dist/ts4.2/src/room/track/LocalAudioTrack.d.ts +0 -1
  37. package/dist/ts4.2/src/room/track/LocalTrack.d.ts +2 -0
  38. package/dist/ts4.2/src/room/track/LocalVideoTrack.d.ts +0 -1
  39. package/package.json +16 -15
  40. package/src/e2ee/worker/tsconfig.json +1 -1
  41. package/src/room/DeviceManager.ts +9 -2
  42. package/src/room/Room.ts +75 -10
  43. package/src/room/defaults.ts +2 -0
  44. package/src/room/participant/LocalParticipant.ts +15 -4
  45. package/src/room/participant/publishUtils.ts +14 -4
  46. package/src/room/track/LocalAudioTrack.ts +0 -16
  47. package/src/room/track/LocalTrack.ts +23 -1
  48. package/src/room/track/LocalVideoTrack.ts +1 -19
  49. package/src/room/track/RemoteAudioTrack.ts +1 -0
  50. package/src/room/track/RemoteVideoTrack.ts +1 -0
  51. package/src/room/track/create.ts +2 -2
  52. package/src/room/track/utils.test.ts +4 -12
  53. 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.1";
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);
@@ -18303,6 +18314,7 @@ class RemoteAudioTrack extends RemoteTrack {
18303
18314
  if (v.type === 'inbound-rtp') {
18304
18315
  receiverStats = {
18305
18316
  type: 'audio',
18317
+ streamId: v.id,
18306
18318
  timestamp: v.timestamp,
18307
18319
  jitter: v.jitter,
18308
18320
  bytesReceived: v.bytesReceived,
@@ -18449,6 +18461,7 @@ class RemoteVideoTrack extends RemoteTrack {
18449
18461
  codecID = v.codecId;
18450
18462
  receiverStats = {
18451
18463
  type: 'video',
18464
+ streamId: v.id,
18452
18465
  framesDecoded: v.framesDecoded,
18453
18466
  framesDropped: v.framesDropped,
18454
18467
  framesReceived: v.framesReceived,
@@ -18748,7 +18761,7 @@ class TrackPublication extends eventsExports.EventEmitter {
18748
18761
  PermissionStatus["Allowed"] = "allowed";
18749
18762
  PermissionStatus["NotAllowed"] = "not_allowed";
18750
18763
  })(TrackPublication.PermissionStatus || (TrackPublication.PermissionStatus = {}));
18751
- })(TrackPublication || (TrackPublication = {}));
18764
+ })(TrackPublication);
18752
18765
 
18753
18766
  class LocalTrackPublication extends TrackPublication {
18754
18767
  get isUpstreamPaused() {
@@ -19322,7 +19335,7 @@ class LocalParticipant extends Participant {
19322
19335
  this.engine = engine;
19323
19336
  this.roomOptions = options;
19324
19337
  this.setupEngine(engine);
19325
- this.activeDeviceMap = new Map();
19338
+ this.activeDeviceMap = new Map([['audioinput', 'default'], ['videoinput', 'default'], ['audiooutput', 'default']]);
19326
19339
  this.pendingSignalRequests = new Map();
19327
19340
  }
19328
19341
  get lastCameraError() {
@@ -19535,6 +19548,16 @@ class LocalParticipant extends Participant {
19535
19548
  default:
19536
19549
  throw new TrackInvalidError(source);
19537
19550
  }
19551
+ } catch (e) {
19552
+ localTracks === null || localTracks === void 0 ? void 0 : localTracks.forEach(tr => {
19553
+ tr.stop();
19554
+ });
19555
+ if (e instanceof Error) {
19556
+ this.emit(ParticipantEvent.MediaDevicesError, e);
19557
+ }
19558
+ throw e;
19559
+ }
19560
+ try {
19538
19561
  const publishPromises = [];
19539
19562
  for (const localTrack of localTracks) {
19540
19563
  this.log.info('publishing track', Object.assign(Object.assign({}, this.logContext), getLogContextFromTrack(localTrack)));
@@ -19548,9 +19571,6 @@ class LocalParticipant extends Participant {
19548
19571
  localTracks === null || localTracks === void 0 ? void 0 : localTracks.forEach(tr => {
19549
19572
  tr.stop();
19550
19573
  });
19551
- if (e instanceof Error && !(e instanceof TrackInvalidError)) {
19552
- this.emit(ParticipantEvent.MediaDevicesError, e);
19553
- }
19554
19574
  throw e;
19555
19575
  } finally {
19556
19576
  this.pendingPublishing.delete(source);
@@ -21215,7 +21235,7 @@ class Room extends eventsExports.EventEmitter {
21215
21235
  */
21216
21236
  constructor(options) {
21217
21237
  var _this;
21218
- var _a, _b;
21238
+ var _a, _b, _c;
21219
21239
  super();
21220
21240
  _this = this;
21221
21241
  this.state = ConnectionState.Disconnected;
@@ -21384,7 +21404,7 @@ class Room extends eventsExports.EventEmitter {
21384
21404
  }
21385
21405
  };
21386
21406
  this.attemptConnection = (url, token, opts, abortController) => __awaiter(this, void 0, void 0, function* () {
21387
- var _a, _b, _c;
21407
+ var _a, _b;
21388
21408
  if (this.state === ConnectionState.Reconnecting || this.isResuming || ((_a = this.engine) === null || _a === void 0 ? void 0 : _a.pendingReconnect)) {
21389
21409
  this.log.info('Reconnection attempt replaced by new connection attempt', this.logContext);
21390
21410
  // make sure we close and recreate the existing engine in order to get rid of any potentially ongoing reconnection attempts
@@ -21446,7 +21466,6 @@ class Room extends eventsExports.EventEmitter {
21446
21466
  }
21447
21467
  if (isWeb()) {
21448
21468
  document.addEventListener('freeze', this.onPageLeave);
21449
- (_c = navigator.mediaDevices) === null || _c === void 0 ? void 0 : _c.addEventListener('devicechange', this.handleDeviceChange);
21450
21469
  }
21451
21470
  this.setAndEmitConnectionState(ConnectionState.Connected);
21452
21471
  this.emit(RoomEvent.Connected);
@@ -21829,13 +21848,41 @@ class Room extends eventsExports.EventEmitter {
21829
21848
  }
21830
21849
  };
21831
21850
  this.handleDeviceChange = () => __awaiter(this, void 0, void 0, function* () {
21851
+ var _a, _b;
21852
+ const previousDevices = DeviceManager.getInstance().previousDevices;
21832
21853
  // check for available devices, but don't request permissions in order to avoid prompts for kinds that haven't been used before
21833
21854
  const availableDevices = yield DeviceManager.getInstance().getDevices(undefined, false);
21855
+ const browser = getBrowser();
21856
+ if ((browser === null || browser === void 0 ? void 0 : browser.name) === 'Chrome' && browser.os !== 'iOS') {
21857
+ for (let availableDevice of availableDevices) {
21858
+ const previousDevice = previousDevices.find(info => info.deviceId === availableDevice.deviceId);
21859
+ if (previousDevice && previousDevice.label !== '' && previousDevice.kind === availableDevice.kind && previousDevice.label !== availableDevice.label) {
21860
+ // label has changed on device the same deviceId, indicating that the default device has changed on the OS level
21861
+ if (this.getActiveDevice(availableDevice.kind) === 'default') {
21862
+ // emit an active device change event only if the selected output device is actually on `default`
21863
+ this.emit(RoomEvent.ActiveDeviceChanged, availableDevice.kind, availableDevice.deviceId);
21864
+ }
21865
+ }
21866
+ }
21867
+ }
21834
21868
  // inputs are automatically handled via TrackEvent.Ended causing a TrackEvent.Restarted. Here we only need to worry about audiooutputs changing
21835
- const kinds = ['audiooutput'];
21869
+ const kinds = ['audiooutput', 'audioinput', 'videoinput'];
21836
21870
  for (let kind of kinds) {
21837
- // switch to first available device if previously active device is not available any more
21838
21871
  const devicesOfKind = availableDevices.filter(d => d.kind === kind);
21872
+ const activeDevice = this.getActiveDevice(kind);
21873
+ if (activeDevice === ((_a = previousDevices.filter(info => info.kind === kind)[0]) === null || _a === void 0 ? void 0 : _a.deviceId)) {
21874
+ // 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
21875
+ // 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
21876
+ if (devicesOfKind.length > 0 && ((_b = devicesOfKind[0]) === null || _b === void 0 ? void 0 : _b.deviceId) !== activeDevice) {
21877
+ yield this.switchActiveDevice(kind, devicesOfKind[0].deviceId);
21878
+ continue;
21879
+ }
21880
+ }
21881
+ if (kind === 'audioinput' && !isSafari() || kind === 'videoinput') {
21882
+ // airpods on Safari need special handling for audioinput as the track doesn't end as soon as you take them out
21883
+ continue;
21884
+ }
21885
+ // switch to first available device if previously active device is not available any more
21839
21886
  if (devicesOfKind.length > 0 && !devicesOfKind.find(deviceInfo => deviceInfo.deviceId === this.getActiveDevice(kind))) {
21840
21887
  yield this.switchActiveDevice(kind, devicesOfKind[0].deviceId);
21841
21888
  }
@@ -21895,7 +21942,7 @@ class Room extends eventsExports.EventEmitter {
21895
21942
  this.emit(RoomEvent.LocalAudioSilenceDetected, pub);
21896
21943
  }
21897
21944
  }
21898
- const deviceId = yield (_f = pub.track) === null || _f === void 0 ? void 0 : _f.getDeviceId();
21945
+ const deviceId = yield (_f = pub.track) === null || _f === void 0 ? void 0 : _f.getDeviceId(false);
21899
21946
  const deviceKind = sourceToKind(pub.source);
21900
21947
  if (deviceKind && deviceId && deviceId !== this.localParticipant.activeDeviceMap.get(deviceKind)) {
21901
21948
  this.localParticipant.activeDeviceMap.set(deviceKind, deviceId);
@@ -21939,7 +21986,7 @@ class Room extends eventsExports.EventEmitter {
21939
21986
  this.options.videoCaptureDefaults = Object.assign(Object.assign({}, videoDefaults), options === null || options === void 0 ? void 0 : options.videoCaptureDefaults);
21940
21987
  this.options.publishDefaults = Object.assign(Object.assign({}, publishDefaults), options === null || options === void 0 ? void 0 : options.publishDefaults);
21941
21988
  this.maybeCreateEngine();
21942
- this.disconnectLock = new h();
21989
+ this.disconnectLock = new _();
21943
21990
  this.localParticipant = new LocalParticipant('', '', this.engine, this.options);
21944
21991
  if (this.options.videoCaptureDefaults.deviceId) {
21945
21992
  this.localParticipant.activeDeviceMap.set('videoinput', unwrapConstraint(this.options.videoCaptureDefaults.deviceId));
@@ -21953,6 +22000,18 @@ class Room extends eventsExports.EventEmitter {
21953
22000
  if (this.options.e2ee) {
21954
22001
  this.setupE2EE();
21955
22002
  }
22003
+ if (isWeb()) {
22004
+ const abortController = new AbortController();
22005
+ // in order to catch device changes prior to room connection we need to register the event in the constructor
22006
+ (_c = navigator.mediaDevices) === null || _c === void 0 ? void 0 : _c.addEventListener('devicechange', this.handleDeviceChange, {
22007
+ signal: abortController.signal
22008
+ });
22009
+ if (Room.cleanupRegistry) {
22010
+ Room.cleanupRegistry.register(this, () => {
22011
+ abortController.abort();
22012
+ });
22013
+ }
22014
+ }
21956
22015
  }
21957
22016
  /**
21958
22017
  * @experimental
@@ -22321,17 +22380,17 @@ class Room extends eventsExports.EventEmitter {
22321
22380
  var _this3 = this;
22322
22381
  let exact = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
22323
22382
  return function* () {
22324
- var _a, _b, _c, _d, _e, _f;
22325
- var _g;
22326
- let deviceHasChanged = false;
22383
+ var _a, _b, _c, _d, _e, _f, _g;
22384
+ var _h;
22327
22385
  let success = true;
22386
+ let needsUpdateWithoutTracks = false;
22328
22387
  const deviceConstraint = exact ? {
22329
22388
  exact: deviceId
22330
22389
  } : deviceId;
22331
22390
  if (kind === 'audioinput') {
22391
+ needsUpdateWithoutTracks = _this3.localParticipant.audioTrackPublications.size === 0;
22332
22392
  const prevDeviceId = (_a = _this3.getActiveDevice(kind)) !== null && _a !== void 0 ? _a : _this3.options.audioCaptureDefaults.deviceId;
22333
22393
  _this3.options.audioCaptureDefaults.deviceId = deviceConstraint;
22334
- deviceHasChanged = prevDeviceId !== deviceConstraint;
22335
22394
  const tracks = Array.from(_this3.localParticipant.audioTrackPublications.values()).filter(track => track.source === Track.Source.Microphone);
22336
22395
  try {
22337
22396
  success = (yield Promise.all(tracks.map(t => {
@@ -22343,9 +22402,9 @@ class Room extends eventsExports.EventEmitter {
22343
22402
  throw e;
22344
22403
  }
22345
22404
  } else if (kind === 'videoinput') {
22405
+ needsUpdateWithoutTracks = _this3.localParticipant.videoTrackPublications.size === 0;
22346
22406
  const prevDeviceId = (_b = _this3.getActiveDevice(kind)) !== null && _b !== void 0 ? _b : _this3.options.videoCaptureDefaults.deviceId;
22347
22407
  _this3.options.videoCaptureDefaults.deviceId = deviceConstraint;
22348
- deviceHasChanged = prevDeviceId !== deviceConstraint;
22349
22408
  const tracks = Array.from(_this3.localParticipant.videoTrackPublications.values()).filter(track => track.source === Track.Source.Camera);
22350
22409
  try {
22351
22410
  success = (yield Promise.all(tracks.map(t => {
@@ -22364,10 +22423,9 @@ class Room extends eventsExports.EventEmitter {
22364
22423
  // setting `default` for web audio output doesn't work, so we need to normalize the id before
22365
22424
  deviceId = (_c = yield DeviceManager.getInstance().normalizeDeviceId('audiooutput', deviceId)) !== null && _c !== void 0 ? _c : '';
22366
22425
  }
22367
- (_d = (_g = _this3.options).audioOutput) !== null && _d !== void 0 ? _d : _g.audioOutput = {};
22426
+ (_d = (_h = _this3.options).audioOutput) !== null && _d !== void 0 ? _d : _h.audioOutput = {};
22368
22427
  const prevDeviceId = (_e = _this3.getActiveDevice(kind)) !== null && _e !== void 0 ? _e : _this3.options.audioOutput.deviceId;
22369
22428
  _this3.options.audioOutput.deviceId = deviceId;
22370
- deviceHasChanged = prevDeviceId !== deviceConstraint;
22371
22429
  try {
22372
22430
  if (_this3.options.webAudioMix) {
22373
22431
  // @ts-expect-error setSinkId is not yet in the typescript type of AudioContext
@@ -22383,8 +22441,9 @@ class Room extends eventsExports.EventEmitter {
22383
22441
  throw e;
22384
22442
  }
22385
22443
  }
22386
- if (deviceHasChanged && success) {
22387
- _this3.localParticipant.activeDeviceMap.set(kind, deviceId);
22444
+ if (needsUpdateWithoutTracks || kind === 'audiooutput') {
22445
+ // if there are not active tracks yet or we're switching audiooutput, we need to manually update the active device map here as changing audio output won't result in a track restart
22446
+ _this3.localParticipant.activeDeviceMap.set(kind, kind === 'audiooutput' && ((_g = _this3.options.audioOutput) === null || _g === void 0 ? void 0 : _g.deviceId) || deviceId);
22388
22447
  _this3.emit(RoomEvent.ActiveDeviceChanged, kind, deviceId);
22389
22448
  }
22390
22449
  return success;
@@ -22859,6 +22918,9 @@ class Room extends eventsExports.EventEmitter {
22859
22918
  return super.emit(event, ...args);
22860
22919
  }
22861
22920
  }
22921
+ Room.cleanupRegistry = typeof FinalizationRegistry !== 'undefined' && new FinalizationRegistry(cleanup => {
22922
+ cleanup();
22923
+ });
22862
22924
  function mapArgs(args) {
22863
22925
  return args.map(arg => {
22864
22926
  if (!arg) {
@@ -23002,8 +23064,12 @@ function createLocalTracks(options) {
23002
23064
  var _a, _b;
23003
23065
  // set default options to true
23004
23066
  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;
23067
+ (_a = options.audio) !== null && _a !== void 0 ? _a : options.audio = {
23068
+ deviceId: 'default'
23069
+ };
23070
+ (_b = options.video) !== null && _b !== void 0 ? _b : options.video = {
23071
+ deviceId: 'default'
23072
+ };
23007
23073
  const {
23008
23074
  audioProcessor,
23009
23075
  videoProcessor
@@ -23519,5 +23585,5 @@ function isFacingModeValue(item) {
23519
23585
  return item === undefined || allowedValues.includes(item);
23520
23586
  }
23521
23587
 
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 };
23588
+ 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
23589
  //# sourceMappingURL=livekit-client.esm.mjs.map