瀏覽代碼

Merge pull request #114 from jitsi/remove-stats-repetitions

Remove repetitions
master
Paweł Domas 9 年之前
父節點
當前提交
803dd95c19
共有 2 個文件被更改,包括 222 次插入167 次删除
  1. 174
    115
      modules/statistics/RTPStatsCollector.js
  2. 48
    52
      modules/statistics/statistics.js

+ 174
- 115
modules/statistics/RTPStatsCollector.js 查看文件

9
 var browserSupported = RTCBrowserType.isChrome() ||
9
 var browserSupported = RTCBrowserType.isChrome() ||
10
         RTCBrowserType.isOpera() || RTCBrowserType.isFirefox();
10
         RTCBrowserType.isOpera() || RTCBrowserType.isFirefox();
11
 
11
 
12
-var keyMap = {};
13
-keyMap[RTCBrowserType.RTC_BROWSER_FIREFOX] = {
12
+/**
13
+ * The LibJitsiMeet browser-agnostic names of the browser-specific keys reported
14
+ * by RTCPeerConnection#getStats mapped by RTCBrowserType.
15
+ */
16
+var KEYS_BY_BROWSER_TYPE = {};
17
+KEYS_BY_BROWSER_TYPE[RTCBrowserType.RTC_BROWSER_FIREFOX] = {
14
     "ssrc": "ssrc",
18
     "ssrc": "ssrc",
15
     "packetsReceived": "packetsReceived",
19
     "packetsReceived": "packetsReceived",
16
     "packetsLost": "packetsLost",
20
     "packetsLost": "packetsLost",
18
     "bytesReceived": "bytesReceived",
22
     "bytesReceived": "bytesReceived",
19
     "bytesSent": "bytesSent"
23
     "bytesSent": "bytesSent"
20
 };
24
 };
21
-keyMap[RTCBrowserType.RTC_BROWSER_CHROME] = {
25
+KEYS_BY_BROWSER_TYPE[RTCBrowserType.RTC_BROWSER_CHROME] = {
22
     "receiveBandwidth": "googAvailableReceiveBandwidth",
26
     "receiveBandwidth": "googAvailableReceiveBandwidth",
23
     "sendBandwidth": "googAvailableSendBandwidth",
27
     "sendBandwidth": "googAvailableSendBandwidth",
24
     "remoteAddress": "googRemoteAddress",
28
     "remoteAddress": "googRemoteAddress",
38
     "audioInputLevel": "audioInputLevel",
42
     "audioInputLevel": "audioInputLevel",
39
     "audioOutputLevel": "audioOutputLevel"
43
     "audioOutputLevel": "audioOutputLevel"
40
 };
44
 };
41
-keyMap[RTCBrowserType.RTC_BROWSER_OPERA] =
42
-    keyMap[RTCBrowserType.RTC_BROWSER_CHROME];
43
-keyMap[RTCBrowserType.RTC_BROWSER_IEXPLORER] =
44
-    keyMap[RTCBrowserType.RTC_BROWSER_CHROME];
45
-keyMap[RTCBrowserType.RTC_BROWSER_SAFARI] =
46
-    keyMap[RTCBrowserType.RTC_BROWSER_CHROME];
45
+KEYS_BY_BROWSER_TYPE[RTCBrowserType.RTC_BROWSER_OPERA] =
46
+    KEYS_BY_BROWSER_TYPE[RTCBrowserType.RTC_BROWSER_CHROME];
47
+KEYS_BY_BROWSER_TYPE[RTCBrowserType.RTC_BROWSER_IEXPLORER] =
48
+    KEYS_BY_BROWSER_TYPE[RTCBrowserType.RTC_BROWSER_CHROME];
49
+KEYS_BY_BROWSER_TYPE[RTCBrowserType.RTC_BROWSER_SAFARI] =
50
+    KEYS_BY_BROWSER_TYPE[RTCBrowserType.RTC_BROWSER_CHROME];
47
 /**
51
 /**
48
  * Calculates packet lost percent using the number of lost packets and the
52
  * Calculates packet lost percent using the number of lost packets and the
49
  * number of all packet.
53
  * number of all packet.
57
     return Math.round((lostPackets/totalPackets)*100);
61
     return Math.round((lostPackets/totalPackets)*100);
58
 }
62
 }
59
 
63
 
60
-function getStatValue(item, name) {
61
-    var browserType = RTCBrowserType.getBrowserType();
62
-    if (!keyMap[browserType][name])
63
-        throw "The property isn't supported!";
64
-    var key = keyMap[browserType][name];
65
-    return (RTCBrowserType.isChrome() || RTCBrowserType.isOpera()) ?
66
-        item.stat(key) : item[key];
67
-}
68
-
69
 function formatAudioLevel(audioLevel) {
64
 function formatAudioLevel(audioLevel) {
70
     return Math.min(Math.max(audioLevel, 0), 1);
65
     return Math.min(Math.max(audioLevel, 0), 1);
71
 }
66
 }
74
  * Checks whether a certain record should be included in the logged statistics.
69
  * Checks whether a certain record should be included in the logged statistics.
75
  */
70
  */
