Calculates quality based on the resolution and upload.

This commit is contained in:
damencho 2016-10-11 14:54:22 -05:00
parent d5541f612f
commit a2c71d05e6
4 changed files with 142 additions and 27 deletions

View File

@ -1372,7 +1372,8 @@ export default {
} }
room.on(ConferenceEvents.CONNECTION_STATS, function (stats) { room.on(ConferenceEvents.CONNECTION_STATS, function (stats) {
ConnectionQuality.updateLocalStats(stats, connectionIsInterrupted); ConnectionQuality.updateLocalStats(
stats, connectionIsInterrupted, localVideo);
}); });
ConnectionQuality.addListener(CQEvents.LOCALSTATS_UPDATED, ConnectionQuality.addListener(CQEvents.LOCALSTATS_UPDATED,
@ -1382,6 +1383,12 @@ export default {
let data = { let data = {
bitrate: stats.bitrate, bitrate: stats.bitrate,
packetLoss: stats.packetLoss}; packetLoss: stats.packetLoss};
if (localVideo && localVideo.resolution) {
data.resolution = {
inputHeight: localVideo.resolution
};
}
try { try {
room.broadcastEndpointMessage({ room.broadcastEndpointMessage({
type: this.commands.defaults.CONNECTION_QUALITY, type: this.commands.defaults.CONNECTION_QUALITY,
@ -1394,10 +1401,15 @@ export default {
room.on(ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED, room.on(ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
(participant, payload) => { (participant, payload) => {
switch(payload.type) { switch(payload.type) {
case this.commands.defaults.CONNECTION_QUALITY: case this.commands.defaults.CONNECTION_QUALITY: {
ConnectionQuality.updateRemoteStats(participant.getId(), let id = participant.getId();
payload.values); ConnectionQuality.updateRemoteStats(
id,
payload.values,
APP.UI.getRemoteVideoType(id),
APP.UI.isRemoteVideoMuted(id));
break; break;
}
default: default:
console.warn("Unknown datachannel message", payload); console.warn("Unknown datachannel message", payload);
} }

View File

@ -779,6 +779,15 @@ UI.getRemoteVideoType = function (jid) {
return VideoLayout.getRemoteVideoType(jid); return VideoLayout.getRemoteVideoType(jid);
}; };
/**
* Return the mute state of the remote video.
* @param jid the jid for the remote video
* @returns the video mute state.
*/
UI.isRemoteVideoMuted = function (jid) {
return VideoLayout.isRemoteVideoMuted(jid);
};
UI.connectionIndicatorShowMore = function(id) { UI.connectionIndicatorShowMore = function(id) {
VideoLayout.showMore(id); VideoLayout.showMore(id);
}; };

View File

@ -327,6 +327,16 @@ var VideoLayout = {
return smallVideo ? smallVideo.getVideoType() : null; return smallVideo ? smallVideo.getVideoType() : null;
}, },
/**
* Return the mute state of the remote video.
* @param id the id for the remote video
* @returns {boolean} the video mute state.
*/
isRemoteVideoMuted (id) {
let smallVideo = VideoLayout.getSmallVideo(id);
return smallVideo ? smallVideo.isVideoMuted : null;
},
isPinned (id) { isPinned (id) {
return (pinnedId) ? (id === pinnedId) : false; return (pinnedId) ? (id === pinnedId) : false;
}, },

View File

@ -1,3 +1,4 @@
/* global config */
import EventEmitter from "events"; import EventEmitter from "events";
import CQEvents from "../../service/connectionquality/CQEvents"; import CQEvents from "../../service/connectionquality/CQEvents";
@ -35,6 +36,58 @@ function calculateQuality(newVal, oldVal) {
return (newVal <= oldVal) ? newVal : (9*oldVal + newVal) / 10; return (newVal <= oldVal) ? newVal : (9*oldVal + newVal) / 10;
} }
// webrtc table describing simulcast resolutions and used bandwidth
// https://chromium.googlesource.com/external/webrtc/+/master/webrtc/media/engine/simulcast.cc#42
var _bandwidthMap = [
{ width: 1920, height: 1080, layers:3, max: 5000, min: 800 },
{ width: 1280, height: 720, layers:3, max: 2500, min: 600 },
{ width: 960, height: 540, layers:3, max: 900, min: 450 },
{ width: 640, height: 360, layers:2, max: 700, min: 150 },
{ width: 480, height: 270, layers:2, max: 450, min: 150 },
{ width: 320, height: 180, layers:1, max: 200, min: 30 }
];
/**
* We disable quality calculations based on bandwidth if simulcast is disabled,
* or enable it in case of no simulcast and we force it.
* @type {boolean}
*/
var disableQualityBasedOnBandwidth =
config.forceQualityBasedOnBandwidth ? false : config.disableSimulcast;
/**
* Calculates the quality percentage based on the input resolution height and
* the upload reported by the client. The value is based on the interval from
* _bandwidthMap.
* @param inputHeight the resolution used to open the camera.
* @param upload the upload rate reported by client.
* @returns {*} the percent of upload based on _bandwidthMap and maximum value
* of 100, as values of the map are approximate and clients can stream above
* those values.
*/
function calculateQualityUsingUpload(inputHeight, upload) {
let foundResolution = null;
for (let i in _bandwidthMap) {
let r = _bandwidthMap[i];
if (r.height <= inputHeight) {
foundResolution = r;
break;
}
}
if (!foundResolution)
return false;
if (upload <= foundResolution.min)
return 0;
return Math.min(
((upload - foundResolution.min)*100)
/ (foundResolution.max - foundResolution.min),
100);
}
export default { export default {
/** /**
* Updates the local statistics * Updates the local statistics
@ -42,15 +95,28 @@ export default {
* @param dontUpdateLocalConnectionQuality {boolean} if true - * @param dontUpdateLocalConnectionQuality {boolean} if true -
* localConnectionQuality wont be recalculated. * localConnectionQuality wont be recalculated.
*/ */
updateLocalStats: function (data, dontUpdateLocalConnectionQuality) { updateLocalStats:
stats = data; function (data, dontUpdateLocalConnectionQuality, localVideo) {
if(!dontUpdateLocalConnectionQuality) { stats = data;
var newVal = 100 - stats.packetLoss.total; if(!dontUpdateLocalConnectionQuality) {
localConnectionQuality = if (!disableQualityBasedOnBandwidth
calculateQuality(newVal, localConnectionQuality); && !localVideo.isMuted()
} && localVideo.videoType !== 'desktop'
eventEmitter.emit(CQEvents.LOCALSTATS_UPDATED, localConnectionQuality, && localVideo.resolution) {
stats); let val = calculateQualityUsingUpload(
localVideo.resolution,
data.bitrate.upload);
if (val) {
localConnectionQuality = val;
}
} else {
var newVal = 100 - stats.packetLoss.total;
localConnectionQuality =
calculateQuality(newVal, localConnectionQuality);
}
}
eventEmitter.emit(
CQEvents.LOCALSTATS_UPDATED, localConnectionQuality, stats);
}, },
/** /**
@ -68,23 +134,41 @@ export default {
* @param id the id associated with the statistics * @param id the id associated with the statistics
* @param data the statistics * @param data the statistics
*/ */
updateRemoteStats: function (id, data) { updateRemoteStats:
if (!data || !("packetLoss" in data) || !("total" in data.packetLoss)) { function (id, data, remoteVideoType, isRemoteVideoMuted) {
eventEmitter.emit(CQEvents.REMOTESTATS_UPDATED, id, null, null); if (!data ||
return; !("packetLoss" in data) ||
} !("total" in data.packetLoss)) {
// Use only the fields we need eventEmitter.emit(CQEvents.REMOTESTATS_UPDATED, id, null, null);
data = {bitrate: data.bitrate, packetLoss: data.packetLoss}; return;
}
remoteStats[id] = data; let inputResolution = data.resolution;
// Use only the fields we need
data = {bitrate: data.bitrate, packetLoss: data.packetLoss};
var newVal = 100 - data.packetLoss.total; remoteStats[id] = data;
var oldVal = remoteConnectionQuality[id];
remoteConnectionQuality[id] = calculateQuality(newVal, oldVal || 100);
eventEmitter.emit( if (disableQualityBasedOnBandwidth
CQEvents.REMOTESTATS_UPDATED, id, remoteConnectionQuality[id], || isRemoteVideoMuted
remoteStats[id]); || remoteVideoType === 'desktop'
|| !inputResolution) {
var newVal = 100 - data.packetLoss.total;
var oldVal = remoteConnectionQuality[id];
remoteConnectionQuality[id]
= calculateQuality(newVal, oldVal || 100);
} else {
let val = calculateQualityUsingUpload(
inputResolution.inputHeight,
data.bitrate.upload);
if (val) {
remoteConnectionQuality[id] = val;
}
}
eventEmitter.emit(
CQEvents.REMOTESTATS_UPDATED, id,
remoteConnectionQuality[id], remoteStats[id]);
}, },
/** /**