Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

LocalStatsCollector.js 3.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. /**
  2. * Provides statistics for the local stream.
  3. */
  4. /**
  5. * Size of the webaudio analyzer buffer.
  6. * @type {number}
  7. */
  8. const WEBAUDIO_ANALYZER_FFT_SIZE = 2048;
  9. /**
  10. * Value of the webaudio analyzer smoothing time parameter.
  11. * @type {number}
  12. */
  13. const WEBAUDIO_ANALYZER_SMOOTING_TIME = 0.8;
  14. window.AudioContext = window.AudioContext || window.webkitAudioContext;
  15. let context = null;
  16. if (window.AudioContext) {
  17. context = new AudioContext();
  18. // XXX Not all browsers define a suspend method on AudioContext. As the
  19. // invocation is at the (ES6 module) global execution level, it breaks the
  20. // loading of the lib-jitsi-meet library in such browsers and, consequently,
  21. // the loading of the very Web app that uses the lib-jitsi-meet library. For
  22. // example, Google Chrome 40 on Android does not define the method but we
  23. // still want to be able to load the lib-jitsi-meet library there and
  24. // display a page which notifies the user that the Web app is not supported
  25. // there.
  26. context.suspend && context.suspend();
  27. }
  28. /**
  29. * Converts time domain data array to audio level.
  30. * @param samples the time domain data array.
  31. * @returns {number} the audio level
  32. */
  33. function timeDomainDataToAudioLevel(samples) {
  34. let maxVolume = 0;
  35. const length = samples.length;
  36. for (let i = 0; i < length; i++) {
  37. if (maxVolume < samples[i]) {
  38. maxVolume = samples[i];
  39. }
  40. }
  41. return parseFloat(((maxVolume - 127) / 128).toFixed(3));
  42. }
  43. /**
  44. * Animates audio level change
  45. * @param newLevel the new audio level
  46. * @param lastLevel the last audio level
  47. * @returns {Number} the audio level to be set
  48. */
  49. function animateLevel(newLevel, lastLevel) {
  50. let value = 0;
  51. const diff = lastLevel - newLevel;
  52. if (diff > 0.2) {
  53. value = lastLevel - 0.2;
  54. } else if (diff < -0.4) {
  55. value = lastLevel + 0.4;
  56. } else {
  57. value = newLevel;
  58. }
  59. return parseFloat(value.toFixed(3));
  60. }
  61. /**
  62. * <tt>LocalStatsCollector</tt> calculates statistics for the local stream.
  63. *
  64. * @param stream the local stream
  65. * @param interval stats refresh interval given in ms.
  66. * @param callback function that receives the audio levels.
  67. * @constructor
  68. */
  69. export default function LocalStatsCollector(stream, interval, callback) {
  70. this.stream = stream;
  71. this.intervalId = null;
  72. this.intervalMilis = interval;
  73. this.audioLevel = 0;
  74. this.callback = callback;
  75. }
  76. /**
  77. * Starts the collecting the statistics.
  78. */
  79. LocalStatsCollector.prototype.start = function() {
  80. if (!LocalStatsCollector.isLocalStatsSupported()) {
  81. return;
  82. }
  83. context.resume();
  84. const analyser = context.createAnalyser();
  85. analyser.smoothingTimeConstant = WEBAUDIO_ANALYZER_SMOOTING_TIME;
  86. analyser.fftSize = WEBAUDIO_ANALYZER_FFT_SIZE;
  87. const source = context.createMediaStreamSource(this.stream);
  88. source.connect(analyser);
  89. this.intervalId = setInterval(
  90. () => {
  91. const array = new Uint8Array(analyser.frequencyBinCount);
  92. analyser.getByteTimeDomainData(array);
  93. const audioLevel = timeDomainDataToAudioLevel(array);
  94. if (audioLevel !== this.audioLevel) {
  95. this.audioLevel = animateLevel(audioLevel, this.audioLevel);
  96. this.callback(this.audioLevel);
  97. }
  98. },
  99. this.intervalMilis
  100. );
  101. };
  102. /**
  103. * Stops collecting the statistics.
  104. */
  105. LocalStatsCollector.prototype.stop = function() {
  106. if (this.intervalId) {
  107. clearInterval(this.intervalId);
  108. this.intervalId = null;
  109. }
  110. };
  111. /**
  112. * Checks if the environment has the necessary conditions to support
  113. * collecting stats from local streams.
  114. *
  115. * @returns {boolean}
  116. */
  117. LocalStatsCollector.isLocalStatsSupported = function() {
  118. return Boolean(context);
  119. };