Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

middleware.web.js 7.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. // @flow
  2. import { AUDIO_ONLY_SCREEN_SHARE_NO_TRACK } from '../../../../modules/UI/UIErrors';
  3. import { showNotification, NOTIFICATION_TIMEOUT_TYPE } from '../../notifications';
  4. import {
  5. setPrejoinPageVisibility,
  6. setSkipPrejoinOnReload
  7. } from '../../prejoin';
  8. import { setScreenAudioShareState, setScreenshareAudioTrack } from '../../screen-share';
  9. import { AudioMixerEffect } from '../../stream-effects/audio-mixer/AudioMixerEffect';
  10. import { setAudioOnly } from '../audio-only';
  11. import { getMultipleVideoSendingSupportFeatureFlag } from '../config/functions.any';
  12. import { JitsiConferenceErrors, JitsiTrackErrors } from '../lib-jitsi-meet';
  13. import { MEDIA_TYPE, setScreenshareMuted, VIDEO_TYPE } from '../media';
  14. import { MiddlewareRegistry } from '../redux';
  15. import {
  16. addLocalTrack,
  17. createLocalTracksF,
  18. getLocalDesktopTrack,
  19. getLocalJitsiAudioTrack,
  20. replaceLocalTrack,
  21. TOGGLE_SCREENSHARING
  22. } from '../tracks';
  23. import { CONFERENCE_FAILED, CONFERENCE_JOIN_IN_PROGRESS, CONFERENCE_JOINED } from './actionTypes';
  24. import { getCurrentConference } from './functions';
  25. import './middleware.any';
  26. MiddlewareRegistry.register(store => next => action => {
  27. const { dispatch, getState } = store;
  28. const { enableForcedReload } = getState()['features/base/config'];
  29. switch (action.type) {
  30. case CONFERENCE_JOIN_IN_PROGRESS: {
  31. dispatch(setPrejoinPageVisibility(false));
  32. break;
  33. }
  34. case CONFERENCE_JOINED: {
  35. if (enableForcedReload) {
  36. dispatch(setSkipPrejoinOnReload(false));
  37. }
  38. break;
  39. }
  40. case CONFERENCE_FAILED: {
  41. const errorName = action.error?.name;
  42. if (errorName === JitsiConferenceErrors.MEMBERS_ONLY_ERROR
  43. || errorName === JitsiConferenceErrors.PASSWORD_REQUIRED) {
  44. dispatch(setPrejoinPageVisibility(false));
  45. } else if (enableForcedReload && errorName === JitsiConferenceErrors.CONFERENCE_RESTARTED) {
  46. dispatch(setSkipPrejoinOnReload(true));
  47. }
  48. break;
  49. }
  50. case TOGGLE_SCREENSHARING: {
  51. getMultipleVideoSendingSupportFeatureFlag(getState()) && _toggleScreenSharing(action, store);
  52. break;
  53. }
  54. }
  55. return next(action);
  56. });
  57. /**
  58. * Displays a UI notification for screensharing failure based on the error passed.
  59. *
  60. * @private
  61. * @param {Object} error - The error.
  62. * @param {Object} store - The redux store.
  63. * @returns {void}
  64. */
  65. function _handleScreensharingError(error, { dispatch }) {
  66. if (error.name === JitsiTrackErrors.SCREENSHARING_USER_CANCELED) {
  67. return;
  68. }
  69. let descriptionKey, titleKey;
  70. if (error.name === JitsiTrackErrors.PERMISSION_DENIED) {
  71. descriptionKey = 'dialog.screenSharingPermissionDeniedError';
  72. titleKey = 'dialog.screenSharingFailedTitle';
  73. } else if (error.name === JitsiTrackErrors.CONSTRAINT_FAILED) {
  74. descriptionKey = 'dialog.cameraConstraintFailedError';
  75. titleKey = 'deviceError.cameraError';
  76. } else if (error.name === JitsiTrackErrors.SCREENSHARING_GENERIC_ERROR) {
  77. descriptionKey = 'dialog.screenSharingFailed';
  78. titleKey = 'dialog.screenSharingFailedTitle';
  79. } else if (error === AUDIO_ONLY_SCREEN_SHARE_NO_TRACK) {
  80. descriptionKey = 'notify.screenShareNoAudio';
  81. titleKey = 'notify.screenShareNoAudioTitle';
  82. }
  83. dispatch(showNotification({
  84. titleKey,
  85. descriptionKey
  86. }, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
  87. }
  88. /**
  89. * Applies the AudioMixer effect on the local audio track if applicable. If there is no local audio track, the desktop
  90. * audio track is added to the conference.
  91. *
  92. * @private
  93. * @param {JitsiLocalTrack} desktopAudioTrack - The audio track to be added to the conference.
  94. * @param {*} state - The redux state.
  95. * @returns {void}
  96. */
  97. async function _maybeApplyAudioMixerEffect(desktopAudioTrack, state) {
  98. const localAudio = getLocalJitsiAudioTrack(state);
  99. const conference = getCurrentConference(state);
  100. if (localAudio) {
  101. // If there is a localAudio stream, mix in the desktop audio stream captured by the screen sharing API.
  102. const mixerEffect = new AudioMixerEffect(desktopAudioTrack);
  103. await localAudio.setEffect(mixerEffect);
  104. } else {
  105. // If no local stream is present ( i.e. no input audio devices) we use the screen share audio
  106. // stream as we would use a regular stream.
  107. await conference.replaceTrack(null, desktopAudioTrack);
  108. }
  109. }
  110. /**
  111. * Toggles screen sharing.
  112. *
  113. * @private
  114. * @param {boolean} enabled - The state to toggle screen sharing to.
  115. * @param {Store} store - The redux store.
  116. * @returns {void}
  117. */
  118. async function _toggleScreenSharing({ enabled, audioOnly = false }, store) {
  119. const { dispatch, getState } = store;
  120. const state = getState();
  121. const conference = getCurrentConference(state);
  122. const localAudio = getLocalJitsiAudioTrack(state);
  123. const localScreenshare = getLocalDesktopTrack(state['features/base/tracks']);
  124. if (enabled) {
  125. let tracks;
  126. try {
  127. tracks = await createLocalTracksF({ devices: [ VIDEO_TYPE.DESKTOP ] });
  128. } catch (error) {
  129. _handleScreensharingError(error, store);
  130. return;
  131. }
  132. const desktopAudioTrack = tracks.find(track => track.getType() === MEDIA_TYPE.AUDIO);
  133. const desktopVideoTrack = tracks.find(track => track.getType() === MEDIA_TYPE.VIDEO);
  134. // Dispose the desktop track for audio-only screensharing.
  135. if (audioOnly) {
  136. desktopVideoTrack.dispose();
  137. if (!desktopAudioTrack) {
  138. _handleScreensharingError(AUDIO_ONLY_SCREEN_SHARE_NO_TRACK, store);
  139. return;
  140. }
  141. } else if (desktopVideoTrack) {
  142. if (localScreenshare) {
  143. await dispatch(replaceLocalTrack(localScreenshare.jitsiTrack, desktopVideoTrack, conference));
  144. } else {
  145. await dispatch(addLocalTrack(desktopVideoTrack));
  146. }
  147. }
  148. // Apply the AudioMixer effect if there is a local audio track, add the desktop track to the conference
  149. // otherwise without unmuting the microphone.
  150. if (desktopAudioTrack) {
  151. _maybeApplyAudioMixerEffect(desktopAudioTrack, state);
  152. dispatch(setScreenshareAudioTrack(desktopAudioTrack));
  153. }
  154. // Disable audio-only or best performance mode if the user starts screensharing. This doesn't apply to
  155. // audio-only screensharing.
  156. const { enabled: bestPerformanceMode } = state['features/base/audio-only'];
  157. if (bestPerformanceMode && !audioOnly) {
  158. dispatch(setAudioOnly(false));
  159. }
  160. } else {
  161. const { desktopAudioTrack } = state['features/screen-share'];
  162. // Mute the desktop track instead of removing it from the conference since we don't want the client to signal
  163. // a source-remove to the remote peer for the screenshare track. Later when screenshare is enabled again, the
  164. // same sender will be re-used without the need for signaling a new ssrc through source-add.
  165. dispatch(setScreenshareMuted(true));
  166. if (desktopAudioTrack) {
  167. if (localAudio) {
  168. localAudio.setEffect(undefined);
  169. } else {
  170. await conference.replaceTrack(desktopAudioTrack, null);
  171. }
  172. desktopAudioTrack.dispose();
  173. dispatch(setScreenshareAudioTrack(null));
  174. }
  175. }
  176. if (audioOnly) {
  177. dispatch(setScreenAudioShareState(enabled));
  178. }
  179. }