Procházet zdrojové kódy

feat(devices): Filter MS Teams Audio device

factor2
Hristo Terezov před 1 rokem
rodič
revize
c025102511

+ 20
- 7
conference.js Zobrazit soubor

@@ -65,7 +65,11 @@ import {
65 65
     updateDeviceList
66 66
 } from './react/features/base/devices/actions.web';
67 67
 import {
68
+    areDevicesDifferent,
69
+    filterIgnoredDevices,
70
+    flattenAvailableDevices,
68 71
     getDefaultDeviceId,
72
+    logDevices,
69 73
     setAudioOutputDeviceId
70 74
 } from './react/features/base/devices/functions.web';
71 75
 import {
@@ -2241,19 +2245,28 @@ export default {
2241 2245
      * @returns {Promise}
2242 2246
      */
2243 2247
     async _onDeviceListChanged(devices) {
2244
-        const oldDevices = APP.store.getState()['features/base/devices'].availableDevices;
2245
-        const localAudio = getLocalJitsiAudioTrack(APP.store.getState());
2246
-        const localVideo = getLocalJitsiVideoTrack(APP.store.getState());
2248
+        const state = APP.store.getState();
2249
+        const { filteredDevices, ignoredDevices } = filterIgnoredDevices(devices);
2250
+        const oldDevices = state['features/base/devices'].availableDevices;
2251
+
2252
+        if (!areDevicesDifferent(flattenAvailableDevices(oldDevices), filteredDevices)) {
2253
+            return Promise.resolve();
2254
+        }
2255
+
2256
+        logDevices(ignoredDevices, 'Ignored devices on device list changed:');
2257
+
2258
+        const localAudio = getLocalJitsiAudioTrack(state);
2259
+        const localVideo = getLocalJitsiVideoTrack(state);
2247 2260
 
2248
-        APP.store.dispatch(updateDeviceList(devices));
2261
+        APP.store.dispatch(updateDeviceList(filteredDevices));
2249 2262
 
2250 2263
         // Firefox users can choose their preferred device in the gUM prompt. In that case
2251 2264
         // we should respect that and not attempt to switch to the preferred device from
2252 2265
         // our settings.
2253
-        const newLabelsOnly = mediaDeviceHelper.newDeviceListAddedLabelsOnly(oldDevices, devices);
2266
+        const newLabelsOnly = mediaDeviceHelper.newDeviceListAddedLabelsOnly(oldDevices, filteredDevices);
2254 2267
         const newDevices
2255 2268
             = mediaDeviceHelper.getNewMediaDevicesAfterDeviceListChanged(
2256
-                devices,
2269
+                filteredDevices,
2257 2270
                 localVideo,
2258 2271
                 localAudio,
2259 2272
                 newLabelsOnly);
@@ -2386,7 +2399,7 @@ export default {
2386 2399
 
2387 2400
         return Promise.all(promises)
2388 2401
             .then(() => {
2389
-                APP.UI.onAvailableDevicesChanged(devices);
2402
+                APP.UI.onAvailableDevicesChanged(filteredDevices);
2390 2403
             });
2391 2404
     },
2392 2405
 

+ 2
- 1
modules/devices/mediaDeviceHelper.js Zobrazit soubor

@@ -5,6 +5,7 @@ import {
5 5
     notifyMicError
6 6
 } from '../../react/features/base/devices/actions.web';
7 7
 import {
8
+    flattenAvailableDevices,
8 9
     getAudioOutputDeviceId
9 10
 } from '../../react/features/base/devices/functions.web';
10 11
 import { updateSettings } from '../../react/features/base/settings/actions';
@@ -186,7 +187,7 @@ export default {
186 187
      * @returns {boolean}
187 188
      */
188 189
     newDeviceListAddedLabelsOnly(oldDevices, newDevices) {
189
-        const oldDevicesFlattend = oldDevices.audioInput.concat(oldDevices.audioOutput).concat(oldDevices.videoInput);
190
+        const oldDevicesFlattend = flattenAvailableDevices(oldDevices);
190 191
 
191 192
         if (oldDevicesFlattend.length !== newDevices.length) {
192 193
             return false;

+ 13
- 3
react/features/base/devices/actions.web.ts Zobrazit soubor

@@ -16,9 +16,13 @@ import {
16 16
 } from './actionTypes';
17 17
 import {
18 18
     areDeviceLabelsInitialized,
19
+    areDevicesDifferent,
20
+    filterIgnoredDevices,
21
+    flattenAvailableDevices,
19 22
     getDeviceIdByLabel,
20 23
     getDeviceLabelById,
21 24
     getDevicesFromURL,
25
+    logDevices,
22 26
     setAudioOutputDeviceId
23 27
 } from './functions';
24 28
 import logger from './logger';
@@ -137,15 +141,21 @@ export function configureInitialDevices() {
137 141
  * @returns {Function}
138 142
  */
139 143
 export function getAvailableDevices() {
140
-    return (dispatch: IStore['dispatch']) => new Promise(resolve => {
144
+    return (dispatch: IStore['dispatch'], getState: IStore['getState']) => new Promise(resolve => {
141 145
         const { mediaDevices } = JitsiMeetJS;
142 146
 
143 147
         if (mediaDevices.isDeviceListAvailable()
144 148
                 && mediaDevices.isDeviceChangeAvailable()) {
145 149
             mediaDevices.enumerateDevices((devices: MediaDeviceInfo[]) => {
146
-                dispatch(updateDeviceList(devices));
150
+                const { filteredDevices, ignoredDevices } = filterIgnoredDevices(devices);
151
+                const oldDevices = flattenAvailableDevices(getState()['features/base/devices'].availableDevices);
147 152
 
148
-                resolve(devices);
153
+                if (areDevicesDifferent(oldDevices, filteredDevices)) {
154
+                    logDevices(ignoredDevices, 'Ignored devices on device list changed:');
155
+                    dispatch(updateDeviceList(filteredDevices));
156
+                }
157
+
158
+                resolve(filteredDevices);
149 159
             });
150 160
         } else {
151 161
             resolve([]);

+ 8
- 0
react/features/base/devices/constants.ts Zobrazit soubor

@@ -0,0 +1,8 @@
1
+/**
2
+ * Prefixes of devices that will be filtered from the device list.
3
+ *
4
+ * NOTE: Currently we filter only 'Microsoft Teams Audio Device' virtual device. It seems that it can't be set
5
+ * as default device on the OS level and this use case is not handled in the code. If we add more device prefixes that
6
+ * can be default devices we should make sure to handle the default device use case.
7
+ */
8
+export const DEVICE_LABEL_PREFIXES_TO_IGNORE = [ 'Microsoft Teams Audio Device' ];

+ 98
- 0
react/features/base/devices/functions.web.ts Zobrazit soubor

@@ -5,6 +5,7 @@ import { ISettingsState } from '../settings/reducer';
5 5
 import { setNewAudioOutputDevice } from '../sounds/functions.web';
6 6
 import { parseURLParams } from '../util/parseURLParams';
7 7
 
8
+import { DEVICE_LABEL_PREFIXES_TO_IGNORE } from './constants';
8 9
 import logger from './logger';
9 10
 import { IDevicesState } from './types';
10 11
 
@@ -176,6 +177,74 @@ export function filterAudioDevices(devices: MediaDeviceInfo[]) {
176 177
     return devices.filter(device => device.kind === 'audioinput');
177 178
 }
178 179
 
180
+/**
181
+ * Filters the devices that start with one of the prefixes from DEVICE_LABEL_PREFIXES_TO_IGNORE.
182
+ *
183
+ * @param {MediaDeviceInfo[]} devices - The devices to be filtered.
184
+ * @returns {MediaDeviceInfo[]} - The filtered devices.
185
+ */
186
+export function filterIgnoredDevices(devices: MediaDeviceInfo[] = []) {
187
+    const ignoredDevices: MediaDeviceInfo[] = [];
188
+    const filteredDevices = devices.filter(device => {
189
+        if (!device.label) {
190
+            return true;
191
+        }
192
+
193
+        if (DEVICE_LABEL_PREFIXES_TO_IGNORE.find(prefix => device.label?.startsWith(prefix))) {
194
+            ignoredDevices.push(device);
195
+
196
+            return false;
197
+        }
198
+
199
+        return true;
200
+    });
201
+
202
+    return {
203
+        filteredDevices,
204
+        ignoredDevices
205
+    };
206
+}
207
+
208
+/**
209
+ * Check if the passed device arrays are different.
210
+ *
211
+ * @param {MediaDeviceInfo[]} devices1 - Array with devices to be compared.
212
+ * @param {MediaDeviceInfo[]} devices2 - Array with devices to be compared.
213
+ * @returns {boolean} - True if the device arrays are different and false otherwise.
214
+*/
215
+export function areDevicesDifferent(devices1: MediaDeviceInfo[] = [], devices2: MediaDeviceInfo[] = []) {
216
+    if (devices1.length !== devices2.length) {
217
+        return true;
218
+    }
219
+
220
+    for (let i = 0; i < devices1.length; i++) {
221
+        const device1 = devices1[i];
222
+        const found = devices2.find(({ deviceId, groupId, kind, label }) =>
223
+            device1.deviceId === deviceId
224
+            && device1.groupId === groupId
225
+            && device1.kind === kind
226
+            && device1.label === label
227
+        );
228
+
229
+        if (!found) {
230
+            return true;
231
+        }
232
+    }
233
+
234
+    return false;
235
+}
236
+
237
+/**
238
+ * Flattens the availableDevices from redux.
239
+ *
240
+ * @param {IDevicesState.availableDevices} devices - The available devices from redux.
241
+ * @returns {MediaDeviceInfo[]} - The flattened array of devices.
242
+ */
243
+export function flattenAvailableDevices(
244
+        { audioInput = [], audioOutput = [], videoInput = [] }: IDevicesState['availableDevices']) {
245
+    return audioInput.concat(audioOutput).concat(videoInput);
246
+}
247
+
179 248
 /**
180 249
  * We want to strip any device details that are not very user friendly, like usb ids put in brackets at the end.
181 250
  *
@@ -240,6 +309,35 @@ export function getVideoDeviceIds(state: IReduxState) {
240 309
     return state['features/base/devices'].availableDevices.videoInput?.map(({ deviceId }) => deviceId);
241 310
 }
242 311
 
312
+/**
313
+ * Converts an array of device info objects into string.
314
+ *
315
+ * @param {MediaDeviceInfo[]} devices - The devices.
316
+ * @returns {string}
317
+ */
318
+function devicesToStr(devices?: MediaDeviceInfo[]) {
319
+    return devices?.map(device => `\t\t${device.label}[${device.deviceId}]`).join('\n');
320
+}
321
+
322
+/**
323
+ * Logs an array of devices.
324
+ *
325
+ * @param {MediaDeviceInfo[]} devices - The array of devices.
326
+ * @param {string} title - The title that will be printed in the log.
327
+ * @returns {void}
328
+ */
329
+export function logDevices(devices: MediaDeviceInfo[], title = '') {
330
+    const deviceList = groupDevicesByKind(devices);
331
+    const audioInputs = devicesToStr(deviceList.audioInput);
332
+    const audioOutputs = devicesToStr(deviceList.audioOutput);
333
+    const videoInputs = devicesToStr(deviceList.videoInput);
334
+
335
+    logger.debug(`${title}:\n`
336
+        + `audioInput:\n${audioInputs}\n`
337
+        + `audioOutput:\n${audioOutputs}\n`
338
+        + `videoInput:\n${videoInputs}`);
339
+}
340
+
243 341
 /**
244 342
  * Set device id of the audio output device which is currently in use.
245 343
  * Empty string stands for default device.

+ 2
- 22
react/features/base/devices/middleware.web.ts Zobrazit soubor

@@ -33,11 +33,10 @@ import {
33 33
 import {
34 34
     areDeviceLabelsInitialized,
35 35
     formatDeviceLabel,
36
-    groupDevicesByKind,
36
+    logDevices,
37 37
     setAudioOutputDeviceId
38 38
 } from './functions';
39 39
 import logger from './logger';
40
-import { IDevicesState } from './types';
41 40
 
42 41
 const JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP = {
43 42
     microphone: {
@@ -62,25 +61,6 @@ const JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP = {
62 61
  */
63 62
 let permissionsListener: Function | undefined;
64 63
 
65
-/**
66
- * Logs the current device list.
67
- *
68
- * @param {Object} deviceList - Whatever is returned by {@link groupDevicesByKind}.
69
- * @returns {string}
70
- */
71
-function logDeviceList(deviceList: IDevicesState['availableDevices']) {
72
-    const devicesToStr = (list?: MediaDeviceInfo[]) =>
73
-        list?.map(device => `\t\t${device.label}[${device.deviceId}]`).join('\n');
74
-    const audioInputs = devicesToStr(deviceList.audioInput);
75
-    const audioOutputs = devicesToStr(deviceList.audioOutput);
76
-    const videoInputs = devicesToStr(deviceList.videoInput);
77
-
78
-    logger.debug('Device list updated:\n'
79
-        + `audioInput:\n${audioInputs}\n`
80
-        + `audioOutput:\n${audioOutputs}\n`
81
-        + `videoInput:\n${videoInputs}`);
82
-}
83
-
84 64
 /**
85 65
  * Implements the middleware of the feature base/devices.
86 66
  *
@@ -199,7 +179,7 @@ MiddlewareRegistry.register(store => next => action => {
199 179
         break;
200 180
     }
201 181
     case UPDATE_DEVICE_LIST:
202
-        logDeviceList(groupDevicesByKind(action.devices));
182
+        logDevices(action.devices, 'Device list updated');
203 183
         if (areDeviceLabelsInitialized(store.getState())) {
204 184
             return _processPendingRequests(store, next, action);
205 185
         }

Načítá se…
Zrušit
Uložit