Pārlūkot izejas kodu

Implement audio problem detection

master
hristoterezov 9 gadus atpakaļ
vecāks
revīzija
bcb3253df2

+ 4
- 3
JitsiConference.js Parādīt failu

16
 var ComponentsVersions = require("./modules/version/ComponentsVersions");
16
 var ComponentsVersions = require("./modules/version/ComponentsVersions");
17
 var GlobalOnErrorHandler = require("./modules/util/GlobalOnErrorHandler");
17
 var GlobalOnErrorHandler = require("./modules/util/GlobalOnErrorHandler");
18
 var JitsiConferenceEventManager = require("./JitsiConferenceEventManager");
18
 var JitsiConferenceEventManager = require("./JitsiConferenceEventManager");
19
+var VideoType = require('../../service/RTC/VideoType');
19
 
20
 
20
 /**
21
 /**
21
  * Creates a JitsiConference object with the given name and properties.
22
  * Creates a JitsiConference object with the given name and properties.
379
         track.ssrcHandler);
380
         track.ssrcHandler);
380
 
381
 
381
     if(track.isAudioTrack() || (track.isVideoTrack() &&
382
     if(track.isAudioTrack() || (track.isVideoTrack() &&
382
-        track.videoType !== "desktop")) {
383
+        track.videoType !== VideoType.DESKTOP)) {
383
         // Report active device to statistics
384
         // Report active device to statistics
384
         var devices = RTC.getCurrentlyAvailableMediaDevices();
385
         var devices = RTC.getCurrentlyAvailableMediaDevices();
385
         device = devices.find(function (d) {
386
         device = devices.find(function (d) {
426
             // send event for starting screen sharing
427
             // send event for starting screen sharing
427
             // FIXME: we assume we have only one screen sharing track
428
             // FIXME: we assume we have only one screen sharing track
428
             // if we change this we need to fix this check
429
             // if we change this we need to fix this check
429
-            if (track.isVideoTrack() && track.videoType === "desktop")
430
+            if (track.isVideoTrack() && track.videoType === VideoType.DESKTOP)
430
                 this.statistics.sendScreenSharingEvent(true);
431
                 this.statistics.sendScreenSharingEvent(true);
431
 
432
 
432
             this.eventEmitter.emit(JitsiConferenceEvents.TRACK_ADDED, track);
433
             this.eventEmitter.emit(JitsiConferenceEvents.TRACK_ADDED, track);
479
     // send event for stopping screen sharing
480
     // send event for stopping screen sharing
480
     // FIXME: we assume we have only one screen sharing track
481
     // FIXME: we assume we have only one screen sharing track
481
     // if we change this we need to fix this check
482
     // if we change this we need to fix this check
482
-    if (track.isVideoTrack() && track.videoType === "desktop")
483
+    if (track.isVideoTrack() && track.videoType === VideoType.DESKTOP)
483
         this.statistics.sendScreenSharingEvent(false);
484
         this.statistics.sendScreenSharingEvent(false);
484
 
485
 
485
     this.eventEmitter.emit(JitsiConferenceEvents.TRACK_REMOVED, track);
486
     this.eventEmitter.emit(JitsiConferenceEvents.TRACK_REMOVED, track);

+ 11
- 0
JitsiConferenceEventManager.js Parādīt failu

553
     conference.statistics.addAudioProblemListener(function (ssrc) {
553
     conference.statistics.addAudioProblemListener(function (ssrc) {
554
         conference._reportAudioProblem(ssrc);
554
         conference._reportAudioProblem(ssrc);
555
     });
555
     });
556
+
557
+    conference.statistics.addByteSentStatsListener(function (stats) {
558
+        var tracks = conference.getLocalTracks();
559
+        conference.getLocalTracks().forEach(function (track) {
560
+            var ssrc = track.getSSRC();
561
+            if(!track.isAudioTrack() || !ssrc || !stats.hasOwnProperty(ssrc))
562
+                return;
563
+
564
+            track._setByteSent(stats[ssrc]);
565
+        });
566
+    });
556
 };
567
 };
557
 
568
 
558
 module.exports = JitsiConferenceEventManager;
569
 module.exports = JitsiConferenceEventManager;

+ 5
- 1
JitsiTrackEvents.js Parādīt failu

18
     /**
18
     /**
19
      * The audio output of the track was changed.
19
      * The audio output of the track was changed.
20
      */
