123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- /* global config */
- import EventEmitter from "events";
-
- import CQEvents from "../../service/connectionquality/CQEvents";
-
- const eventEmitter = new EventEmitter();
-
- /**
- * local stats
- * @type {{}}
- */
- var stats = {};
-
- /**
- * remote stats
- * @type {{}}
- */
- var remoteStats = {};
-
- /**
- * Quality percent( 100% - good, 0% - bad.) for the local user.
- */
- var localConnectionQuality = 100;
-
- /**
- * Quality percent( 100% - good, 0% - bad.) stored per id.
- */
- var remoteConnectionQuality = {};
-
- /**
- * Calculates the quality percent based on passed new and old value.
- * @param newVal the new value
- * @param oldVal the old value
- */
- function calculateQuality(newVal, oldVal) {
- 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
- const _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}
- */
- const 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 {int} 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. Returns undefined if no result is found.
- */
- function calculateQualityUsingUpload(inputHeight, upload) {
- // found resolution from _bandwidthMap which height is equal or less than
- // the inputHeight
- let foundResolution = _bandwidthMap.find((r) => (r.height <= inputHeight));
-
- if (!foundResolution)
- return undefined;
-
- if (upload <= foundResolution.min)
- return 0;
-
- return Math.min(
- ((upload - foundResolution.min)*100)
- / (foundResolution.max - foundResolution.min),
- 100);
- }
-
- export default {
- /**
- * Updates the local statistics
- * @param data new statistics
- * @param dontUpdateLocalConnectionQuality {boolean} if true -
- * localConnectionQuality wont be recalculated.
- * @param videoType the local video type
- * @param isMuted current state of local video, whether it is muted
- * @param resolution the current resolution used by local video
- */
- updateLocalStats:
- function (data, dontUpdateLocalConnectionQuality,
- videoType, isMuted, resolution) {
- stats = data;
- if(!dontUpdateLocalConnectionQuality) {
- let val = this._getNewQualityValue(
- stats,
- localConnectionQuality,
- videoType,
- isMuted,
- resolution);
- if (val !== undefined)
- localConnectionQuality = val;
- }
- eventEmitter.emit(
- CQEvents.LOCALSTATS_UPDATED, localConnectionQuality, stats);
- },
-
- /**
- * Updates only the localConnectionQuality value
- * @param values {int} the new value. should be from 0 - 100.
- */
- updateLocalConnectionQuality: function (value) {
- localConnectionQuality = value;
- eventEmitter.emit(CQEvents.LOCALSTATS_UPDATED, localConnectionQuality,
- stats);
- },
-
- /**
- * Updates remote statistics
- * @param id the id associated with the statistics
- * @param data the statistics received
- * @param remoteVideoType the video type of the remote video
- * @param isRemoteVideoMuted whether remote video is muted
- */
- updateRemoteStats:
- function (id, data, remoteVideoType, isRemoteVideoMuted) {
- if (!data ||
- !("packetLoss" in data) ||
- !("total" in data.packetLoss)) {
- eventEmitter.emit(CQEvents.REMOTESTATS_UPDATED, id, null, null);
- return;
- }
-
- let inputResolution = data.resolution;
- // Use only the fields we need
- data = {bitrate: data.bitrate, packetLoss: data.packetLoss};
-
- remoteStats[id] = data;
-
- let val = this._getNewQualityValue(
- data,
- remoteConnectionQuality[id],
- remoteVideoType,
- isRemoteVideoMuted,
- inputResolution);
- if (val !== undefined)
- remoteConnectionQuality[id] = val;
-
- eventEmitter.emit(
- CQEvents.REMOTESTATS_UPDATED, id,
- remoteConnectionQuality[id], remoteStats[id]);
- },
-
- /**
- * Returns the new quality value based on the input parameters.
- * Used to calculate remote and local values.
- * @param data the data
- * @param lastQualityValue the last value we calculated
- * @param videoType need to check whether we are screen sharing
- * @param isMuted is video muted
- * @param resolution the input resolution used by the camera
- * @returns {*} the newly calculated value or undefined if no result
- * @private
- */
- _getNewQualityValue:
- function (data, lastQualityValue, videoType, isMuted, resolution) {
- if (disableQualityBasedOnBandwidth
- || isMuted
- || videoType === 'desktop'
- || !resolution) {
- return calculateQuality(
- 100 - data.packetLoss.total,
- lastQualityValue || 100);
- } else {
- return calculateQualityUsingUpload(
- resolution,
- data.bitrate.upload);
- }
- },
-
- /**
- * Returns the local statistics.
- */
- getStats: function () {
- return stats;
- },
-
- addListener: function (type, listener) {
- eventEmitter.on(type, listener);
- }
- };
|