Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

NoAudioSignalDetection.js 5.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. import EventEmitter from 'events';
  2. import * as JitsiConferenceEvents from '../../JitsiConferenceEvents';
  3. import * as JitsiTrackEvents from '../../JitsiTrackEvents';
  4. import browser from '../browser';
  5. import * as DetectionEvents from './DetectionEvents';
  6. // We wait a certain time interval for constant silence input from the current device to account for
  7. // potential abnormalities and for a better use experience i.e. don't generate event the instant
  8. // an audio track is added to the tcr.
  9. // Potential improvement - add this as a configurable parameter.
  10. const SILENCE_PERIOD_MS = 4000;
  11. /**
  12. * Detect if there is no audio input on the current TraceAblePeerConnection selected track. The no audio
  13. * state must be constant for a configured amount of time in order for the event to be triggered.
  14. * @fires DetectionEvents.AUDIO_INPUT_STATE_CHANGE
  15. * @fires DetectionEvents.NO_AUDIO_INPUT
  16. */
  17. export default class NoAudioSignalDetection extends EventEmitter {
  18. /**
  19. * Creates new NoAudioSignalDetection.
  20. *
  21. * @param conference the JitsiConference instance that created us.
  22. * @constructor
  23. */
  24. constructor(conference) {
  25. super();
  26. this._conference = conference;
  27. this._timeoutTrigger = null;
  28. this._hasAudioInput = null;
  29. if (!browser.supportsReceiverStats()) {
  30. conference.statistics.addAudioLevelListener(this._audioLevel.bind(this));
  31. }
  32. conference.on(JitsiConferenceEvents.TRACK_ADDED, this._trackAdded.bind(this));
  33. }
  34. /**
  35. * Clear the timeout state.
  36. */
  37. _clearTriggerTimeout() {
  38. clearTimeout(this._timeoutTrigger);
  39. this._timeoutTrigger = null;
  40. }
  41. /**
  42. * Generated event triggered by a change in the current conference audio input state.
  43. *
  44. * @param {*} audioLevel - The audio level of the ssrc.
  45. * @fires DetectionEvents.AUDIO_INPUT_STATE_CHANGE
  46. */
  47. _handleAudioInputStateChange(audioLevel) {
  48. // Current audio input state of the active local track in the conference, true for audio input false for no
  49. // audio input.
  50. const status = audioLevel !== 0;
  51. // If this is the first audio event picked up or the current status is different from the previous trigger
  52. // the event.
  53. if (this._hasAudioInput === null || this._hasAudioInput !== status) {
  54. this._hasAudioInput = status;
  55. this.emit(DetectionEvents.AUDIO_INPUT_STATE_CHANGE, this._hasAudioInput);
  56. }
  57. }
  58. /**
  59. * Generate event triggered by a prolonged period of no audio input.
  60. *
  61. * @param {number} audioLevel - The audio level of the ssrc.
  62. * @fires DetectionEvents.NO_AUDIO_INPUT
  63. */
  64. _handleNoAudioInputDetection(audioLevel) {
  65. if (this._eventFired) {
  66. return;
  67. }
  68. if (audioLevel === 0 && !this._timeoutTrigger) {
  69. this._timeoutTrigger = setTimeout(() => {
  70. this._eventFired = true;
  71. this.emit(DetectionEvents.NO_AUDIO_INPUT);
  72. }, SILENCE_PERIOD_MS);
  73. } else if (audioLevel !== 0 && this._timeoutTrigger) {
  74. this._clearTriggerTimeout();
  75. }
  76. }
  77. /**
  78. * Receives audio level events for all send and receive streams on the current TraceablePeerConnection.
  79. *
  80. * @param {TraceablePeerConnection} tpc - TraceablePeerConnection of the owning conference.
  81. * @param {number} ssrc - The synchronization source identifier (SSRC) of the endpoint/participant/stream
  82. * being reported.
  83. * @param {number} audioLevel - The audio level of the ssrc.
  84. * @param {boolean} isLocal - true for local/send streams or false for remote/receive streams.
  85. */
  86. _audioLevel(tpc, ssrc, audioLevel, isLocal) {
  87. // We are interested in the local audio streams
  88. if (!isLocal || !this._audioTrack) {
  89. return;
  90. }
  91. // Get currently active local tracks from the TraceablePeerConnection
  92. const localSSRCs = tpc.localSSRCs.get(this._audioTrack.rtcId);
  93. // Only target the current active track in the tpc. For some reason audio levels for previous
  94. // devices are also picked up from the PeerConnection so we filter them out.
  95. if (!localSSRCs || !localSSRCs.ssrcs.includes(ssrc)) {
  96. return;
  97. }
  98. // First handle audio input state change. In case the state changed to no input the no audio input event
  99. // can try to fire again.
  100. this._handleAudioInputStateChange(audioLevel);
  101. this._handleNoAudioInputDetection(audioLevel);
  102. }
  103. /**
  104. * Notifies NoAudioSignalDetection that a JitsiTrack was added to the associated JitsiConference.
  105. * Only take into account local audio tracks.
  106. *
  107. * @param {JitsiTrack} track - The added JitsiTrack.
  108. */
  109. _trackAdded(track) {
  110. if (track.isLocalAudioTrack()) {
  111. // Reset state for the new track.
  112. this._audioTrack = track;
  113. this._eventFired = false;
  114. this._clearTriggerTimeout();
  115. // Listen for the audio levels on the newly added audio track
  116. if (browser.supportsReceiverStats()) {
  117. track.on(
  118. JitsiTrackEvents.NO_AUDIO_INPUT,
  119. audioLevel => {
  120. this._handleNoAudioInputDetection(audioLevel);
  121. }
  122. );
  123. track.on(
  124. JitsiTrackEvents.TRACK_AUDIO_LEVEL_CHANGED,
  125. audioLevel => {
  126. this._handleNoAudioInputDetection(audioLevel);
  127. this._handleAudioInputStateChange(audioLevel);
  128. }
  129. );
  130. }
  131. }
  132. }
  133. }