bigbluebutton-Github/bigbluebutton-html5/imports/utils/stats.js

211 lines
4.5 KiB
JavaScript
Raw Normal View History

import logger from '/imports/startup/client/logger';
2020-01-28 21:07:21 +08:00
const STATS = Meteor.settings.public.stats;
// Probes done in an interval
const PROBES = 5;
const INTERVAL = STATS.interval / PROBES;
2019-11-30 19:29:51 +08:00
const stop = callback => {
2020-01-30 01:52:55 +08:00
logger.debug(
{ logCode: 'stats_stop_monitor' },
2019-11-30 19:29:51 +08:00
'Lost peer connection. Stopping monitor'
);
callback(clearResult());
return;
};
const isActive = conn => {
let active = false;
if (conn) {
const { connectionState } = conn;
const logCode = 'stats_connection_state';
switch (connectionState) {
case 'new':
case 'connecting':
case 'connected':
case 'disconnected':
active = true;
break;
case 'failed':
case 'closed':
default:
logger.warn({ logCode }, connectionState);
}
} else {
logger.error(
{ logCode: 'stats_missing_connection' },
'Missing connection'
);
}
return active;
2020-02-15 05:14:21 +08:00
};
const collect = (conn, callback) => {
let stats = [];
const monitor = (conn, stats) => {
if (!isActive(conn)) return stop(callback);
conn.getStats().then(results => {
2019-11-30 19:29:51 +08:00
if (!results) return stop(callback);
let inboundRTP;
let remoteInboundRTP;
results.forEach(res => {
switch (res.type) {
case 'inbound-rtp':
inboundRTP = res;
break;
case 'remote-inbound-rtp':
remoteInboundRTP = res;
break;
default:
}
});
if (inboundRTP || remoteInboundRTP) {
if (!inboundRTP) {
2020-01-30 01:52:55 +08:00
logger.debug(
{ logCode: 'stats_missing_inbound_rtc' },
'Missing local inbound RTC. Using remote instead'
);
}
stats.push(buildData(inboundRTP || remoteInboundRTP));
while (stats.length > PROBES) stats.shift();
const interval = calculateInterval(stats);
callback(buildResult(interval));
}
setTimeout(monitor, INTERVAL, conn, stats);
2020-01-30 01:52:55 +08:00
}).catch(error => {
logger.debug(
{
logCode: 'stats_get_stats_error',
extraInfo: { error }
},
'WebRTC stats not available'
);
});
};
2020-01-30 01:52:55 +08:00
monitor(conn, stats, 1);
};
const buildData = inboundRTP => {
return {
packets: {
received: inboundRTP.packetsReceived,
lost: inboundRTP.packetsLost
},
bytes: {
received: inboundRTP.bytesReceived
},
jitter: inboundRTP.jitter
};
};
const buildResult = (interval) => {
const rate = calculateRate(interval.packets);
return {
packets: {
received: interval.packets.received,
lost: interval.packets.lost
},
bytes: {
received: interval.bytes.received
},
jitter: interval.jitter,
rate: rate,
loss: calculateLoss(rate),
MOS: calculateMOS(rate)
};
};
const clearResult = () => {
2019-11-30 19:29:51 +08:00
return {
packets: {
received: 0,
lost: 0
},
bytes: {
received: 0
},
jitter: 0,
rate: 0,
loss: 0,
MOS: 0
};
};
const diff = (single, first, last) => Math.abs((single ? 0 : last) - first);
const calculateInterval = (stats) => {
const single = stats.length === 1;
const first = stats[0];
const last = stats[stats.length - 1];
return {
packets: {
received: diff(single, first.packets.received, last.packets.received),
lost: diff(single, first.packets.lost, last.packets.lost)
},
bytes: {
received: diff(single, first.bytes.received, last.bytes.received)
},
jitter: Math.max.apply(Math, stats.map(s => s.jitter))
};
};
const calculateRate = (packets) => {
const { received, lost } = packets;
const rate = (received > 0) ? ((received - lost) / received) * 100 : 100;
if (rate < 0 || rate > 100) return 100;
return rate;
};
const calculateLoss = (rate) => {
return 1 - (rate / 100);
};
const calculateMOS = (rate) => {
return 1 + (0.035) * rate + (0.000007) * rate * (rate - 60) * (100 - rate);
};
2020-01-30 01:52:55 +08:00
const monitorAudioConnection = conn => {
if (!conn) return;
logger.debug(
{ logCode: 'stats_audio_monitor' },
'Starting to monitor audio connection'
);
collect(conn, (result) => {
const event = new CustomEvent('audiostats', { detail: result });
window.dispatchEvent(event);
});
};
const monitorVideoConnection = conn => {
if (!conn) return;
2020-01-30 01:52:55 +08:00
logger.debug(
{ logCode: 'stats_video_monitor' },
'Starting to monitor video connection'
);
collect(conn, (result) => {
const event = new CustomEvent('videostats', { detail: result });
window.dispatchEvent(event);
});
};
export {
monitorAudioConnection,
monitorVideoConnection
}