use statistics module from the library

This commit is contained in:
isymchych 2016-01-29 17:33:16 +02:00
parent 103b5d71d0
commit d19789005e
13 changed files with 29 additions and 971 deletions

6
app.js
View File

@ -17,7 +17,6 @@ import URLProcessor from "./modules/config/URLProcessor";
import RoomnameGenerator from './modules/util/RoomnameGenerator';
import UI from "./modules/UI/UI";
import statistics from "./modules/statistics/statistics";
import settings from "./modules/settings/Settings";
import conference from './conference';
import API from './modules/API/API';
@ -59,13 +58,10 @@ function buildRoomName () {
const APP = {
UI,
statistics,
settings,
conference,
API,
init () {
this.connectionquality =
require("./modules/connectionquality/connectionquality");
this.desktopsharing =
require("./modules/desktopsharing/desktopsharing");
this.keyboardshortcut =
@ -87,8 +83,6 @@ function init() {
});
APP.desktopsharing.init(JitsiMeetJS.isDesktopSharingEnabled());
APP.statistics.start();
APP.connectionquality.init();
APP.keyboardshortcut.init();
}).catch(function (err) {
console.error(err);

View File

@ -5,6 +5,8 @@ import createRoomLocker from './modules/UI/authentication/RoomLocker';
//FIXME:
import AuthHandler from './modules/UI/authentication/AuthHandler';
import ConnectionQuality from './modules/connectionquality/connectionquality';
import CQEvents from './service/connectionquality/CQEvents';
import UIEvents from './service/UI/UIEvents';
import DSEvents from './service/desktopsharing/DesktopSharingEventTypes';
@ -154,6 +156,7 @@ class ConferenceConnector {
case ConferenceErrors.CONFERENCE_DESTROYED:
{
let [reason] = params;
APP.UI.hideStats();
APP.UI.notifyConferenceDestroyed(reason);
}
break;
@ -556,6 +559,7 @@ export default {
});
room.on(ConferenceEvents.KICKED, () => {
APP.UI.hideStats();
APP.UI.notifyKicked();
// FIXME close
});
@ -599,14 +603,17 @@ export default {
});
}
APP.connectionquality.addListener(
room.on(ConferenceEvents.CONNECTION_STATS, function (stats) {
ConnectionQuality.updateLocalStats(stats);
});
ConnectionQuality.addListener(
CQEvents.LOCALSTATS_UPDATED,
(percent, stats) => {
APP.UI.updateLocalStats(percent, stats);
// send local stats to other users
room.sendCommandOnce(Commands.CONNECTION_QUALITY, {
children: APP.connectionquality.convertToMUCStats(stats),
children: ConnectionQuality.convertToMUCStats(stats),
attributes: {
xmlns: 'http://jitsi.org/jitmeet/stats'
}
@ -614,17 +621,12 @@ export default {
}
);
APP.connectionquality.addListener(CQEvents.STOP, () => {
APP.UI.hideStats();
room.removeCommand(Commands.CONNECTION_QUALITY);
});
// listen to remote stats
room.addCommandListener(Commands.CONNECTION_QUALITY,(values, from) => {
APP.connectionquality.updateRemoteStats(from, values);
ConnectionQuality.updateRemoteStats(from, values);
});
APP.connectionquality.addListener(CQEvents.REMOTESTATS_UPDATED,
ConnectionQuality.addListener(CQEvents.REMOTESTATS_UPDATED,
(id, percent, stats) => {
APP.UI.updateRemoteStats(id, percent, stats);
});

View File

@ -59,7 +59,7 @@ ConnectionIndicator.getStringFromArray = function (array) {
* @returns {string} the html content.
*/
ConnectionIndicator.prototype.generateText = function () {
var downloadBitrate, uploadBitrate, packetLoss, resolution, i;
var downloadBitrate, uploadBitrate, packetLoss, i;
var translate = APP.translation.translateString;
@ -86,44 +86,12 @@ ConnectionIndicator.prototype.generateText = function () {
"%";
}
var resolutionValue = null;
if(this.resolution && this.id) {
var keys = Object.keys(this.resolution);
for(var ssrc in this.resolution) {
// skip resolutions for ssrc that don't have this info
// like receive-only ssrc for FF
if(this.resolution[ssrc]
&& this.resolution[ssrc].height != -1
&& this.resolution[ssrc].width != -1)
resolutionValue = this.resolution[ssrc];
}
}
if(this.id === null) {
resolution = "";
if(this.resolution === null || !Object.keys(this.resolution) ||
Object.keys(this.resolution).length === 0) {
resolution = "N/A";
} else {
for (i in this.resolution) {
resolutionValue = this.resolution[i];
if (resolutionValue) {
if (resolutionValue.height &&
resolutionValue.width) {
resolution += (resolution === "" ? "" : ", ") +
resolutionValue.width + "x" +
resolutionValue.height;
}
}
}
}
} else if(!resolutionValue ||
!resolutionValue.height ||
!resolutionValue.width) {
resolution = "N/A";
} else {
resolution = resolutionValue.width + "x" + resolutionValue.height;
}
// GENERATE RESOLUTIONS STRING
let resolutions = this.resolution || {};
let resolutionStr = Object.keys(resolutions).map(function (ssrc) {
let {width, height} = resolutions[ssrc];
return `${width}x${height}`;
}).join(', ') || 'N/A';
var result = "<table style='width:100%'>" +
"<tr>" +
@ -139,7 +107,7 @@ ConnectionIndicator.prototype.generateText = function () {
"</tr><tr>" +
"<td><span class='jitsipopover_blue' data-i18n='connectionindicator.resolution'>" +
translate("connectionindicator.resolution") + "</span></td>" +
"<td>" + resolution + "</td></tr></table>";
"<td>" + resolutionStr + "</td></tr></table>";
if(this.videoContainer.videoSpanId == "localVideoContainer") {
result += "<div class=\"jitsipopover_showmore\" " +

View File

@ -728,13 +728,9 @@ var VideoLayout = {
* @param object
*/
updateLocalConnectionStats (percent, object) {
let resolutions = {};
if (object.resolution !== null) {
resolutions = object.resolution;
var id = Strophe.getResourceFromJid(
APP.conference._room.room.session.me);
object.resolution = resolutions[id];
}
let resolutions = object.resolution;
object.resolution = resolutions[APP.conference.localId];
localVideoThumbnail.updateStatsIndicator(percent, object);
Object.keys(resolutions).forEach(function (id) {

View File

@ -1,9 +1,10 @@
/* global APP, require */
/* jshint -W101 */
var EventEmitter = require("events");
var eventEmitter = new EventEmitter();
var CQEvents = require("../../service/connectionquality/CQEvents");
var StatisticsEvents = require("../../service/statistics/Events");
import EventEmitter from "events";
import CQEvents from "../../service/connectionquality/CQEvents";
const eventEmitter = new EventEmitter();
/**
* local stats
@ -50,16 +51,7 @@ function parseMUCStats(stats) {
};
}
var ConnectionQuality = {
init: function () {
APP.statistics.addListener(
StatisticsEvents.CONNECTION_STATS, this.updateLocalStats
);
APP.statistics.addListener(
StatisticsEvents.STOP, this.stopSendingStats
);
},
export default {
/**
* Updates the local statistics
* @param data new statistics
@ -87,14 +79,6 @@ var ConnectionQuality = {
);
},
/**
* Stops statistics sending.
*/
stopSendingStats: function () {
//notify UI about stopping statistics gathering
eventEmitter.emit(CQEvents.STOP);
},
/**
* Returns the local statistics.
*/
@ -125,5 +109,3 @@ var ConnectionQuality = {
];
}
};
module.exports = ConnectionQuality;

View File

@ -1,4 +1,4 @@
/* global config JitsiMeetJS */
/* global config, JitsiMeetJS */
// Load the integration of a third-party analytics API such as Google Analytics.
// Since we cannot guarantee the quality of the third-party service (e.g. their

View File

@ -1,664 +0,0 @@
/* global require, ssrc2jid */
/* jshint -W117 */
/* jshint -W101 */
var RTCBrowserType = require("../RTC/RTCBrowserType");
var StatisticsEvents = require("../../service/statistics/Events");
/* Whether we support the browser we are running into for logging statistics */
var browserSupported = RTCBrowserType.isChrome() ||
RTCBrowserType.isOpera() || RTCBrowserType.isFirefox();
/**
* Calculates packet lost percent using the number of lost packets and the
* number of all packet.
* @param lostPackets the number of lost packets
* @param totalPackets the number of all packets.
* @returns {number} packet loss percent
*/
function calculatePacketLoss(lostPackets, totalPackets) {
if(!totalPackets || totalPackets <= 0 || !lostPackets || lostPackets <= 0)
return 0;
return Math.round((lostPackets/totalPackets)*100);
}
function getStatValue(item, name) {
var browserType = RTCBrowserType.getBrowserType();
if (!keyMap[browserType][name])
throw "The property isn't supported!";
var key = keyMap[browserType][name];
return (RTCBrowserType.isChrome() || RTCBrowserType.isOpera()) ?
item.stat(key) : item[key];
}
/**
* Peer statistics data holder.
* @constructor
*/
function PeerStats()
{
this.ssrc2Loss = {};
this.ssrc2AudioLevel = {};
this.ssrc2bitrate = {};
this.ssrc2resolution = {};
}
/**
* The bandwidth
* @type {{}}
*/
PeerStats.bandwidth = {};
/**
* The bit rate
* @type {{}}
*/
PeerStats.bitrate = {};
/**
* The packet loss rate
* @type {{}}
*/
PeerStats.packetLoss = null;
/**
* Sets packets loss rate for given <tt>ssrc</tt> that blong to the peer
* represented by this instance.
* @param ssrc audio or video RTP stream SSRC.
* @param lossRate new packet loss rate value to be set.
*/
PeerStats.prototype.setSsrcLoss = function (ssrc, lossRate)
{
this.ssrc2Loss[ssrc] = lossRate;
};
/**
* Sets resolution for given <tt>ssrc</tt> that belong to the peer
* represented by this instance.
* @param ssrc audio or video RTP stream SSRC.
* @param resolution new resolution value to be set.
*/
PeerStats.prototype.setSsrcResolution = function (ssrc, resolution)
{
if(resolution === null && this.ssrc2resolution[ssrc])
{
delete this.ssrc2resolution[ssrc];
}
else if(resolution !== null)
this.ssrc2resolution[ssrc] = resolution;
};
/**
* Sets the bit rate for given <tt>ssrc</tt> that blong to the peer
* represented by this instance.
* @param ssrc audio or video RTP stream SSRC.
* @param bitrate new bitrate value to be set.
*/
PeerStats.prototype.setSsrcBitrate = function (ssrc, bitrate)
{
if(this.ssrc2bitrate[ssrc])
{
this.ssrc2bitrate[ssrc].download += bitrate.download;
this.ssrc2bitrate[ssrc].upload += bitrate.upload;
}
else {
this.ssrc2bitrate[ssrc] = bitrate;
}
};
/**
* Sets new audio level(input or output) for given <tt>ssrc</tt> that identifies
* the stream which belongs to the peer represented by this instance.
* @param ssrc RTP stream SSRC for which current audio level value will be
* updated.
* @param audioLevel the new audio level value to be set. Value is truncated to
* fit the range from 0 to 1.
*/
PeerStats.prototype.setSsrcAudioLevel = function (ssrc, audioLevel)
{
// Range limit 0 - 1
this.ssrc2AudioLevel[ssrc] = formatAudioLevel(audioLevel);
};
function formatAudioLevel(audioLevel) {
return Math.min(Math.max(audioLevel, 0), 1);
}
/**
* Array with the transport information.
* @type {Array}
*/
PeerStats.transport = [];
/**
* <tt>StatsCollector</tt> registers for stats updates of given
* <tt>peerconnection</tt> in given <tt>interval</tt>. On each update particular
* stats are extracted and put in {@link PeerStats} objects. Once the processing
* is done <tt>audioLevelsUpdateCallback</tt> is called with <tt>this</tt>
* instance as an event source.
*
* @param peerconnection webRTC peer connection object.
* @param interval stats refresh interval given in ms.
* @param {function(StatsCollector)} audioLevelsUpdateCallback the callback
* called on stats update.
* @constructor
*/
function StatsCollector(peerconnection, audioLevelsInterval, statsInterval, eventEmitter)
{
this.peerconnection = peerconnection;
this.baselineAudioLevelsReport = null;
this.currentStatsReport = null;
this.baselineStatsReport = null;
this.audioLevelsIntervalId = null;
this.eventEmitter = eventEmitter;
/**
* Gather PeerConnection stats once every this many milliseconds.
*/
this.GATHER_INTERVAL = 15000;
/**
* Log stats via the focus once every this many milliseconds.
*/
this.LOG_INTERVAL = 60000;
/**
* Gather stats and store them in this.statsToBeLogged.
*/
this.gatherStatsIntervalId = null;
/**
* Send the stats already saved in this.statsToBeLogged to be logged via
* the focus.
*/
this.logStatsIntervalId = null;
/**
* Stores the statistics which will be send to the focus to be logged.
*/
this.statsToBeLogged =
{
timestamps: [],
stats: {}
};
// Updates stats interval
this.audioLevelsIntervalMilis = audioLevelsInterval;
this.statsIntervalId = null;
this.statsIntervalMilis = statsInterval;
// Map of jids to PeerStats
this.jid2stats = {};
}
module.exports = StatsCollector;
/**
* Stops stats updates.
*/
StatsCollector.prototype.stop = function () {
if (this.audioLevelsIntervalId) {
clearInterval(this.audioLevelsIntervalId);
this.audioLevelsIntervalId = null;
}
if (this.statsIntervalId)
{
clearInterval(this.statsIntervalId);
this.statsIntervalId = null;
}
if(this.logStatsIntervalId)
{
clearInterval(this.logStatsIntervalId);
this.logStatsIntervalId = null;
}
if(this.gatherStatsIntervalId)
{
clearInterval(this.gatherStatsIntervalId);
this.gatherStatsIntervalId = null;
}
};
/**
* Callback passed to <tt>getStats</tt> method.
* @param error an error that occurred on <tt>getStats</tt> call.
*/
StatsCollector.prototype.errorCallback = function (error)
{
console.error("Get stats error", error);
this.stop();
};
/**
* Starts stats updates.
*/
StatsCollector.prototype.start = function ()
{
var self = this;
if (!config.disableAudioLevels) {
this.audioLevelsIntervalId = setInterval(
function () {
// Interval updates
self.peerconnection.getStats(
function (report) {
var results = null;
if (!report || !report.result ||
typeof report.result != 'function') {
results = report;
}
else {
results = report.result();
}
//console.error("Got interval report", results);
self.baselineAudioLevelsReport = results;
},
self.errorCallback
);
},
self.audioLevelsIntervalMilis
);
}
if (!config.disableStats && browserSupported) {
this.statsIntervalId = setInterval(
function () {
// Interval updates
self.peerconnection.getStats(
function (report) {
var results = null;
if (!report || !report.result ||
typeof report.result != 'function') {
//firefox
results = report;
}
else {
//chrome
results = report.result();
}
//console.error("Got interval report", results);
self.currentStatsReport = results;
try {
self.processStatsReport();
}
catch (e) {
console.error("Unsupported key:" + e, e);
}
self.baselineStatsReport = self.currentStatsReport;
},
self.errorCallback
);
},
self.statsIntervalMilis
);
}
// Logging statistics does not support firefox
if (config.logStats && (browserSupported && !RTCBrowserType.isFirefox())) {
this.gatherStatsIntervalId = setInterval(
function () {
self.peerconnection.getStats(
function (report) {
self.addStatsToBeLogged(report.result());
},
function () {
}
);
},
this.GATHER_INTERVAL
);
this.logStatsIntervalId = setInterval(
function() { self.logStats(); },
this.LOG_INTERVAL);
}
};
/**
* Checks whether a certain record should be included in the logged statistics.
*/
function acceptStat(reportId, reportType, statName) {
if (reportType == "googCandidatePair" && statName == "googChannelId")
return false;
if (reportType == "ssrc") {
if (statName == "googTrackId" ||
statName == "transportId" ||
statName == "ssrc")
return false;
}
return true;
}
/**
* Checks whether a certain record should be included in the logged statistics.
*/
function acceptReport(id, type) {
if (id.substring(0, 15) == "googCertificate" ||
id.substring(0, 9) == "googTrack" ||
id.substring(0, 20) == "googLibjingleSession")
return false;
if (type == "googComponent")
return false;
return true;
}
/**
* Converts the stats to the format used for logging, and saves the data in
* this.statsToBeLogged.
* @param reports Reports as given by webkitRTCPerConnection.getStats.
*/
StatsCollector.prototype.addStatsToBeLogged = function (reports) {
var self = this;
var num_records = this.statsToBeLogged.timestamps.length;
this.statsToBeLogged.timestamps.push(new Date().getTime());
reports.map(function (report) {
if (!acceptReport(report.id, report.type))
return;
var stat = self.statsToBeLogged.stats[report.id];
if (!stat) {
stat = self.statsToBeLogged.stats[report.id] = {};
}
stat.type = report.type;
report.names().map(function (name) {
if (!acceptStat(report.id, report.type, name))
return;
var values = stat[name];
if (!values) {
values = stat[name] = [];
}
while (values.length < num_records) {
values.push(null);
}
values.push(report.stat(name));
});
});
};
StatsCollector.prototype.logStats = function () {
if(!APP.conference._room.xmpp.sendLogs(this.statsToBeLogged))
return;
// Reset the stats
this.statsToBeLogged.stats = {};
this.statsToBeLogged.timestamps = [];
};
var keyMap = {};
keyMap[RTCBrowserType.RTC_BROWSER_FIREFOX] = {
"ssrc": "ssrc",
"packetsReceived": "packetsReceived",
"packetsLost": "packetsLost",
"packetsSent": "packetsSent",
"bytesReceived": "bytesReceived",
"bytesSent": "bytesSent"
};
keyMap[RTCBrowserType.RTC_BROWSER_CHROME] = {
"receiveBandwidth": "googAvailableReceiveBandwidth",
"sendBandwidth": "googAvailableSendBandwidth",
"remoteAddress": "googRemoteAddress",
"transportType": "googTransportType",
"localAddress": "googLocalAddress",
"activeConnection": "googActiveConnection",
"ssrc": "ssrc",
"packetsReceived": "packetsReceived",
"packetsSent": "packetsSent",
"packetsLost": "packetsLost",
"bytesReceived": "bytesReceived",
"bytesSent": "bytesSent",
"googFrameHeightReceived": "googFrameHeightReceived",
"googFrameWidthReceived": "googFrameWidthReceived",
"googFrameHeightSent": "googFrameHeightSent",
"googFrameWidthSent": "googFrameWidthSent",
"audioInputLevel": "audioInputLevel",
"audioOutputLevel": "audioOutputLevel"
};
keyMap[RTCBrowserType.RTC_BROWSER_OPERA] =
keyMap[RTCBrowserType.RTC_BROWSER_CHROME];
/**
* Stats processing logic.
*/
StatsCollector.prototype.processStatsReport = function () {
if (!this.baselineStatsReport) {
return;
}
for (var idx in this.currentStatsReport) {
var now = this.currentStatsReport[idx];
try {
if (getStatValue(now, 'receiveBandwidth') ||
getStatValue(now, 'sendBandwidth')) {
PeerStats.bandwidth = {
"download": Math.round(
(getStatValue(now, 'receiveBandwidth')) / 1000),
"upload": Math.round(
(getStatValue(now, 'sendBandwidth')) / 1000)
};
}
}
catch(e){/*not supported*/}
if(now.type == 'googCandidatePair')
{
var ip, type, localIP, active;
try {
ip = getStatValue(now, 'remoteAddress');
type = getStatValue(now, "transportType");
localIP = getStatValue(now, "localAddress");
active = getStatValue(now, "activeConnection");
}
catch(e){/*not supported*/}
if(!ip || !type || !localIP || active != "true")
continue;
var addressSaved = false;
for(var i = 0; i < PeerStats.transport.length; i++)
{
if(PeerStats.transport[i].ip == ip &&
PeerStats.transport[i].type == type &&
PeerStats.transport[i].localip == localIP)
{
addressSaved = true;
}
}
if(addressSaved)
continue;
PeerStats.transport.push({localip: localIP, ip: ip, type: type});
continue;
}
if(now.type == "candidatepair")
{
if(now.state == "succeeded")
continue;
var local = this.currentStatsReport[now.localCandidateId];
var remote = this.currentStatsReport[now.remoteCandidateId];
PeerStats.transport.push({localip: local.ipAddress + ":" + local.portNumber,
ip: remote.ipAddress + ":" + remote.portNumber, type: local.transport});
}
if (now.type != 'ssrc' && now.type != "outboundrtp" &&
now.type != "inboundrtp") {
continue;
}
var before = this.baselineStatsReport[idx];
if (!before) {
console.warn(getStatValue(now, 'ssrc') + ' not enough data');
continue;
}
var ssrc = getStatValue(now, 'ssrc');
if(!ssrc)
continue;
var jid = APP.conference._room.room.getJidBySSRC(ssrc);
if (!jid && (Date.now() - now.timestamp) < 3000) {
console.warn("No jid for ssrc: " + ssrc);
continue;
}
var jidStats = this.jid2stats[jid];
if (!jidStats) {
jidStats = new PeerStats();
this.jid2stats[jid] = jidStats;
}
var isDownloadStream = true;
var key = 'packetsReceived';
var packetsNow = getStatValue(now, key);
if (typeof packetsNow === 'undefined' || packetsNow === null) {
isDownloadStream = false;
key = 'packetsSent';
packetsNow = getStatValue(now, key);
if (typeof packetsNow === 'undefined' || packetsNow === null) {
console.warn("No packetsReceived nor packetsSent stat found");
continue;
}
}
if (!packetsNow || packetsNow < 0)
packetsNow = 0;
var packetsBefore = getStatValue(before, key);
if (!packetsBefore || packetsBefore < 0)
packetsBefore = 0;
var packetRate = packetsNow - packetsBefore;
if (!packetRate || packetRate < 0)
packetRate = 0;
var currentLoss = getStatValue(now, 'packetsLost');
if (!currentLoss || currentLoss < 0)
currentLoss = 0;
var previousLoss = getStatValue(before, 'packetsLost');
if (!previousLoss || previousLoss < 0)
previousLoss = 0;
var lossRate = currentLoss - previousLoss;
if (!lossRate || lossRate < 0)
lossRate = 0;
var packetsTotal = (packetRate + lossRate);
jidStats.setSsrcLoss(ssrc,
{"packetsTotal": packetsTotal,
"packetsLost": lossRate,
"isDownloadStream": isDownloadStream});
var bytesReceived = 0, bytesSent = 0;
if(getStatValue(now, "bytesReceived")) {
bytesReceived = getStatValue(now, "bytesReceived") -
getStatValue(before, "bytesReceived");
}
if(getStatValue(now, "bytesSent")) {
bytesSent = getStatValue(now, "bytesSent") -
getStatValue(before, "bytesSent");
}
var time = Math.round((now.timestamp - before.timestamp) / 1000);
if(bytesReceived <= 0 || time <= 0) {
bytesReceived = 0;
} else {
bytesReceived = Math.round(((bytesReceived * 8) / time) / 1000);
}
if(bytesSent <= 0 || time <= 0) {
bytesSent = 0;
} else {
bytesSent = Math.round(((bytesSent * 8) / time) / 1000);
}
jidStats.setSsrcBitrate(ssrc, {
"download": bytesReceived,
"upload": bytesSent});
var resolution = {height: null, width: null};
try {
if (getStatValue(now, "googFrameHeightReceived") &&
getStatValue(now, "googFrameWidthReceived")) {
resolution.height = getStatValue(now, "googFrameHeightReceived");
resolution.width = getStatValue(now, "googFrameWidthReceived");
}
else if (getStatValue(now, "googFrameHeightSent") &&
getStatValue(now, "googFrameWidthSent")) {
resolution.height = getStatValue(now, "googFrameHeightSent");
resolution.width = getStatValue(now, "googFrameWidthSent");
}
}
catch(e){/*not supported*/}
if(resolution.height && resolution.width) {
jidStats.setSsrcResolution(ssrc, resolution);
} else {
jidStats.setSsrcResolution(ssrc, null);
}
}
var self = this;
// Jid stats
var totalPackets = {download: 0, upload: 0};
var lostPackets = {download: 0, upload: 0};
var bitrateDownload = 0;
var bitrateUpload = 0;
var resolutions = {};
Object.keys(this.jid2stats).forEach(
function (jid) {
Object.keys(self.jid2stats[jid].ssrc2Loss).forEach(
function (ssrc) {
var type = "upload";
if(self.jid2stats[jid].ssrc2Loss[ssrc].isDownloadStream)
type = "download";
totalPackets[type] +=
self.jid2stats[jid].ssrc2Loss[ssrc].packetsTotal;
lostPackets[type] +=
self.jid2stats[jid].ssrc2Loss[ssrc].packetsLost;
}
);
Object.keys(self.jid2stats[jid].ssrc2bitrate).forEach(
function (ssrc) {
bitrateDownload +=
self.jid2stats[jid].ssrc2bitrate[ssrc].download;
bitrateUpload +=
self.jid2stats[jid].ssrc2bitrate[ssrc].upload;
delete self.jid2stats[jid].ssrc2bitrate[ssrc];
}
);
resolutions[jid] = self.jid2stats[jid].ssrc2resolution;
}
);
PeerStats.bitrate = {"upload": bitrateUpload, "download": bitrateDownload};
PeerStats.packetLoss = {
total:
calculatePacketLoss(lostPackets.download + lostPackets.upload,
totalPackets.download + totalPackets.upload),
download:
calculatePacketLoss(lostPackets.download, totalPackets.download),
upload:
calculatePacketLoss(lostPackets.upload, totalPackets.upload)
};
let idResolution = {};
if (resolutions) { // use id instead of jid
Object.keys(resolutions).forEach(function (jid) {
let id = Strophe.getResourceFromJid(jid);
idResolution[id] = resolutions[jid];
});
}
this.eventEmitter.emit(StatisticsEvents.CONNECTION_STATS,
{
"bitrate": PeerStats.bitrate,
"packetLoss": PeerStats.packetLoss,
"bandwidth": PeerStats.bandwidth,
"resolution": idResolution,
"transport": PeerStats.transport
});
PeerStats.transport = [];
};

View File

@ -1,71 +0,0 @@
/* global require, APP */
/**
* Created by hristo on 8/4/14.
*/
var RTPStats = require("./RTPStatsCollector.js");
var EventEmitter = require("events");
var StreamEventTypes = require("../../service/RTC/StreamEventTypes.js");
var XMPPEvents = require("../../service/xmpp/XMPPEvents");
var RTCEvents = require("../../service/RTC/RTCEvents");
var StatisticsEvents = require("../../service/statistics/Events");
var eventEmitter = new EventEmitter();
var rtpStats = null;
function stopRemote() {
if (rtpStats) {
rtpStats.stop();
eventEmitter.emit(StatisticsEvents.STOP);
rtpStats = null;
}
}
function startRemoteStats (peerconnection) {
if (rtpStats) {
rtpStats.stop();
}
rtpStats = new RTPStats(peerconnection, 200, 2000, eventEmitter);
rtpStats.start();
}
function onDisposeConference(onUnload) {
stopRemote();
if (onUnload) {
eventEmitter.removeAllListeners();
}
}
export default {
/**
* Indicates that this audio level is for local jid.
* @type {string}
*/
LOCAL_JID: 'local',
addListener: function(type, listener) {
eventEmitter.on(type, listener);
},
removeListener: function (type, listener) {
eventEmitter.removeListener(type, listener);
},
stop: function () {
stopRemote();
if (eventEmitter) {
eventEmitter.removeAllListeners();
}
},
start: function () {
const xmpp = APP.conference._room.xmpp;
xmpp.addListener(
XMPPEvents.DISPOSE_CONFERENCE,
onDisposeConference
);
//FIXME: we may want to change CALL INCOMING event to
// onnegotiationneeded
xmpp.addListener(XMPPEvents.CALL_INCOMING, function (event) {
startRemoteStats(event.peerconnection);
});
}
};

View File

@ -1,18 +0,0 @@
var RTCEvents = {
RTC_READY: "rtc.ready",
DATA_CHANNEL_OPEN: "rtc.data_channel_open",
CREATE_OFFER_FAILED: "rtc.create_offer_failed",
CREATE_ANSWER_FAILED: "rtc.create_answer_failed",
SET_LOCAL_DESCRIPTION_FAILED: "rtc.set_local_description_failed",
SET_REMOTE_DESCRIPTION_FAILED: "rtc.set_remote_description_failed",
ADD_ICE_CANDIDATE_FAILED: "rtc.add_ice_candidate_failed",
GET_USER_MEDIA_FAILED: "rtc.get_user_media_failed",
LASTN_CHANGED: "rtc.lastn_changed",
DOMINANTSPEAKER_CHANGED: "rtc.dominantspeaker_changed",
LASTN_ENDPOINT_CHANGED: "rtc.lastn_endpoint_changed",
AVAILABLE_DEVICES_CHANGED: "rtc.available_devices_changed",
AUDIO_MUTE: "rtc.audio_mute",
VIDEO_MUTE: "rtc.video_mute"
};
module.exports = RTCEvents;

View File

@ -1,13 +0,0 @@
var StreamEventTypes = {
EVENT_TYPE_LOCAL_CREATED: "stream.local_created",
EVENT_TYPE_LOCAL_CHANGED: "stream.local_changed",
EVENT_TYPE_LOCAL_ENDED: "stream.local_ended",
EVENT_TYPE_REMOTE_CREATED: "stream.remote_created",
EVENT_TYPE_REMOTE_ENDED: "stream.remote_ended"
};
module.exports = StreamEventTypes;

View File

@ -1,12 +0,0 @@
var AuthenticationEvents = {
/**
* Event callback arguments:
* function(authenticationEnabled, userIdentity)
* authenticationEnabled - indicates whether authentication has been enabled
* in this session
* userIdentity - if user has been logged in then it contains user name. If
* contains 'null' or 'undefined' then user is not logged in.
*/
IDENTITY_UPDATED: "authentication.identity_updated"
};
module.exports = AuthenticationEvents;

View File

@ -1,14 +0,0 @@
module.exports = {
/**
* An event carrying connection statistics.
*/
CONNECTION_STATS: "statistics.connectionstats",
/**
* FIXME: needs documentation.
*/
AUDIO_LEVEL: "statistics.audioLevel",
/**
* FIXME: needs documentation.
*/
STOP: "statistics.stop"
};

View File

@ -1,92 +0,0 @@
var XMPPEvents = {
// Designates an event indicating that the connection to the XMPP server
// failed.
CONNECTION_FAILED: "xmpp.connection.failed",
// Designates an event indicating that the media (ICE) connection was
// interrupted. This should go to the RTC module.
CONNECTION_INTERRUPTED: "xmpp.connection.interrupted",
// Designates an event indicating that the media (ICE) connection was
// restored. This should go to the RTC module.
CONNECTION_RESTORED: "xmpp.connection.restored",
// Designates an event indicating that an offer (e.g. Jingle
// session-initiate) was received.
CALL_INCOMING: "xmpp.callincoming.jingle",
// Designates an event indicating that we were kicked from the XMPP MUC.
KICKED: "xmpp.kicked",
// Designates an event indicating that the userID for a specific JID has
// changed.
// Note: currently this event fires every time we receive presence from
// someone (regardless of whether or not the "userID" changed).
USER_ID_CHANGED: "xmpp.user_id_changed",
// Designates an event indicating that we have joined the XMPP MUC.
MUC_JOINED: "xmpp.muc_joined",
// Designates an event indicating that a participant joined the XMPP MUC.
MUC_MEMBER_JOINED: "xmpp.muc_member_joined",
// Designates an event indicating that a participant left the XMPP MUC.
MUC_MEMBER_LEFT: "xmpp.muc_member_left",
// Designates an event indicating that the MUC role of a participant has
// changed.
MUC_ROLE_CHANGED: "xmpp.muc_role_changed",
// Designates an event indicating that the XMPP MUC was destroyed.
MUC_DESTROYED: "xmpp.muc_destroyed",
// Designates an event indicating that the display name of a participant
// has changed.
DISPLAY_NAME_CHANGED: "xmpp.display_name_changed",
// Designates an event indicating that we received statistics from a
// participant in the MUC.
REMOTE_STATS: "xmpp.remote_stats",
// Designates an event indicating that our role in the XMPP MUC has changed.
LOCAL_ROLE_CHANGED: "xmpp.localrole_changed",
// Designates an event indicating that the subject of the XMPP MUC has
// changed.
SUBJECT_CHANGED: "xmpp.subject_changed",
// Designates an event indicating that an XMPP message in the MUC was
// received.
MESSAGE_RECEIVED: "xmpp.message_received",
// Designates an event indicating that we sent an XMPP message to the MUC.
SENDING_CHAT_MESSAGE: "xmpp.sending_chat_message",
// Designates an event indicating that the video type (e.g. 'camera' or
// 'screen') for a participant has changed.
// Note: currently this event fires every time we receive presence from
// someone (regardless of whether or not the "video type" changed).
PARTICIPANT_VIDEO_TYPE_CHANGED: "xmpp.video_type",
// Designates an event indicating that a participant in the XMPP MUC has
// advertised that they have audio muted (or unmuted).
PARTICIPANT_AUDIO_MUTED: "xmpp.audio_muted",
// Designates an event indicating that a participant in the XMPP MUC has
// advertised that they have video muted (or unmuted).
PARTICIPANT_VIDEO_MUTED: "xmpp.video_muted",
// Designates an event indicating that the focus has asked us to mute our
// audio.
AUDIO_MUTED_BY_FOCUS: "xmpp.audio_muted_by_focus",
// Designates an event indicating that a moderator in the room changed the
// "start muted" settings for the conference.
START_MUTED_SETTING_CHANGED: "xmpp.start_muted_setting_changed",
// Designates an event indicating that we should join the conference with
// audio and/or video muted.
START_MUTED_FROM_FOCUS: "xmpp.start_muted_from_focus",
// Designates an event indicating that a remote participant's available
// devices (whether he supports a audio and/or video) changed.
// Note: currently this event fires every time we receive presence from
// someone (regardless of whether or not the devices changed).
DEVICE_AVAILABLE: "xmpp.device_available",
PEERCONNECTION_READY: "xmpp.peerconnection_ready",
CONFERENCE_SETUP_FAILED: "xmpp.conference_setup_failed",
PASSWORD_REQUIRED: "xmpp.password_required",
AUTHENTICATION_REQUIRED: "xmpp.authentication_required",
CHAT_ERROR_RECEIVED: "xmpp.chat_error_received",
ETHERPAD: "xmpp.etherpad",
BRIDGE_DOWN: "xmpp.bridge_down",
PRESENCE_STATUS: "xmpp.presence_status",
RESERVATION_ERROR: "xmpp.room_reservation_error",
DISPOSE_CONFERENCE: "xmpp.dispose_conference",
GRACEFUL_SHUTDOWN: "xmpp.graceful_shutdown",
JINGLE_FATAL_ERROR: 'xmpp.jingle_fatal_error',
PROMPT_FOR_LOGIN: 'xmpp.prompt_for_login',
FOCUS_DISCONNECTED: 'xmpp.focus_disconnected',
// xmpp is connected and obtained user media
READY_TO_JOIN: 'xmpp.ready_to_join'
};
module.exports = XMPPEvents;