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.

functions.js 4.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. import { findIndex } from 'lodash';
  2. import { CONNECTION_TYPE } from './constants';
  3. const LOSS_AUDIO_THRESHOLDS = [ 0.33, 0.05 ];
  4. const LOSS_VIDEO_THRESHOLDS = [ 0.33, 0.1, 0.05 ];
  5. const THROUGHPUT_AUDIO_THRESHOLDS = [ 8, 20 ];
  6. const THROUGHPUT_VIDEO_THRESHOLDS = [ 60, 750 ];
  7. /**
  8. * Returns the level based on a list of thresholds.
  9. *
  10. * @param {number[]} thresholds - The thresholds array.
  11. * @param {number} value - The value against which the level is calculated.
  12. * @param {boolean} descending - The order based on which the level is calculated.
  13. *
  14. * @returns {number}
  15. */
  16. function _getLevel(thresholds, value, descending = true) {
  17. let predicate;
  18. if (descending) {
  19. predicate = function(threshold) {
  20. return value > threshold;
  21. };
  22. } else {
  23. predicate = function(threshold) {
  24. return value < threshold;
  25. };
  26. }
  27. const i = findIndex(thresholds, predicate);
  28. if (i === -1) {
  29. return thresholds.length;
  30. }
  31. return i;
  32. }
  33. /**
  34. * Returns the connection details from the test results.
  35. *
  36. * @param {{
  37. * fractionalLoss: number,
  38. * throughput: number
  39. * }} testResults - The state of the app.
  40. *
  41. * @returns {{
  42. * connectionType: string,
  43. * connectionDetails: string[]
  44. * }}
  45. */
  46. function _getConnectionDataFromTestResults({ fractionalLoss: l, throughput: t }) {
  47. const loss = {
  48. audioQuality: _getLevel(LOSS_AUDIO_THRESHOLDS, l),
  49. videoQuality: _getLevel(LOSS_VIDEO_THRESHOLDS, l)
  50. };
  51. const throughput = {
  52. audioQuality: _getLevel(THROUGHPUT_AUDIO_THRESHOLDS, t, false),
  53. videoQuality: _getLevel(THROUGHPUT_VIDEO_THRESHOLDS, t, false)
  54. };
  55. let connectionType = CONNECTION_TYPE.NONE;
  56. const connectionDetails = [];
  57. if (throughput.audioQuality === 0 || loss.audioQuality === 0) {
  58. // Calls are impossible.
  59. connectionType = CONNECTION_TYPE.POOR;
  60. connectionDetails.push('prejoin.connectionDetails.veryPoorConnection');
  61. } else if (
  62. throughput.audioQuality === 2
  63. && throughput.videoQuality === 2
  64. && loss.audioQuality === 2
  65. && loss.videoQuality === 3
  66. ) {
  67. // Ideal conditions for both audio and video. Show only one message.
  68. connectionType = CONNECTION_TYPE.GOOD;
  69. connectionDetails.push('prejoin.connectionDetails.goodQuality');
  70. } else {
  71. connectionType = CONNECTION_TYPE.NON_OPTIMAL;
  72. if (throughput.audioQuality === 1) {
  73. // Minimum requirements for a call are met.
  74. connectionDetails.push('prejoin.connectionDetails.audioLowNoVideo');
  75. } else {
  76. // There are two paragraphs: one saying something about audio and the other about video.
  77. if (loss.audioQuality === 1) {
  78. connectionDetails.push('prejoin.connectionDetails.audioClipping');
  79. } else {
  80. connectionDetails.push('prejoin.connectionDetails.audioHighQuality');
  81. }
  82. if (throughput.videoQuality === 0 || loss.videoQuality === 0) {
  83. connectionDetails.push('prejoin.connectionDetails.noVideo');
  84. } else if (throughput.videoQuality === 1) {
  85. connectionDetails.push('prejoin.connectionDetails.videoLowQuality');
  86. } else if (loss.videoQuality === 1) {
  87. connectionDetails.push('prejoin.connectionDetails.videoFreezing');
  88. } else if (loss.videoQuality === 2) {
  89. connectionDetails.push('prejoin.connectionDetails.videoTearing');
  90. } else {
  91. connectionDetails.push('prejoin.connectionDetails.videoHighQuality');
  92. }
  93. }
  94. connectionDetails.push('prejoin.connectionDetails.undetectable');
  95. }
  96. return {
  97. connectionType,
  98. connectionDetails
  99. };
  100. }
  101. /**
  102. * Selector for determining the connection type & details.
  103. *
  104. * @param {Object} state - The state of the app.
  105. * @returns {{
  106. * connectionType: string,
  107. * connectionDetails: string[]
  108. * }}
  109. */
  110. export function getConnectionData(state) {
  111. const { precallTestResults } = state['features/prejoin'];
  112. if (precallTestResults) {
  113. if (precallTestResults.mediaConnectivity) {
  114. return _getConnectionDataFromTestResults(precallTestResults);
  115. }
  116. return {
  117. connectionType: CONNECTION_TYPE.POOR,
  118. connectionDetails: [ 'prejoin.connectionDetails.noMediaConnectivity' ]
  119. };
  120. }
  121. return {
  122. connectionType: CONNECTION_TYPE.NONE,
  123. connectionDetails: []
  124. };
  125. }