@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.mjs CHANGED
@@ -224,6 +224,7 @@ class Logger {
224
224
  }
225
225
  }
226
226
 
227
+ const logger$b = new Logger();
227
228
  const AUDIO_SETTINGS = {
228
229
  codecOptions: {
229
230
  opusDtx: true,
@@ -398,7 +399,14 @@ function getIsCodecDecodingPowerEfficient(codec) {
398
399
  }
399
400
  function sortCodecsByPowerEfficiency(codecs) {
400
401
  return __awaiter(this, void 0, void 0, function* () {
401
- const codecPowerEfficiencyEntries = yield Promise.all(codecs.map(({ mimeType }) => getIsCodecDecodingPowerEfficient(mimeType).then((val) => [mimeType, val])));
402
+ let codecPowerEfficiencyEntries;
403
+ try {
404
+ codecPowerEfficiencyEntries = yield Promise.all(codecs.map(({ mimeType }) => getIsCodecDecodingPowerEfficient(mimeType).then((val) => [mimeType, val])));
405
+ }
406
+ catch (error) {
407
+ logger$b.error(error);
408
+ return codecs;
409
+ }
402
410
  const codecPowerEfficiencies = Object.fromEntries(codecPowerEfficiencyEntries);
403
411
  const sorted = codecs.sort((a, b) => {
404
412
  const aPowerEfficient = codecPowerEfficiencies[a.mimeType];
@@ -2505,75 +2513,11 @@ var rtcManagerEvents = {
2505
2513
 
2506
2514
  var _a$2, _b$1;
2507
2515
  const adapter$2 = (_a$2 = adapterRaw.default) !== null && _a$2 !== void 0 ? _a$2 : adapterRaw;
2508
- const isSafari = ((_b$1 = adapter$2.browserDetails) === null || _b$1 === void 0 ? void 0 : _b$1.browser) === "safari";
2509
- const parseResolution = (res) => res.split(/[^\d]/g).map((n) => parseInt(n, 10));
2510
- function getMediaConstraints({ audioWanted, videoWanted, disableAEC, disableAGC, hd, lax, lowDataMode, preferredDeviceIds, resolution, simulcast, widescreen, }) {
2511
- let HIGH_HEIGHT = 480;
2512
- let LOW_HEIGHT = 240;
2513
- if (hd) {
2514
- HIGH_HEIGHT = lax || isSafari ? 720 : { min: 360, ideal: 720 };
2515
- }
2516
- if (simulcast) {
2517
- if (hd === false) {
2518
- HIGH_HEIGHT = 360;
2519
- LOW_HEIGHT = 270;
2520
- }
2521
- else {
2522
- LOW_HEIGHT = 360;
2523
- }
2524
- }
2525
- const { audioId, videoId } = preferredDeviceIds;
2526
- const constraints = Object.assign(Object.assign({}, (audioWanted && {
2527
- audio: Object.assign(Object.assign(Object.assign({}, (audioId ? { deviceId: audioId } : {})), (disableAGC ? { autoGainControl: false } : {})), (disableAEC ? { echoCancellation: false } : {})),
2528
- })), (videoWanted && {
2529
- video: Object.assign({ height: lowDataMode ? LOW_HEIGHT : HIGH_HEIGHT, frameRate: lowDataMode && !simulcast ? 15 : 24 }, (videoId ? { deviceId: videoId } : { facingMode: "user" })),
2530
- }));
2531
- if (lax) {
2532
- if (audioWanted && !audioId)
2533
- constraints.audio = true;
2534
- if (videoWanted && !videoId && typeof constraints.video === "object") {
2535
- delete constraints.video.facingMode;
2536
- }
2537
- return constraints;
2538
- }
2539
- if (videoWanted && typeof constraints.video === "object") {
2540
- if (resolution) {
2541
- const [w, h, fps] = parseResolution(resolution);
2542
- if (w)
2543
- constraints.video.width = { exact: w };
2544
- if (h)
2545
- constraints.video.height = { exact: h };
2546
- if (fps)
2547
- constraints.video.frameRate = { exact: fps };
2548
- delete constraints.video.facingMode;
2549
- }
2550
- else {
2551
- constraints.video.aspectRatio = widescreen ? 16 / 9 : 4 / 3;
2552
- }
2553
- }
2554
- return constraints;
2555
- }
2556
- function getConstraints({ devices, videoId, audioId, options, type = "ideal" }) {
2557
- const audioDevices = devices.filter((d) => d.kind === "audioinput");
2558
- const videoDevices = devices.filter((d) => d.kind === "videoinput");
2559
- const preferredDeviceIds = {};
2560
- if (typeof audioId === "string" && audioDevices.some((d) => d.deviceId === audioId)) {
2561
- preferredDeviceIds.audioId = { [type]: audioId };
2562
- }
2563
- if (typeof videoId === "string" && videoDevices.some((d) => d.deviceId === videoId)) {
2564
- preferredDeviceIds.videoId = { [type]: videoId };
2565
- }
2566
- const constraints = getMediaConstraints(Object.assign({ preferredDeviceIds, audioWanted: Boolean(audioId) && audioDevices.length > 0, videoWanted: Boolean(videoId) && videoDevices.length > 0 }, options));
2567
- return constraints;
2568
- }
2569
-
2570
- var _a$1, _b;
2571
- const adapter$1 = (_a$1 = adapterRaw.default) !== null && _a$1 !== void 0 ? _a$1 : adapterRaw;
2572
2516
  const logger$6 = new Logger();
2573
2517
  const ICE_PUBLIC_IP_GATHERING_TIMEOUT = 3 * 1000;
2574
2518
  const ICE_RESTART_DELAY = 2 * 1000;
2575
- const browserName$1 = (_b = adapter$1.browserDetails) === null || _b === void 0 ? void 0 : _b.browser;
2576
- const browserVersion = adapter$1.browserDetails.version;
2519
+ const browserName$1 = (_b$1 = adapter$2.browserDetails) === null || _b$1 === void 0 ? void 0 : _b$1.browser;
2520
+ const browserVersion = adapter$2.browserDetails.version;
2577
2521
  let unloading$1 = false;
2578
2522
  if (browserName$1 === "chrome") {
2579
2523
  window.document.addEventListener("beforeunload", () => {
@@ -2596,6 +2540,9 @@ class P2pRtcManager {
2596
2540
  this._features = features || {};
2597
2541
  this._isAudioOnlyMode = false;
2598
2542
  this._closed = false;
2543
+ this._fetchMediaServersTimer = null;
2544
+ this._stopCameraTimeout = null;
2545
+ this._icePublicIPGatheringTimeoutID = null;
2599
2546
  this.offerOptions = { offerToReceiveAudio: true, offerToReceiveVideo: true };
2600
2547
  this._audioTrackOnEnded = () => {
2601
2548
  rtcStats.sendEvent("audio_ended", { unloading: unloading$1 });
@@ -2797,6 +2744,7 @@ class P2pRtcManager {
2797
2744
  message: answer,
2798
2745
  });
2799
2746
  })).catch) === null || _b === void 0 ? void 0 : _b.call(_a, (e) => {
2747
+ logger$6.error(e);
2800
2748
  this.analytics.numPcOnOfferFailure++;
2801
2749
  });
2802
2750
  }),
@@ -3204,11 +3152,12 @@ class P2pRtcManager {
3204
3152
  return session;
3205
3153
  }
3206
3154
  _maybeRestartIce(clientId, session) {
3155
+ var _a;
3207
3156
  const pc = session.pc;
3208
3157
  if (!(pc.iceConnectionState === "disconnected" || pc.iceConnectionState === "failed")) {
3209
3158
  return;
3210
3159
  }
3211
- if (pc.localDescription.type === "offer") {
3160
+ if (((_a = pc.localDescription) === null || _a === void 0 ? void 0 : _a.type) === "offer") {
3212
3161
  session.wasEverConnected = false;
3213
3162
  session.relayCandidateSeen = false;
3214
3163
  session.serverReflexiveCandidateSeen = false;
@@ -3287,7 +3236,7 @@ class P2pRtcManager {
3287
3236
  }
3288
3237
  if (rtpAbsCaptureTimeOn)
3289
3238
  offer.sdp = addAbsCaptureTimeExtMap(offer.sdp);
3290
- if (browserName$1 === "firefox") {
3239
+ if (browserName$1 === "firefox" && browserVersion < 128) {
3291
3240
  offer.sdp = setCodecPreferenceSDP({
3292
3241
  sdp: offer.sdp,
3293
3242
  redOn,
@@ -3377,9 +3326,9 @@ class P2pRtcManager {
3377
3326
  const connection = event.target;
3378
3327
  switch (connection.iceGatheringState) {
3379
3328
  case "gathering":
3380
- if (this.icePublicIPGatheringTimeoutID)
3381
- clearTimeout(this.icePublicIPGatheringTimeoutID);
3382
- this.icePublicIPGatheringTimeoutID = setTimeout(() => {
3329
+ if (this._icePublicIPGatheringTimeoutID)
3330
+ clearTimeout(this._icePublicIPGatheringTimeoutID);
3331
+ this._icePublicIPGatheringTimeoutID = setTimeout(() => {
3383
3332
  if (!session.publicHostCandidateSeen &&
3384
3333
  !session.relayCandidateSeen &&
3385
3334
  !session.serverReflexiveCandidateSeen) {
@@ -3389,9 +3338,9 @@ class P2pRtcManager {
3389
3338
  }, ICE_PUBLIC_IP_GATHERING_TIMEOUT);
3390
3339
  break;
3391
3340
  case "complete":
3392
- if (this.icePublicIPGatheringTimeoutID)
3393
- clearTimeout(this.icePublicIPGatheringTimeoutID);
3394
- this.icePublicIPGatheringTimeoutID = undefined;
3341
+ if (this._icePublicIPGatheringTimeoutID)
3342
+ clearTimeout(this._icePublicIPGatheringTimeoutID);
3343
+ this._icePublicIPGatheringTimeoutID = null;
3395
3344
  break;
3396
3345
  }
3397
3346
  };
@@ -3540,9 +3489,13 @@ class P2pRtcManager {
3540
3489
  if (!["chrome", "safari"].includes(browserName$1)) {
3541
3490
  return;
3542
3491
  }
3492
+ if (this._stopCameraTimeout) {
3493
+ clearTimeout(this._stopCameraTimeout);
3494
+ this._stopCameraTimeout = null;
3495
+ }
3543
3496
  if (enable === false) {
3544
3497
  const stopCameraDelay = ((_a = localStream.getVideoTracks().find((t) => !t.enabled)) === null || _a === void 0 ? void 0 : _a.readyState) === "ended" ? 0 : 5000;
3545
- setTimeout(() => {
3498
+ this._stopCameraTimeout = setTimeout(() => {
3546
3499
  localStream.getVideoTracks().forEach((track) => {
3547
3500
  if (track.enabled === false) {
3548
3501
  track.stop();
@@ -3558,7 +3511,7 @@ class P2pRtcManager {
3558
3511
  }
3559
3512
  else {
3560
3513
  if (localStream.getVideoTracks().length === 0) {
3561
- const constraints = getConstraints(this._webrtcProvider.getMediaConstraints()).video;
3514
+ const constraints = this._webrtcProvider.getMediaConstraints().video;
3562
3515
  if (!constraints) {
3563
3516
  return;
3564
3517
  }
@@ -4427,10 +4380,10 @@ const STREAM_TYPES = {
4427
4380
  screenshare: "screenshare",
4428
4381
  };
4429
4382
 
4430
- var _a;
4431
- const adapter = (_a = adapterRaw.default) !== null && _a !== void 0 ? _a : adapterRaw;
4383
+ var _a$1;
4384
+ const adapter$1 = (_a$1 = adapterRaw.default) !== null && _a$1 !== void 0 ? _a$1 : adapterRaw;
4432
4385
  const logger$2 = new Logger();
4433
- const browserName = adapter.browserDetails.browser;
4386
+ const browserName = adapter$1.browserDetails.browser;
4434
4387
  let unloading = false;
4435
4388
  const RESTARTICE_ERROR_RETRY_THRESHOLD_IN_MS = 3500;
4436
4389
  const RESTARTICE_ERROR_MAX_RETRY_COUNT = 5;
@@ -5496,8 +5449,11 @@ class VegaRtcManager {
5496
5449
  if (!["chrome", "safari"].includes(browserName)) {
5497
5450
  return;
5498
5451
  }
5499
- if (!enable) {
5452
+ if (this._stopCameraTimeout) {
5500
5453
  clearTimeout(this._stopCameraTimeout);
5454
+ this._stopCameraTimeout = null;
5455
+ }
5456
+ if (!enable) {
5501
5457
  const stopCameraDelay = ((_a = localStream.getVideoTracks().find((t) => !t.enabled)) === null || _a === void 0 ? void 0 : _a.readyState) === "ended" ? 0 : 5000;
5502
5458
  this._stopCameraTimeout = setTimeout(() => {
5503
5459
  localStream.getVideoTracks().forEach((track) => {
@@ -5514,7 +5470,7 @@ class VegaRtcManager {
5514
5470
  }, stopCameraDelay);
5515
5471
  }
5516
5472
  else if (localStream.getVideoTracks().length === 0) {
5517
- const constraints = getConstraints(this._webrtcProvider.getMediaConstraints()).video;
5473
+ const constraints = this._webrtcProvider.getMediaConstraints().video;
5518
5474
  navigator.mediaDevices
5519
5475
  .getUserMedia({ video: constraints })
5520
5476
  .then((stream) => {
@@ -7039,6 +6995,73 @@ class BandwidthTester extends EventEmitter {
7039
6995
  }
7040
6996
  }
7041
6997
 
6998
+ var _a, _b;
6999
+ const adapter = (_a = adapterRaw.default) !== null && _a !== void 0 ? _a : adapterRaw;
7000
+ const isSafari = ((_b = adapter.browserDetails) === null || _b === void 0 ? void 0 : _b.browser) === "safari";
7001
+ const parseResolution = (res) => res.split(/[^\d]/g).map((n) => parseInt(n, 10));
7002
+ function getMediaConstraints({ disableAEC, disableAGC, hd, lax, lowDataMode, preferredDeviceIds, resolution, simulcast, widescreen, }) {
7003
+ var _a, _b;
7004
+ let HIGH_HEIGHT = 480;
7005
+ let LOW_HEIGHT = 240;
7006
+ if (hd) {
7007
+ HIGH_HEIGHT = lax || isSafari ? 720 : { min: 360, ideal: 720 };
7008
+ }
7009
+ if (simulcast) {
7010
+ if (hd === false) {
7011
+ HIGH_HEIGHT = 360;
7012
+ LOW_HEIGHT = 270;
7013
+ }
7014
+ else {
7015
+ LOW_HEIGHT = 360;
7016
+ }
7017
+ }
7018
+ const constraints = {
7019
+ audio: Object.assign({}, (preferredDeviceIds.audioId && { deviceId: preferredDeviceIds.audioId })),
7020
+ video: Object.assign(Object.assign({}, (preferredDeviceIds.videoId ? { deviceId: preferredDeviceIds.videoId } : { facingMode: "user" })), { height: lowDataMode ? LOW_HEIGHT : HIGH_HEIGHT, frameRate: lowDataMode && !simulcast ? 15 : 24 }),
7021
+ };
7022
+ if (lax) {
7023
+ if (!((_a = constraints.audio) === null || _a === void 0 ? void 0 : _a.deviceId))
7024
+ constraints.audio = true;
7025
+ (_b = constraints.video) === null || _b === void 0 ? true : delete _b.facingMode;
7026
+ return constraints;
7027
+ }
7028
+ if (resolution) {
7029
+ const [w, h, fps] = parseResolution(resolution);
7030
+ if (w)
7031
+ constraints.video.width = { exact: w };
7032
+ if (h)
7033
+ constraints.video.height = { exact: h };
7034
+ if (fps)
7035
+ constraints.video.frameRate = { exact: fps };
7036
+ delete constraints.video.facingMode;
7037
+ }
7038
+ else {
7039
+ constraints.video.aspectRatio = widescreen ? 16 / 9 : 4 / 3;
7040
+ }
7041
+ if (disableAGC)
7042
+ constraints.audio.autoGainControl = false;
7043
+ if (disableAEC)
7044
+ constraints.audio.echoCancellation = false;
7045
+ return constraints;
7046
+ }
7047
+ function getConstraints({ devices, videoId, audioId, options, type = "ideal" }) {
7048
+ const audioDevices = devices.filter((d) => d.kind === "audioinput");
7049
+ const videoDevices = devices.filter((d) => d.kind === "videoinput");
7050
+ const useDefaultAudio = !audioId || !audioDevices.some((d) => d.deviceId === audioId);
7051
+ const useDefaultVideo = !videoId || !videoDevices.some((d) => d.deviceId === videoId);
7052
+ const constraints = getMediaConstraints(Object.assign({ preferredDeviceIds: {
7053
+ audioId: useDefaultAudio ? null : { [type]: audioId },
7054
+ videoId: useDefaultVideo ? null : { [type]: videoId },
7055
+ } }, options));
7056
+ if (audioId === false || !audioDevices.length) {
7057
+ delete constraints.audio;
7058
+ }
7059
+ if (videoId === false || !videoDevices.length) {
7060
+ delete constraints.video;
7061
+ }
7062
+ return constraints;
7063
+ }
7064
+
7042
7065
  const logger = new Logger();
7043
7066
  const isMobile = /mobi/i.test(navigator.userAgent);
7044
7067
  class NoDevicesError extends Error {
@@ -7072,29 +7095,23 @@ function buildDeviceList({ busyDeviceIds, devices, kind }) {
7072
7095
  label: `${busyDeviceIds.includes(d.deviceId) ? "(busy) " : ""}${d.label || d.deviceId.slice(0, 5)}`,
7073
7096
  busy: busyDeviceIds.includes(d.deviceId),
7074
7097
  }));
7075
- return deviceList && deviceList.length !== 0 ? deviceList : [{ [idFieldsByKind[kind]]: "", label: "Default" }];
7098
+ return deviceList && deviceList.length !== 0
7099
+ ? deviceList
7100
+ : [{ [idFieldsByKind[kind]]: "", label: "Default" }];
7076
7101
  }
7077
7102
  function getUserMedia(constraints) {
7078
- return __awaiter(this, void 0, void 0, function* () {
7079
- if (!constraints.audio && !constraints.video) {
7080
- throw new NoDevicesError("No provided devices");
7081
- }
7082
- try {
7083
- const stream = yield navigator.mediaDevices.getUserMedia(constraints);
7084
- return stream;
7085
- }
7086
- catch (error) {
7087
- const message = `${error}, ${JSON.stringify(constraints, null, 2)}`;
7088
- logger.error(`getUserMedia ${message}`);
7089
- throw error;
7090
- }
7103
+ if (!constraints.audio && !constraints.video) {
7104
+ return Promise.reject(new NoDevicesError("No provided devices"));
7105
+ }
7106
+ return navigator.mediaDevices.getUserMedia(constraints).catch((error) => {
7107
+ const message = `${error}, ${JSON.stringify(constraints, null, 2)}`;
7108
+ logger.error(`getUserMedia ${message}`);
7109
+ throw error;
7091
7110
  });
7092
7111
  }
7093
7112
  function getSettingsFromTrack(kind, track, devices, lastUsedId) {
7094
7113
  var _a, _b, _c;
7095
- let settings = {
7096
- deviceId: undefined,
7097
- };
7114
+ let settings = { deviceId: null };
7098
7115
  if (!track) {
7099
7116
  if (lastUsedId && devices) {
7100
7117
  settings.deviceId = (_a = devices.find((d) => d.deviceId === lastUsedId && d.kind === kind)) === null || _a === void 0 ? void 0 : _a.deviceId;
@@ -7118,10 +7135,11 @@ function getSettingsFromTrack(kind, track, devices, lastUsedId) {
7118
7135
  if (settings.deviceId)
7119
7136
  return settings;
7120
7137
  settings.deviceId = (_c = track.getConstraints()) === null || _c === void 0 ? void 0 : _c.deviceId;
7138
+ settings.broken = 1;
7121
7139
  return settings;
7122
7140
  }
7123
7141
  function getDeviceData({ audioTrack, videoTrack, devices, stoppedVideoTrack, lastAudioId, lastVideoId, }) {
7124
- const usable = (d) => ((d === null || d === void 0 ? void 0 : d.readyState) === "live" ? d : undefined);
7142
+ const usable = (d) => ((d === null || d === void 0 ? void 0 : d.readyState) === "live" ? d : null);
7125
7143
  videoTrack = usable(videoTrack) || stoppedVideoTrack;
7126
7144
  audioTrack = usable(audioTrack);
7127
7145
  const video = getSettingsFromTrack("videoinput", videoTrack, devices, lastVideoId);
@@ -7154,7 +7172,7 @@ function getStream(constraintOpt_1) {
7154
7172
  let newConstraints;
7155
7173
  let retryConstraintOpt;
7156
7174
  let stream = null;
7157
- const only = (!constraintOpt.audioId && "video") || (!constraintOpt.videoId && "audio");
7175
+ const only = (constraintOpt.audioId === false && "video") || (constraintOpt.videoId === false && "audio");
7158
7176
  const stopTracks = isMobile || only !== "video";
7159
7177
  const constraints = getConstraints(constraintOpt);
7160
7178
  const addDetails = (err, orgErr) => {
@@ -7165,7 +7183,7 @@ function getStream(constraintOpt_1) {
7165
7183
  return err;
7166
7184
  }
7167
7185
  else {
7168
- return new Error("GetStream: gUM rejected without error");
7186
+ return new Error("Unknown error");
7169
7187
  }
7170
7188
  };
7171
7189
  const getSingleStream = (e) => __awaiter(this, void 0, void 0, function* () {
@@ -7199,18 +7217,12 @@ function getStream(constraintOpt_1) {
7199
7217
  }
7200
7218
  if ((e === null || e === void 0 ? void 0 : e.name) === "OverconstrainedError") {
7201
7219
  const laxConstraints = {
7202
- deviceId: { videoId: true, audioId: true },
7220
+ deviceId: { videoId: null, audioId: null },
7203
7221
  width: { lax: true },
7204
7222
  height: { lax: true },
7205
- "": { audioId: true, videoId: true, lax: true },
7223
+ "": { audioId: null, videoId: null, lax: true },
7206
7224
  };
7207
7225
  retryConstraintOpt = laxConstraints[e.constraint || ""];
7208
- if (!constraintOpt.audioId || only === "video") {
7209
- delete retryConstraintOpt.audioId;
7210
- }
7211
- if (!constraintOpt.videoId || only === "audio") {
7212
- delete retryConstraintOpt.videoId;
7213
- }
7214
7226
  }
7215
7227
  else if ((e === null || e === void 0 ? void 0 : e.name) === "NotFoundError") {
7216
7228
  yield getSingleStream(e);
@@ -7234,7 +7246,7 @@ function getStream(constraintOpt_1) {
7234
7246
  const problemWith = { audio: "audioId", video: "videoId" }[((_a = /(video|audio)/.exec(errMsg)) === null || _a === void 0 ? void 0 : _a[0]) || only || ""];
7235
7247
  if (!stream && problemWith) {
7236
7248
  try {
7237
- stream = yield getUserMedia(getConstraints(Object.assign(Object.assign({}, constraintOpt), { [problemWith]: true })));
7249
+ stream = yield getUserMedia(getConstraints(Object.assign(Object.assign({}, constraintOpt), { [problemWith]: null })));
7238
7250
  }
7239
7251
  catch (e2) {
7240
7252
  logger.warn(`Re-tried ${problemWith} with no constraints, but failed: ${"" + e2}`);
@@ -7294,9 +7306,8 @@ const defaultDisplayMediaConstraints = {
7294
7306
  height: { max: window.screen.height },
7295
7307
  },
7296
7308
  };
7297
- function getDisplayMedia() {
7298
- return __awaiter(this, arguments, void 0, function* (constraints = defaultDisplayMediaConstraints, contentHint = "detail") {
7299
- const stream = yield navigator.mediaDevices.getDisplayMedia(constraints);
7309
+ function getDisplayMedia(constraints = defaultDisplayMediaConstraints, contentHint = "detail") {
7310
+ return navigator.mediaDevices.getDisplayMedia(constraints).then((stream) => {
7300
7311
  stream.getVideoTracks().forEach((t) => {
7301
7312
  if ("contentHint" in t) {
7302
7313
  t.contentHint = contentHint;
@@ -7354,10 +7365,14 @@ function getUpdatedDevices({ oldDevices, newDevices, currentAudioId, currentVide
7354
7365
  }
7355
7366
  if (currentDeviceId) {
7356
7367
  if (changes.removed[currentDeviceId]) {
7357
- removedDevices[kind] = changes.removed[currentDeviceId];
7368
+ changedDevices[kind] = { deviceId: null };
7369
+ if (kind === "audiooutput") {
7370
+ const fallbackSpeakerDevice = newDevices.find((d) => d.kind === "audiooutput");
7371
+ changedDevices[kind] = { deviceId: fallbackSpeakerDevice === null || fallbackSpeakerDevice === void 0 ? void 0 : fallbackSpeakerDevice.deviceId };
7372
+ }
7358
7373
  }
7359
7374
  if (changes.changed[currentDeviceId]) {
7360
- changedDevices[kind] = changes.changed[currentDeviceId];
7375
+ changedDevices[kind] = { deviceId: currentDeviceId };
7361
7376
  }
7362
7377
  }
7363
7378
  if (Object.keys(changes.added).length > 0) {
@@ -7365,6 +7380,10 @@ function getUpdatedDevices({ oldDevices, newDevices, currentAudioId, currentVide
7365
7380
  const add = changes.added[deviceAdded];
7366
7381
  addedDevices[kind] = { deviceId: add.deviceId, label: add.label, kind: add.kind };
7367
7382
  }
7383
+ if (Object.keys(changes.removed).length > 0) {
7384
+ const [deviceRemoved] = Object.keys(changes.removed).slice(0, 1);
7385
+ removedDevices[kind] = changes.removed[deviceRemoved];
7386
+ }
7368
7387
  });
7369
7388
  return { addedDevices, changedDevices, removedDevices };
7370
7389
  }