livekit-client 1.11.0 → 1.11.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 (34) hide show
  1. package/dist/livekit-client.esm.mjs +139 -89
  2. package/dist/livekit-client.esm.mjs.map +1 -1
  3. package/dist/livekit-client.umd.js +1 -1
  4. package/dist/livekit-client.umd.js.map +1 -1
  5. package/dist/src/room/Room.d.ts +5 -4
  6. package/dist/src/room/Room.d.ts.map +1 -1
  7. package/dist/src/room/events.d.ts +6 -1
  8. package/dist/src/room/events.d.ts.map +1 -1
  9. package/dist/src/room/track/LocalAudioTrack.d.ts +1 -1
  10. package/dist/src/room/track/LocalAudioTrack.d.ts.map +1 -1
  11. package/dist/src/room/track/LocalTrack.d.ts +2 -2
  12. package/dist/src/room/track/LocalTrack.d.ts.map +1 -1
  13. package/dist/src/room/track/LocalVideoTrack.d.ts +1 -1
  14. package/dist/src/room/track/LocalVideoTrack.d.ts.map +1 -1
  15. package/dist/src/room/track/RemoteVideoTrack.d.ts +3 -1
  16. package/dist/src/room/track/RemoteVideoTrack.d.ts.map +1 -1
  17. package/dist/src/room/utils.d.ts +1 -0
  18. package/dist/src/room/utils.d.ts.map +1 -1
  19. package/dist/ts4.2/src/room/Room.d.ts +5 -4
  20. package/dist/ts4.2/src/room/events.d.ts +6 -1
  21. package/dist/ts4.2/src/room/track/LocalAudioTrack.d.ts +1 -1
  22. package/dist/ts4.2/src/room/track/LocalTrack.d.ts +2 -2
  23. package/dist/ts4.2/src/room/track/LocalVideoTrack.d.ts +1 -1
  24. package/dist/ts4.2/src/room/track/RemoteVideoTrack.d.ts +3 -1
  25. package/dist/ts4.2/src/room/utils.d.ts +1 -0
  26. package/package.json +1 -1
  27. package/src/room/Room.test.ts +29 -0
  28. package/src/room/Room.ts +55 -10
  29. package/src/room/events.ts +6 -0
  30. package/src/room/track/LocalAudioTrack.ts +4 -3
  31. package/src/room/track/LocalTrack.ts +13 -10
  32. package/src/room/track/LocalVideoTrack.ts +8 -4
  33. package/src/room/track/RemoteVideoTrack.ts +3 -8
  34. package/src/room/utils.ts +23 -0
@@ -14187,7 +14187,7 @@ function getMatch(exp, ua) {
14187
14187
  return match && match.length >= id && match[id] || '';
14188
14188
  }
14189
14189
 
14190
- var version$1 = "1.11.0";
14190
+ var version$1 = "1.11.2";
14191
14191
 
14192
14192
  const version = version$1;
14193
14193
  const protocolVersion = 9;
@@ -14636,6 +14636,27 @@ class Mutex {
14636
14636
  return willUnlock;
14637
14637
  }
14638
14638
  }
14639
+ function unwrapConstraint(constraint) {
14640
+ if (typeof constraint === 'string') {
14641
+ return constraint;
14642
+ }
14643
+ if (Array.isArray(constraint)) {
14644
+ return constraint[0];
14645
+ }
14646
+ if (constraint.exact) {
14647
+ if (Array.isArray(constraint.exact)) {
14648
+ return constraint.exact[0];
14649
+ }
14650
+ return constraint.exact;
14651
+ }
14652
+ if (constraint.ideal) {
14653
+ if (Array.isArray(constraint.ideal)) {
14654
+ return constraint.ideal[0];
14655
+ }
14656
+ return constraint.ideal;
14657
+ }
14658
+ throw Error('could not unwrap constraint');
14659
+ }
14639
14660
 
14640
14661
  var QueueTaskStatus;
