mirror of https://github.com/jitsi/jitsi-meet
parent
103b5d71d0
commit
d19789005e
@ -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 = []; |
||||
|
||||
}; |
@ -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); |
||||
}); |
||||
} |
||||
}; |
@ -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; |
@ -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; |
@ -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; |
@ -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" |
||||
}; |
@ -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; |
Loading…
Reference in new issue