20
      */
21
-    TRACK_AUDIO_OUTPUT_CHANGED: "track.audioOutputChanged"
21
+    TRACK_AUDIO_OUTPUT_CHANGED: "track.audioOutputChanged",
22
+    /**
23
+     * Detects that no audio have been sent.
24
+     */
25
+    TRACK_AUDIO_NOT_WORKING: "track.audioNotWorking"
22
 };
26
 };
23
 
27
 
24
 module.exports = JitsiTrackEvents;
28
 module.exports = JitsiTrackEvents;

+ 22
- 0
modules/RTC/JitsiLocalTrack.js Parādīt failu

55
     // enumerateDevices() list.
55
     // enumerateDevices() list.
56
     this._trackEnded = false;
56
     this._trackEnded = false;
57
 
57
 
58
+    this._bytesSent = null;
59
+
60
+    this._testByteSent = true;
61
+
58
     // Currently there is no way to determine with what device track was
62
     // Currently there is no way to determine with what device track was
59
     // created (until getConstraints() support), however we can associate tracks
63
     // created (until getConstraints() support), however we can associate tracks
60
     // with real devices obtained from enumerateDevices() call as soon as it's
64
     // with real devices obtained from enumerateDevices() call as soon as it's
445
     return this._realDeviceId || this.deviceId;
449
     return this._realDeviceId || this.deviceId;
446
 };
450
 };
447
 
451
 
452
+/**
453
+ * Sets the value of bytes sent statistic.
454
+ * @param bytesSent {intiger} the new value
455
+ * NOTE: used only for audio tracks to detect audio issues.
456
+ */
457
+JitsiLocalTrack.prototype._setByteSent = function (bytesSent) {
458
+    this._bytesSent = bytesSent;
459
+    if(this._testByteSent) {
460
+        setTimeout(function () {
461
+            if(this._bytesSent <= 0){
462
+                //we are not receiving anything from the microphone
463
+                this.eventEmitter.emit(JitsiTrackEvents.TRACK_AUDIO_NOT_WORKING);
464
+            }
465
+        }.bind(this), 3000);
466
+        this._testByteSent = false;
467
+    }
468
+}
469
+
448
 /**
470
 /**
449
  * Returns facing mode for video track from camera. For other cases (e.g. audio
471
  * Returns facing mode for video track from camera. For other cases (e.g. audio
450
  * track or 'desktop' video track) returns undefined.
472
  * track or 'desktop' video track) returns undefined.

+ 42
- 35
modules/statistics/RTPStatsCollector.js Parādīt failu

202
  * @param audioLevelsInterval
202
  * @param audioLevelsInterval
203
  * @param statsInterval stats refresh interval given in ms.
203
  * @param statsInterval stats refresh interval given in ms.
204
  * @param eventEmitter
204
  * @param eventEmitter
205
- * @param config {object} supports the following properties: disableAudioLevels,
206
- * disableStats, logStats
207
  * @constructor
205
  * @constructor
208
  */
206
  */
