浏览代码

refactor / address code review

master
Andrei Gavrilescu 5 年前
父节点
当前提交
191da551e3

+ 0
- 143
react/features/base/devices/services/device-detect/ActiveDeviceDetector.js 查看文件

@@ -1,143 +0,0 @@
1
-// @flow
2
-import EventEmitter from 'events';
3
-import { ACTIVE_DEVICE_DETECTED } from './Events';
4
-import logger from '../../logger';
5
-import JitsiMeetJS from '../../../lib-jitsi-meet';
6
-
7
-const JitsiTrackEvents = JitsiMeetJS.events.track;
8
-
9
-// If after 3000 ms the detector did not find any active devices consider that there aren't any usable ones available
10
-// i.e. audioLevel > 0.008
11
-const DETECTION_TIMEOUT = 3000;
12
-
13
-/**
14
- * Detect active input devices based on their audio levels, currently this is very simplistic. It works by simply
15
- * checking all monitored devices for TRACK_AUDIO_LEVEL_CHANGED if a device has a audio level > 0.008 ( 0.008 is
16
- * no input from the perspective of a JitsiLocalTrack ), at which point it triggers a ACTIVE_DEVICE_DETECTED event.
17
- * If there are no devices that meet that criteria for DETECTION_TIMEOUT an event with empty deviceLabel parameter
18
- * will be triggered,
19
- * signaling that no active device was detected.
20
- * TODO Potentially improve the active device detection using rnnoise VAD scoring.
21
- */
22
-export class ActiveDeviceDetector extends EventEmitter {
23
-
24
-    /**
25
-     * Currently monitored devices.
26
-     */
27
-    _availableDevices: Array<Object>;
28
-
29
-    /**
30
-     * State flag, check if the instance was destroyed.
31
-     */
32
-    _destroyed: boolean = false;
33
-
34
-    /**
35
-     * Create active device detector.
36
-     *
37
-     * @param {Array<MediaDeviceInfo>} micDeviceList - Device list that is monitored inside the service.
38
-     *
39
-     * @returns {ActiveDeviceDetector}
40
-     */
41
-    static async create(micDeviceList: Array<MediaDeviceInfo>) {
42
-        const availableDevices = [];
43
-
44
-        try {
45
-            for (const micDevice of micDeviceList) {
46
-                const localTrack = await JitsiMeetJS.createLocalTracks({
47
-                    devices: [ 'audio' ],
48
-                    micDeviceId: micDevice.deviceId
49
-                });
50
-
51
-                // We provide a specific deviceId thus we expect a single JitsiLocalTrack to be returned.
52
-                availableDevices.push(localTrack[0]);
53
-            }
54
-
55
-            return new ActiveDeviceDetector(availableDevices);
56
-        } catch (error) {
57
-            logger.error('Cleaning up remaining JitsiLocalTrack, due to ActiveDeviceDetector create fail!');
58
-
59
-            for (const device of availableDevices) {
60
-                device.stopStream();
61
-            }
62
-
63
-            throw error;
64
-        }
65
-    }
66
-
67
-    /**
68
-     * Constructor.
69
-     *
70
-     * @param {Array<Object>} availableDevices - Device list that is monitored inside the service.
71
-     */
72
-    constructor(availableDevices: Array<Object>) {
73
-        super();
74
-
75
-        this._availableDevices = availableDevices;
76
-
77
-        // Setup event handlers for monitored devices.
78
-        for (const device of this._availableDevices) {
79
-            device.on(JitsiTrackEvents.TRACK_AUDIO_LEVEL_CHANGED, audioLevel => {
80
-                this._handleAudioLevelEvent(device, audioLevel);
81
-            });
82
-        }
83
-
84
-        // Cancel the detection in case no devices was found with audioLevel > 0 in te set timeout.
85
-        setTimeout(this._handleDetectionTimeout.bind(this), DETECTION_TIMEOUT);
86
-    }
87
-
88
-    /**
89
-     * Handle what happens if no device publishes a score in the defined time frame, i.e. Emit an event with empty
90
-     * deviceLabel.
91
-     *
92
-     * @returns {void}
93
-     */
94
-    _handleDetectionTimeout() {
95
-        if (!this._destroyed) {
96
-            this.emit(ACTIVE_DEVICE_DETECTED, { deviceLabel: '',
97
-                audioLevel: 0 });
98
-            this.destroy();
99
-        }
100
-    }
101
-
102
-    /**
103
-     * Handles audio level event generated by JitsiLocalTracks.
104
-     *
105
-     * @param {Object} device - Label of the emitting track.
106
-     * @param {number} audioLevel - Audio level generated by device.
107
-     *
108
-     * @returns {void}
109
-     */
110
-    _handleAudioLevelEvent(device, audioLevel) {
111
-        if (!this._destroyed) {
112
-            // This is a very naive approach but works is most, a more accurate approach would ne to use rnnoise
113
-            // in order to limit the number of false positives.
114
-            // The 0.008 constant is due to how LocalStatsCollector from lib-jitsi-meet publishes audio-levels, in this
115
-            // case 0.008 denotes no input.
116
-            // TODO potentially refactor lib-jitsi-meet to expose this constant as a function. i.e. getSilenceLevel.
117
-            if (audioLevel > 0.008) {
118
-                this.emit(ACTIVE_DEVICE_DETECTED, { deviceId: device.deviceId,
119
-                    deviceLabel: device.track.label,
120
-                    audioLevel });
121
-                this.destroy();
122
-            }
123
-        }
124
-    }
125
-
126
-    /**
127
-     * Destroy the ActiveDeviceDetector, clean up the currently monitored devices associated JitsiLocalTracks.
128
-     *
129
-     * @returns {void}.
130
-     */
131
-    destroy() {
132
-        if (this._destroyed) {
133
-            return;
134
-        }
135
-
136
-        for (const device of this._availableDevices) {
137
-            device.removeAllListeners();
138
-            device.stopStream();
139
-        }
140
-
141
-        this._destroyed = true;
142
-    }
143
-}

