Browse Source

fix(AudioProblemDetection):Reduce false positives

dev1
Hristo Terezov 6 years ago
parent
commit
486c61e2a9

+ 61
- 35
modules/statistics/AudioOutputProblemDetector.js View File

10
 const logger = getLogger(__filename);
10
 const logger = getLogger(__filename);
11
 
11
 
12
 /**
12
 /**
13
- * Number of remote samples that will be used for comparison with local ones.
13
+ * Number of local samples that will be used for comparison before and after the remote sample is received.
14
  */
14
  */
15
-const NUMBER_OF_REMOTE_SAMPLES = 3;
15
+const NUMBER_OF_LOCAL_SAMPLES = 2;
16
 
16
 
17
 /**
17
 /**
18
  * Collects the average audio levels per participant from the local stats and the stats received by every remote
18
  * Collects the average audio levels per participant from the local stats and the stats received by every remote
27
      */
27
      */
28
     constructor(conference) {
28
     constructor(conference) {
29
         this._conference = conference;
29
         this._conference = conference;
30
-        this._lastReceivedAudioLevel = {};
30
+        this._localAudioLevelCache = {};
31
         this._reportedParticipants = [];
31
         this._reportedParticipants = [];
32
+        this._audioProblemCandidates = {};
33
+        this._numberOfRemoteAudioLevelsReceived = {};
32
         this._onLocalAudioLevelsReport = this._onLocalAudioLevelsReport.bind(this);
34
         this._onLocalAudioLevelsReport = this._onLocalAudioLevelsReport.bind(this);
33
         this._onRemoteAudioLevelReceived = this._onRemoteAudioLevelReceived.bind(this);
35
         this._onRemoteAudioLevelReceived = this._onRemoteAudioLevelReceived.bind(this);
34
         this._clearUserData = this._clearUserData.bind(this);
36
         this._clearUserData = this._clearUserData.bind(this);
45
      * @returns {void}
47
      * @returns {void}
46
      */
48
      */
47
     _onRemoteAudioLevelReceived(userID, { avgAudioLevels }) {
49
     _onRemoteAudioLevelReceived(userID, { avgAudioLevels }) {
48
-        if (this._reportedParticipants.indexOf(userID) !== -1) {
50
+        const numberOfReports = (this._numberOfRemoteAudioLevelsReceived[userID] + 1) || 0;
51
+
52
+        this._numberOfRemoteAudioLevelsReceived[userID] = numberOfReports;
53
+
54
+        if (this._reportedParticipants.indexOf(userID) !== -1 || (userID in this._audioProblemCandidates)
55
+                || avgAudioLevels <= 0 || numberOfReports < 3) {
49
             return;
56
             return;
50
         }
57
         }
51
 
58
 
52
-        if (!Array.isArray(this._lastReceivedAudioLevel[userID])) {
53
-            this._lastReceivedAudioLevel[userID] = [ ];
54
-        } else if (this._lastReceivedAudioLevel[userID].length >= NUMBER_OF_REMOTE_SAMPLES) {
55
-            this._lastReceivedAudioLevel[userID].shift();
59
+        const participant = this._conference.getParticipantById(userID);
60
+
61
+        if (participant) {
62
+            const tracks = participant.getTracksByMediaType(MediaType.AUDIO);
63
+
64
+            if (tracks.length > 0 && participant.isAudioMuted()) {
65
+                // We don't need to report an error if everything seems fine with the participant and its tracks but
66
+                // the participant is audio muted. Since those are average audio levels we potentially can receive non
67
+                // zero values for muted track.
68
+                return;
69
+            }
56
         }
70
         }
57
 
71
 
58
-        this._lastReceivedAudioLevel[userID].push(avgAudioLevels);
72
+        const localAudioLevels = this._localAudioLevelCache[userID];
59
 
73
 
74
+        if (!Array.isArray(localAudioLevels) || localAudioLevels.every(audioLevel => audioLevel === 0)) {
75
+            this._audioProblemCandidates[userID] = {
76
+                remoteAudioLevels: avgAudioLevels,
77
+                localAudioLevels: []
78
+            };
79
+        }
60
     }
80
     }
61
 
81
 
62
     /**
82
     /**
71
             return;
91
             return;
72
         }
92
         }
73
 
93
 
74
-        Object.keys(this._lastReceivedAudioLevel).forEach(userID => {
94
+        Object.keys(avgAudioLevels).forEach(userID => {
75
             if (this._reportedParticipants.indexOf(userID) !== -1) {
95
             if (this._reportedParticipants.indexOf(userID) !== -1) {
76
-                // Do not report the participant again.
77
                 return;
96
                 return;
78
             }
97
             }
79
 
98
 
80
-            const remoteAudioLevels = this._lastReceivedAudioLevel[userID];
81
-            const participant = this._conference.getParticipantById(userID);
99
+            const localAudioLevels = this._localAudioLevelCache[userID];
82
 
100
 
83
-            if (participant) {
84
-                const tracks = participant.getTracksByMediaType(MediaType.AUDIO);
101
+            if (!Array.isArray(localAudioLevels)) {
102
+                this._localAudioLevelCache[userID] = [ ];
103
+            } else if (localAudioLevels.length >= NUMBER_OF_LOCAL_SAMPLES) {
104
+                localAudioLevels.shift();
105
+            }
85
 
106
 
86
-                if (tracks.length > 0 && participant.isAudioMuted()) {
87
-                    // We don't need to report an error if everything seems fine with the participant and its tracks but
88
-                    // the participant is audio muted.
89
-                    return;
107
+            this._localAudioLevelCache[userID].push(avgAudioLevels[userID]);
108
+        });
109
+
110
+
111
+        Object.keys(this._audioProblemCandidates).forEach(userID => {
112
+            const { localAudioLevels, remoteAudioLevels } = this._audioProblemCandidates[userID];
113
+
114
+            localAudioLevels.push(avgAudioLevels[userID]);
115
+
116
+            if (localAudioLevels.length === NUMBER_OF_LOCAL_SAMPLES) {
117
+                if (localAudioLevels.every(audioLevel => typeof audioLevel === 'undefined' || audioLevel === 0)) {
118
+                    const localAudioLevelsString = JSON.stringify(localAudioLevels);
119
+
120
+                    Statistics.sendAnalytics(
121
+                        createAudioOutputProblemEvent(userID, localAudioLevelsString, remoteAudioLevels));
122
+                    logger.warn(`A potential problem is detected with the audio output for participant ${
123
+                        userID}, local audio levels: ${localAudioLevelsString}, remote audio levels: ${
124
+                        remoteAudioLevels}`);
125
+                    this._reportedParticipants.push(userID);
126
+                    this._clearUserData(userID);
90
                 }
127
                 }
91
-            }
92
 
128
 
93
-            if ((!(userID in avgAudioLevels) || avgAudioLevels[userID] === 0)
94
-                    && Array.isArray(remoteAudioLevels)
95
-                    && remoteAudioLevels.length === NUMBER_OF_REMOTE_SAMPLES
96
-                    && remoteAudioLevels.every(audioLevel => audioLevel > 0)) {
97
-                const remoteAudioLevelsString = JSON.stringify(remoteAudioLevels);
98
-
99
-                Statistics.sendAnalytics(
100
-                    createAudioOutputProblemEvent(userID, avgAudioLevels[userID], remoteAudioLevelsString));
101
-                logger.warn(`A potential problem is detected with the audio output for participant ${
102
-                    userID}, local audio levels: ${avgAudioLevels[userID]}, remote audio levels: ${
103
-                    remoteAudioLevelsString}`);
104
-                this._reportedParticipants.push(userID);
105
-                this._clearUserData();
129
+                delete this._audioProblemCandidates[userID];
106
             }
130
             }
107
         });
131
         });
108
     }
132
     }
114
      * @returns {void}
138
      * @returns {void}
115
      */
139
      */
116
     _clearUserData(userID) {
140
     _clearUserData(userID) {
117
-        delete this._lastReceivedAudioLevel[userID];
141
+        delete this._localAudioLevelCache[userID];
118
     }
142
     }
119
 
143
 
120
     /**
144
     /**
126
         this._conference.off(ConnectionQualityEvents.REMOTE_STATS_UPDATED, this._onRemoteAudioLevelReceived);
150
         this._conference.off(ConnectionQualityEvents.REMOTE_STATS_UPDATED, this._onRemoteAudioLevelReceived);
127
         this._conference.off(ConferenceEvents.USER_LEFT, this._clearUserData);
151
         this._conference.off(ConferenceEvents.USER_LEFT, this._clearUserData);
128
         this._conference.statistics.removeConnectionStatsListener(this._onLocalAudioLevelsReport);
152
         this._conference.statistics.removeConnectionStatsListener(this._onLocalAudioLevelsReport);
129
-        this._lastReceivedAudioLevel = undefined;
153
+        this._localAudioLevelCache = undefined;
154
+        this._audioProblemCandidates = undefined;
130
         this._reportedParticipants = undefined;
155
         this._reportedParticipants = undefined;
156
+        this._numberOfRemoteAudioLevelsReceived = undefined;
131
         this._conference = undefined;
157
         this._conference = undefined;
132
     }
158
     }
133
 }
159
 }

+ 3
- 3
service/statistics/AnalyticsEvents.js View File

475
  * the local audio levels and the remote audio levels that triggered the event).
475
  * the local audio levels and the remote audio levels that triggered the event).
476
  *
476
  *
477
  * @param {string} userID - The user id of the affected participant.
477
  * @param {string} userID - The user id of the affected participant.
478
- * @param {*} localAudioLevel - The local audio levels.
478
+ * @param {*} localAudioLevels - The local audio levels.
479
  * @param {*} remoteAudioLevels - The audio levels received from the participant.
479
  * @param {*} remoteAudioLevels - The audio levels received from the participant.
480
  */
480
  */
481
-export function createAudioOutputProblemEvent(userID, localAudioLevel, remoteAudioLevels) {
481
+export function createAudioOutputProblemEvent(userID, localAudioLevels, remoteAudioLevels) {
482
     return {
482
     return {
483
         type: TYPE_OPERATIONAL,
483
         type: TYPE_OPERATIONAL,
484
         action: 'audio.output.problem',
484
         action: 'audio.output.problem',
485
         attributes: {
485
         attributes: {
486
             userID,
486
             userID,
487
-            localAudioLevel,
487
+            localAudioLevels,
488
             remoteAudioLevels
488
             remoteAudioLevels
489
         }
489
         }
490
     };
490
     };

Loading…
Cancel
Save