modified lib-jitsi-meet dev repo
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

VADTalkMutedDetection.js 5.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. import { EventEmitter } from 'events';
  2. import { VAD_SCORE_PUBLISHED, VAD_TALK_WHILE_MUTED } from './DetectionEvents';
  3. import { getLogger } from 'jitsi-meet-logger';
  4. import TrackVADEmitter from '../detection/TrackVADEmitter';
  5. import * as JitsiConferenceEvents from '../../JitsiConferenceEvents';
  6. const logger = getLogger(__filename);
  7. /**
  8. * The value which we use to say, every sound over this threshold
  9. * is talking on the mic.
  10. * @type {number}
  11. */
  12. const VAD_DETECT_THRESHOLD = 0.7;
  13. /**
  14. * Detect user trying to speek while is locally muted and fires an event.
  15. */
  16. export default class VADTalkMutedDetection extends EventEmitter {
  17. /**
  18. * Creates TalkMutedDetection
  19. * @param conference the JitsiConference instance that created us.
  20. * @param callback the callback to call when detected that the local user is
  21. * talking while her microphone is muted.
  22. * @constructor
  23. */
  24. constructor(conference, vadProcessor) {
  25. super();
  26. logger.info('[ADBG] Created VADTalkMutedDetection.');
  27. /**
  28. * The indicator which determines whether <tt>callback</tt> has been
  29. * invoked for the current local audio track of <tt>conference</tt> so
  30. * that it is invoked once only.
  31. *
  32. * @private
  33. */
  34. this._eventFired = false;
  35. this._vadProcessor = vadProcessor;
  36. this._vadEmitter = null;
  37. this._processing = false;
  38. this._scoreArray = [];
  39. conference.on(JitsiConferenceEvents.TRACK_MUTE_CHANGED, this._trackMuteChanged.bind(this));
  40. conference.on(JitsiConferenceEvents.TRACK_ADDED, this._trackAdded.bind(this));
  41. // TODO do we need to handle the case where tracks are removed, make sure this cleans up properly so
  42. // we don't have any leeks i.e. stale JitsiLocalTracks
  43. }
  44. /* eslint-disable max-params */
  45. /**
  46. * Receives audio level events for all send and receive streams.
  47. *
  48. * @param {TraceablePeerConnection} pc - WebRTC PeerConnection object of the
  49. * @param {number} ssrc - The synchronization source identifier (SSRC) of
  50. * the endpoint/participant/stream being reported.
  51. * @param {number} audioLevel - The audio level of <tt>ssrc</tt>.
  52. * @param {boolean} isLocal - <tt>true</tt> if <tt>ssrc</tt> represents a
  53. * local/send stream or <tt>false</tt> for a remote/receive stream.
  54. */
  55. _processVADScore(vadScore) {
  56. // We are interested in the local audio stream only and if event is not
  57. // sent yet.
  58. if (this._eventFired) {
  59. return;
  60. }
  61. if (this.audioTrack.isMuted()) {
  62. if (vadScore.score > 0.8 && !this._processing) {
  63. this._processing = true;
  64. this._processTimeout = setTimeout(() => {
  65. let scoreSum = 0;
  66. for (const score of this._scoreArray) {
  67. scoreSum += score;
  68. }
  69. const avgScore = scoreSum / this._scoreArray.length;
  70. if (avgScore > VAD_DETECT_THRESHOLD) {
  71. this.emit(VAD_TALK_WHILE_MUTED, '');
  72. this._eventFired = true;
  73. console.log('[ADBG] Triggered array size: ', this._scoreArray, '. AVG: ', avgScore);
  74. } else {
  75. console.log('[ADBG] Not triggered array size: ', this._scoreArray, '. AVG: ', avgScore);
  76. }
  77. this._scoreArray = [];
  78. this._processing = false;
  79. }, 1500);
  80. }
  81. if (this._processing) {
  82. this._scoreArray.push(vadScore.score);
  83. }
  84. }
  85. }
  86. /* eslint-enable max-params */
  87. /**
  88. * Determines whether a specific {@link JitsiTrack} represents a local audio
  89. * track.
  90. *
  91. * @param {JitsiTrack} track - The <tt>JitsiTrack</tt> to be checked whether
  92. * it represents a local audio track.
  93. * @private
  94. * @return {boolean} - <tt>true</tt> if the specified <tt>track</tt>
  95. * represents a local audio track; otherwise, <tt>false</tt>.
  96. */
  97. _isLocalAudioTrack(track) {
  98. return track.isAudioTrack() && track.isLocal();
  99. }
  100. /**
  101. * Notifies this <tt>TalkMutedDetection</tt> that a {@link JitsiTrack} was
  102. * added to the associated {@link JitsiConference}. Looks for the local
  103. * audio track only.
  104. *
  105. * @param {JitsiTrack} track - The added <tt>JitsiTrack</tt>.
  106. * @private
  107. */
  108. _trackAdded(track) {
  109. if (this._isLocalAudioTrack(track)) {
  110. logger.info('[ADBG] Audio track added.');
  111. this.audioTrack = track;
  112. this._vadProcessor().then(vadProcessor => {
  113. TrackVADEmitter.create(track.getDeviceId(), 4096, vadProcessor).then(vadEmitter => {
  114. if (this._vadEmitter) {
  115. this._vadEmitter.destroy();
  116. }
  117. this._vadEmitter = vadEmitter;
  118. this._vadEmitter.on(VAD_SCORE_PUBLISHED, this._processVADScore.bind(this));
  119. this._eventFired = false;
  120. this._processing = false;
  121. clearTimeout(this._processTimeout);
  122. });
  123. });
  124. }
  125. }
  126. /**
  127. * Notifies this <tt>TalkMutedDetection</tt> that the mute state of a
  128. * {@link JitsiTrack} has changed. Looks for the local audio track only.
  129. *
  130. * @param {JitsiTrack} track - The <tt>JitsiTrack</tt> whose mute state has
  131. * changed.
  132. * @private
  133. */
  134. _trackMuteChanged(track) {
  135. if (this._isLocalAudioTrack(track) && track.isMuted()) {
  136. logger.info('[ADBG] Audio track muted.');
  137. this._eventFired = false;
  138. clearTimeout(this._processTimeout);
  139. }
  140. }
  141. }