+ 0
- 8
react/features/base/devices/services/device-detect/Events.js 查看文件

@@ -1,8 +0,0 @@
1
-// Event triggered when the ActiveDeviceDetector finds an audio device that has audio input.
2
-// Note it does not check if the input is valid or not it simply checks for intensity > 0.008.
3
-// Event structure:
4
-// { deviceId: string,
5
-//   deviceLabel: string,
6
-//   audioLevel: number }
7
-// TO DO. Potentially use rnnoise service to get a more accurate reading.
8
-export const ACTIVE_DEVICE_DETECTED = 'active_device_detected';

+ 0
- 2
react/features/base/devices/services/index.js 查看文件

@@ -1,4 +1,2 @@
1
-export * from './device-detect/ActiveDeviceDetector';
2
-export * from './device-detect/Events';
3 1
 export * from './vad-reporter/Events';
4 2
 export * from './vad-reporter/VADReportingService';

+ 1
- 1
react/features/base/tracks/reducer.js 查看文件

@@ -141,7 +141,7 @@ ReducerRegistry.register('features/base/tracks', (state = [], action) => {
141 141
 ReducerRegistry.register('features/base/no-src-data', (state = {}, action) => {
142 142
     switch (action.type) {
143 143
     case SET_NO_SRC_DATA_NOTI_UID:
144
-        return set(state, 'noSrcDataNotiUid', action.uid);
144
+        return set(state, 'noSrcDataNotificationUid', action.uid);
145 145
 
146 146
     default:
147 147
         return state;

+ 1
- 1
react/features/no-audio-signal/actionTypes.js 查看文件

@@ -8,4 +8,4 @@
8 8
  *     uid: ?number
9 9
  * }
10 10
  */
11
-export const SET_NO_AUDIO_SIGNAL_NOTI_UID = 'SET_NO_AUDIO_SIGNAL_NOTI_UID';
11
+export const SET_NO_AUDIO_SIGNAL_NOTIFICATION_UID = 'SET_NO_AUDIO_SIGNAL_NOTIFICATION_UID';

+ 3
- 3
react/features/no-audio-signal/actions.js 查看文件

@@ -1,6 +1,6 @@
1 1
 // @flow
2 2
 
3
-import { SET_NO_AUDIO_SIGNAL_NOTI_UID } from './actionTypes';
3
+import { SET_NO_AUDIO_SIGNAL_NOTIFICATION_UID } from './actionTypes';
4 4
 
5 5
 /**
6 6
  * Sets UID of the the pending notification to use it when hiding
@@ -9,13 +9,13 @@ import { SET_NO_AUDIO_SIGNAL_NOTI_UID } from './actionTypes';
9 9
  *
10 10
  * @param {?number} uid - The UID of the notification.
11 11
  * @returns {{
12
- *     type: SET_NO_AUDIO_SIGNAL_NOTI_UID,
12
+ *     type: SET_NO_AUDIO_SIGNAL_NOTIFICATION_UID,
13 13
  *     uid: number
14 14
  * }}
15 15
  */
16 16
 export function setNoAudioSignalNotificationUid(uid: ?number) {
17 17
     return {
18
-        type: SET_NO_AUDIO_SIGNAL_NOTI_UID,
18
+        type: SET_NO_AUDIO_SIGNAL_NOTIFICATION_UID,
19 19
         uid
20 20
     };
21 21
 }

+ 69
- 71
react/features/no-audio-signal/middleware.js 查看文件

@@ -3,14 +3,10 @@ import { setNoAudioSignalNotificationUid } from './actions';
3 3
 import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../base/app';
4 4
 import { CONFERENCE_JOINED } from '../base/conference';
5 5
 import {
6
-    ACTIVE_DEVICE_DETECTED,
7
-    ActiveDeviceDetector,
8
-    filterAudioDevices,
9 6
     formatDeviceLabel,
10
-    getAvailableDevices,
11 7
     setAudioInputDevice
12 8
 } from '../base/devices';
13
-import { JitsiConferenceEvents } from '../base/lib-jitsi-meet';
9
+import JitsiMeetJS, { JitsiConferenceEvents } from '../base/lib-jitsi-meet';
14 10
 import { MiddlewareRegistry } from '../base/redux';
15 11
 import { updateSettings } from '../base/settings';
16 12
 import { playSound, registerSound, unregisterSound } from '../base/sounds';
@@ -22,7 +18,7 @@ MiddlewareRegistry.register(store => next => async action => {
22 18
     const result = next(action);
23 19
     const { dispatch, getState } = store;
24 20
     const { conference } = action;
25
-    let audioDetectService = null;
21
+    let confAudioInputState;
26 22
 
27 23
     switch (action.type) {
28 24
     case APP_WILL_MOUNT:
@@ -33,84 +29,86 @@ MiddlewareRegistry.register(store => next => async action => {
33 29
         break;
34 30
 
35 31
     case CONFERENCE_JOINED: {
36
-        conference.on(JitsiConferenceEvents.TRACK_ADDED, track => {
32
+        conference.on(JitsiConferenceEvents.AUDIO_INPUT_STATE_CHANGE, hasAudioInput => {
37 33
             const { noAudioSignalNotificationUid } = getState()['features/no-audio-signal'];
38 34
 
39
-            if (track.isAudioTrack() && track.isLocal()) {
40
-                // In case the device is switched attempt to destroy, this should prevent the notification firing
41
-                // when the device was switched, however it is possible that a user switches the device and the
42
-                // notification from the previous devices pops up, but this will probably happen very rarely and even
43
-                // if it does it's not that disruptive to the ux.
44
-                if (audioDetectService) {
45
-                    audioDetectService.destroy();
46
-                    audioDetectService = null;
47
-                }
48
-
49
-                // When a new track is added hide the current notification is one is displayed, and reset the redux
50
-                // state so that we begin monitoring on the new device as well.
51
-                if (noAudioSignalNotificationUid) {
52
-                    dispatch(hideNotification(noAudioSignalNotificationUid));
53
-                    dispatch(setNoAudioSignalNotificationUid());
54
-                }
35
+            confAudioInputState = hasAudioInput;
36
+
37
+            if (noAudioSignalNotificationUid && hasAudioInput) {
38
+                dispatch(hideNotification(noAudioSignalNotificationUid));
39
+                dispatch(setNoAudioSignalNotificationUid());
55 40
             }
56 41
         });
57 42
         conference.on(JitsiConferenceEvents.NO_AUDIO_INPUT, async () => {
58
-            const { noSrcDataNotiUid } = getState()['features/base/no-src-data'];
43
+            const { noSrcDataNotificationUid } = getState()['features/base/no-src-data'];
59 44
 
60 45
             // In case the 'no data detected from source' notification was already shown, we prevent the
61 46
             // no audio signal notification as it's redundant i.e. it's clear that the users microphone is
62 47
             // muted from system settings.
63
-            if (noSrcDataNotiUid) {
48
+            if (noSrcDataNotificationUid) {
64 49
                 return;
65 50
             }
66 51
 
67
-            const devices = await dispatch(getAvailableDevices());
68
-            const audioDevices = filterAudioDevices(devices);
69
-
70
-            audioDetectService = await ActiveDeviceDetector.create(audioDevices);
71
-
72
-            audioDetectService.on(ACTIVE_DEVICE_DETECTED, detectEvent => {
73
-                let descriptionKey = 'toolbar.noAudioSignalDesc';
74
-                let customActionNameKey = null;
75
-                let customActionHandler = null;
76
-
77
-                // In case the detector picked up a device show a notification with a device suggestion
78
-                if (detectEvent.deviceLabel !== '') {
79
-                    descriptionKey = 'toolbar.noAudioSignalDescSuggestion';
80
-
81
-                    // Preferably the label should be passed as an argument paired with a i18next string, however
82
-                    // at the point of the implementation the showNotification function only supports doing that for
83
-                    // the description.
84
-                    // TODO Add support for arguments to showNotification title and customAction strings.
85
-                    customActionNameKey = `Use ${formatDeviceLabel(detectEvent.deviceLabel)}`;
86
-                    customActionHandler = () => {
87
-                        // Select device callback
88
-                        dispatch(
89
-                                updateSettings({
90
-                                    userSelectedMicDeviceId: detectEvent.deviceId,
91
-                                    userSelectedMicDeviceLabel: detectEvent.deviceLabel
92
-                                })
93
-                        );
94
-
95
-                        dispatch(setAudioInputDevice(detectEvent.deviceId));
96
-                    };
97
-                }
98
-
99
-                const notification = showNotification({
100
-                    titleKey: 'toolbar.noAudioSignalTitle',
101
-                    descriptionKey,
102
-                    customActionNameKey,
103
-                    customActionHandler
104
-                });
105
-
106
-                dispatch(notification);
107
-
108
-                dispatch(playSound(NO_AUDIO_SIGNAL_SOUND_ID));
109
-
110
-                // Store the current notification uid so we can check for this state and hide it in case
111
-                // a new track was added, thus changing the context of the notification
112
-                dispatch(setNoAudioSignalNotificationUid(notification.uid));
52
+            // Force the flag to false in case AUDIO_INPUT_STATE_CHANGE is received after the notification is displayed,
53
+            // thus making sure we check properly if the notification should display.
54
+            confAudioInputState = false;
55
+
56
+
57
+            const activeDevice = await JitsiMeetJS.getActiveAudioDevice();
58
+
59
+            if (confAudioInputState) {
60
+                return;
61
+            }
62
+
63
+            // In case there is a previous notification displayed just hide it.
64
+            const { noAudioSignalNotificationUid } = getState()['features/no-audio-signal'];
65
+
66
+            if (noAudioSignalNotificationUid) {
67
+                dispatch(hideNotification(noAudioSignalNotificationUid));
68
+                dispatch(setNoAudioSignalNotificationUid());
69
+            }
70
+
71
+
72
+            let descriptionKey = 'toolbar.noAudioSignalDesc';
73
+            let customActionNameKey;
74
+            let customActionHandler;
75
+
76
+            // In case the detector picked up a device show a notification with a device suggestion
77
+            if (activeDevice.deviceLabel !== '') {
78
+                descriptionKey = 'toolbar.noAudioSignalDescSuggestion';
79
+
80
+                // Preferably the label should be passed as an argument paired with a i18next string, however
81
+                // at the point of the implementation the showNotification function only supports doing that for
82
+                // the description.
83
+                // TODO Add support for arguments to showNotification title and customAction strings.
84
+                customActionNameKey = `Use ${formatDeviceLabel(activeDevice.deviceLabel)}`;
85
+                customActionHandler = () => {
86
+                    // Select device callback
87
+                    dispatch(
88
+                            updateSettings({
89
+                                userSelectedMicDeviceId: activeDevice.deviceId,
90
+                                userSelectedMicDeviceLabel: activeDevice.deviceLabel
91
+                            })
92
+                    );
93
+
94
+                    dispatch(setAudioInputDevice(activeDevice.deviceId));
95
+                };
96
+            }
97
+
98
+            const notification = showNotification({
99
+                titleKey: 'toolbar.noAudioSignalTitle',
100
+                descriptionKey,
101
+                customActionNameKey,
102
+                customActionHandler
113 103
             });
104
+
105
+            dispatch(notification);
106
+
107
+            dispatch(playSound(NO_AUDIO_SIGNAL_SOUND_ID));
108
+
109
+            // Store the current notification uid so we can check for this state and hide it in case
110
+            // a new track was added, thus changing the context of the notification
111
+            dispatch(setNoAudioSignalNotificationUid(notification.uid));
114 112
         });
115 113
         break;
116 114
     }

+ 2
- 2
react/features/no-audio-signal/reducer.js 查看文件

@@ -2,14 +2,14 @@
2 2
 
3 3
 import { ReducerRegistry, set } from '../base/redux';
4 4
 
5
-import { SET_NO_AUDIO_SIGNAL_NOTI_UID } from './actionTypes';
5
+import { SET_NO_AUDIO_SIGNAL_NOTIFICATION_UID } from './actionTypes';
6 6
 
7 7
 /**
8 8
  * Reduces the redux actions of the feature no audio signal
9 9
  */
10 10
 ReducerRegistry.register('features/no-audio-signal', (state = {}, action) => {
11 11
     switch (action.type) {
12
-    case SET_NO_AUDIO_SIGNAL_NOTI_UID:
12
+    case SET_NO_AUDIO_SIGNAL_NOTIFICATION_UID:
13 13
         return set(state, 'noAudioSignalNotificationUid', action.uid);
14 14
     }
15 15
 

正在加载...
取消
保存