76
 function acceptStat(reportId, reportType, statName) {
71
 function acceptStat(reportId, reportType, statName) {
77
-    if (reportType == "googCandidatePair" && statName == "googChannelId")
78
-        return false;
72
+    if (reportType == "googCandidatePair") {
73
+        if (statName == "googChannelId")
74
+            return false;
79
 
75
 
80
-    if (reportType == "ssrc") {
76
+    } else if (reportType == "ssrc") {
81
         if (statName == "googTrackId" ||
77
         if (statName == "googTrackId" ||
82
             statName == "transportId" ||
78
             statName == "transportId" ||
83
             statName == "ssrc")
79
             statName == "ssrc")
91
  * Checks whether a certain record should be included in the logged statistics.
87
  * Checks whether a certain record should be included in the logged statistics.
92
  */
88
  */
93
 function acceptReport(id, type) {
89
 function acceptReport(id, type) {
90
+    if (type == "googComponent")
91
+        return false;
92
+
94
     if (id.substring(0, 15) == "googCertificate" ||
93
     if (id.substring(0, 15) == "googCertificate" ||
95
         id.substring(0, 9) == "googTrack" ||
94
         id.substring(0, 9) == "googTrack" ||
96
         id.substring(0, 20) == "googLibjingleSession")
95
         id.substring(0, 20) == "googLibjingleSession")
97
         return false;
96
         return false;
98
 
97
 
99
-    if (type == "googComponent")
100
-        return false;
101
-
102
     return true;
98
     return true;
103
 }
99
 }
104
 
100
 
106
  * Peer statistics data holder.
102
  * Peer statistics data holder.
107
  * @constructor
103
  * @constructor
108
  */
104
  */
109
-function PeerStats()
110
-{
105
+function PeerStats() {
111
     this.ssrc2Loss = {};
106
     this.ssrc2Loss = {};
112
     this.ssrc2AudioLevel = {};
107
     this.ssrc2AudioLevel = {};
113
     this.ssrc2bitrate = {
108
     this.ssrc2bitrate = {
167
 
162
 
168
 function ConferenceStats() {
163
 function ConferenceStats() {
169
 
164
 
170
-
171
     /**
165
     /**
172
      * The bandwidth
166
      * The bandwidth
173
      * @type {{}}
167
      * @type {{}}
186
      */
180
      */
187
     this.packetLoss = null;
181
     this.packetLoss = null;
188
 
182
 
189
-
190
     /**
183
     /**
191
      * Array with the transport information.
184
      * Array with the transport information.
192
      * @type {Array}
185
      * @type {Array}
201
  * is done <tt>audioLevelsUpdateCallback</tt> is called with <tt>this</tt>
194
  * is done <tt>audioLevelsUpdateCallback</tt> is called with <tt>this</tt>
202
  * instance as an event source.
195
  * instance as an event source.
203
  *
196
  *
204
- * @param peerconnection webRTC peer connection object.
205
- * @param interval stats refresh interval given in ms.
206
- * @param {function(StatsCollector)} audioLevelsUpdateCallback the callback
207
- * called on stats update.
208
- * @param config {object} supports the following properties - disableAudioLevels, disableStats, logStats
197
+ * @param peerconnection WebRTC PeerConnection object.
198
+ * @param audioLevelsInterval
199
+ * @param statsInterval stats refresh interval given in ms.
200
+ * @param eventEmitter
201
+ * @param config {object} supports the following properties: disableAudioLevels,
202
+ * disableStats, logStats
209
  * @constructor
203
  * @constructor
210
  */
204
  */
211
-function StatsCollector(peerconnection, audioLevelsInterval, statsInterval, eventEmitter, config)
212
-{
205
+function StatsCollector(
206
+        peerconnection,
207
+        audioLevelsInterval,
208
+        statsInterval,
209
+        eventEmitter,
210
+        config) {
211
+    // StatsCollector depends entirely on the format of the reports returned by
212
+    // RTCPeerConnection#getStats. Given that the value of
213
+    // RTCBrowserType#getBrowserType() is very unlikely to change at runtime, it
214
+    // makes sense to discover whether StatsCollector supports the executing
215
+    // browser as soon as possible. Otherwise, (1) getStatValue would have to
216
+    // needlessly check a "static" condition multiple times very very often and
217
+    // (2) the lack of support for the executing browser would be discovered and
218
+    // reported multiple times very very often too late in the execution in some
219
+    // totally unrelated callback.
220
+    /**
221
+     * The RTCBrowserType supported by this StatsCollector. In other words, the
222
+     * RTCBrowserType of the browser which initialized this StatsCollector
223
+     * instance.
224
+     * @private
225
+     */
226
+    this._browserType = RTCBrowserType.getBrowserType();
227
+    var keys = KEYS_BY_BROWSER_TYPE[this._browserType];
228
+    if (!keys)
229
+        throw "The browser type '" + this._browserType + "' isn't supported!";
230
+    /**
231
+     * The function which is to be used to retrieve the value associated in a
232
+     * report returned by RTCPeerConnection#getStats with a LibJitsiMeet
233
+     * browser-agnostic name/key.
234
+     * @function
235
+     * @private
236
+     */
237
+    this._getStatValue = this._defineGetStatValueMethod(keys);
238
+
213
     this.peerconnection = peerconnection;
239
     this.peerconnection = peerconnection;
214
     this.baselineAudioLevelsReport = null;
240
     this.baselineAudioLevelsReport = null;
215
     this.currentAudioLevelsReport = null;
241
     this.currentAudioLevelsReport = null;
233
     /**
259
     /**
234
      * Stores the statistics which will be send to the focus to be logged.
260
      * Stores the statistics which will be send to the focus to be logged.
235
      */
261
      */
236
-    this.statsToBeLogged =
237
-    {
262
+    this.statsToBeLogged = {
238
         timestamps: [],
263
         timestamps: [],
239
         stats: {}
264
         stats: {}
240
     };
265
     };
259
         this.audioLevelsIntervalId = null;
284
         this.audioLevelsIntervalId = null;
260
     }
285
     }
261
 
286
 
262
-    if (this.statsIntervalId)
263
-    {
287
+    if (this.statsIntervalId) {
264
         clearInterval(this.statsIntervalId);
288
         clearInterval(this.statsIntervalId);
265
         this.statsIntervalId = null;
289
         this.statsIntervalId = null;
266
     }
290
     }
267
 
291
 
268
-    if(this.gatherStatsIntervalId)
269
-    {
292
+    if (this.gatherStatsIntervalId) {
270
         clearInterval(this.gatherStatsIntervalId);
293
         clearInterval(this.gatherStatsIntervalId);
271
         this.gatherStatsIntervalId = null;
294
         this.gatherStatsIntervalId = null;
272
     }
295
     }
276
  * Callback passed to <tt>getStats</tt> method.
299
  * Callback passed to <tt>getStats</tt> method.
277
  * @param error an error that occurred on <tt>getStats</tt> call.
300
  * @param error an error that occurred on <tt>getStats</tt> call.
278
  */
301
  */
279
-StatsCollector.prototype.errorCallback = function (error)
280
-{
302
+StatsCollector.prototype.errorCallback = function (error) {
281
     logger.error("Get stats error", error);
303
     logger.error("Get stats error", error);
282
     this.stop();
304
     this.stop();
283
 };
305
 };
285
 /**
307
 /**
286
  * Starts stats updates.
308
  * Starts stats updates.
287
  */
309
  */
288
-StatsCollector.prototype.start = function ()
289
-{
310
+StatsCollector.prototype.start = function () {
290
     var self = this;
311
     var self = this;
291
     this.audioLevelsIntervalId = setInterval(
312
     this.audioLevelsIntervalId = setInterval(
292
         function () {
313
         function () {
347
         );
368
         );
348
     }
369
     }
349
 
370
 
350
-    // logging statistics does not support firefox
351
-    if (this.config.logStats && (browserSupported && !RTCBrowserType.isFirefox())) {
371
+    if (this.config.logStats
372
+            && browserSupported
373
+            // logging statistics does not support firefox
374
+            && this._browserType !== RTCBrowserType.RTC_BROWSER_FIREFOX) {
352
         this.gatherStatsIntervalId = setInterval(
375
         this.gatherStatsIntervalId = setInterval(
353
             function () {
376
             function () {
354
                 self.peerconnection.getStats(
377
                 self.peerconnection.getStats(
406
    this.statsToBeLogged.timestamps = [];
429
    this.statsToBeLogged.timestamps = [];
407
 };
430
 };
408
 
431
 
432
+/**
433
+ * Defines a function which (1) is to be used as a StatsCollector method and (2)
434
+ * gets the value from a specific report returned by RTCPeerConnection#getStats
435
+ * associated with a LibJitsiMeet browser-agnostic name.
436
+ *
437
+ * @param {Object.<string,string>} keys the map of LibJitsi browser-agnostic
438
+ * names to RTCPeerConnection#getStats browser-specific keys
439
+ */
440
+StatsCollector.prototype._defineGetStatValueMethod = function (keys) {
441
+    // Define the function which converts a LibJitsiMeet browser-asnostic name
442
+    // to a browser-specific key of a report returned by
443
+    // RTCPeerConnection#getStats.
444
+    var keyFromName = function (name) {
445
+        var key = keys[name];
446
+        if (key)
447
+            return key;
448
+        else
449
+            throw "The property '" + name + "' isn't supported!";
450
+    };
451
+
452
+    // Define the function which retrieves the value from a specific report
453
+    // returned by RTCPeerConnection#getStats associated with a given
454
+    // browser-specific key.
455
+    var itemStatByKey;
456
+    switch (this._browserType) {
457
+    case RTCBrowserType.RTC_BROWSER_CHROME:
458
+    case RTCBrowserType.RTC_BROWSER_OPERA:
459
+        // TODO What about other types of browser which are based on Chrome such
460
+        // as NW.js? Every time we want to support a new type browser we have to
461
+        // go and add more conditions (here and in multiple other places).
462
+        // Cannot we do a feature detection instead of a browser type check? For
463
+        // example, if item has a stat property of type function, then it's very
464
+        // likely that whoever defined it wanted you to call it in order to
465
+        // retrieve the value associated with a specific key.
466
+        itemStatByKey = function (item, key) { return item.stat(key) };
467
+        break;
468
+    default:
469
+        itemStatByKey = function (item, key) { return item[key] };
470
+    }
471
+
472
+    // Compose the 2 functions defined above to get a function which retrieves
473
+    // the value from a specific report returned by RTCPeerConnection#getStats
474
+    // associated with a specific LibJitsiMeet browser-agnostic name.
475
+    return function (item, name) {
476
+        return itemStatByKey(item, keyFromName(name))
477
+    };
478
+};
409
 
479
 
410
 /**
480
 /**
411
  * Stats processing logic.
481
  * Stats processing logic.
415
         return;
485
         return;
416
     }
486
     }
417
 
487
 
488
+    var getStatValue = this._getStatValue;
489
+
418
     for (var idx in this.currentStatsReport) {
490
     for (var idx in this.currentStatsReport) {
419
         var now = this.currentStatsReport[idx];
491
         var now = this.currentStatsReport[idx];
420
         try {
492
         try {
421
-            if (getStatValue(now, 'receiveBandwidth') ||
422
-                getStatValue(now, 'sendBandwidth')) {
493
+            var receiveBandwidth = getStatValue(now, 'receiveBandwidth');
494
+            var sendBandwidth = getStatValue(now, 'sendBandwidth');
495
+            if (receiveBandwidth || sendBandwidth) {
423
                 this.conferenceStats.bandwidth = {
496
                 this.conferenceStats.bandwidth = {
424
-                    "download": Math.round(
425
-                            (getStatValue(now, 'receiveBandwidth')) / 1000),
426
-                    "upload": Math.round(
427
-                            (getStatValue(now, 'sendBandwidth')) / 1000)
497
+                    "download": Math.round(receiveBandwidth / 1000),
498
+                    "upload": Math.round(sendBandwidth / 1000)
428
                 };
499
                 };
429
             }
500
             }
430
         }
501
         }
432
 
503
 
433
         if(now.type == 'googCandidatePair')
504
         if(now.type == 'googCandidatePair')
434
         {
505
         {
435
-            var ip, type, localIP, active;
506
+            var ip, type, localip, active;
436
             try {
507
             try {
437
                 ip = getStatValue(now, 'remoteAddress');
508
                 ip = getStatValue(now, 'remoteAddress');
438
                 type = getStatValue(now, "transportType");
509
                 type = getStatValue(now, "transportType");
439
-                localIP = getStatValue(now, "localAddress");
510
+                localip = getStatValue(now, "localAddress");
440
                 active = getStatValue(now, "activeConnection");
511
                 active = getStatValue(now, "activeConnection");
441
             }
512
             }
442
             catch(e){/*not supported*/}
513
             catch(e){/*not supported*/}
443
-            if(!ip || !type || !localIP || active != "true")
514
+            if(!ip || !type || !localip || active != "true")
444
                 continue;
515
                 continue;
445
-            var addressSaved = false;
446
-            for(var i = 0; i < this.conferenceStats.transport.length; i++)
447
-            {
448
-                if(this.conferenceStats.transport[i].ip == ip &&
449
-                    this.conferenceStats.transport[i].type == type &&
450
-                    this.conferenceStats.transport[i].localip == localIP)
451
-                {
452
-                    addressSaved = true;
453
-                }
516
+            // Save the address unless it has been saved already.
517
+            var conferenceStatsTransport = this.conferenceStats.transport;
518
+            if(!conferenceStatsTransport.some(function (t) { return (
519
+                        t.ip == ip && t.type == type && t.localip == localip
520
+                    )})) {
521
+                conferenceStatsTransport.push(
522
+                    {ip: ip, type: type, localip: localip});
454
             }
523
             }
455
-            if(addressSaved)
456
-                continue;
457
-            this.conferenceStats.transport.push({localip: localIP, ip: ip, type: type});
458
             continue;
524
             continue;
459
         }
525
         }
460
 
526
 
461
-        if(now.type == "candidatepair")
462
-        {
527
+        if(now.type == "candidatepair") {
463
             if(now.state == "succeeded")
528
             if(now.state == "succeeded")
464
                 continue;
529
                 continue;
465
 
530
 
466
             var local = this.currentStatsReport[now.localCandidateId];
531
             var local = this.currentStatsReport[now.localCandidateId];
467
             var remote = this.currentStatsReport[now.remoteCandidateId];
532
             var remote = this.currentStatsReport[now.remoteCandidateId];
468
-            this.conferenceStats.transport.push({localip: local.ipAddress + ":" + local.portNumber,
469
-                ip: remote.ipAddress + ":" + remote.portNumber, type: local.transport});
470
-
533
+            this.conferenceStats.transport.push({
534
+                ip: remote.ipAddress + ":" + remote.portNumber,
535
+                type: local.transport,
536
+                localip: local.ipAddress + ":" + local.portNumber
537
+            });
471
         }
538
         }
472
 
539
 
473
         if (now.type != 'ssrc' && now.type != "outboundrtp" &&
540
         if (now.type != 'ssrc' && now.type != "outboundrtp" &&
476
         }
543
         }
477
 
544
 
478
         var before = this.baselineStatsReport[idx];
545
         var before = this.baselineStatsReport[idx];
546
+        var ssrc = getStatValue(now, 'ssrc');
479
         if (!before) {
547
         if (!before) {
480
-            logger.warn(getStatValue(now, 'ssrc') + ' not enough data');
548
+            logger.warn(ssrc + ' not enough data');
481
             continue;
549
             continue;
482
         }
550
         }
483
 
551
 
484
-        var ssrc = getStatValue(now, 'ssrc');
485
         if(!ssrc)
552
         if(!ssrc)
486
             continue;
553
             continue;
487
 
554
 
488
-        var ssrcStats = this.ssrc2stats[ssrc];
489
-        if (!ssrcStats) {
490
-            ssrcStats = new PeerStats();
491
-            this.ssrc2stats[ssrc] = ssrcStats;
492
-        }
493
-
555
+        var ssrcStats
556
+          = this.ssrc2stats[ssrc] || (this.ssrc2stats[ssrc] = new PeerStats());
494
 
557
 
495
         var isDownloadStream = true;
558
         var isDownloadStream = true;
496
         var key = 'packetsReceived';
559
         var key = 'packetsReceived';
531
             isDownloadStream: isDownloadStream
594
             isDownloadStream: isDownloadStream
532
         });
595
         });
533
 
596
 
534
-
535
         var bytesReceived = 0, bytesSent = 0;
597
         var bytesReceived = 0, bytesSent = 0;
536
-        if(getStatValue(now, "bytesReceived")) {
537
-            bytesReceived = getStatValue(now, "bytesReceived") -
538
-                getStatValue(before, "bytesReceived");
598
+        var nowBytesTransmitted = getStatValue(now, "bytesReceived");
599
+        if(nowBytesTransmitted) {
600
+            bytesReceived
601
+                = nowBytesTransmitted - getStatValue(before, "bytesReceived");
539
         }
602
         }
540
-
541
-        if (getStatValue(now, "bytesSent")) {
542
-            bytesSent = getStatValue(now, "bytesSent") -
543
-                getStatValue(before, "bytesSent");
603
+        nowBytesTransmitted = getStatValue(now, "bytesSent");
604
+        if (nowBytesTransmitted) {
605
+            bytesSent = nowBytesTransmitted - getStatValue(before, "bytesSent");
544
         }
606
         }
545
 
607
 
546
         var time = Math.round((now.timestamp - before.timestamp) / 1000);
608
         var time = Math.round((now.timestamp - before.timestamp) / 1000);
563
 
625
 
564
         var resolution = {height: null, width: null};
626
         var resolution = {height: null, width: null};
565
         try {
627
         try {
566
-            if (getStatValue(now, "googFrameHeightReceived") &&
567
-                getStatValue(now, "googFrameWidthReceived")) {
568
-                resolution.height =
569
-                    getStatValue(now, "googFrameHeightReceived");
570
-                resolution.width = getStatValue(now, "googFrameWidthReceived");
628
+            var height, width;
629
+            if ((height = getStatValue(now, "googFrameHeightReceived")) &&
630
+                (width = getStatValue(now, "googFrameWidthReceived"))) {
631
+                resolution.height = height;
632
+                resolution.width = width;
571
             }
633
             }
572
-            else if (getStatValue(now, "googFrameHeightSent") &&
573
-                getStatValue(now, "googFrameWidthSent")) {
574
-                resolution.height = getStatValue(now, "googFrameHeightSent");
575
-                resolution.width = getStatValue(now, "googFrameWidthSent");
634
+            else if ((height = getStatValue(now, "googFrameHeightSent")) &&
635
+                (width = getStatValue(now, "googFrameWidthSent"))) {
636
+                resolution.height = height;
637
+                resolution.width = width;
576
             }
638
             }
577
         }
639
         }
578
         catch(e){/*not supported*/}
640
         catch(e){/*not supported*/}
619
         this
681
         this
620
     );
682
     );
621
 
683
 
622
-    this.conferenceStats.bitrate = {"upload": bitrateUpload, "download": bitrateDownload};
684
+    this.conferenceStats.bitrate
685
+      = {"upload": bitrateUpload, "download": bitrateDownload};
623
 
686
 
624
     this.conferenceStats.packetLoss = {
687
     this.conferenceStats.packetLoss = {
625
         total:
688
         total:
630
         upload:
693
         upload:
631
             calculatePacketLoss(lostPackets.upload, totalPackets.upload)
694
             calculatePacketLoss(lostPackets.upload, totalPackets.upload)
632
     };
695
     };
633
-    this.eventEmitter.emit(StatisticsEvents.CONNECTION_STATS,
634
-        {
696
+    this.eventEmitter.emit(StatisticsEvents.CONNECTION_STATS, {
635
             "bitrate": this.conferenceStats.bitrate,
697
             "bitrate": this.conferenceStats.bitrate,
636
             "packetLoss": this.conferenceStats.packetLoss,
698
             "packetLoss": this.conferenceStats.packetLoss,
637
             "bandwidth": this.conferenceStats.bandwidth,
699
             "bandwidth": this.conferenceStats.bandwidth,
639
             "transport": this.conferenceStats.transport
701
             "transport": this.conferenceStats.transport
640
         });
702
         });
641
     this.conferenceStats.transport = [];
703
     this.conferenceStats.transport = [];
642
-
643
 };
704
 };
644
 
705
 
645
 /**
706
 /**
650
         return;
711
         return;
651
     }
712
     }
652
 
713
 
714
+    var getStatValue = this._getStatValue;
715
+
653
     for (var idx in this.currentAudioLevelsReport) {
716
     for (var idx in this.currentAudioLevelsReport) {
654
         var now = this.currentAudioLevelsReport[idx];
717
         var now = this.currentAudioLevelsReport[idx];
655
 
718
 
659
         }
722
         }
660
 
723
 
661
         var before = this.baselineAudioLevelsReport[idx];
724
         var before = this.baselineAudioLevelsReport[idx];
725
+        var ssrc = getStatValue(now, 'ssrc');
662
         if (!before) {
726
         if (!before) {
663
-            logger.warn(getStatValue(now, 'ssrc') + ' not enough data');
727
+            logger.warn(ssrc + ' not enough data');
664
             continue;
728
             continue;
665
         }
729
         }
666
 
730
 
667
-        var ssrc = getStatValue(now, 'ssrc');
668
         if (!ssrc) {
731
         if (!ssrc) {
669
-            if((Date.now() - now.timestamp) < 3000)
732
+            if ((Date.now() - now.timestamp) < 3000)
670
                 logger.warn("No ssrc: ");
733
                 logger.warn("No ssrc: ");
671
             continue;
734
             continue;
672
         }
735
         }
673
 
736
 
674
-        var ssrcStats = this.ssrc2stats[ssrc];
675
-        if (!ssrcStats) {
676
-            ssrcStats = new PeerStats();
677
-            this.ssrc2stats[ssrc] = ssrcStats;
678
-        }
737
+        var ssrcStats
738
+            = this.ssrc2stats[ssrc]
739
+                || (this.ssrc2stats[ssrc] = new PeerStats());
679
 
740
 
680
         // Audio level
741
         // Audio level
681
-        var audioLevel = null;
682
-
683
         try {
742
         try {
684
-            audioLevel = getStatValue(now, 'audioInputLevel');
685
-            if (!audioLevel)
686
-                audioLevel = getStatValue(now, 'audioOutputLevel');
743
+            var audioLevel
744
+                = getStatValue(now, 'audioInputLevel')
745
+                    || getStatValue(now, 'audioOutputLevel');
687
         }
746
         }
688
         catch(e) {/*not supported*/
747
         catch(e) {/*not supported*/
689
             logger.warn("Audio Levels are not available in the statistics.");
748
             logger.warn("Audio Levels are not available in the statistics.");

+ 48
- 52
modules/statistics/statistics.js 查看文件

1
 /* global require */
1
 /* global require */
2
 var LocalStats = require("./LocalStatsCollector.js");
2
 var LocalStats = require("./LocalStatsCollector.js");
3
+var logger = require("jitsi-meet-logger").getLogger(__filename);
3
 var RTPStats = require("./RTPStatsCollector.js");
4
 var RTPStats = require("./RTPStatsCollector.js");
4
 var EventEmitter = require("events");
5
 var EventEmitter = require("events");
5
 var StatisticsEvents = require("../../service/statistics/Events");
6
 var StatisticsEvents = require("../../service/statistics/Events");
6
 var CallStats = require("./CallStats");
7
 var CallStats = require("./CallStats");
7
 var ScriptUtil = require('../util/ScriptUtil');
8
 var ScriptUtil = require('../util/ScriptUtil');
8
 
9
 
9
-// Since callstats.io is a third party, we cannot guarantee the quality of
10
-// their service. More specifically, their server may take noticeably long
11
-// time to respond. Consequently, it is in our best interest (in the sense
12
-// that the intergration of callstats.io is pretty important to us but not
13
-// enough to allow it to prevent people from joining a conference) to (1)
14
-// start downloading their API as soon as possible and (2) do the
15
-// downloading asynchronously.
10
+// Since callstats.io is a third party, we cannot guarantee the quality of their
11
+// service. More specifically, their server may take noticeably long time to
12
+// respond. Consequently, it is in our best interest (in the sense that the
13
+// intergration of callstats.io is pretty important to us but not enough to
14
+// allow it to prevent people from joining a conference) to (1) start
15
+// downloading their API as soon as possible and (2) do the downloading
16
+// asynchronously.
16
 function loadCallStatsAPI() {
17
 function loadCallStatsAPI() {
17
     ScriptUtil.loadScript(
18
     ScriptUtil.loadScript(
18
             'https://api.callstats.io/static/callstats.min.js',
19
             'https://api.callstats.io/static/callstats.min.js',
22
     // have loaded by the time we needed it (i.e. CallStats.init is invoked).
23
     // have loaded by the time we needed it (i.e. CallStats.init is invoked).
23
 }
24
 }
24
 
25
 
25
-
26
 /**
26
 /**
27
  * Log stats via the focus once every this many milliseconds.
27
  * Log stats via the focus once every this many milliseconds.
28
  */
28
  */
29
 var LOG_INTERVAL = 60000;
29
 var LOG_INTERVAL = 60000;
30
 
30
 
31
-var eventEmitter = new EventEmitter();
32
-
33
 function Statistics(xmpp, options) {
31
 function Statistics(xmpp, options) {
34
     this.rtpStats = null;
32
     this.rtpStats = null;
35
     this.eventEmitter = new EventEmitter();
33
     this.eventEmitter = new EventEmitter();
37
     this.options = options || {};
35
     this.options = options || {};
38
     this.callStatsIntegrationEnabled
36
     this.callStatsIntegrationEnabled
39
         = this.options.callStatsID && this.options.callStatsSecret
37
         = this.options.callStatsID && this.options.callStatsSecret
40
-        // Even though AppID and AppSecret may be specified, the integration of
41
-        // callstats.io may be disabled because of globally-disallowed requests
42
-        // to any third parties.
43
-        && (this.options.disableThirdPartyRequests !== true);
38
+            // Even though AppID and AppSecret may be specified, the integration
39
+            // of callstats.io may be disabled because of globally-disallowed
40
+            // requests to any third parties.
41
+            && (this.options.disableThirdPartyRequests !== true);
44
     if(this.callStatsIntegrationEnabled)
42
     if(this.callStatsIntegrationEnabled)
45
         loadCallStatsAPI();
43
         loadCallStatsAPI();
46
     this.callStats = null;
44
     this.callStats = null;
47
 
45
 
48
     /**
46
     /**
49
-     * Send the stats already saved in rtpStats to be logged via
50
-     * the focus.
47
+     * Send the stats already saved in rtpStats to be logged via the focus.
51
      */
48
      */
52
     this.logStatsIntervalId = null;
49
     this.logStatsIntervalId = null;
53
 }
50
 }
59
 
56
 
60
     this.stopRemoteStats();
57
     this.stopRemoteStats();
61
 
58
 
62
-    this.rtpStats = new RTPStats(peerconnection, 200, 2000, this.eventEmitter);
63
-    this.rtpStats.start();
64
-
65
-    this.logStatsIntervalId = setInterval(function () {
66
-        var stats = this.rtpStats.getCollectedStats();
67
-        if (this.xmpp.sendLogs(stats)) {
68
-            this.rtpStats.clearCollectedStats();
69
-        }
70
-    }.bind(this), LOG_INTERVAL);
59
+    try {
60
+        this.rtpStats
61
+            = new RTPStats(peerconnection, 200, 2000, this.eventEmitter);
62
+        this.rtpStats.start();
63
+    } catch (e) {
64
+        this.rtpStats = null;
65
+        logger.error('Failed to start collecting remote statistics: ' + e);
66
+    }
67
+    if (this.rtpStats) {
68
+        this.logStatsIntervalId = setInterval(function () {
69
+            var stats = this.rtpStats.getCollectedStats();
70
+            if (this.xmpp.sendLogs(stats)) {
71
+                this.rtpStats.clearCollectedStats();
72
+            }
73
+        }.bind(this), LOG_INTERVAL);
74
+    }
71
 };
75
 };
72
 
76
 
73
 Statistics.localStats = [];
77
 Statistics.localStats = [];
80
     localStats.start();
84
     localStats.start();
81
 };
85
 };
82
 
86
 
83
-Statistics.prototype.addAudioLevelListener = function(listener)
84
-{
87
+Statistics.prototype.addAudioLevelListener = function(listener) {
85
     if(!Statistics.audioLevelsEnabled)
88
     if(!Statistics.audioLevelsEnabled)
86
         return;
89
         return;
87
     this.eventEmitter.on(StatisticsEvents.AUDIO_LEVEL, listener);
90
     this.eventEmitter.on(StatisticsEvents.AUDIO_LEVEL, listener);
88
 };
91
 };
89
 
92
 
90
-Statistics.prototype.removeAudioLevelListener = function(listener)
91
-{
93
+Statistics.prototype.removeAudioLevelListener = function(listener) {
92
     if(!Statistics.audioLevelsEnabled)
94
     if(!Statistics.audioLevelsEnabled)
93
         return;
95
         return;
94
     this.eventEmitter.removeListener(StatisticsEvents.AUDIO_LEVEL, listener);
96
     this.eventEmitter.removeListener(StatisticsEvents.AUDIO_LEVEL, listener);
108
         this.stopRemoteStats();
110
         this.stopRemoteStats();
109
         if(this.eventEmitter)
111
         if(this.eventEmitter)
110
             this.eventEmitter.removeAllListeners();
112
             this.eventEmitter.removeAllListeners();
111
-
112
-        if(eventEmitter)
113
-            eventEmitter.removeAllListeners();
114
     }
113
     }
115
 };
114
 };
116
 
115
 
117
-
118
 Statistics.stopAllLocalStats = function () {
116
 Statistics.stopAllLocalStats = function () {
119
     if(!Statistics.audioLevelsEnabled)
117
     if(!Statistics.audioLevelsEnabled)
120
         return;
118
         return;
180
  * @param {RTCPeerConnection} pc connection on which failure occured.
178
  * @param {RTCPeerConnection} pc connection on which failure occured.
181
  */
179
  */
182
 Statistics.prototype.sendIceConnectionFailedEvent = function (pc) {
180
 Statistics.prototype.sendIceConnectionFailedEvent = function (pc) {
183
-    if(this.callStatsIntegrationEnabled && this.callstats)
181
+    if(this.callstats)
184
         this.callstats.sendIceConnectionFailedEvent(pc, this.callstats);
182
         this.callstats.sendIceConnectionFailedEvent(pc, this.callstats);
185
 };
183
 };
186
 
184
 
190
  * @param type {String} "audio"/"video"
188
  * @param type {String} "audio"/"video"
191
  */
189
  */
192
 Statistics.prototype.sendMuteEvent = function (muted, type) {
190
 Statistics.prototype.sendMuteEvent = function (muted, type) {
193
-    if(this.callStatsIntegrationEnabled)
191
+    if(this.callstats)
194
         CallStats.sendMuteEvent(muted, type, this.callstats);
192
         CallStats.sendMuteEvent(muted, type, this.callstats);
195
 };
193
 };
196
 
194
 
200
  * false for not stopping
198
  * false for not stopping
201
  */
199
  */
202
 Statistics.prototype.sendScreenSharingEvent = function (start) {
200
 Statistics.prototype.sendScreenSharingEvent = function (start) {
203
-    if(this.callStatsIntegrationEnabled)
201
+    if(this.callstats)
204
         CallStats.sendScreenSharingEvent(start, this.callstats);
202
         CallStats.sendScreenSharingEvent(start, this.callstats);
205
 };
203
 };
206
 
204
 
209
  * conference.
207
  * conference.
210
  */
208
  */
211
 Statistics.prototype.sendDominantSpeakerEvent = function () {
209
 Statistics.prototype.sendDominantSpeakerEvent = function () {
212
-    if(this.callStatsIntegrationEnabled)
210
+    if(this.callstats)
213
         CallStats.sendDominantSpeakerEvent(this.callstats);
211
         CallStats.sendDominantSpeakerEvent(this.callstats);
214
 };
212
 };
215
 
213
 
226
  */
224
  */
227
 Statistics.prototype.associateStreamWithVideoTag =
225
 Statistics.prototype.associateStreamWithVideoTag =
228
 function (ssrc, isLocal, usageLabel, containerId) {
226
 function (ssrc, isLocal, usageLabel, containerId) {
229
-    if(this.callStatsIntegrationEnabled && this.callstats) {
227
+    if(this.callstats) {
230
         this.callstats.associateStreamWithVideoTag(
228
         this.callstats.associateStreamWithVideoTag(
231
             ssrc, isLocal, usageLabel, containerId);
229
             ssrc, isLocal, usageLabel, containerId);
232
     }
230
     }
238
  * @param {Error} e error to send
236
  * @param {Error} e error to send
239
  */
237
  */
240
 Statistics.prototype.sendGetUserMediaFailed = function (e) {
238
 Statistics.prototype.sendGetUserMediaFailed = function (e) {
241
-    if(this.callStatsIntegrationEnabled)
239
+    if(this.callstats)
242
         CallStats.sendGetUserMediaFailed(e, this.callstats);
240
         CallStats.sendGetUserMediaFailed(e, this.callstats);
243
 };
241
 };
244
 
242
 
258
  * @param {RTCPeerConnection} pc connection on which failure occured.
256
  * @param {RTCPeerConnection} pc connection on which failure occured.
259
  */
257
  */
260
 Statistics.prototype.sendCreateOfferFailed = function (e, pc) {
258
 Statistics.prototype.sendCreateOfferFailed = function (e, pc) {
261
-    if(this.callStatsIntegrationEnabled)
259
+    if(this.callstats)
262
         CallStats.sendCreateOfferFailed(e, pc, this.callstats);
260
         CallStats.sendCreateOfferFailed(e, pc, this.callstats);
263
 };
261
 };
264
 
262
 
269
  * @param {RTCPeerConnection} pc connection on which failure occured.
267
  * @param {RTCPeerConnection} pc connection on which failure occured.
270
  */
268
  */
271
 Statistics.prototype.sendCreateAnswerFailed = function (e, pc) {
269
 Statistics.prototype.sendCreateAnswerFailed = function (e, pc) {
272
-    if(this.callStatsIntegrationEnabled)
270
+    if(this.callstats)
273
         CallStats.sendCreateAnswerFailed(e, pc, this.callstats);
271
         CallStats.sendCreateAnswerFailed(e, pc, this.callstats);
274
 };
272
 };
275
 
273
 
280
  * @param {RTCPeerConnection} pc connection on which failure occured.
278
  * @param {RTCPeerConnection} pc connection on which failure occured.
281
  */
279
  */
282
 Statistics.prototype.sendSetLocalDescFailed = function (e, pc) {
280
 Statistics.prototype.sendSetLocalDescFailed = function (e, pc) {
283
-    if(this.callStatsIntegrationEnabled)
281
+    if(this.callstats)
284
         CallStats.sendSetLocalDescFailed(e, pc, this.callstats);
282
         CallStats.sendSetLocalDescFailed(e, pc, this.callstats);
285
 };
283
 };
286
 
284
 
291
  * @param {RTCPeerConnection} pc connection on which failure occured.
289
  * @param {RTCPeerConnection} pc connection on which failure occured.
292
  */
290
  */
293
 Statistics.prototype.sendSetRemoteDescFailed = function (e, pc) {
291
 Statistics.prototype.sendSetRemoteDescFailed = function (e, pc) {
294
-    if(this.callStatsIntegrationEnabled)
292
+    if(this.callstats)
295
         CallStats.sendSetRemoteDescFailed(e, pc, this.callstats);
293
         CallStats.sendSetRemoteDescFailed(e, pc, this.callstats);
296
 };
294
 };
297
 
295
 
302
  * @param {RTCPeerConnection} pc connection on which failure occured.
300
  * @param {RTCPeerConnection} pc connection on which failure occured.
303
  */
301
  */
304
 Statistics.prototype.sendAddIceCandidateFailed = function (e, pc) {
302
 Statistics.prototype.sendAddIceCandidateFailed = function (e, pc) {
305
-    if(this.callStatsIntegrationEnabled)
303
+    if(this.callstats)
306
         CallStats.sendAddIceCandidateFailed(e, pc, this.callstats);
304
         CallStats.sendAddIceCandidateFailed(e, pc, this.callstats);
307
 };
305
 };
308
 
306
 
313
  * @param {RTCPeerConnection} pc connection on which failure occured.
311
  * @param {RTCPeerConnection} pc connection on which failure occured.
314
  */
312
  */
315
 Statistics.prototype.sendUnhandledError = function (e) {
313
 Statistics.prototype.sendUnhandledError = function (e) {
316
-    if(this.callStatsIntegrationEnabled)
314
+    if(this.callstats)
317
         CallStats.sendUnhandledError(e, this.callstats);
315
         CallStats.sendUnhandledError(e, this.callstats);
318
 };
316
 };
319
 
317
 
329
 /**
327
 /**
330
  * Sends the given feedback through CallStats.
328
  * Sends the given feedback through CallStats.
331
  *
329
  *
332
- * @param overallFeedback an integer between 1 and 5 indicating the
333
- * user feedback
334
- * @param detailedFeedback detailed feedback from the user. Not yet used
330
+ * @param overall an integer between 1 and 5 indicating the user feedback
331
+ * @param detailed detailed feedback from the user. Not yet used
335
  */
332
  */
336
-Statistics.prototype.sendFeedback =
337
-function(overallFeedback, detailedFeedback){
338
-    if(this.callStatsIntegrationEnabled && this.callstats)
339
-        this.callstats.sendFeedback(overallFeedback, detailedFeedback);
333
+Statistics.prototype.sendFeedback = function(overall, detailed) {
334
+    if(this.callstats)
335
+        this.callstats.sendFeedback(overall, detailed);
340
 };
336
 };
341
 
337
 
342
 Statistics.LOCAL_JID = require("../../service/statistics/constants").LOCAL_JID;
338
 Statistics.LOCAL_JID = require("../../service/statistics/constants").LOCAL_JID;

Loading…
取消
儲存