Переглянути джерело

fix(AudioProblemDetection):Reduce false positives

dev1
Hristo Terezov 5 роки тому
джерело
коміт
486c61e2a9

+ 61
- 35
modules/statistics/AudioOutputProblemDetector.js Переглянути файл

@@ -10,9 +10,9 @@ import Statistics from './statistics';
10 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 18
  * Collects the average audio levels per participant from the local stats and the stats received by every remote
@@ -27,8 +27,10 @@ export default class AudioOutputProblemDetector {
27 27
      */
28 28
     constructor(conference) {
29 29
         this._conference = conference;
30
-        this._lastReceivedAudioLevel = {};
30
+        this._localAudioLevelCache = {};
31 31
         this._reportedParticipants = [];
32
+        this._audioProblemCandidates = {};
33
+        this._numberOfRemoteAudioLevelsReceived = {};
32 34
         this._onLocalAudioLevelsReport = this._onLocalAudioLevelsReport.bind(this);
33 35
         this._onRemoteAudioLevelReceived = this._onRemoteAudioLevelReceived.bind(this);
34 36
         this._clearUserData = this._clearUserData.bind(this);
@@ -45,18 +47,36 @@ export default class AudioOutputProblemDetector {
45 47
      * @returns {void}
46 48
      */
47 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 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,38 +91,42 @@ export default class AudioOutputProblemDetector {
71 91
             return;
72 92
         }
73 93
 
74
-        Object.keys(this._lastReceivedAudioLevel).forEach(userID => {
94
+        Object.keys(avgAudioLevels).forEach(userID => {
75 95
             if (this._reportedParticipants.indexOf(userID) !== -1) {
76
-                // Do not report the participant again.
77 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,7 +138,7 @@ export default class AudioOutputProblemDetector {
114 138
      * @returns {void}
115 139
      */
116 140
     _clearUserData(userID) {
117
-        delete this._lastReceivedAudioLevel[userID];
141
+        delete this._localAudioLevelCache[userID];
118 142
     }
119 143
 
120 144
     /**
@@ -126,8 +150,10 @@ export default class AudioOutputProblemDetector {
126 150
         this._conference.off(ConnectionQualityEvents.REMOTE_STATS_UPDATED, this._onRemoteAudioLevelReceived);
127 151
         this._conference.off(ConferenceEvents.USER_LEFT, this._clearUserData);
128 152
         this._conference.statistics.removeConnectionStatsListener(this._onLocalAudioLevelsReport);
129
-        this._lastReceivedAudioLevel = undefined;
153
+        this._localAudioLevelCache = undefined;
154
+        this._audioProblemCandidates = undefined;
130 155
         this._reportedParticipants = undefined;
156
+        this._numberOfRemoteAudioLevelsReceived = undefined;
131 157
         this._conference = undefined;
132 158
     }
133 159
 }

+ 3
- 3
service/statistics/AnalyticsEvents.js Переглянути файл

@@ -475,16 +475,16 @@ export const createRttByRegionEvent = function(attributes) {
475 475
  * the local audio levels and the remote audio levels that triggered the event).
476 476
  *
477 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 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 482
     return {
483 483
         type: TYPE_OPERATIONAL,
484 484
         action: 'audio.output.problem',
485 485
         attributes: {
486 486
             userID,
487
-            localAudioLevel,
487
+            localAudioLevels,
488 488
             remoteAudioLevels
489 489
         }
490 490
     };

Завантаження…
Відмінити
Зберегти