@whereby.com/media 6.0.0 → 7.0.1

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.
package/dist/index.cjs CHANGED
@@ -245,6 +245,7 @@ class Logger {
245
245
  }
246
246
  }
247
247
 
248
+ const logger$b = new Logger();
248
249
  const AUDIO_SETTINGS = {
249
250
  codecOptions: {
250
251
  opusDtx: true,
@@ -419,7 +420,14 @@ function getIsCodecDecodingPowerEfficient(codec) {
419
420
  }
420
421
  function sortCodecsByPowerEfficiency(codecs) {
421
422
  return __awaiter(this, void 0, void 0, function* () {
422
- const codecPowerEfficiencyEntries = yield Promise.all(codecs.map(({ mimeType }) => getIsCodecDecodingPowerEfficient(mimeType).then((val) => [mimeType, val])));
423
+ let codecPowerEfficiencyEntries;
424
+ try {
425
+ codecPowerEfficiencyEntries = yield Promise.all(codecs.map(({ mimeType }) => getIsCodecDecodingPowerEfficient(mimeType).then((val) => [mimeType, val])));
426
+ }
427
+ catch (error) {
428
+ logger$b.error(error);
429
+ return codecs;
430
+ }
423
431
  const codecPowerEfficiencies = Object.fromEntries(codecPowerEfficiencyEntries);
424
432
  const sorted = codecs.sort((a, b) => {
425
433
  const aPowerEfficient = codecPowerEfficiencies[a.mimeType];
@@ -2526,75 +2534,11 @@ var rtcManagerEvents = {
2526
2534
 
2527
2535
  var _a$2, _b$1;
2528
2536
  const adapter$2 = (_a$2 = adapterRaw.default) !== null && _a$2 !== void 0 ? _a$2 : adapterRaw;
2529
- const isSafari = ((_b$1 = adapter$2.browserDetails) === null || _b$1 === void 0 ? void 0 : _b$1.browser) === "safari";
2530
- const parseResolution = (res) => res.split(/[^\d]/g).map((n) => parseInt(n, 10));
2531
- function getMediaConstraints({ audioWanted, videoWanted, disableAEC, disableAGC, hd, lax, lowDataMode, preferredDeviceIds, resolution, simulcast, widescreen, }) {
2532
- let HIGH_HEIGHT = 480;
2533
- let LOW_HEIGHT = 240;
2534
- if (hd) {
2535
- HIGH_HEIGHT = lax || isSafari ? 720 : { min: 360, ideal: 720 };
2536
- }
2537
- if (simulcast) {
2538
- if (hd === false) {
2539
- HIGH_HEIGHT = 360;
2540
- LOW_HEIGHT = 270;
2541
- }
2542
- else {
2543
- LOW_HEIGHT = 360;
2544
- }
2545
- }
2546
- const { audioId, videoId } = preferredDeviceIds;
2547
- const constraints = Object.assign(Object.assign({}, (audioWanted && {
2548
- audio: Object.assign(Object.assign(Object.assign({}, (audioId ? { deviceId: audioId } : {})), (disableAGC ? { autoGainControl: false } : {})), (disableAEC ? { echoCancellation: false } : {})),
2549
- })), (videoWanted && {
2550
- video: Object.assign({ height: lowDataMode ? LOW_HEIGHT : HIGH_HEIGHT, frameRate: lowDataMode && !simulcast ? 15 : 24 }, (videoId ? { deviceId: videoId } : { facingMode: "user" })),
2551
- }));
2552
- if (lax) {
2553
- if (audioWanted && !audioId)
2554
- constraints.audio = true;
2555
- if (videoWanted && !videoId && typeof constraints.video === "object") {
2556
- delete constraints.video.facingMode;
2557
- }
2558
- return constraints;
2559
- }
2560
- if (videoWanted && typeof constraints.video === "object") {
2561
- if (resolution) {
2562
- const [w, h, fps] = parseResolution(resolution);
2563
- if (w)
2564
- constraints.video.width = { exact: w };
2565
- if (h)
2566
- constraints.video.height = { exact: h };
2567
- if (fps)
2568
- constraints.video.frameRate = { exact: fps };
2569
- delete constraints.video.facingMode;
2570
- }
2571
- else {
2572
- constraints.video.aspectRatio = widescreen ? 16 / 9 : 4 / 3;
2573
- }
2574
- }
2575
- return constraints;
2576
- }
2577
- function getConstraints({ devices, videoId, audioId, options, type = "ideal" }) {
2578
- const audioDevices = devices.filter((d) => d.kind === "audioinput");
2579
- const videoDevices = devices.filter((d) => d.kind === "videoinput");
2580
- const preferredDeviceIds = {};
2581
- if (typeof audioId === "string" && audioDevices.some((d) => d.deviceId === audioId)) {
2582
- preferredDeviceIds.audioId = { [type]: audioId };
2583
- }
2584
- if (typeof videoId === "string" && videoDevices.some((d) => d.deviceId === videoId)) {
2585
- preferredDeviceIds.videoId = { [type]: videoId };
2586
- }
2587
- const constraints = getMediaConstraints(Object.assign({ preferredDeviceIds, audioWanted: Boolean(audioId) && audioDevices.length > 0, videoWanted: Boolean(videoId) && videoDevices.length > 0 }, options));
2588
- return constraints;
2589
- }
2590
-
2591
- var _a$1, _b;
2592
- const adapter$1 = (_a$1 = adapterRaw.default) !== null && _a$1 !== void 0 ? _a$1 : adapterRaw;
2593
2537
  const logger$6 = new Logger();
2594
2538
  const ICE_PUBLIC_IP_GATHERING_TIMEOUT = 3 * 1000;
2595
2539
  const ICE_RESTART_DELAY = 2 * 1000;
2596
- const browserName$1 = (_b = adapter$1.browserDetails) === null || _b === void 0 ? void 0 : _b.browser;
2597
- const browserVersion = adapter$1.browserDetails.version;
2540
+ const browserName$1 = (_b$1 = adapter$2.browserDetails) === null || _b$1 === void 0 ? void 0 : _b$1.browser;
2541
+ const browserVersion = adapter$2.browserDetails.version;
2598
2542
  let unloading$1 = false;
2599
2543
  if (browserName$1 === "chrome") {
2600
2544
  window.document.addEventListener("beforeunload", () => {
@@ -2617,6 +2561,9 @@ class P2pRtcManager {
2617
2561
  this._features = features || {};
2618
2562
  this._isAudioOnlyMode = false;
2619
2563
  this._closed = false;
2564
+ this._fetchMediaServersTimer = null;
2565
+ this._stopCameraTimeout = null;
2566
+ this._icePublicIPGatheringTimeoutID = null;
2620
2567
  this.offerOptions = { offerToReceiveAudio: true, offerToReceiveVideo: true };
2621
2568
  this._audioTrackOnEnded = () => {
2622
2569
  rtcStats.sendEvent("audio_ended", { unloading: unloading$1 });
@@ -2818,6 +2765,7 @@ class P2pRtcManager {
2818
2765
  message: answer,
2819
2766
  });
2820
2767
  })).catch) === null || _b === void 0 ? void 0 : _b.call(_a, (e) => {
2768
+ logger$6.error(e);
2821
2769
  this.analytics.numPcOnOfferFailure++;
2822
2770
  });
2823
2771
  }),
@@ -3225,11 +3173,12 @@ class P2pRtcManager {
3225
3173
  return session;
3226
3174
  }
3227
3175
  _maybeRestartIce(clientId, session) {
3176
+ var _a;
3228
3177
  const pc = session.pc;
3229
3178
  if (!(pc.iceConnectionState === "disconnected" || pc.iceConnectionState === "failed")) {
3230
3179
  return;
3231
3180
  }
3232
- if (pc.localDescription.type === "offer") {
3181
+ if (((_a = pc.localDescription) === null || _a === void 0 ? void 0 : _a.type) === "offer") {
3233
3182
  session.wasEverConnected = false;
3234
3183
  session.relayCandidateSeen = false;
3235
3184
  session.serverReflexiveCandidateSeen = false;
@@ -3308,7 +3257,7 @@ class P2pRtcManager {
3308
3257
  }
3309
3258
  if (rtpAbsCaptureTimeOn)
3310
3259
  offer.sdp = addAbsCaptureTimeExtMap(offer.sdp);
3311
- if (browserName$1 === "firefox") {
3260
+ if (browserName$1 === "firefox" && browserVersion < 128) {
3312
3261
  offer.sdp = setCodecPreferenceSDP({
3313
3262
  sdp: offer.sdp,
3314
3263
  redOn,
@@ -3398,9 +3347,9 @@ class P2pRtcManager {
3398
3347
  const connection = event.target;
3399
3348
  switch (connection.iceGatheringState) {
3400
3349
  case "gathering":
3401
- if (this.icePublicIPGatheringTimeoutID)
3402
- clearTimeout(this.icePublicIPGatheringTimeoutID);
3403
- this.icePublicIPGatheringTimeoutID = setTimeout(() => {
3350
+ if (this._icePublicIPGatheringTimeoutID)
3351
+ clearTimeout(this._icePublicIPGatheringTimeoutID);
3352
+ this._icePublicIPGatheringTimeoutID = setTimeout(() => {
3404
3353
  if (!session.publicHostCandidateSeen &&
3405
3354
  !session.relayCandidateSeen &&
3406
3355
  !session.serverReflexiveCandidateSeen) {
@@ -3410,9 +3359,9 @@ class P2pRtcManager {
3410
3359
  }, ICE_PUBLIC_IP_GATHERING_TIMEOUT);
3411
3360
  break;
3412
3361
  case "complete":
3413
- if (this.icePublicIPGatheringTimeoutID)
3414
- clearTimeout(this.icePublicIPGatheringTimeoutID);
3415
- this.icePublicIPGatheringTimeoutID = undefined;
3362
+ if (this._icePublicIPGatheringTimeoutID)
3363
+ clearTimeout(this._icePublicIPGatheringTimeoutID);
3364
+ this._icePublicIPGatheringTimeoutID = null;
3416
3365
  break;
3417
3366
  }
3418
3367
  };
@@ -3561,9 +3510,13 @@ class P2pRtcManager {
3561
3510
  if (!["chrome", "safari"].includes(browserName$1)) {
3562
3511
  return;
3563
3512
  }
3513
+ if (this._stopCameraTimeout) {
3514
+ clearTimeout(this._stopCameraTimeout);
3515
+ this._stopCameraTimeout = null;
3516
+ }
3564
3517
  if (enable === false) {
3565
3518
  const stopCameraDelay = ((_a = localStream.getVideoTracks().find((t) => !t.enabled)) === null || _a === void 0 ? void 0 : _a.readyState) === "ended" ? 0 : 5000;
3566
- setTimeout(() => {
3519
+ this._stopCameraTimeout = setTimeout(() => {
3567
3520
  localStream.getVideoTracks().forEach((track) => {
3568
3521
  if (track.enabled === false) {
3569
3522
  track.stop();
@@ -3579,7 +3532,7 @@ class P2pRtcManager {
3579
3532
  }
3580
3533
  else {
3581
3534
  if (localStream.getVideoTracks().length === 0) {
3582
- const constraints = getConstraints(this._webrtcProvider.getMediaConstraints()).video;
3535
+ const constraints = this._webrtcProvider.getMediaConstraints().video;
3583
3536
  if (!constraints) {
3584
3537
  return;
3585
3538
  }
@@ -4448,10 +4401,10 @@ const STREAM_TYPES = {
4448
4401
  screenshare: "screenshare",
4449
4402
  };
4450
4403
 
4451
- var _a;
4452
- const adapter = (_a = adapterRaw.default) !== null && _a !== void 0 ? _a : adapterRaw;
4404
+ var _a$1;
4405
+ const adapter$1 = (_a$1 = adapterRaw.default) !== null && _a$1 !== void 0 ? _a$1 : adapterRaw;
4453
4406
  const logger$2 = new Logger();
4454
- const browserName = adapter.browserDetails.browser;
4407
+ const browserName = adapter$1.browserDetails.browser;
4455
4408
  let unloading = false;
4456
4409
  const RESTARTICE_ERROR_RETRY_THRESHOLD_IN_MS = 3500;
4457
4410
  const RESTARTICE_ERROR_MAX_RETRY_COUNT = 5;
@@ -5517,8 +5470,11 @@ class VegaRtcManager {
5517
5470
  if (!["chrome", "safari"].includes(browserName)) {
5518
5471
  return;
5519
5472
  }
5520
- if (!enable) {
5473
+ if (this._stopCameraTimeout) {
5521
5474
  clearTimeout(this._stopCameraTimeout);
5475
+ this._stopCameraTimeout = null;
5476
+ }
5477
+ if (!enable) {
5522
5478
  const stopCameraDelay = ((_a = localStream.getVideoTracks().find((t) => !t.enabled)) === null || _a === void 0 ? void 0 : _a.readyState) === "ended" ? 0 : 5000;
5523
5479
  this._stopCameraTimeout = setTimeout(() => {
5524
5480
  localStream.getVideoTracks().forEach((track) => {
@@ -5535,7 +5491,7 @@ class VegaRtcManager {
5535
5491
  }, stopCameraDelay);
5536
5492
  }
5537
5493
  else if (localStream.getVideoTracks().length === 0) {
5538
- const constraints = getConstraints(this._webrtcProvider.getMediaConstraints()).video;
5494
+ const constraints = this._webrtcProvider.getMediaConstraints().video;
5539
5495
  navigator.mediaDevices
5540
5496
  .getUserMedia({ video: constraints })
5541
5497
  .then((stream) => {
@@ -7060,6 +7016,73 @@ class BandwidthTester extends EventEmitter {
7060
7016
  }
7061
7017
  }
7062
7018
 
7019
+ var _a, _b;
7020
+ const adapter = (_a = adapterRaw.default) !== null && _a !== void 0 ? _a : adapterRaw;
7021
+ const isSafari = ((_b = adapter.browserDetails) === null || _b === void 0 ? void 0 : _b.browser) === "safari";
7022
+ const parseResolution = (res) => res.split(/[^\d]/g).map((n) => parseInt(n, 10));
7023
+ function getMediaConstraints({ disableAEC, disableAGC, hd, lax, lowDataMode, preferredDeviceIds, resolution, simulcast, widescreen, }) {
7024
+ var _a, _b;
7025
+ let HIGH_HEIGHT = 480;
7026
+ let LOW_HEIGHT = 240;
7027
+ if (hd) {
7028
+ HIGH_HEIGHT = lax || isSafari ? 720 : { min: 360, ideal: 720 };
7029
+ }
7030
+ if (simulcast) {
7031
+ if (hd === false) {
7032
+ HIGH_HEIGHT = 360;
7033
+ LOW_HEIGHT = 270;
7034
+ }
7035
+ else {
7036
+ LOW_HEIGHT = 360;
7037
+ }
7038
+ }
7039
+ const constraints = {
7040
+ audio: Object.assign({}, (preferredDeviceIds.audioId && { deviceId: preferredDeviceIds.audioId })),
7041
+ video: Object.assign(Object.assign({}, (preferredDeviceIds.videoId ? { deviceId: preferredDeviceIds.videoId } : { facingMode: "user" })), { height: lowDataMode ? LOW_HEIGHT : HIGH_HEIGHT, frameRate: lowDataMode && !simulcast ? 15 : 24 }),
7042
+ };
7043
+ if (lax) {
7044
+ if (!((_a = constraints.audio) === null || _a === void 0 ? void 0 : _a.deviceId))
7045
+ constraints.audio = true;
7046
+ (_b = constraints.video) === null || _b === void 0 ? true : delete _b.facingMode;
7047
+ return constraints;
7048
+ }
7049
+ if (resolution) {
7050
+ const [w, h, fps] = parseResolution(resolution);
7051
+ if (w)
7052
+ constraints.video.width = { exact: w };
7053
+ if (h)
7054
+ constraints.video.height = { exact: h };
7055
+ if (fps)
7056
+ constraints.video.frameRate = { exact: fps };
7057
+ delete constraints.video.facingMode;
7058
+ }
7059
+ else {
7060
+ constraints.video.aspectRatio = widescreen ? 16 / 9 : 4 / 3;
7061
+ }
7062
+ if (disableAGC)
7063
+ constraints.audio.autoGainControl = false;
7064
+ if (disableAEC)
7065
+ constraints.audio.echoCancellation = false;
7066
+ return constraints;
7067
+ }
7068
+ function getConstraints({ devices, videoId, audioId, options, type = "ideal" }) {
7069
+ const audioDevices = devices.filter((d) => d.kind === "audioinput");
7070
+ const videoDevices = devices.filter((d) => d.kind === "videoinput");
7071
+ const useDefaultAudio = !audioId || !audioDevices.some((d) => d.deviceId === audioId);
7072
+ const useDefaultVideo = !videoId || !videoDevices.some((d) => d.deviceId === videoId);
7073
+ const constraints = getMediaConstraints(Object.assign({ preferredDeviceIds: {
7074
+ audioId: useDefaultAudio ? null : { [type]: audioId },
7075
+ videoId: useDefaultVideo ? null : { [type]: videoId },
7076
+ } }, options));
7077
+ if (audioId === false || !audioDevices.length) {
7078
+ delete constraints.audio;
7079
+ }
7080
+ if (videoId === false || !videoDevices.length) {
7081
+ delete constraints.video;
7082
+ }
7083
+ return constraints;
7084
+ }
7085
+
7063
7086
  const logger = new Logger();
7064
7087
  const isMobile = /mobi/i.test(navigator.userAgent);
7065
7088
  class NoDevicesError extends Error {
@@ -7093,29 +7116,23 @@ function buildDeviceList({ busyDeviceIds, devices, kind }) {
7093
7116
  label: `${busyDeviceIds.includes(d.deviceId) ? "(busy) " : ""}${d.label || d.deviceId.slice(0, 5)}`,
7094
7117
  busy: busyDeviceIds.includes(d.deviceId),
7095
7118
  }));
7096
- return deviceList && deviceList.length !== 0 ? deviceList : [{ [idFieldsByKind[kind]]: "", label: "Default" }];
7119
+ return deviceList && deviceList.length !== 0
7120
+ ? deviceList
7121
+ : [{ [idFieldsByKind[kind]]: "", label: "Default" }];
7097
7122
  }
7098
7123
  function getUserMedia(constraints) {
7099
- return __awaiter(this, void 0, void 0, function* () {
7100
- if (!constraints.audio && !constraints.video) {
7101
- throw new NoDevicesError("No provided devices");
7102
- }
7103
- try {
7104
- const stream = yield navigator.mediaDevices.getUserMedia(constraints);
7105
- return stream;
7106
- }
7107
- catch (error) {
7108
- const message = `${error}, ${JSON.stringify(constraints, null, 2)}`;
7109
- logger.error(`getUserMedia ${message}`);
7110
- throw error;
7111
- }
7124
+ if (!constraints.audio && !constraints.video) {
7125
+ return Promise.reject(new NoDevicesError("No provided devices"));
7126
+ }
7127
+ return navigator.mediaDevices.getUserMedia(constraints).catch((error) => {
7128
+ const message = `${error}, ${JSON.stringify(constraints, null, 2)}`;
7129
+ logger.error(`getUserMedia ${message}`);
7130
+ throw error;
7112
7131
  });
7113
7132
  }
7114
7133
  function getSettingsFromTrack(kind, track, devices, lastUsedId) {
7115
7134
  var _a, _b, _c;
7116
- let settings = {
7117
- deviceId: undefined,
7118
- };
7135
+ let settings = { deviceId: null };
7119
7136
  if (!track) {
7120
7137
  if (lastUsedId && devices) {
7121
7138
  settings.deviceId = (_a = devices.find((d) => d.deviceId === lastUsedId && d.kind === kind)) === null || _a === void 0 ? void 0 : _a.deviceId;
@@ -7139,10 +7156,11 @@ function getSettingsFromTrack(kind, track, devices, lastUsedId) {
7139
7156
  if (settings.deviceId)
7140
7157
  return settings;
7141
7158
  settings.deviceId = (_c = track.getConstraints()) === null || _c === void 0 ? void 0 : _c.deviceId;
7159
+ settings.broken = 1;
7142
7160
  return settings;
7143
7161
  }
7144
7162
  function getDeviceData({ audioTrack, videoTrack, devices, stoppedVideoTrack, lastAudioId, lastVideoId, }) {
7145
- const usable = (d) => ((d === null || d === void 0 ? void 0 : d.readyState) === "live" ? d : undefined);
7163
+ const usable = (d) => ((d === null || d === void 0 ? void 0 : d.readyState) === "live" ? d : null);
7146
7164
  videoTrack = usable(videoTrack) || stoppedVideoTrack;
7147
7165
  audioTrack = usable(audioTrack);
7148
7166
  const video = getSettingsFromTrack("videoinput", videoTrack, devices, lastVideoId);
@@ -7175,7 +7193,7 @@ function getStream(constraintOpt_1) {
7175
7193
  let newConstraints;
7176
7194
  let retryConstraintOpt;
7177
7195
  let stream = null;
7178
- const only = (!constraintOpt.audioId && "video") || (!constraintOpt.videoId && "audio");
7196
+ const only = (constraintOpt.audioId === false && "video") || (constraintOpt.videoId === false && "audio");
7179
7197
  const stopTracks = isMobile || only !== "video";
7180
7198
  const constraints = getConstraints(constraintOpt);
7181
7199
  const addDetails = (err, orgErr) => {
@@ -7186,7 +7204,7 @@ function getStream(constraintOpt_1) {
7186
7204
  return err;
7187
7205
  }
7188
7206
  else {
7189
- return new Error("GetStream: gUM rejected without error");
7207
+ return new Error("Unknown error");
7190
7208
  }
7191
7209
  };
7192
7210
  const getSingleStream = (e) => __awaiter(this, void 0, void 0, function* () {
@@ -7220,18 +7238,12 @@ function getStream(constraintOpt_1) {
7220
7238
  }
7221
7239
  if ((e === null || e === void 0 ? void 0 : e.name) === "OverconstrainedError") {
7222
7240
  const laxConstraints = {
7223
- deviceId: { videoId: true, audioId: true },
7241
+ deviceId: { videoId: null, audioId: null },
7224
7242
  width: { lax: true },
7225
7243
  height: { lax: true },
7226
- "": { audioId: true, videoId: true, lax: true },
7244
+ "": { audioId: null, videoId: null, lax: true },
7227
7245
  };
7228
7246
  retryConstraintOpt = laxConstraints[e.constraint || ""];
7229
- if (!constraintOpt.audioId || only === "video") {
7230
- delete retryConstraintOpt.audioId;
7231
- }
7232
- if (!constraintOpt.videoId || only === "audio") {
7233
- delete retryConstraintOpt.videoId;
7234
- }
7235
7247
  }
7236
7248
  else if ((e === null || e === void 0 ? void 0 : e.name) === "NotFoundError") {
7237
7249
  yield getSingleStream(e);
@@ -7255,7 +7267,7 @@ function getStream(constraintOpt_1) {
7255
7267
  const problemWith = { audio: "audioId", video: "videoId" }[((_a = /(video|audio)/.exec(errMsg)) === null || _a === void 0 ? void 0 : _a[0]) || only || ""];
7256
7268
  if (!stream && problemWith) {
7257
7269
  try {
7258
- stream = yield getUserMedia(getConstraints(Object.assign(Object.assign({}, constraintOpt), { [problemWith]: true })));
7270
+ stream = yield getUserMedia(getConstraints(Object.assign(Object.assign({}, constraintOpt), { [problemWith]: null })));
7259
7271
  }
7260
7272
  catch (e2) {
7261
7273
  logger.warn(`Re-tried ${problemWith} with no constraints, but failed: ${"" + e2}`);
@@ -7315,9 +7327,8 @@ const defaultDisplayMediaConstraints = {
7315
7327
  height: { max: window.screen.height },
7316
7328
  },
7317
7329
  };
7318
- function getDisplayMedia() {
7319
- return __awaiter(this, arguments, void 0, function* (constraints = defaultDisplayMediaConstraints, contentHint = "detail") {
7320
- const stream = yield navigator.mediaDevices.getDisplayMedia(constraints);
7330
+ function getDisplayMedia(constraints = defaultDisplayMediaConstraints, contentHint = "detail") {
7331
+ return navigator.mediaDevices.getDisplayMedia(constraints).then((stream) => {
7321
7332
  stream.getVideoTracks().forEach((t) => {
7322
7333
  if ("contentHint" in t) {
7323
7334
  t.contentHint = contentHint;
@@ -7375,10 +7386,14 @@ function getUpdatedDevices({ oldDevices, newDevices, currentAudioId, currentVide
7375
7386
  }
7376
7387
  if (currentDeviceId) {
7377
7388
  if (changes.removed[currentDeviceId]) {
7378
- removedDevices[kind] = changes.removed[currentDeviceId];
7389
+ changedDevices[kind] = { deviceId: null };
7390
+ if (kind === "audiooutput") {
7391
+ const fallbackSpeakerDevice = newDevices.find((d) => d.kind === "audiooutput");
7392
+ changedDevices[kind] = { deviceId: fallbackSpeakerDevice === null || fallbackSpeakerDevice === void 0 ? void 0 : fallbackSpeakerDevice.deviceId };
7393
+ }
7379
7394
  }
7380
7395
  if (changes.changed[currentDeviceId]) {
7381
- changedDevices[kind] = changes.changed[currentDeviceId];
7396
+ changedDevices[kind] = { deviceId: currentDeviceId };
7382
7397
  }
7383
7398
  }
7384
7399
  if (Object.keys(changes.added).length > 0) {
@@ -7386,6 +7401,10 @@ function getUpdatedDevices({ oldDevices, newDevices, currentAudioId, currentVide
7386
7401
  const add = changes.added[deviceAdded];
7387
7402
  addedDevices[kind] = { deviceId: add.deviceId, label: add.label, kind: add.kind };
7388
7403
  }
7404
+ if (Object.keys(changes.removed).length > 0) {
7405
+ const [deviceRemoved] = Object.keys(changes.removed).slice(0, 1);
7406
+ removedDevices[kind] = changes.removed[deviceRemoved];
7407
+ }
7389
7408
  });
7390
7409
  return { addedDevices, changedDevices, removedDevices };
7391
7410
  }