You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

connectionquality.js 5.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. /* global config */
  2. import EventEmitter from "events";
  3. import CQEvents from "../../service/connectionquality/CQEvents";
  4. const eventEmitter = new EventEmitter();
  5. /**
  6. * local stats
  7. * @type {{}}
  8. */
  9. var stats = {};
  10. /**
  11. * remote stats
  12. * @type {{}}
  13. */
  14. var remoteStats = {};
  15. /**
  16. * Quality percent( 100% - good, 0% - bad.) for the local user.
  17. */
  18. var localConnectionQuality = 100;
  19. /**
  20. * Quality percent( 100% - good, 0% - bad.) stored per id.
  21. */
  22. var remoteConnectionQuality = {};
  23. /**
  24. * Calculates the quality percent based on passed new and old value.
  25. * @param newVal the new value
  26. * @param oldVal the old value
  27. */
  28. function calculateQuality(newVal, oldVal) {
  29. return (newVal <= oldVal) ? newVal : (9*oldVal + newVal) / 10;
  30. }
  31. // webrtc table describing simulcast resolutions and used bandwidth
  32. // https://chromium.googlesource.com/external/webrtc/+/master/webrtc/media/engine/simulcast.cc#42
  33. var _bandwidthMap = [
  34. { width: 1920, height: 1080, layers:3, max: 5000, min: 800 },
  35. { width: 1280, height: 720, layers:3, max: 2500, min: 600 },
  36. { width: 960, height: 540, layers:3, max: 900, min: 450 },
  37. { width: 640, height: 360, layers:2, max: 700, min: 150 },
  38. { width: 480, height: 270, layers:2, max: 450, min: 150 },
  39. { width: 320, height: 180, layers:1, max: 200, min: 30 }
  40. ];
  41. /**
  42. * We disable quality calculations based on bandwidth if simulcast is disabled,
  43. * or enable it in case of no simulcast and we force it.
  44. * @type {boolean}
  45. */
  46. var disableQualityBasedOnBandwidth =
  47. config.forceQualityBasedOnBandwidth ? false : config.disableSimulcast;
  48. /**
  49. * Calculates the quality percentage based on the input resolution height and
  50. * the upload reported by the client. The value is based on the interval from
  51. * _bandwidthMap.
  52. * @param inputHeight the resolution used to open the camera.
  53. * @param upload the upload rate reported by client.
  54. * @returns {*} the percent of upload based on _bandwidthMap and maximum value
  55. * of 100, as values of the map are approximate and clients can stream above
  56. * those values.
  57. */
  58. function calculateQualityUsingUpload(inputHeight, upload) {
  59. let foundResolution = null;
  60. for (let i in _bandwidthMap) {
  61. let r = _bandwidthMap[i];
  62. if (r.height <= inputHeight) {
  63. foundResolution = r;
  64. break;
  65. }
  66. }
  67. if (!foundResolution)
  68. return false;
  69. if (upload <= foundResolution.min)
  70. return 0;
  71. return Math.min(
  72. ((upload - foundResolution.min)*100)
  73. / (foundResolution.max - foundResolution.min),
  74. 100);
  75. }
  76. export default {
  77. /**
  78. * Updates the local statistics
  79. * @param data new statistics
  80. * @param dontUpdateLocalConnectionQuality {boolean} if true -
  81. * localConnectionQuality wont be recalculated.
  82. */
  83. updateLocalStats:
  84. function (data, dontUpdateLocalConnectionQuality, localVideo) {
  85. stats = data;
  86. if(!dontUpdateLocalConnectionQuality) {
  87. if (!disableQualityBasedOnBandwidth
  88. && !localVideo.isMuted()
  89. && localVideo.videoType !== 'desktop'
  90. && localVideo.resolution) {
  91. let val = calculateQualityUsingUpload(
  92. localVideo.resolution,
  93. data.bitrate.upload);
  94. if (val) {
  95. localConnectionQuality = val;
  96. }
  97. } else {
  98. var newVal = 100 - stats.packetLoss.total;
  99. localConnectionQuality =
  100. calculateQuality(newVal, localConnectionQuality);
  101. }
  102. }
  103. eventEmitter.emit(
  104. CQEvents.LOCALSTATS_UPDATED, localConnectionQuality, stats);
  105. },
  106. /**
  107. * Updates only the localConnectionQuality value
  108. * @param values {int} the new value. should be from 0 - 100.
  109. */
  110. updateLocalConnectionQuality: function (value) {
  111. localConnectionQuality = value;
  112. eventEmitter.emit(CQEvents.LOCALSTATS_UPDATED, localConnectionQuality,
  113. stats);
  114. },
  115. /**
  116. * Updates remote statistics
  117. * @param id the id associated with the statistics
  118. * @param data the statistics
  119. */
  120. updateRemoteStats:
  121. function (id, data, remoteVideoType, isRemoteVideoMuted) {
  122. if (!data ||
  123. !("packetLoss" in data) ||
  124. !("total" in data.packetLoss)) {
  125. eventEmitter.emit(CQEvents.REMOTESTATS_UPDATED, id, null, null);
  126. return;
  127. }
  128. let inputResolution = data.resolution;
  129. // Use only the fields we need
  130. data = {bitrate: data.bitrate, packetLoss: data.packetLoss};
  131. remoteStats[id] = data;
  132. if (disableQualityBasedOnBandwidth
  133. || isRemoteVideoMuted
  134. || remoteVideoType === 'desktop'
  135. || !inputResolution) {
  136. var newVal = 100 - data.packetLoss.total;
  137. var oldVal = remoteConnectionQuality[id];
  138. remoteConnectionQuality[id]
  139. = calculateQuality(newVal, oldVal || 100);
  140. } else {
  141. let val = calculateQualityUsingUpload(
  142. inputResolution.inputHeight,
  143. data.bitrate.upload);
  144. if (val) {
  145. remoteConnectionQuality[id] = val;
  146. }
  147. }
  148. eventEmitter.emit(
  149. CQEvents.REMOTESTATS_UPDATED, id,
  150. remoteConnectionQuality[id], remoteStats[id]);
  151. },
  152. /**
  153. * Returns the local statistics.
  154. */
  155. getStats: function () {
  156. return stats;
  157. },
  158. addListener: function (type, listener) {
  159. eventEmitter.on(type, listener);
  160. }
  161. };