209
 function StatsCollector(
207
 function StatsCollector(
210
         peerconnection,
208
         peerconnection,
211
         audioLevelsInterval,
209
         audioLevelsInterval,
212
         statsInterval,
210
         statsInterval,
213
-        eventEmitter,
214
-        config) {
211
+        eventEmitter) {
215
     // StatsCollector depends entirely on the format of the reports returned by
212
     // StatsCollector depends entirely on the format of the reports returned by
216
     // RTCPeerConnection#getStats. Given that the value of
213
     // RTCPeerConnection#getStats. Given that the value of
217
     // RTCBrowserType#getBrowserType() is very unlikely to change at runtime, it
214
     // RTCBrowserType#getBrowserType() is very unlikely to change at runtime, it
247
     this.baselineStatsReport = null;
244
     this.baselineStatsReport = null;
248
     this.audioLevelsIntervalId = null;
245
     this.audioLevelsIntervalId = null;
249
     this.eventEmitter = eventEmitter;
246
     this.eventEmitter = eventEmitter;
250
-    this.config = config || {};
251
     this.conferenceStats = new ConferenceStats();
247
     this.conferenceStats = new ConferenceStats();
252
 
248
 
253
     /**
249
     /**
312
 /**
308
 /**
313
  * Starts stats updates.
309
  * Starts stats updates.
314
  */
310
  */
315
-StatsCollector.prototype.start = function () {
311
+StatsCollector.prototype.start = function (startAudioLevelStats) {
316
     var self = this;
312
     var self = this;
317
-    this.audioLevelsIntervalId = setInterval(
318
-        function () {
319
-            // Interval updates
320
-            self.peerconnection.getStats(
321
-                function (report) {
322
-                    var results = null;
323
-                    if (!report || !report.result ||
324
-                        typeof report.result != 'function') {
325
-                        results = report;
326
-                    }
327
-                    else {
328
-                        results = report.result();
329
-                    }
330
-                    self.currentAudioLevelsReport = results;
331
-                    self.processAudioLevelReport();
332
-                    self.baselineAudioLevelsReport =
333
-                        self.currentAudioLevelsReport;
334
-                },
335
-                self.errorCallback
336
-            );
337
-        },
338
-        self.audioLevelsIntervalMilis
339
-    );
313
+    if(startAudioLevelStats) {
314
+        this.audioLevelsIntervalId = setInterval(
315
+            function () {
316
+                // Interval updates
317
+                self.peerconnection.getStats(
318
+                    function (report) {
319
+                        var results = null;
320
+                        if (!report || !report.result ||
321
+                            typeof report.result != 'function') {
322
+                            results = report;
323
+                        }
324
+                        else {
325
+                            results = report.result();
326
+                        }
327
+                        self.currentAudioLevelsReport = results;
328
+                        self.processAudioLevelReport();
329
+                        self.baselineAudioLevelsReport =
330
+                            self.currentAudioLevelsReport;
331
+                    },
332
+                    self.errorCallback
333
+                );
334
+            },
335
+            self.audioLevelsIntervalMilis
336
+        );
337
+    }
340
 
338
 
341
-    if (!this.config.disableStats && browserSupported) {
339
+    if (browserSupported) {
342
         this.statsIntervalId = setInterval(
340
         this.statsIntervalId = setInterval(
343
             function () {
341
             function () {
344
                 // Interval updates
342
                 // Interval updates
372
         );
370
         );
373
     }
371
     }
374
 
372
 
375
-    if (this.config.logStats
376
-            && browserSupported
373
+    if (browserSupported
377
             // logging statistics does not support firefox
374
             // logging statistics does not support firefox
378
             && this._browserType !== RTCBrowserType.RTC_BROWSER_FIREFOX) {
375
             && this._browserType !== RTCBrowserType.RTC_BROWSER_FIREFOX) {
379
         this.gatherStatsIntervalId = setInterval(
376
         this.gatherStatsIntervalId = setInterval(
507
     }
504
     }
508
 
505
 
509
     var getStatValue = this._getStatValue;
506
     var getStatValue = this._getStatValue;
507
+    byteSentStats = {};
510
 
508
 
511
     for (var idx in this.currentStatsReport) {
509
     for (var idx in this.currentStatsReport) {
512
         var now = this.currentStatsReport[idx];
510
         var now = this.currentStatsReport[idx];
622
                 = nowBytesTransmitted - getStatValue(before, "bytesReceived");
620
                 = nowBytesTransmitted - getStatValue(before, "bytesReceived");
623
         }
621
         }
624
         nowBytesTransmitted = getStatValue(now, "bytesSent");
622
         nowBytesTransmitted = getStatValue(now, "bytesSent");
625
-        if (nowBytesTransmitted) {
626
-            bytesSent = nowBytesTransmitted - getStatValue(before, "bytesSent");
623
+        if(typeof(nowBytesTransmitted) === "number" ||
624
+            typeof(nowBytesTransmitted) === "string") {
625
+            nowBytesTransmitted = Number(nowBytesTransmitted);
626
+            if(!isNaN(nowBytesTransmitted)){
627
+                byteSentStats[ssrc] = nowBytesTransmitted;
628
+                if (nowBytesTransmitted > 0) {
629
+                    bytesSent = nowBytesTransmitted -
630
+                        getStatValue(before, "bytesSent");
631
+                }
632
+            }
627
         }
633
         }
628
 
634
 
629
         var time = Math.round((now.timestamp - before.timestamp) / 1000);
635
         var time = Math.round((now.timestamp - before.timestamp) / 1000);
687
     Object.keys(this.ssrc2stats).forEach(
693
     Object.keys(this.ssrc2stats).forEach(
688
         function (ssrc) {
694
         function (ssrc) {
689
             var ssrcStats = this.ssrc2stats[ssrc];
695
             var ssrcStats = this.ssrc2stats[ssrc];
690
-
691
             // process package loss stats
696
             // process package loss stats
692
             var ssrc2Loss = ssrcStats.ssrc2Loss;
697
             var ssrc2Loss = ssrcStats.ssrc2Loss;
693
             var type = ssrc2Loss.isDownloadStream ? "download" : "upload";
698
             var type = ssrc2Loss.isDownloadStream ? "download" : "upload";
707
         this
712
         this
708
     );
713
     );
709
 
714
 
715
+    this.eventEmitter.emit(StatisticsEvents.BYTE_SENT_STATS, byteSentStats);
716
+
710
     this.conferenceStats.bitrate
717
     this.conferenceStats.bitrate
711
       = {"upload": bitrateUpload, "download": bitrateDownload};
718
       = {"upload": bitrateUpload, "download": bitrateDownload};
712
 
719
 

+ 18
- 14
modules/statistics/statistics.js Parādīt failu

142
 Statistics.callsStatsInstances = [];
142
 Statistics.callsStatsInstances = [];
143
 
143
 
144
 Statistics.prototype.startRemoteStats = function (peerconnection) {
144
 Statistics.prototype.startRemoteStats = function (peerconnection) {
145
-    if(!Statistics.audioLevelsEnabled)
146
-        return;
147
-
148
     this.stopRemoteStats();
145
     this.stopRemoteStats();
149
 
146
 
150
     try {
147
     try {
151
         this.rtpStats
148
         this.rtpStats
152
             = new RTPStats(peerconnection,
149
             = new RTPStats(peerconnection,
153
                     Statistics.audioLevelsInterval, 2000, this.eventEmitter);
150
                     Statistics.audioLevelsInterval, 2000, this.eventEmitter);
154
-        this.rtpStats.start();
151
+        this.rtpStats.start(Statistics.audioLevelsEnabled);
155
     } catch (e) {
152
     } catch (e) {
156
         this.rtpStats = null;
153
         this.rtpStats = null;
157
         logger.error('Failed to start collecting remote statistics: ' + e);
154
         logger.error('Failed to start collecting remote statistics: ' + e);
189
     this.eventEmitter.removeListener(StatisticsEvents.AUDIO_LEVEL, listener);
186
     this.eventEmitter.removeListener(StatisticsEvents.AUDIO_LEVEL, listener);
190
 };
187
 };
191
 
188
 
192
-Statistics.prototype.addConnectionStatsListener = function (listener) {
193
-    this.eventEmitter.on(StatisticsEvents.CONNECTION_STATS, listener);
194
-};
195
-
196
 /**
189
 /**
197
  * Adds listener for detected audio problems.
190
  * Adds listener for detected audio problems.
198
  * @param listener the listener.
191
  * @param listener the listener.
201
     this.eventEmitter.on(StatisticsEvents.AUDIO_NOT_WORKING, listener);
194
     this.eventEmitter.on(StatisticsEvents.AUDIO_NOT_WORKING, listener);
202
 };
195
 };
203
 
196
 
197
+Statistics.prototype.addConnectionStatsListener = function (listener) {
198
+    this.eventEmitter.on(StatisticsEvents.CONNECTION_STATS, listener);
199
+};
200
+
204
 Statistics.prototype.removeConnectionStatsListener = function (listener) {
201
 Statistics.prototype.removeConnectionStatsListener = function (listener) {
205
     this.eventEmitter.removeListener(StatisticsEvents.CONNECTION_STATS, listener);
202
     this.eventEmitter.removeListener(StatisticsEvents.CONNECTION_STATS, listener);
206
 };
203
 };
207
 
204
 
205
+Statistics.prototype.addByteSentStatsListener = function (listener) {
206
+    this.eventEmitter.on(StatisticsEvents.BYTE_SENT_STATS, listener);
207
+};
208
+
209
+Statistics.prototype.removeByteSentStatsListener = function (listener) {
210
+    this.eventEmitter.removeListener(StatisticsEvents.BYTE_SENT_STATS,
211
+        listener);
212
+};
213
+
208
 Statistics.prototype.dispose = function () {
214
 Statistics.prototype.dispose = function () {
209
-    if(Statistics.audioLevelsEnabled) {
210
-        this.stopRemoteStats();
211
-        if(this.eventEmitter)
212
-            this.eventEmitter.removeAllListeners();
213
-    }
215
+    this.stopRemoteStats();
216
+    if(this.eventEmitter)
217
+        this.eventEmitter.removeAllListeners();
214
 };
218
 };
215
 
219
 
216
 Statistics.stopLocalStats = function (stream) {
220
 Statistics.stopLocalStats = function (stream) {
226
 };
230
 };
227
 
231
 
228
 Statistics.prototype.stopRemoteStats = function () {
232
 Statistics.prototype.stopRemoteStats = function () {
229
-    if (!Statistics.audioLevelsEnabled || !this.rtpStats) {
233
+    if (!this.rtpStats) {
230
         return;
234
         return;
231
     }
235
     }
232
 
236
 

+ 9
- 5
service/statistics/Events.js Parādīt failu

1
 module.exports = {
1
 module.exports = {
2
-    /**
3
-     * An event carrying connection statistics.
4
-     */
5
-    CONNECTION_STATS: "statistics.connectionstats",
6
     /**
2
     /**
7
      * FIXME: needs documentation.
3
      * FIXME: needs documentation.
8
      */
4
      */
10
     /**
6
     /**
11
      * Notifies about audio problem with remote participant.
7
      * Notifies about audio problem with remote participant.
12
      */
8
      */
13
-    AUDIO_NOT_WORKING: "statistics.audio_not_working"
9
+    AUDIO_NOT_WORKING: "statistics.audio_not_working",
10
+    /**
11
+     * An event carrying connection statistics.
12
+     */
13
+    CONNECTION_STATS: "statistics.connectionstats",
14
+    /**
15
+     * An event carrying all statistics by ssrc.
16
+     */
17
+    BYTE_SENT_STATS: "statistics.byte_sent_stats"
14
 };
18
 };

Notiek ielāde…
Atcelt
Saglabāt