瀏覽代碼

Implement audio problem detection

dev1
hristoterezov 8 年之前
父節點
當前提交
bcb3253df2

+ 4
- 3
JitsiConference.js 查看文件

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

+ 11
- 0
JitsiConferenceEventManager.js 查看文件

@@ -553,6 +553,17 @@ JitsiConferenceEventManager.prototype.setupStatisticsListeners = function () {
553 553
     conference.statistics.addAudioProblemListener(function (ssrc) {
554 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 569
 module.exports = JitsiConferenceEventManager;

+ 5
- 1
JitsiTrackEvents.js 查看文件

@@ -18,7 +18,11 @@ var JitsiTrackEvents = {
18 18
     /**
19 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 28
 module.exports = JitsiTrackEvents;

+ 22
- 0
modules/RTC/JitsiLocalTrack.js 查看文件

@@ -55,6 +55,10 @@ function JitsiLocalTrack(stream, track, mediaType, videoType, resolution,
55 55
     // enumerateDevices() list.
56 56
     this._trackEnded = false;
57 57
 
58
+    this._bytesSent = null;
59
+
60
+    this._testByteSent = true;
61
+
58 62
     // Currently there is no way to determine with what device track was
59 63
     // created (until getConstraints() support), however we can associate tracks
60 64
     // with real devices obtained from enumerateDevices() call as soon as it's
@@ -445,6 +449,24 @@ JitsiLocalTrack.prototype.getDeviceId = function () {
445 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 471
  * Returns facing mode for video track from camera. For other cases (e.g. audio
450 472
  * track or 'desktop' video track) returns undefined.

+ 42
- 35
modules/statistics/RTPStatsCollector.js 查看文件

@@ -202,16 +202,13 @@ function ConferenceStats() {
202 202
  * @param audioLevelsInterval
203 203
  * @param statsInterval stats refresh interval given in ms.
204 204
  * @param eventEmitter
205
- * @param config {object} supports the following properties: disableAudioLevels,
206
- * disableStats, logStats
207 205
  * @constructor
208 206
  */
209 207
 function StatsCollector(
210 208
         peerconnection,
211 209
         audioLevelsInterval,
212 210
         statsInterval,
213
-        eventEmitter,
214
-        config) {
211
+        eventEmitter) {
215 212
     // StatsCollector depends entirely on the format of the reports returned by
216 213
     // RTCPeerConnection#getStats. Given that the value of
217 214
     // RTCBrowserType#getBrowserType() is very unlikely to change at runtime, it
@@ -247,7 +244,6 @@ function StatsCollector(
247 244
     this.baselineStatsReport = null;
248 245
     this.audioLevelsIntervalId = null;
249 246
     this.eventEmitter = eventEmitter;
250
-    this.config = config || {};
251 247
     this.conferenceStats = new ConferenceStats();
252 248
 
253 249
     /**
@@ -312,33 +308,35 @@ StatsCollector.prototype.errorCallback = function (error) {
312 308
 /**
313 309
  * Starts stats updates.
314 310
  */
315
-StatsCollector.prototype.start = function () {
311
+StatsCollector.prototype.start = function (startAudioLevelStats) {
316 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 340
         this.statsIntervalId = setInterval(
343 341
             function () {
344 342
                 // Interval updates
@@ -372,8 +370,7 @@ StatsCollector.prototype.start = function () {
372 370
         );
373 371
     }
374 372
 
375
-    if (this.config.logStats
376
-            && browserSupported
373
+    if (browserSupported
377 374
             // logging statistics does not support firefox
378 375
             && this._browserType !== RTCBrowserType.RTC_BROWSER_FIREFOX) {
379 376
         this.gatherStatsIntervalId = setInterval(
@@ -507,6 +504,7 @@ StatsCollector.prototype.processStatsReport = function () {
507 504
     }
508 505
 
509 506
     var getStatValue = this._getStatValue;
507
+    byteSentStats = {};
510 508
 
511 509
     for (var idx in this.currentStatsReport) {
512 510
         var now = this.currentStatsReport[idx];
@@ -622,8 +620,16 @@ StatsCollector.prototype.processStatsReport = function () {
622 620
                 = nowBytesTransmitted - getStatValue(before, "bytesReceived");
623 621
         }
624 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 635
         var time = Math.round((now.timestamp - before.timestamp) / 1000);
@@ -687,7 +693,6 @@ StatsCollector.prototype.processStatsReport = function () {
687 693
     Object.keys(this.ssrc2stats).forEach(
688 694
         function (ssrc) {
689 695
             var ssrcStats = this.ssrc2stats[ssrc];
690
-
691 696
             // process package loss stats
692 697
             var ssrc2Loss = ssrcStats.ssrc2Loss;
693 698
             var type = ssrc2Loss.isDownloadStream ? "download" : "upload";
@@ -707,6 +712,8 @@ StatsCollector.prototype.processStatsReport = function () {
707 712
         this
708 713
     );
709 714
 
715
+    this.eventEmitter.emit(StatisticsEvents.BYTE_SENT_STATS, byteSentStats);
716
+
710 717
     this.conferenceStats.bitrate
711 718
       = {"upload": bitrateUpload, "download": bitrateDownload};
712 719
 

+ 18
- 14
modules/statistics/statistics.js 查看文件

@@ -142,16 +142,13 @@ Statistics.analytics = AnalyticsAdapter;
142 142
 Statistics.callsStatsInstances = [];
143 143
 
144 144
 Statistics.prototype.startRemoteStats = function (peerconnection) {
145
-    if(!Statistics.audioLevelsEnabled)
146
-        return;
147
-
148 145
     this.stopRemoteStats();
149 146
 
150 147
     try {
151 148
         this.rtpStats
152 149
             = new RTPStats(peerconnection,
153 150
                     Statistics.audioLevelsInterval, 2000, this.eventEmitter);
154
-        this.rtpStats.start();
151
+        this.rtpStats.start(Statistics.audioLevelsEnabled);
155 152
     } catch (e) {
156 153
         this.rtpStats = null;
157 154
         logger.error('Failed to start collecting remote statistics: ' + e);
@@ -189,10 +186,6 @@ Statistics.prototype.removeAudioLevelListener = function(listener) {
189 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 190
  * Adds listener for detected audio problems.
198 191
  * @param listener the listener.
@@ -201,16 +194,27 @@ Statistics.prototype.addAudioProblemListener = function (listener) {
201 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 201
 Statistics.prototype.removeConnectionStatsListener = function (listener) {
205 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 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 220
 Statistics.stopLocalStats = function (stream) {
@@ -226,7 +230,7 @@ Statistics.stopLocalStats = function (stream) {
226 230
 };
227 231
 
228 232
 Statistics.prototype.stopRemoteStats = function () {
229
-    if (!Statistics.audioLevelsEnabled || !this.rtpStats) {
233
+    if (!this.rtpStats) {
230 234
         return;
231 235
     }
232 236
 

+ 9
- 5
service/statistics/Events.js 查看文件

@@ -1,8 +1,4 @@
1 1
 module.exports = {
2
-    /**
3
-     * An event carrying connection statistics.
4
-     */
5
-    CONNECTION_STATS: "statistics.connectionstats",
6 2
     /**
7 3
      * FIXME: needs documentation.
8 4
      */
@@ -10,5 +6,13 @@ module.exports = {
10 6
     /**
11 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
 };

Loading…
取消
儲存