14641
14662
  (function (QueueTaskStatus) {
@@ -16734,6 +16755,11 @@ var RoomEvent;
16734
16755
  * args: (isLow: boolean, kind: [[DataPacket_Kind]])
16735
16756
  */
16736
16757
  RoomEvent["DCBufferStatusChanged"] = "dcBufferStatusChanged";
16758
+ /**
16759
+ * Triggered by a call to room.switchActiveDevice
16760
+ * args: (kind: MediaDeviceKind, deviceId: string)
16761
+ */
16762
+ RoomEvent["ActiveDeviceChanged"] = "activeDeviceChanged";
16737
16763
  })(RoomEvent || (RoomEvent = {}));
16738
16764
  var ParticipantEvent;
16739
16765
  (function (ParticipantEvent) {
@@ -18377,8 +18403,56 @@ class LocalTrack extends Track {
18377
18403
  if (this.isInBackground) {
18378
18404
  this.reacquireTrack = true;
18379
18405
  }
18406
+ this._mediaStreamTrack.removeEventListener('mute', this.pauseUpstream);
18407
+ this._mediaStreamTrack.removeEventListener('unmute', this.resumeUpstream);
18380
18408
  this.emit(TrackEvent.Ended, this);
18381
18409
  };
18410
+ /**
18411
+ * pauses publishing to the server without disabling the local MediaStreamTrack
18412
+ * this is used to display a user's own video locally while pausing publishing to
18413
+ * the server.
18414
+ * this API is unsupported on Safari < 12 due to a bug
18415
+ **/
18416
+ this.pauseUpstream = () => __awaiter(this, void 0, void 0, function* () {
18417
+ const unlock = yield this.pauseUpstreamLock.lock();
18418
+ try {
18419
+ if (this._isUpstreamPaused === true) {
18420
+ return;
18421
+ }
18422
+ if (!this.sender) {
18423
+ livekitLogger.warn('unable to pause upstream for an unpublished track');
18424
+ return;
18425
+ }
18426
+ this._isUpstreamPaused = true;
18427
+ this.emit(TrackEvent.UpstreamPaused, this);
18428
+ const browser = getBrowser();
18429
+ if ((browser === null || browser === void 0 ? void 0 : browser.name) === 'Safari' && compareVersions(browser.version, '12.0') < 0) {
18430
+ // https://bugs.webkit.org/show_bug.cgi?id=184911
18431
+ throw new DeviceUnsupportedError('pauseUpstream is not supported on Safari < 12.');
18432
+ }
18433
+ yield this.sender.replaceTrack(null);
18434
+ } finally {
18435
+ unlock();
18436
+ }
18437
+ });
18438
+ this.resumeUpstream = () => __awaiter(this, void 0, void 0, function* () {
18439
+ const unlock = yield this.pauseUpstreamLock.lock();
18440
+ try {
18441
+ if (this._isUpstreamPaused === false) {
18442
+ return;
18443
+ }
18444
+ if (!this.sender) {
18445
+ livekitLogger.warn('unable to resume upstream for an unpublished track');
18446
+ return;
18447
+ }
18448
+ this._isUpstreamPaused = false;
18449
+ this.emit(TrackEvent.UpstreamResumed, this);
18450
+ // this operation is noop if mediastreamtrack is already being sent
18451
+ yield this.sender.replaceTrack(this._mediaStreamTrack);
18452
+ } finally {
18453
+ unlock();
18454
+ }
18455
+ });
18382
18456
  this.reacquireTrack = false;
18383
18457
  this.providedByUser = userProvidedTrack;
18384
18458
  this.muteLock = new Mutex();
@@ -18444,10 +18518,7 @@ class LocalTrack extends Track {
18444
18518
  // the track is "muted"
18445
18519
  // note this is different from LocalTrack.mute because we do not want to
18446
18520
  // touch MediaStreamTrack.enabled
18447
- newTrack.addEventListener('mute', () => {
18448
- livekitLogger.info('pausing upstream due to device mute');
18449
- this.pauseUpstream();
18450
- });
18521
+ newTrack.addEventListener('mute', this.pauseUpstream);
18451
18522
  newTrack.addEventListener('unmute', this.resumeUpstream);
18452
18523
  this.constraints = newTrack.getConstraints();
18453
18524
  }
@@ -18519,7 +18590,7 @@ class LocalTrack extends Track {
18519
18590
  throw new TrackInvalidError('unable to replace an unpublished track');
18520
18591
  }
18521
18592
  livekitLogger.debug('replace MediaStreamTrack');
18522
- this.setMediaStreamTrack(track);
18593
+ yield this.setMediaStreamTrack(track);
18523
18594
  // this must be synced *after* setting mediaStreamTrack above, since it relies
18524
18595
  // on the previous state in order to cleanup
18525
18596
  this.providedByUser = userProvidedTrack;
@@ -18559,7 +18630,7 @@ class LocalTrack extends Track {
18559
18630
  const newTrack = mediaStream.getTracks()[0];
18560
18631
  newTrack.addEventListener('ended', this.handleEnded);
18561
18632
  livekitLogger.debug('re-acquired MediaStreamTrack');
18562
- this.setMediaStreamTrack(newTrack);
18633
+ yield this.setMediaStreamTrack(newTrack);
18563
18634
  this.constraints = constraints;
18564
18635
  if (this.processor) {
18565
18636
  const processor = this.processor;
@@ -18605,59 +18676,12 @@ class LocalTrack extends Track {
18605
18676
  stop() {
18606
18677
  var _a;
18607
18678
  super.stop();
18679
+ this._mediaStreamTrack.removeEventListener('ended', this.handleEnded);
18680
+ this._mediaStreamTrack.removeEventListener('mute', this.pauseUpstream);
18681
+ this._mediaStreamTrack.removeEventListener('unmute', this.resumeUpstream);
18608
18682
  (_a = this.processor) === null || _a === void 0 ? void 0 : _a.destroy();
18609
18683
  this.processor = undefined;
18610
18684
  }
18611
- /**
18612
- * pauses publishing to the server without disabling the local MediaStreamTrack
18613
- * this is used to display a user's own video locally while pausing publishing to
18614
- * the server.
18615
- * this API is unsupported on Safari < 12 due to a bug
18616
- **/
18617
- pauseUpstream() {
18618
- return __awaiter(this, void 0, void 0, function* () {
18619
- const unlock = yield this.pauseUpstreamLock.lock();
18620
- try {
18621
- if (this._isUpstreamPaused === true) {
18622
- return;
18623
- }
18624
- if (!this.sender) {
18625
- livekitLogger.warn('unable to pause upstream for an unpublished track');
18626
- return;
18627
- }
18628
- this._isUpstreamPaused = true;
18629
- this.emit(TrackEvent.UpstreamPaused, this);
18630
- const browser = getBrowser();
18631
- if ((browser === null || browser === void 0 ? void 0 : browser.name) === 'Safari' && compareVersions(browser.version, '12.0') < 0) {
18632
- // https://bugs.webkit.org/show_bug.cgi?id=184911
18633
- throw new DeviceUnsupportedError('pauseUpstream is not supported on Safari < 12.');
18634
- }
18635
- yield this.sender.replaceTrack(null);
18636
- } finally {
18637
- unlock();
18638
- }
18639
- });
18640
- }
18641
- resumeUpstream() {
18642
- return __awaiter(this, void 0, void 0, function* () {
18643
- const unlock = yield this.pauseUpstreamLock.lock();
18644
- try {
18645
- if (this._isUpstreamPaused === false) {
18646
- return;
18647
- }
18648
- if (!this.sender) {
18649
- livekitLogger.warn('unable to resume upstream for an unpublished track');
18650
- return;
18651
- }
18652
- this._isUpstreamPaused = false;
18653
- this.emit(TrackEvent.UpstreamResumed, this);
18654
- // this operation is noop if mediastreamtrack is already being sent
18655
- yield this.sender.replaceTrack(this._mediaStreamTrack);
18656
- } finally {
18657
- unlock();
18658
- }
18659
- });
18660
- }
18661
18685
  /**
18662
18686
  * Sets a processor on this track.
18663
18687
  * See https://github.com/livekit/track-processors-js for example usage
@@ -18768,12 +18792,13 @@ class LocalAudioTrack extends LocalTrack {
18768
18792
  setDeviceId(deviceId) {
18769
18793
  return __awaiter(this, void 0, void 0, function* () {
18770
18794
  if (this.constraints.deviceId === deviceId) {
18771
- return;
18795
+ return true;
18772
18796
  }
18773
18797
  this.constraints.deviceId = deviceId;
18774
18798
  if (!this.isMuted) {
18775
18799
  yield this.restartTrack();
18776
18800
  }
18801
+ return unwrapConstraint(deviceId) === this.mediaStreamTrack.getSettings().deviceId;
18777
18802
  });
18778
18803
  }
18779
18804
  mute() {
@@ -19361,8 +19386,8 @@ class LocalVideoTrack extends LocalTrack {
19361
19386
  }
19362
19387
  setDeviceId(deviceId) {
19363
19388
  return __awaiter(this, void 0, void 0, function* () {
19364
- if (this.constraints.deviceId === deviceId) {
19365
- return;
19389
+ if (this.constraints.deviceId === deviceId && this._mediaStreamTrack.getSettings().deviceId === unwrapConstraint(deviceId)) {
19390
+ return true;
19366
19391
  }
19367
19392
  this.constraints.deviceId = deviceId;
19368
19393
  // when video is muted, underlying media stream track is stopped and
@@ -19370,6 +19395,7 @@ class LocalVideoTrack extends LocalTrack {
19370
19395
  if (!this.isMuted) {
19371
19396
  yield this.restartTrack();
19372
19397
  }
19398
+ return unwrapConstraint(deviceId) === this._mediaStreamTrack.getSettings().deviceId;
19373
19399
  });
19374
19400
  }
19375
19401
  restartTrack(options) {
@@ -19864,7 +19890,6 @@ class RemoteVideoTrack extends RemoteTrack {
19864
19890
  constructor(mediaTrack, sid, receiver, adaptiveStreamSettings) {
19865
19891
  super(mediaTrack, sid, Track.Kind.Video, receiver);
19866
19892
  this.elementInfos = [];
19867
- this.isObserved = false;
19868
19893
  this.monitorReceiver = () => __awaiter(this, void 0, void 0, function* () {
19869
19894
  if (!this.receiver) {
19870
19895
  this._currentBitrate = 0;
@@ -19884,10 +19909,10 @@ class RemoteVideoTrack extends RemoteTrack {
19884
19909
  get isAdaptiveStream() {
19885
19910
  return this.adaptiveStreamSettings !== undefined;
19886
19911
  }
19912
+ /**
19913
+ * Note: When using adaptiveStream, you need to use remoteVideoTrack.attach() to add the track to a HTMLVideoElement, otherwise your video tracks might never start
19914
+ */
19887
19915
  get mediaStreamTrack() {
19888
- if (this.isAdaptiveStream && !this.isObserved) {
19889
- livekitLogger.warn('When using adaptiveStream, you need to use remoteVideoTrack.attach() to add the track to a HTMLVideoElement, otherwise your video tracks might never start');
19890
- }
19891
19916
  return this._mediaStreamTrack;
19892
19917
  }
19893
19918
  /** @internal */
@@ -19936,7 +19961,6 @@ class RemoteVideoTrack extends RemoteTrack {
19936
19961
  // the tab comes into focus for the first time.
19937
19962
  this.debouncedHandleResize();
19938
19963
  this.updateVisibility();
19939
- this.isObserved = true;
19940
19964
  } else {
19941
19965
  livekitLogger.warn('visibility resize observer not triggered');
19942
19966
  }
@@ -22064,6 +22088,7 @@ class Room extends EventEmitter {
22064
22088
  */
22065
22089
  constructor(options) {
22066
22090
  var _this;
22091
+ var _a;
22067
22092
  super();
22068
22093
  _this = this;
22069
22094
  this.state = ConnectionState.Disconnected;
@@ -22089,7 +22114,7 @@ class Room extends EventEmitter {
22089
22114
  this.setAndEmitConnectionState(ConnectionState.Connecting);
22090
22115
  const urlProvider = new RegionUrlProvider(url, token);
22091
22116
  const connectFn = (resolve, reject, regionUrl) => __awaiter(this, void 0, void 0, function* () {
22092
- var _a;
22117
+ var _b;
22093
22118
  if (this.abortController) {
22094
22119
  this.abortController.abort();
22095
22120
  }
@@ -22104,7 +22129,7 @@ class Room extends EventEmitter {
22104
22129
  if (isCloud(new URL(url)) && e instanceof ConnectionError && e.reason !== 3 /* ConnectionErrorReason.Cancelled */) {
22105
22130
  let nextUrl = null;
22106
22131
  try {
22107
- nextUrl = yield urlProvider.getNextBestRegionUrl((_a = this.abortController) === null || _a === void 0 ? void 0 : _a.signal);
22132
+ nextUrl = yield urlProvider.getNextBestRegionUrl((_b = this.abortController) === null || _b === void 0 ? void 0 : _b.signal);
22108
22133
  } catch (error) {
22109
22134
  if (error instanceof ConnectionError && (error.status === 401 || error.reason === 3 /* ConnectionErrorReason.Cancelled */)) {
22110
22135
  reject(error);
@@ -22166,7 +22191,7 @@ class Room extends EventEmitter {
22166
22191
  }
22167
22192
  };
22168
22193
  this.attemptConnection = (url, token, opts, abortController) => __awaiter(this, void 0, void 0, function* () {
22169
- var _b;
22194
+ var _c;
22170
22195
  if (this.state === ConnectionState.Reconnecting) {
22171
22196
  livekitLogger.info('Reconnection attempt replaced by new connection attempt');
22172
22197
  // make sure we close and recreate the existing engine in order to get rid of any potentially ongoing reconnection attempts
@@ -22225,7 +22250,7 @@ class Room extends EventEmitter {
22225
22250
  }
22226
22251
  if (isWeb()) {
22227
22252
  document.addEventListener('freeze', this.onPageLeave);
22228
- (_b = navigator.mediaDevices) === null || _b === void 0 ? void 0 : _b.addEventListener('devicechange', this.handleDeviceChange);
22253
+ (_c = navigator.mediaDevices) === null || _c === void 0 ? void 0 : _c.addEventListener('devicechange', this.handleDeviceChange);
22229
22254
  }
22230
22255
  this.setAndEmitConnectionState(ConnectionState.Connected);
22231
22256
  this.emit(RoomEvent.Connected);
@@ -22237,7 +22262,7 @@ class Room extends EventEmitter {
22237
22262
  this.disconnect = function () {
22238
22263
  let stopTracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
22239
22264
  return __awaiter(_this, void 0, void 0, function* () {
22240
- var _c, _d, _e, _f;
22265
+ var _d, _e, _f, _g;
22241
22266
  const unlock = yield this.disconnectLock.lock();
22242
22267
  try {
22243
22268
  if (this.state === ConnectionState.Disconnected) {
@@ -22250,13 +22275,13 @@ class Room extends EventEmitter {
22250
22275
  if (this.state === ConnectionState.Connecting || this.state === ConnectionState.Reconnecting) {
22251
22276
  // try aborting pending connection attempt
22252
22277
  livekitLogger.warn('abort connection attempt');
22253
- (_c = this.abortController) === null || _c === void 0 ? void 0 : _c.abort();
22278
+ (_d = this.abortController) === null || _d === void 0 ? void 0 : _d.abort();
22254
22279
  // in case the abort controller didn't manage to cancel the connection attempt, reject the connect promise explicitly
22255
- (_e = (_d = this.connectFuture) === null || _d === void 0 ? void 0 : _d.reject) === null || _e === void 0 ? void 0 : _e.call(_d, new ConnectionError('Client initiated disconnect'));
22280
+ (_f = (_e = this.connectFuture) === null || _e === void 0 ? void 0 : _e.reject) === null || _f === void 0 ? void 0 : _f.call(_e, new ConnectionError('Client initiated disconnect'));
22256
22281
  this.connectFuture = undefined;
22257
22282
  }
22258
22283
  // send leave
22259
- if ((_f = this.engine) === null || _f === void 0 ? void 0 : _f.client.isConnected) {
22284
+ if ((_g = this.engine) === null || _g === void 0 ? void 0 : _g.client.isConnected) {
22260
22285
  yield this.engine.client.sendLeave();
22261
22286
  }
22262
22287
  // close engine (also closes client)
@@ -22326,7 +22351,7 @@ class Room extends EventEmitter {
22326
22351
  livekitLogger.debug("fully reconnected to server", {
22327
22352
  region: joinResponse.serverRegion
22328
22353
  });
22329
- } catch (_g) {
22354
+ } catch (_h) {
22330
22355
  // reconnection failed, handleDisconnect is being invoked already, just return here
22331
22356
  return;
22332
22357
  }
@@ -22551,6 +22576,16 @@ class Room extends EventEmitter {
22551
22576
  this.options.publishDefaults = Object.assign(Object.assign({}, publishDefaults), options === null || options === void 0 ? void 0 : options.publishDefaults);
22552
22577
  this.maybeCreateEngine();
22553
22578
  this.disconnectLock = new Mutex();
22579
+ this.activeDeviceMap = new Map();
22580
+ if (this.options.videoCaptureDefaults.deviceId) {
22581
+ this.activeDeviceMap.set('videoinput', unwrapConstraint(this.options.videoCaptureDefaults.deviceId));
22582
+ }
22583
+ if (this.options.audioCaptureDefaults.deviceId) {
22584
+ this.activeDeviceMap.set('audioinput', unwrapConstraint(this.options.audioCaptureDefaults.deviceId));
22585
+ }
22586
+ if ((_a = this.options.audioOutput) === null || _a === void 0 ? void 0 : _a.deviceId) {
22587
+ this.switchActiveDevice('audiooutput', unwrapConstraint(this.options.audioOutput.deviceId));
22588
+ }
22554
22589
  this.localParticipant = new LocalParticipant('', '', this.engine, this.options);
22555
22590
  }
22556
22591
  /**
@@ -22816,15 +22851,16 @@ class Room extends EventEmitter {
22816
22851
  }
22817
22852
  /**
22818
22853
  * Returns the active audio output device used in this room.
22819
- *
22820
- * Note: to get the active `audioinput` or `videoinput` use [[LocalTrack.getDeviceId()]]
22821
- *
22822
22854
  * @return the previously successfully set audio output device ID or an empty string if the default device is used.
22855
+ * @deprecated use `getActiveDevice('audiooutput')` instead
22823
22856
  */
22824
22857
  getActiveAudioOutputDevice() {
22825
22858
  var _a, _b;
22826
22859
  return (_b = (_a = this.options.audioOutput) === null || _a === void 0 ? void 0 : _a.deviceId) !== null && _b !== void 0 ? _b : '';
22827
22860
  }
22861
+ getActiveDevice(kind) {
22862
+ return this.activeDeviceMap.get(kind);
22863
+ }
22828
22864
  /**
22829
22865
  * Switches all active devices used in this room to the given device.
22830
22866
  *
@@ -22837,21 +22873,24 @@ class Room extends EventEmitter {
22837
22873
  */
22838
22874
  switchActiveDevice(kind, deviceId) {
22839
22875
  let exact = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
22840
- var _a;
22841
- var _b;
22876
+ var _a, _b;
22877
+ var _c;
22842
22878
  return __awaiter(this, void 0, void 0, function* () {
22879
+ let deviceHasChanged = false;
22880
+ let success = true;
22843
22881
  const deviceConstraint = exact ? {
22844
22882
  exact: deviceId
22845
22883
  } : deviceId;
22846
22884
  if (kind === 'audioinput') {
22847
22885
  const prevDeviceId = this.options.audioCaptureDefaults.deviceId;
22848
22886
  this.options.audioCaptureDefaults.deviceId = deviceConstraint;
22887
+ deviceHasChanged = prevDeviceId !== deviceConstraint;
22849
22888
  const tracks = Array.from(this.localParticipant.audioTracks.values()).filter(track => track.source === Track.Source.Microphone);
22850
22889
  try {
22851
- yield Promise.all(tracks.map(t => {
22890
+ success = (yield Promise.all(tracks.map(t => {
22852
22891
  var _a;
22853
22892
  return (_a = t.audioTrack) === null || _a === void 0 ? void 0 : _a.setDeviceId(deviceConstraint);
22854
- }));
22893
+ }))).every(val => val === true);
22855
22894
  } catch (e) {
22856
22895
  this.options.audioCaptureDefaults.deviceId = prevDeviceId;
22857
22896
  throw e;
@@ -22859,33 +22898,44 @@ class Room extends EventEmitter {
22859
22898
  } else if (kind === 'videoinput') {
22860
22899
  const prevDeviceId = this.options.videoCaptureDefaults.deviceId;
22861
22900
  this.options.videoCaptureDefaults.deviceId = deviceConstraint;
22901
+ deviceHasChanged = prevDeviceId !== deviceConstraint;
22862
22902
  const tracks = Array.from(this.localParticipant.videoTracks.values()).filter(track => track.source === Track.Source.Camera);
22863
22903
  try {
22864
- yield Promise.all(tracks.map(t => {
22904
+ success = (yield Promise.all(tracks.map(t => {
22865
22905
  var _a;
22866
22906
  return (_a = t.videoTrack) === null || _a === void 0 ? void 0 : _a.setDeviceId(deviceConstraint);
22867
- }));
22907
+ }))).every(val => val === true);
22868
22908
  } catch (e) {
22869
22909
  this.options.videoCaptureDefaults.deviceId = prevDeviceId;
22870
22910
  throw e;
22871
22911
  }
22872
22912
  } else if (kind === 'audiooutput') {
22873
- // TODO add support for webaudio mix once the API becomes available https://github.com/WebAudio/web-audio-api/pull/2498
22874
- if (!supportsSetSinkId()) {
22913
+ if (!supportsSetSinkId() && !this.options.expWebAudioMix || this.audioContext && !('setSinkId' in this.audioContext)) {
22875
22914
  throw new Error('cannot switch audio output, setSinkId not supported');
22876
22915
  }
22877
- (_a = (_b = this.options).audioOutput) !== null && _a !== void 0 ? _a : _b.audioOutput = {};
22916
+ (_a = (_c = this.options).audioOutput) !== null && _a !== void 0 ? _a : _c.audioOutput = {};
22878
22917
  const prevDeviceId = this.options.audioOutput.deviceId;
22879
22918
  this.options.audioOutput.deviceId = deviceId;
22919
+ deviceHasChanged = prevDeviceId !== deviceConstraint;
22880
22920
  try {
22881
- yield Promise.all(Array.from(this.participants.values()).map(p => p.setAudioOutput({
22882
- deviceId
22883
- })));
22921
+ if (this.options.expWebAudioMix) {
22922
+ // @ts-expect-error setSinkId is not yet in the typescript type of AudioContext
22923
+ (_b = this.audioContext) === null || _b === void 0 ? void 0 : _b.setSinkId(deviceId);
22924
+ } else {
22925
+ yield Promise.all(Array.from(this.participants.values()).map(p => p.setAudioOutput({
22926
+ deviceId
22927
+ })));
22928
+ }
22884
22929
  } catch (e) {
22885
22930
  this.options.audioOutput.deviceId = prevDeviceId;
22886
22931
  throw e;
22887
22932
  }
22888
22933
  }
22934
+ if (deviceHasChanged && success) {
22935
+ this.activeDeviceMap.set(kind, deviceId);
22936
+ this.emit(RoomEvent.ActiveDeviceChanged, kind, deviceId);
22937
+ }
22938
+ return success;
22889
22939
  });
22890
22940
  }
22891
22941
  setupLocalParticipantEvents() {