2018-04-26 15:33:47 +02:00
|
|
|
"use strict";
|
|
|
|
/*
|
2018-05-06 02:20:25 +02:00
|
|
|
* (C) Copyright 2017-2018 OpenVidu (https://openvidu.io/)
|
2018-04-26 15:33:47 +02:00
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
exports.__esModule = true;
|
|
|
|
var platform = require("platform");
|
|
|
|
var WebRtcStats = /** @class */ (function () {
|
|
|
|
function WebRtcStats(stream) {
|
|
|
|
this.stream = stream;
|
|
|
|
this.webRtcStatsEnabled = false;
|
|
|
|
this.statsInterval = 1;
|
|
|
|
this.stats = {
|
|
|
|
inbound: {
|
|
|
|
audio: {
|
|
|
|
bytesReceived: 0,
|
|
|
|
packetsReceived: 0,
|
|
|
|
packetsLost: 0
|
|
|
|
},
|
|
|
|
video: {
|
|
|
|
bytesReceived: 0,
|
|
|
|
packetsReceived: 0,
|
|
|
|
packetsLost: 0,
|
|
|
|
framesDecoded: 0,
|
|
|
|
nackCount: 0
|
|
|
|
}
|
|
|
|
},
|
|
|
|
outbound: {
|
|
|
|
audio: {
|
|
|
|
bytesSent: 0,
|
|
|
|
packetsSent: 0
|
|
|
|
},
|
|
|
|
video: {
|
|
|
|
bytesSent: 0,
|
|
|
|
packetsSent: 0,
|
|
|
|
framesEncoded: 0,
|
|
|
|
nackCount: 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
WebRtcStats.prototype.isEnabled = function () {
|
|
|
|
return this.webRtcStatsEnabled;
|
|
|
|
};
|
|
|
|
WebRtcStats.prototype.initWebRtcStats = function () {
|
|
|
|
var _this = this;
|
|
|
|
var elastestInstrumentation = localStorage.getItem('elastest-instrumentation');
|
|
|
|
if (elastestInstrumentation) {
|
|
|
|
// ElasTest instrumentation object found in local storage
|
|
|
|
console.warn('WebRtc stats enabled for stream ' + this.stream.streamId + ' of connection ' + this.stream.connection.connectionId);
|
|
|
|
this.webRtcStatsEnabled = true;
|
|
|
|
var instrumentation_1 = JSON.parse(elastestInstrumentation);
|
|
|
|
this.statsInterval = instrumentation_1.webrtc.interval; // Interval in seconds
|
|
|
|
console.warn('localStorage item: ' + JSON.stringify(instrumentation_1));
|
|
|
|
this.webRtcStatsIntervalId = setInterval(function () {
|
|
|
|
_this.sendStatsToHttpEndpoint(instrumentation_1);
|
|
|
|
}, this.statsInterval * 1000);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
console.debug('WebRtc stats not enabled');
|
|
|
|
};
|
|
|
|
WebRtcStats.prototype.stopWebRtcStats = function () {
|
|
|
|
if (this.webRtcStatsEnabled) {
|
|
|
|
clearInterval(this.webRtcStatsIntervalId);
|
|
|
|
console.warn('WebRtc stats stopped for disposed stream ' + this.stream.streamId + ' of connection ' + this.stream.connection.connectionId);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
WebRtcStats.prototype.sendStatsToHttpEndpoint = function (instrumentation) {
|
|
|
|
var _this = this;
|
|
|
|
var sendPost = function (json) {
|
|
|
|
var http = new XMLHttpRequest();
|
|
|
|
var url = instrumentation.webrtc.httpEndpoint;
|
|
|
|
http.open('POST', url, true);
|
|
|
|
http.setRequestHeader('Content-type', 'application/json');
|
|
|
|
http.onreadystatechange = function () {
|
|
|
|
if (http.readyState === 4 && http.status === 200) {
|
|
|
|
console.log('WebRtc stats successfully sent to ' + url + ' for stream ' + _this.stream.streamId + ' of connection ' + _this.stream.connection.connectionId);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
http.send(json);
|
|
|
|
};
|
|
|
|
var f = function (stats) {
|
|
|
|
if (platform.name.indexOf('Firefox') !== -1) {
|
|
|
|
stats.forEach(function (stat) {
|
|
|
|
var json = {};
|
|
|
|
if ((stat.type === 'inbound-rtp') &&
|
|
|
|
(
|
|
|
|
// Avoid firefox empty outbound-rtp statistics
|
|
|
|
stat.nackCount !== null &&
|
|
|
|
stat.isRemote === false &&
|
|
|
|
stat.id.startsWith('inbound') &&
|
|
|
|
stat.remoteId.startsWith('inbound'))) {
|
|
|
|
var metricId = 'webrtc_inbound_' + stat.mediaType + '_' + stat.ssrc;
|
|
|
|
var jit = stat.jitter * 1000;
|
|
|
|
var metrics = {
|
|
|
|
bytesReceived: (stat.bytesReceived - _this.stats.inbound[stat.mediaType].bytesReceived) / _this.statsInterval,
|
|
|
|
jitter: jit,
|
|
|
|
packetsReceived: (stat.packetsReceived - _this.stats.inbound[stat.mediaType].packetsReceived) / _this.statsInterval,
|
|
|
|
packetsLost: (stat.packetsLost - _this.stats.inbound[stat.mediaType].packetsLost) / _this.statsInterval
|
|
|
|
};
|
|
|
|
var units = {
|
|
|
|
bytesReceived: 'bytes',
|
|
|
|
jitter: 'ms',
|
|
|
|
packetsReceived: 'packets',
|
|
|
|
packetsLost: 'packets'
|
|
|
|
};
|
|
|
|
if (stat.mediaType === 'video') {
|
|
|
|
metrics['framesDecoded'] = (stat.framesDecoded - _this.stats.inbound.video.framesDecoded) / _this.statsInterval;
|
|
|
|
metrics['nackCount'] = (stat.nackCount - _this.stats.inbound.video.nackCount) / _this.statsInterval;
|
|
|
|
units['framesDecoded'] = 'frames';
|
|
|
|
units['nackCount'] = 'packets';
|
|
|
|
_this.stats.inbound.video.framesDecoded = stat.framesDecoded;
|
|
|
|
_this.stats.inbound.video.nackCount = stat.nackCount;
|
|
|
|
}
|
|
|
|
_this.stats.inbound[stat.mediaType].bytesReceived = stat.bytesReceived;
|
|
|
|
_this.stats.inbound[stat.mediaType].packetsReceived = stat.packetsReceived;
|
|
|
|
_this.stats.inbound[stat.mediaType].packetsLost = stat.packetsLost;
|
|
|
|
json = {
|
|
|
|
'@timestamp': new Date(stat.timestamp).toISOString(),
|
|
|
|
'exec': instrumentation.exec,
|
|
|
|
'component': instrumentation.component,
|
|
|
|
'stream': 'webRtc',
|
|
|
|
'type': metricId,
|
|
|
|
'stream_type': 'composed_metrics',
|
|
|
|
'units': units
|
|
|
|
};
|
|
|
|
json[metricId] = metrics;
|
|
|
|
sendPost(JSON.stringify(json));
|
|
|
|
}
|
|
|
|
else if ((stat.type === 'outbound-rtp') &&
|
|
|
|
(
|
|
|
|
// Avoid firefox empty inbound-rtp statistics
|
|
|
|
stat.isRemote === false &&
|
|
|
|
stat.id.toLowerCase().includes('outbound'))) {
|
|
|
|
var metricId = 'webrtc_outbound_' + stat.mediaType + '_' + stat.ssrc;
|
|
|
|
var metrics = {
|
|
|
|
bytesSent: (stat.bytesSent - _this.stats.outbound[stat.mediaType].bytesSent) / _this.statsInterval,
|
|
|
|
packetsSent: (stat.packetsSent - _this.stats.outbound[stat.mediaType].packetsSent) / _this.statsInterval
|
|
|
|
};
|
|
|
|
var units = {
|
|
|
|
bytesSent: 'bytes',
|
|
|
|
packetsSent: 'packets'
|
|
|
|
};
|
|
|
|
if (stat.mediaType === 'video') {
|
|
|
|
metrics['framesEncoded'] = (stat.framesEncoded - _this.stats.outbound.video.framesEncoded) / _this.statsInterval;
|
|
|
|
units['framesEncoded'] = 'frames';
|
|
|
|
_this.stats.outbound.video.framesEncoded = stat.framesEncoded;
|
|
|
|
}
|
|
|
|
_this.stats.outbound[stat.mediaType].bytesSent = stat.bytesSent;
|
|
|
|
_this.stats.outbound[stat.mediaType].packetsSent = stat.packetsSent;
|
|
|
|
json = {
|
|
|
|
'@timestamp': new Date(stat.timestamp).toISOString(),
|
|
|
|
'exec': instrumentation.exec,
|
|
|
|
'component': instrumentation.component,
|
|
|
|
'stream': 'webRtc',
|
|
|
|
'type': metricId,
|
|
|
|
'stream_type': 'composed_metrics',
|
|
|
|
'units': units
|
|
|
|
};
|
|
|
|
json[metricId] = metrics;
|
|
|
|
sendPost(JSON.stringify(json));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
else if (platform.name.indexOf('Chrome') !== -1) {
|
|
|
|
for (var _i = 0, _a = Object.keys(stats); _i < _a.length; _i++) {
|
|
|
|
var key = _a[_i];
|
|
|
|
var stat = stats[key];
|
|
|
|
if (stat.type === 'ssrc') {
|
|
|
|
var json = {};
|
|
|
|
if ('bytesReceived' in stat && ((stat.mediaType === 'audio' && 'audioOutputLevel' in stat) ||
|
|
|
|
(stat.mediaType === 'video' && 'qpSum' in stat))) {
|
|
|
|
// inbound-rtp
|
|
|
|
var metricId = 'webrtc_inbound_' + stat.mediaType + '_' + stat.ssrc;
|
|
|
|
var metrics = {
|
|
|
|
bytesReceived: (stat.bytesReceived - _this.stats.inbound[stat.mediaType].bytesReceived) / _this.statsInterval,
|
|
|
|
jitter: stat.googJitterBufferMs,
|
|
|
|
packetsReceived: (stat.packetsReceived - _this.stats.inbound[stat.mediaType].packetsReceived) / _this.statsInterval,
|
|
|
|
packetsLost: (stat.packetsLost - _this.stats.inbound[stat.mediaType].packetsLost) / _this.statsInterval
|
|
|
|
};
|
|
|
|
var units = {
|
|
|
|
bytesReceived: 'bytes',
|
|
|
|
jitter: 'ms',
|
|
|
|
packetsReceived: 'packets',
|
|
|
|
packetsLost: 'packets'
|
|
|
|
};
|
|
|
|
if (stat.mediaType === 'video') {
|
|
|
|
metrics['framesDecoded'] = (stat.framesDecoded - _this.stats.inbound.video.framesDecoded) / _this.statsInterval;
|
|
|
|
metrics['nackCount'] = (stat.googNacksSent - _this.stats.inbound.video.nackCount) / _this.statsInterval;
|
|
|
|
units['framesDecoded'] = 'frames';
|
|
|
|
units['nackCount'] = 'packets';
|
|
|
|
_this.stats.inbound.video.framesDecoded = stat.framesDecoded;
|
|
|
|
_this.stats.inbound.video.nackCount = stat.googNacksSent;
|
|
|
|
}
|
|
|
|
_this.stats.inbound[stat.mediaType].bytesReceived = stat.bytesReceived;
|
|
|
|
_this.stats.inbound[stat.mediaType].packetsReceived = stat.packetsReceived;
|
|
|
|
_this.stats.inbound[stat.mediaType].packetsLost = stat.packetsLost;
|
|
|
|
json = {
|
|
|
|
'@timestamp': new Date(stat.timestamp).toISOString(),
|
|
|
|
'exec': instrumentation.exec,
|
|
|
|
'component': instrumentation.component,
|
|
|
|
'stream': 'webRtc',
|
|
|
|
'type': metricId,
|
|
|
|
'stream_type': 'composed_metrics',
|
|
|
|
'units': units
|
|
|
|
};
|
|
|
|
json[metricId] = metrics;
|
|
|
|
sendPost(JSON.stringify(json));
|
|
|
|
}
|
|
|
|
else if ('bytesSent' in stat) {
|
|
|
|
// outbound-rtp
|
|
|
|
var metricId = 'webrtc_outbound_' + stat.mediaType + '_' + stat.ssrc;
|
|
|
|
var metrics = {
|
|
|
|
bytesSent: (stat.bytesSent - _this.stats.outbound[stat.mediaType].bytesSent) / _this.statsInterval,
|
|
|
|
packetsSent: (stat.packetsSent - _this.stats.outbound[stat.mediaType].packetsSent) / _this.statsInterval
|
|
|
|
};
|
|
|
|
var units = {
|
|
|
|
bytesSent: 'bytes',
|
|
|
|
packetsSent: 'packets'
|
|
|
|
};
|
|
|
|
if (stat.mediaType === 'video') {
|
|
|
|
metrics['framesEncoded'] = (stat.framesEncoded - _this.stats.outbound.video.framesEncoded) / _this.statsInterval;
|
|
|
|
units['framesEncoded'] = 'frames';
|
|
|
|
_this.stats.outbound.video.framesEncoded = stat.framesEncoded;
|
|
|
|
}
|
|
|
|
_this.stats.outbound[stat.mediaType].bytesSent = stat.bytesSent;
|
|
|
|
_this.stats.outbound[stat.mediaType].packetsSent = stat.packetsSent;
|
|
|
|
json = {
|
|
|
|
'@timestamp': new Date(stat.timestamp).toISOString(),
|
|
|
|
'exec': instrumentation.exec,
|
|
|
|
'component': instrumentation.component,
|
|
|
|
'stream': 'webRtc',
|
|
|
|
'type': metricId,
|
|
|
|
'stream_type': 'composed_metrics',
|
|
|
|
'units': units
|
|
|
|
};
|
|
|
|
json[metricId] = metrics;
|
|
|
|
sendPost(JSON.stringify(json));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
this.getStatsAgnostic(this.stream.getRTCPeerConnection(), f, function (error) { console.log(error); });
|
|
|
|
};
|
|
|
|
WebRtcStats.prototype.standardizeReport = function (response) {
|
|
|
|
if (platform.name.indexOf('Firefox') !== -1) {
|
|
|
|
return response;
|
|
|
|
}
|
|
|
|
var standardReport = {};
|
|
|
|
response.result().forEach(function (report) {
|
|
|
|
var standardStats = {
|
|
|
|
id: report.id,
|
|
|
|
timestamp: report.timestamp,
|
|
|
|
type: report.type
|
|
|
|
};
|
|
|
|
report.names().forEach(function (name) {
|
|
|
|
standardStats[name] = report.stat(name);
|
|
|
|
});
|
|
|
|
standardReport[standardStats.id] = standardStats;
|
|
|
|
});
|
|
|
|
return standardReport;
|
|
|
|
};
|
|
|
|
WebRtcStats.prototype.getStatsAgnostic = function (pc, successCb, failureCb) {
|
|
|
|
var _this = this;
|
|
|
|
if (platform.name.indexOf('Firefox') !== -1) {
|
|
|
|
// getStats takes args in different order in Chrome and Firefox
|
|
|
|
return pc.getStats(null, function (response) {
|
|
|
|
var report = _this.standardizeReport(response);
|
|
|
|
successCb(report);
|
|
|
|
}, failureCb);
|
|
|
|
}
|
|
|
|
else if (platform.name.indexOf('Chrome') !== -1) {
|
|
|
|
// In Chrome, the first two arguments are reversed
|
|
|
|
return pc.getStats(function (response) {
|
|
|
|
var report = _this.standardizeReport(response);
|
|
|
|
successCb(report);
|
|
|
|
}, null, failureCb);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
return WebRtcStats;
|
|
|
|
}());
|
|
|
|
exports.WebRtcStats = WebRtcStats;
|
|
|
|
//# sourceMappingURL=WebRtcStats.js.map
|