src/Health.js
import 'platform';
import util from './Util.js'
import l from './Logger.js'
class Health{
constructor(ctx){
this.interval=5000;
this.statsReportTimer = null;
this.context = ctx;
this.oldStats = null;
this.result = null;
this.fractionLost = {
audio: [
{rating: 1, fromAflost: 0, toAflost: 50},
{rating: 2, fromAflost: 51, toAflost: 150},
{rating: 3, fromAflost: 151, toAflost: 250},
{rating: 4, fromAflost: 251, toAflost: 350},
{rating: 5, fromAflost: 351, toAflost: 9999999},
],
video: [
{rating: 1, fromAflost: 0, toAflost: 40},
{rating: 2, fromAflost: 41, toAflost: 55},
{rating: 3, fromAflost: 56, toAflost: 70},
{rating: 4, fromAflost: 71, toAflost: 90},
{rating: 5, fromAflost: 91, toAflost: 9999999},
]
};
}
stop(){
if(this.statsReportTimer){
window.clearInterval(this.statsReportTimer);
this.statsReportTimer = null;
}
}
start(){
if (this.context.isCaller===true)return; // master만 health 던지자
l.i("Health is start w/interval:"+ this.interval);
if(this.statsReportTimer){
window.clearInterval(this.statsReportTimer);
this.statsReportTimer = null;
}
this.statsReportTimer = window.setInterval(util.bind(function(){
this.getStats(util.bind(function(stats){
var i = 0,
len = 0,
attr = null,
result = {
localCandidate: "",
remoteCandidate: "",
localFrameWidth : "",
localFrameHeight : "",
remoteFrameWidth: "",
remoteFrameHeight: "",
localFrameRate: "",
remoteFrameRate: "",
availableSendBandwidth: 0,
availableReceiveBandwidth: 0,
rtt: 0,
rttRating: "",
localAudioFractionLost: 0,
localVideoFractionLost: 0,
localAudioFractionRating: 0,
localVideoFractionRating: 0,
remoteAudioFractionLost: 0,
remoteVideoFractionLost: 0,
remoteAudioFractionRating: 0,
remoteVideoFractionRating: 0,
fractionRating: 0,
localAudioPacketsLost: 0,
remoteAudioPaketsLost: 0,
localVideoPacketsLost: 0,
remoteVideoPacketsLost: 0,
},
nowLocalAPLost = 0, nowLocalVPLost = 0,
nowLocalAPSent = 0, nowLocalVPSent = 0,
oldLocalAPLost = 0, oldLocalVPLost = 0,
oldLocalAPSent = 0, oldLocalVPSent = 0,
nowRemoteAPLost = 0, nowRemoteVPLost = 0,
nowRemoteAPReceived = 0, nowRemoteVPReceived = 0,
oldRemoteAPLost = 0, oldRemoteVPLost = 0,
oldRemoteAPReceived = 0, oldRemoteVPReceived = 0,
oldStats = this.oldStats,
ffAudioRtt,
ffVideoRtt,
message;
if(platform.name === "Firefox"){
for(attr in stats){
if(stats[attr].type === "candidatepair" && stats[attr].selected === true){
result.localCandidate = stats[stats[attr].localCandidateId].candidateType;
result.remoteCandidate = stats[stats[attr].remoteCandidateId].candidateType;
continue;
}
if(stats[attr].type === "inboundrtp" && stats[attr].mediaType === "audio" && stats[attr].isRemote === true){
if(stats[attr].hasOwnProperty("mozRtt")){
ffAudioRtt = stats[attr].mozRtt;
}
if(stats[attr].hasOwnProperty("packetsLost")){
nowRemoteAPLost = stats[attr].packetsLost;
}
if(stats[attr].hasOwnProperty("packetsReceived")){
nowRemoteAPReceived = stats[attr].packetsReceived;
}
continue;
}
if(stats[attr].type === "inboundrtp" && stats[attr].mediaType === "video" && stats[attr].isRemote === true){
if(stats[attr].hasOwnProperty("mozRtt")){
ffVideoRtt = stats[attr].mozRtt;
}
if(stats[attr].hasOwnProperty("packetsLost")){
nowRemoteVPLost = stats[attr].packetsLost;
}
if(stats[attr].hasOwnProperty("packetsReceived")){
nowRemoteVPReceived = stats[attr].packetsReceived;
}
continue;
}
}
for(attr in oldStats){
if(stats[attr].type === "inboundrtp" && stats[attr].mediaType === "audio" && stats[attr].isRemote === true){
if(stats[attr].hasOwnProperty("packetsLost")){
oldRemoteAPLost = stats[attr].packetsLost;
}
if(stats[attr].hasOwnProperty("packetsReceived")){
oldRemoteAPReceived = stats[attr].packetsReceived;
}
continue;
}
if(stats[attr].type === "inboundrtp" && stats[attr].mediaType === "video" && stats[attr].isRemote === true){
if(stats[attr].hasOwnProperty("packetsLost")){
oldRemoteVPLost = stats[attr].packetsLost;
}
if(stats[attr].hasOwnProperty("packetsReceived")){
oldRemoteVPReceived = stats[attr].packetsReceived;
}
continue;
}
}
if(this.context.useVideo===false){
result.rtt = ffAudioRtt;
}
else{
result.rtt = ffVideoRtt;
}
}else{// chrome
len = stats.length;
for(; i<len; i++){
if(stats[i].googActiveConnection === "true"){
result.localCandidate = stats[i].googLocalCandidateType;
result.remoteCandidate = stats[i].googRemoteCandidateType;
//result.rtt = parseInt(stats[i].googRtt);
continue;
}
if(stats[i].hasOwnProperty("audioInputLevel")){
nowLocalAPLost = parseInt(stats[i].packetsLost);
nowLocalAPSent = parseInt(stats[i].packetsSent);
if(this.context.useVideo ===false){
result.rtt = parseInt(stats[i].googRtt);
}
continue;
}
if(stats[i].hasOwnProperty("googFrameWidthSent")){
result.localFrameWidth = parseInt(stats[i].googFrameWidthSent);
result.localFrameHeight = parseInt(stats[i].googFrameHeightSent);
result.localFrameRate = parseInt(stats[i].googFrameRateSent);
nowLocalVPLost = parseInt(stats[i].packetsLost);
nowLocalVPSent = parseInt(stats[i].packetsSent);
if(this.context.useVideo===true){
result.rtt = parseInt(stats[i].googRtt);
}
continue;
}
if(stats[i].hasOwnProperty("audioOutputLevel")){
nowRemoteAPLost = parseInt(stats[i].packetsLost);
nowRemoteAPReceived = parseInt(stats[i].packetsReceived);
continue;
}
if(stats[i].hasOwnProperty("googFrameWidthReceived")){
result.remoteFrameWidth = parseInt(stats[i].googFrameWidthReceived);
result.remoteFrameHeight = parseInt(stats[i].googFrameHeightReceived);
result.remoteFrameRate = parseInt(stats[i].googFrameRateReceived);
nowRemoteVPLost = parseInt(stats[i].packetsLost);
nowRemoteVPReceived = parseInt(stats[i].packetsReceived);
continue;
}
if(stats[i].hasOwnProperty("googAvailableSendBandwidth")){
result.availableSendBandwidth = parseInt(stats[i].googAvailableSendBandwidth);
result.availableReceiveBandwidth = parseInt(stats[i].googAvailableReceiveBandwidth);
continue;
}
}
for(i=0; i<oldStats.length; i++){
if(oldStats[i].hasOwnProperty("audioInputLevel")){
oldLocalAPLost = parseInt(oldStats[i].packetsLost);
oldLocalAPSent = parseInt(oldStats[i].packetsSent);
continue;
}
if(oldStats[i].hasOwnProperty("googFrameWidthSent")){
oldLocalVPLost = parseInt(oldStats[i].packetsLost);
oldLocalVPSent = parseInt(oldStats[i].packetsSent);
continue;
}
if(oldStats[i].hasOwnProperty("audioOutputLevel")){
oldRemoteAPLost = parseInt(oldStats[i].packetsLost);
oldRemoteAPReceived = parseInt(oldStats[i].packetsReceived);
continue;
}
if(oldStats[i].hasOwnProperty("googFrameWidthReceived")){
oldRemoteVPLost = parseInt(oldStats[i].packetsLost);
oldRemoteVPReceived = parseInt(oldStats[i].packetsReceived);
continue;
}
}
}
//var localAFLost = parseFloat(parseFloat((nowLocalAPLost - oldLocalAPLost) / (nowLocalAPSent - oldLocalAPSent) * 255 || 0).toFixed(4));
var localAFLost = parseInt((nowLocalAPLost - oldLocalAPLost) / (nowLocalAPSent - oldLocalAPSent) * 255 || 0);
//var localVFLost = parseFloat(parseFloat((nowLocalVPLost - oldLocalVPLost) / (nowLocalVPSent - oldLocalVPSent) * 255 || 0).toFixed(4));
var localVFLost = parseInt((nowLocalVPLost - oldLocalVPLost) / (nowLocalVPSent - oldLocalVPSent) * 255 || 0);
result.localAudioPacketsLost = parseInt( (nowLocalAPLost - oldLocalAPLost)/this.interval )
result.localVideoPacketsLost = parseInt( (nowLocalVPLost - oldLocalVPLost)/this.interval)
result.remoteAudioPacketsLost = parseInt( (nowRemoteAPLost - oldRemoteAPLost)/this.interval )
result.remoteVideoPacketsLost = parseInt( (nowRemoteVPLost - oldRemoteVPLost)/this.interval )
result.localAudioFractionLost = localAFLost;
result.localVideoFractionLost = localVFLost;
result.localAudioFractionRating = this.getFractionLostRating(localAFLost, this.fractionLost.audio);
result.localVideoFractionRating = this.getFractionLostRating(localVFLost, this.fractionLost.video);
// var remoteAFLost = parseFloat(parseFloat((nowRemoteAPLost - oldRemoteAPLost) / (nowRemoteAPReceived - oldRemoteAPReceived) * 255 || 0).toFixed(4));
var remoteAFLost = parseInt((nowRemoteAPLost - oldRemoteAPLost) / (nowRemoteAPReceived - oldRemoteAPReceived) * 255 || 0);
// var remoteVFLost = parseFloat(parseFloat((nowRemoteVPLost - oldRemoteVPLost) / (nowRemoteVPReceived - oldRemoteVPReceived) * 255 || 0).toFixed(4));
var remoteVFLost = parseInt((nowRemoteVPLost - oldRemoteVPLost) / (nowRemoteVPReceived - oldRemoteVPReceived) * 255 || 0);
result.remoteAudioFractionLost = remoteAFLost;
result.remoteVideoFractionLost = remoteVFLost;
result.remoteAudioFractionRating = this.getFractionLostRating(remoteAFLost, this.fractionLost.audio);
result.remoteVideoFractionRating = this.getFractionLostRating(remoteVFLost, this.fractionLost.video);
result.fractionRating = result.remoteVideoFractionRating;
result.rttRating = this.getRttRating(result.rtt);
this.oldStats = stats;
//console.log(result);
this.result = result;
if (this.context.eventManager.hasEventListener('onStat')) {
this.context.eventManager.dispatchEvent('onStat', result);
}
//l.i("this result:");
message = this.context.signalingConnection.createMessage({ command: 'health', body: JSON.stringify(result) });
//l.i(message);
if (message)this.context.signalingConnection.send(JSON.stringify(message));
},this));
},this), this.interval);
}
getRttRating(rtt){
var rttRating = 0;
if (rtt >= 1000){ rttRating = 5;}
else if (rtt >= 800){ rttRating = 4;}
else if (rtt >= 600){ rttRating = 3;}
else if (rtt >= 400){ rttRating = 2;}
else if (rtt < 400){ rttRating = 1;}
return rttRating;
}
getFractionLostRating(loss, fl){
var f = fl;
for(let i=0; i<f.length; i++){
if(f[i].fromFractionLost <= loss && f[i].toFractionLost >= loss){
return f[i].rating;
}
}
return 1;
}
getStats(fn){
if(platform.name === "Firefox"){
this.context.peerConnection.getStats(null, util.bind(function(res){
fn.call(this, res);
}, this), function(){});
}
else{
this.context.peerConnection.getStats(util.bind(function(res){
var items = [ ];
res.result().forEach(function (result) {
var item = { };
result.names().forEach(function (name) {
item[name] = result.stat(name);
});
item.id = result.id;
item.type = result.type;
item.timestamp = result.timestamp;
items.push(item);
});
fn.call(this, items);
}, this));
}
}
}
export default Health;