Sfoglia il codice sorgente

Merge pull request #228 from jitsi/detect_local_media_not_working

Implement audio problem detection
dev1
Дамян Минков 9 anni fa
parent
commit
8121bb3e44

+ 4
- 3
JitsiConference.js Vedi File

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
 var Transcriber = require("./modules/transcription/transcriber");
20
 var Transcriber = require("./modules/transcription/transcriber");
20
 
21
 
21
 /**
22
 /**
403
         track.ssrcHandler);
404
         track.ssrcHandler);
404
 
405
 
405
     if(track.isAudioTrack() || (track.isVideoTrack() &&
406
     if(track.isAudioTrack() || (track.isVideoTrack() &&
406
-        track.videoType !== "desktop")) {
407
+        track.videoType !== VideoType.DESKTOP)) {
407
         // Report active device to statistics
408
         // Report active device to statistics
408
         var devices = RTC.getCurrentlyAvailableMediaDevices();
409
         var devices = RTC.getCurrentlyAvailableMediaDevices();
409
         device = devices.find(function (d) {
410
         device = devices.find(function (d) {
450
             // send event for starting screen sharing
451
             // send event for starting screen sharing
451
             // FIXME: we assume we have only one screen sharing track
452
             // FIXME: we assume we have only one screen sharing track
452
             // if we change this we need to fix this check
453
             // if we change this we need to fix this check
453
-            if (track.isVideoTrack() && track.videoType === "desktop")
454
+            if (track.isVideoTrack() && track.videoType === VideoType.DESKTOP)
454
                 this.statistics.sendScreenSharingEvent(true);
455
                 this.statistics.sendScreenSharingEvent(true);
455
 
456
 
456
             this.eventEmitter.emit(JitsiConferenceEvents.TRACK_ADDED, track);
457
             this.eventEmitter.emit(JitsiConferenceEvents.TRACK_ADDED, track);
503
     // send event for stopping screen sharing
504
     // send event for stopping screen sharing
504
     // FIXME: we assume we have only one screen sharing track
505
     // FIXME: we assume we have only one screen sharing track
505
     // if we change this we need to fix this check
506
     // if we change this we need to fix this check
506
-    if (track.isVideoTrack() && track.videoType === "desktop")
507
+    if (track.isVideoTrack() && track.videoType === VideoType.DESKTOP)
507
         this.statistics.sendScreenSharingEvent(false);
508
         this.statistics.sendScreenSharingEvent(false);
508
 
509
 
509
     this.eventEmitter.emit(JitsiConferenceEvents.TRACK_REMOVED, track);
510
     this.eventEmitter.emit(JitsiConferenceEvents.TRACK_REMOVED, track);

+ 13
- 3
JitsiConferenceEventManager.js Vedi File

66
 
66
 
67
                         conference.eventEmitter.emit(
67
                         conference.eventEmitter.emit(
68
                             JitsiConferenceEvents.TRACK_REMOVED, track);
68
                             JitsiConferenceEvents.TRACK_REMOVED, track);
69
-                        
69
+
70
                         if(conference.transcriber){
70
                         if(conference.transcriber){
71
-                            conference.transcriber.removeTrack(track);    
71
+                            conference.transcriber.removeTrack(track);
72
                         }
72
                         }
73
-                        
73
+
74
                         return;
74
                         return;
75
                     }
75
                     }
76
                 }
76
                 }
561
     conference.statistics.addAudioProblemListener(function (ssrc) {
561
     conference.statistics.addAudioProblemListener(function (ssrc) {
562
         conference._reportAudioProblem(ssrc);
562
         conference._reportAudioProblem(ssrc);
563
     });
563
     });
564
+
565
+    conference.statistics.addByteSentStatsListener(function (stats) {
566
+        conference.getLocalTracks().forEach(function (track) {
567
+            var ssrc = track.getSSRC();
568
+            if(!track.isAudioTrack() || !ssrc || !stats.hasOwnProperty(ssrc))
569
+                return;
570
+
571
+            track._setByteSent(stats[ssrc]);
572
+        });
573
+    });
564
 };
574
 };
565
 
575
 
566
 module.exports = JitsiConferenceEventManager;
576
 module.exports = JitsiConferenceEventManager;

+ 5
- 1
JitsiTrackEvents.js Vedi File

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;

+ 30
- 0
modules/RTC/JitsiLocalTrack.js Vedi File

55
     // enumerateDevices() list.
55
     // enumerateDevices() list.
56
     this._trackEnded = false;
56
     this._trackEnded = false;
57
 
57
 
58
+    /**
59
+     * The value of bytes sent received from the statistics module.
60
+     */
61
+    this._bytesSent = null;
62
+
63
+    /**
64
+     * Used only for detection of audio problems. We want to check only once
65
+     * whether the track is sending bytes ot not. This flag is set to false
66
+     * after the check.
67
+     */
68
+    this._testByteSent = true;
69
+
58
     // Currently there is no way to determine with what device track was
70
     // Currently there is no way to determine with what device track was
59
     // created (until getConstraints() support), however we can associate tracks
71
     // created (until getConstraints() support), however we can associate tracks
60
     // with real devices obtained from enumerateDevices() call as soon as it's
72
     // with real devices obtained from enumerateDevices() call as soon as it's
445
     return this._realDeviceId || this.deviceId;
457
     return this._realDeviceId || this.deviceId;
446
 };
458
 };
447
 
459
 
460
+/**
461
+ * Sets the value of bytes sent statistic.
462
+ * @param bytesSent {intiger} the new value
463
+ * NOTE: used only for audio tracks to detect audio issues.
464
+ */
465
+JitsiLocalTrack.prototype._setByteSent = function (bytesSent) {
466
+    this._bytesSent = bytesSent;
467
+    if(this._testByteSent) {
468
+        setTimeout(function () {
469
+            if(this._bytesSent <= 0){
470
+                //we are not receiving anything from the microphone
471
+                this.eventEmitter.emit(JitsiTrackEvents.TRACK_AUDIO_NOT_WORKING);
472
+            }
473
+        }.bind(this), 3000);
474
+        this._testByteSent = false;
475
+    }
476
+}
477
+
448
 /**
478
 /**
449
  * Returns facing mode for video track from camera. For other cases (e.g. audio
479
  * Returns facing mode for video track from camera. For other cases (e.g. audio
450
  * track or 'desktop' video track) returns undefined.
480
  * track or 'desktop' video track) returns undefined.

+ 42
- 35
modules/statistics/RTPStatsCollector.js Vedi File

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 Vedi File

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 Vedi File

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
 };

Loading…
Annulla
Salva