|
@@ -1,5 +1,5 @@
|
1
|
|
-/* global require, ssrc2jid */
|
2
|
|
-/* jshint -W117 */
|
|
1
|
+/* global require */
|
|
2
|
+/* jshint -W101 */
|
3
|
3
|
|
4
|
4
|
var logger = require("jitsi-meet-logger").getLogger(__filename);
|
5
|
5
|
var RTCBrowserType = require("../RTC/RTCBrowserType");
|
|
@@ -7,7 +7,7 @@ var StatisticsEvents = require("../../service/statistics/Events");
|
7
|
7
|
|
8
|
8
|
/* Whether we support the browser we are running into for logging statistics */
|
9
|
9
|
var browserSupported = RTCBrowserType.isChrome() ||
|
10
|
|
- RTCBrowserType.isOpera();
|
|
10
|
+ RTCBrowserType.isOpera() || RTCBrowserType.isFirefox();
|
11
|
11
|
|
12
|
12
|
var keyMap = {};
|
13
|
13
|
keyMap[RTCBrowserType.RTC_BROWSER_FIREFOX] = {
|
|
@@ -110,18 +110,20 @@ function PeerStats()
|
110
|
110
|
{
|
111
|
111
|
this.ssrc2Loss = {};
|
112
|
112
|
this.ssrc2AudioLevel = {};
|
113
|
|
- this.ssrc2bitrate = {};
|
|
113
|
+ this.ssrc2bitrate = {
|
|
114
|
+ download: 0,
|
|
115
|
+ upload: 0
|
|
116
|
+ };
|
114
|
117
|
this.ssrc2resolution = {};
|
115
|
118
|
}
|
116
|
119
|
|
117
|
120
|
/**
|
118
|
|
- * Sets packets loss rate for given <tt>ssrc</tt> that blong to the peer
|
|
121
|
+ * Sets packets loss rate for given <tt>ssrc</tt> that belong to the peer
|
119
|
122
|
* represented by this instance.
|
120
|
123
|
* @param lossRate new packet loss rate value to be set.
|
121
|
124
|
*/
|
122
|
|
-PeerStats.prototype.setSsrcLoss = function (lossRate)
|
123
|
|
-{
|
124
|
|
- this.ssrc2Loss = lossRate;
|
|
125
|
+PeerStats.prototype.setSsrcLoss = function (lossRate) {
|
|
126
|
+ this.ssrc2Loss = lossRate || {};
|
125
|
127
|
};
|
126
|
128
|
|
127
|
129
|
/**
|
|
@@ -129,46 +131,38 @@ PeerStats.prototype.setSsrcLoss = function (lossRate)
|
129
|
131
|
* represented by this instance.
|
130
|
132
|
* @param resolution new resolution value to be set.
|
131
|
133
|
*/
|
132
|
|
-PeerStats.prototype.setSsrcResolution = function (resolution)
|
133
|
|
-{
|
134
|
|
- if(resolution === null && this.ssrc2resolution[ssrc])
|
135
|
|
- {
|
136
|
|
- delete this.ssrc2resolution[ssrc];
|
137
|
|
- }
|
138
|
|
- else if(resolution !== null)
|
139
|
|
- this.ssrc2resolution[ssrc] = resolution;
|
|
134
|
+PeerStats.prototype.setSsrcResolution = function (resolution) {
|
|
135
|
+ this.ssrc2resolution = resolution || {};
|
140
|
136
|
};
|
141
|
137
|
|
142
|
138
|
/**
|
143
|
|
- * Sets the bit rate for given <tt>ssrc</tt> that blong to the peer
|
|
139
|
+ * Sets the bit rate for given <tt>ssrc</tt> that belong to the peer
|
144
|
140
|
* represented by this instance.
|
145
|
|
- * @param ssrc audio or video RTP stream SSRC.
|
146
|
141
|
* @param bitrate new bitrate value to be set.
|
147
|
142
|
*/
|
148
|
|
-PeerStats.prototype.setSsrcBitrate = function (ssrc, bitrate)
|
149
|
|
-{
|
150
|
|
- if(this.ssrc2bitrate[ssrc])
|
151
|
|
- {
|
152
|
|
- this.ssrc2bitrate[ssrc].download += bitrate.download;
|
153
|
|
- this.ssrc2bitrate[ssrc].upload += bitrate.upload;
|
154
|
|
- }
|
155
|
|
- else {
|
156
|
|
- this.ssrc2bitrate[ssrc] = bitrate;
|
157
|
|
- }
|
|
143
|
+PeerStats.prototype.setSsrcBitrate = function (bitrate) {
|
|
144
|
+ this.ssrc2bitrate.download += bitrate.download;
|
|
145
|
+ this.ssrc2bitrate.upload += bitrate.upload;
|
|
146
|
+};
|
|
147
|
+
|
|
148
|
+/**
|
|
149
|
+ * Resets the bit rate for given <tt>ssrc</tt> that belong to the peer
|
|
150
|
+ * represented by this instance.
|
|
151
|
+ */
|
|
152
|
+PeerStats.prototype.resetSsrcBitrate = function () {
|
|
153
|
+ this.ssrc2bitrate.download = 0;
|
|
154
|
+ this.ssrc2bitrate.upload = 0;
|
158
|
155
|
};
|
159
|
156
|
|
160
|
157
|
/**
|
161
|
158
|
* Sets new audio level(input or output) for given <tt>ssrc</tt> that identifies
|
162
|
159
|
* the stream which belongs to the peer represented by this instance.
|
163
|
|
- * @param ssrc RTP stream SSRC for which current audio level value will be
|
164
|
|
- * updated.
|
165
|
160
|
* @param audioLevel the new audio level value to be set. Value is truncated to
|
166
|
161
|
* fit the range from 0 to 1.
|
167
|
162
|
*/
|
168
|
|
-PeerStats.prototype.setSsrcAudioLevel = function (ssrc, audioLevel)
|
169
|
|
-{
|
|
163
|
+PeerStats.prototype.setSsrcAudioLevel = function (audioLevel) {
|
170
|
164
|
// Range limit 0 - 1
|
171
|
|
- this.ssrc2AudioLevel[ssrc] = formatAudioLevel(audioLevel);
|
|
165
|
+ this.ssrc2AudioLevel = formatAudioLevel(audioLevel);
|
172
|
166
|
};
|
173
|
167
|
|
174
|
168
|
function ConferenceStats() {
|
|
@@ -231,22 +225,11 @@ function StatsCollector(peerconnection, audioLevelsInterval, statsInterval, even
|
231
|
225
|
*/
|
232
|
226
|
this.GATHER_INTERVAL = 15000;
|
233
|
227
|
|
234
|
|
- /**
|
235
|
|
- * Log stats via the focus once every this many milliseconds.
|
236
|
|
- */
|
237
|
|
- this.LOG_INTERVAL = 60000;
|
238
|
|
-
|
239
|
228
|
/**
|
240
|
229
|
* Gather stats and store them in this.statsToBeLogged.
|
241
|
230
|
*/
|
242
|
231
|
this.gatherStatsIntervalId = null;
|
243
|
232
|
|
244
|
|
- /**
|
245
|
|
- * Send the stats already saved in this.statsToBeLogged to be logged via
|
246
|
|
- * the focus.
|
247
|
|
- */
|
248
|
|
- this.logStatsIntervalId = null;
|
249
|
|
-
|
250
|
233
|
/**
|
251
|
234
|
* Stores the statistics which will be send to the focus to be logged.
|
252
|
235
|
*/
|
|
@@ -282,12 +265,6 @@ StatsCollector.prototype.stop = function () {
|
282
|
265
|
this.statsIntervalId = null;
|
283
|
266
|
}
|
284
|
267
|
|
285
|
|
- if(this.logStatsIntervalId)
|
286
|
|
- {
|
287
|
|
- clearInterval(this.logStatsIntervalId);
|
288
|
|
- this.logStatsIntervalId = null;
|
289
|
|
- }
|
290
|
|
-
|
291
|
268
|
if(this.gatherStatsIntervalId)
|
292
|
269
|
{
|
293
|
270
|
clearInterval(this.gatherStatsIntervalId);
|
|
@@ -336,58 +313,55 @@ StatsCollector.prototype.start = function ()
|
336
|
313
|
self.audioLevelsIntervalMilis
|
337
|
314
|
);
|
338
|
315
|
|
339
|
|
-// if (!this.config.disableStats && browserSupported) {
|
340
|
|
-// this.statsIntervalId = setInterval(
|
341
|
|
-// function () {
|
342
|
|
-// // Interval updates
|
343
|
|
-// self.peerconnection.getStats(
|
344
|
|
-// function (report) {
|
345
|
|
-// var results = null;
|
346
|
|
-// if (!report || !report.result ||
|
347
|
|
-// typeof report.result != 'function') {
|
348
|
|
-// //firefox
|
349
|
|
-// results = report;
|
350
|
|
-// }
|
351
|
|
-// else {
|
352
|
|
-// //chrome
|
353
|
|
-// results = report.result();
|
354
|
|
-// }
|
355
|
|
-// //logger.error("Got interval report", results);
|
356
|
|
-// self.currentStatsReport = results;
|
357
|
|
-// try {
|
358
|
|
-// self.processStatsReport();
|
359
|
|
-// }
|
360
|
|
-// catch (e) {
|
361
|
|
-// logger.error("Unsupported key:" + e, e);
|
362
|
|
-// }
|
363
|
|
-//
|
364
|
|
-// self.baselineStatsReport = self.currentStatsReport;
|
365
|
|
-// },
|
366
|
|
-// self.errorCallback
|
367
|
|
-// );
|
368
|
|
-// },
|
369
|
|
-// self.statsIntervalMilis
|
370
|
|
-// );
|
371
|
|
-// }
|
372
|
|
-//
|
373
|
|
-// if (this.config.logStats && browserSupported) {
|
374
|
|
-// this.gatherStatsIntervalId = setInterval(
|
375
|
|
-// function () {
|
376
|
|
-// self.peerconnection.getStats(
|
377
|
|
-// function (report) {
|
378
|
|
-// self.addStatsToBeLogged(report.result());
|
379
|
|
-// },
|
380
|
|
-// function () {
|
381
|
|
-// }
|
382
|
|
-// );
|
383
|
|
-// },
|
384
|
|
-// this.GATHER_INTERVAL
|
385
|
|
-// );
|
386
|
|
-//
|
387
|
|
-// this.logStatsIntervalId = setInterval(
|
388
|
|
-// function() { self.logStats(); },
|
389
|
|
-// this.LOG_INTERVAL);
|
390
|
|
-// }
|
|
316
|
+ if (!this.config.disableStats && browserSupported) {
|
|
317
|
+ this.statsIntervalId = 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
|
+ //firefox
|
|
326
|
+ results = report;
|
|
327
|
+ }
|
|
328
|
+ else {
|
|
329
|
+ //chrome
|
|
330
|
+ results = report.result();
|
|
331
|
+ }
|
|
332
|
+ //logger.error("Got interval report", results);
|
|
333
|
+ self.currentStatsReport = results;
|
|
334
|
+ try {
|
|
335
|
+ self.processStatsReport();
|
|
336
|
+ }
|
|
337
|
+ catch (e) {
|
|
338
|
+ logger.error("Unsupported key:" + e, e);
|
|
339
|
+ }
|
|
340
|
+
|
|
341
|
+ self.baselineStatsReport = self.currentStatsReport;
|
|
342
|
+ },
|
|
343
|
+ self.errorCallback
|
|
344
|
+ );
|
|
345
|
+ },
|
|
346
|
+ self.statsIntervalMilis
|
|
347
|
+ );
|
|
348
|
+ }
|
|
349
|
+
|
|
350
|
+ // logging statistics does not support firefox
|
|
351
|
+ if (this.config.logStats && (browserSupported && !RTCBrowserType.isFirefox())) {
|
|
352
|
+ this.gatherStatsIntervalId = setInterval(
|
|
353
|
+ function () {
|
|
354
|
+ self.peerconnection.getStats(
|
|
355
|
+ function (report) {
|
|
356
|
+ self.addStatsToBeLogged(report.result());
|
|
357
|
+ },
|
|
358
|
+ function () {
|
|
359
|
+ }
|
|
360
|
+ );
|
|
361
|
+ },
|
|
362
|
+ this.GATHER_INTERVAL
|
|
363
|
+ );
|
|
364
|
+ }
|
391
|
365
|
};
|
392
|
366
|
|
393
|
367
|
/**
|
|
@@ -399,7 +373,7 @@ StatsCollector.prototype.addStatsToBeLogged = function (reports) {
|
399
|
373
|
var self = this;
|
400
|
374
|
var num_records = this.statsToBeLogged.timestamps.length;
|
401
|
375
|
this.statsToBeLogged.timestamps.push(new Date().getTime());
|
402
|
|
- reports.map(function (report) {
|
|
376
|
+ reports.forEach(function (report) {
|
403
|
377
|
if (!acceptReport(report.id, report.type))
|
404
|
378
|
return;
|
405
|
379
|
var stat = self.statsToBeLogged.stats[report.id];
|
|
@@ -407,7 +381,7 @@ StatsCollector.prototype.addStatsToBeLogged = function (reports) {
|
407
|
381
|
stat = self.statsToBeLogged.stats[report.id] = {};
|
408
|
382
|
}
|
409
|
383
|
stat.type = report.type;
|
410
|
|
- report.names().map(function (name) {
|
|
384
|
+ report.names().forEach(function (name) {
|
411
|
385
|
if (!acceptStat(report.id, report.type, name))
|
412
|
386
|
return;
|
413
|
387
|
var values = stat[name];
|
|
@@ -422,16 +396,15 @@ StatsCollector.prototype.addStatsToBeLogged = function (reports) {
|
422
|
396
|
});
|
423
|
397
|
};
|
424
|
398
|
|
|
399
|
+StatsCollector.prototype.getCollectedStats = function () {
|
|
400
|
+ return this.statsToBeLogged;
|
|
401
|
+};
|
425
|
402
|
|
426
|
|
-//FIXME:
|
427
|
|
-//StatsCollector.prototype.logStats = function () {
|
428
|
|
-//
|
429
|
|
-// if(!APP.xmpp.sendLogs(this.statsToBeLogged))
|
430
|
|
-// return;
|
431
|
|
-// // Reset the stats
|
432
|
|
-// this.statsToBeLogged.stats = {};
|
433
|
|
-// this.statsToBeLogged.timestamps = [];
|
434
|
|
-//};
|
|
403
|
+StatsCollector.prototype.clearCollectedStats = function () {
|
|
404
|
+ // Reset the stats
|
|
405
|
+ this.statsToBeLogged.stats = {};
|
|
406
|
+ this.statsToBeLogged.timestamps = [];
|
|
407
|
+};
|
435
|
408
|
|
436
|
409
|
|
437
|
410
|
/**
|
|
@@ -551,10 +524,11 @@ StatsCollector.prototype.processStatsReport = function () {
|
551
|
524
|
lossRate = 0;
|
552
|
525
|
var packetsTotal = (packetRate + lossRate);
|
553
|
526
|
|
554
|
|
- ssrcStats.setSsrcLoss(ssrc,
|
555
|
|
- {"packetsTotal": packetsTotal,
|
556
|
|
- "packetsLost": lossRate,
|
557
|
|
- "isDownloadStream": isDownloadStream});
|
|
527
|
+ ssrcStats.setSsrcLoss({
|
|
528
|
+ packetsTotal: packetsTotal,
|
|
529
|
+ packetsLost: lossRate,
|
|
530
|
+ isDownloadStream: isDownloadStream
|
|
531
|
+ });
|
558
|
532
|
|
559
|
533
|
|
560
|
534
|
var bytesReceived = 0, bytesSent = 0;
|
|
@@ -581,9 +555,10 @@ StatsCollector.prototype.processStatsReport = function () {
|
581
|
555
|
bytesSent = Math.round(((bytesSent * 8) / time) / 1000);
|
582
|
556
|
}
|
583
|
557
|
|
584
|
|
- ssrcStats.setSsrcBitrate(ssrc, {
|
|
558
|
+ ssrcStats.setSsrcBitrate({
|
585
|
559
|
"download": bytesReceived,
|
586
|
|
- "upload": bytesSent});
|
|
560
|
+ "upload": bytesSent
|
|
561
|
+ });
|
587
|
562
|
|
588
|
563
|
var resolution = {height: null, width: null};
|
589
|
564
|
try {
|
|
@@ -602,44 +577,45 @@ StatsCollector.prototype.processStatsReport = function () {
|
602
|
577
|
catch(e){/*not supported*/}
|
603
|
578
|
|
604
|
579
|
if (resolution.height && resolution.width) {
|
605
|
|
- ssrcStats.setSsrcResolution(ssrc, resolution);
|
|
580
|
+ ssrcStats.setSsrcResolution(resolution);
|
606
|
581
|
} else {
|
607
|
|
- ssrcStats.setSsrcResolution(ssrc, null);
|
|
582
|
+ ssrcStats.setSsrcResolution(null);
|
608
|
583
|
}
|
609
|
584
|
}
|
610
|
585
|
|
611
|
|
- var self = this;
|
612
|
|
- // Jid stats
|
613
|
|
- var totalPackets = {download: 0, upload: 0};
|
614
|
|
- var lostPackets = {download: 0, upload: 0};
|
|
586
|
+ // process stats
|
|
587
|
+ var totalPackets = {
|
|
588
|
+ download: 0,
|
|
589
|
+ upload: 0
|
|
590
|
+ };
|
|
591
|
+ var lostPackets = {
|
|
592
|
+ download: 0,
|
|
593
|
+ upload: 0
|
|
594
|
+ };
|
615
|
595
|
var bitrateDownload = 0;
|
616
|
596
|
var bitrateUpload = 0;
|
617
|
597
|
var resolutions = {};
|
618
|
598
|
Object.keys(this.ssrc2stats).forEach(
|
619
|
|
- function (jid) {
|
620
|
|
- Object.keys(self.ssrc2stats[jid].ssrc2Loss).forEach(
|
621
|
|
- function (ssrc) {
|
622
|
|
- var type = "upload";
|
623
|
|
- if(self.ssrc2stats[jid].ssrc2Loss[ssrc].isDownloadStream)
|
624
|
|
- type = "download";
|
625
|
|
- totalPackets[type] +=
|
626
|
|
- self.ssrc2stats[jid].ssrc2Loss[ssrc].packetsTotal;
|
627
|
|
- lostPackets[type] +=
|
628
|
|
- self.ssrc2stats[jid].ssrc2Loss[ssrc].packetsLost;
|
629
|
|
- }
|
630
|
|
- );
|
631
|
|
- Object.keys(self.ssrc2stats[jid].ssrc2bitrate).forEach(
|
632
|
|
- function (ssrc) {
|
633
|
|
- bitrateDownload +=
|
634
|
|
- self.ssrc2stats[jid].ssrc2bitrate[ssrc].download;
|
635
|
|
- bitrateUpload +=
|
636
|
|
- self.ssrc2stats[jid].ssrc2bitrate[ssrc].upload;
|
637
|
|
-
|
638
|
|
- delete self.ssrc2stats[jid].ssrc2bitrate[ssrc];
|
639
|
|
- }
|
640
|
|
- );
|
641
|
|
- resolutions[jid] = self.ssrc2stats[jid].ssrc2resolution;
|
642
|
|
- }
|
|
599
|
+ function (ssrc) {
|
|
600
|
+ var ssrcStats = this.ssrc2stats[ssrc];
|
|
601
|
+
|
|
602
|
+ // process package loss stats
|
|
603
|
+ var ssrc2Loss = ssrcStats.ssrc2Loss;
|
|
604
|
+ var type = ssrc2Loss.isDownloadStream ? "download" : "upload";
|
|
605
|
+ totalPackets[type] += ssrc2Loss.packetsTotal;
|
|
606
|
+ lostPackets[type] += ssrc2Loss.packetLost;
|
|
607
|
+
|
|
608
|
+ // process bitrate stats
|
|
609
|
+ var ssrc2bitrate = ssrcStats.ssrc2bitrate;
|
|
610
|
+ bitrateDownload += ssrc2bitrate.download;
|
|
611
|
+ bitrateUpload += ssrc2bitrate.upload;
|
|
612
|
+
|
|
613
|
+ ssrcStats.resetSsrcBitrate();
|
|
614
|
+
|
|
615
|
+ // collect resolutions
|
|
616
|
+ resolutions[ssrc] = ssrcStats.ssrc2resolution;
|
|
617
|
+ },
|
|
618
|
+ this
|
643
|
619
|
);
|
644
|
620
|
|
645
|
621
|
this.conferenceStats.bitrate = {"upload": bitrateUpload, "download": bitrateDownload};
|
|
@@ -718,7 +694,7 @@ StatsCollector.prototype.processAudioLevelReport = function () {
|
718
|
694
|
// TODO: can't find specs about what this value really is,
|
719
|
695
|
// but it seems to vary between 0 and around 32k.
|
720
|
696
|
audioLevel = audioLevel / 32767;
|
721
|
|
- ssrcStats.setSsrcAudioLevel(ssrc, audioLevel);
|
|
697
|
+ ssrcStats.setSsrcAudioLevel(audioLevel);
|
722
|
698
|
this.eventEmitter.emit(
|
723
|
699
|
StatisticsEvents.AUDIO_LEVEL, ssrc, audioLevel);
|
724
|
700
|
}
|