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 8.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. // @flow
  2. import type { Dispatch } from 'redux';
  3. import {
  4. addPendingDeviceRequest,
  5. areDeviceLabelsInitialized,
  6. getAudioOutputDeviceId,
  7. getAvailableDevices,
  8. getDeviceIdByLabel,
  9. groupDevicesByKind,
  10. setAudioInputDeviceAndUpdateSettings,
  11. setAudioOutputDevice,
  12. setVideoInputDeviceAndUpdateSettings
  13. } from '../base/devices';
  14. import { isIosMobileBrowser } from '../base/environment/utils';
  15. import JitsiMeetJS from '../base/lib-jitsi-meet';
  16. import { toState } from '../base/redux';
  17. import {
  18. getUserSelectedCameraDeviceId,
  19. getUserSelectedMicDeviceId,
  20. getUserSelectedOutputDeviceId
  21. } from '../base/settings';
  22. /**
  23. * Returns the properties for the device selection dialog from Redux state.
  24. *
  25. * @param {(Function|Object)} stateful -The (whole) redux state, or redux's
  26. * {@code getState} function to be used to retrieve the state.
  27. * @returns {Object} - The properties for the device selection dialog.
  28. */
  29. export function getDeviceSelectionDialogProps(stateful: Object | Function) {
  30. // On mobile Safari because of https://bugs.webkit.org/show_bug.cgi?id=179363#c30, the old track is stopped
  31. // by the browser when a new track is created for preview. That's why we are disabling all previews.
  32. const disablePreviews = isIosMobileBrowser();
  33. const state = toState(stateful);
  34. const settings = state['features/base/settings'];
  35. const { conference } = state['features/base/conference'];
  36. const { permissions } = state['features/base/devices'];
  37. const cameraChangeSupported = JitsiMeetJS.mediaDevices.isDeviceChangeAvailable('input');
  38. const speakerChangeSupported = JitsiMeetJS.mediaDevices.isDeviceChangeAvailable('output');
  39. const userSelectedCamera = getUserSelectedCameraDeviceId(state);
  40. const userSelectedMic = getUserSelectedMicDeviceId(state);
  41. let disableAudioInputChange = !JitsiMeetJS.mediaDevices.isMultipleAudioInputSupported();
  42. let disableVideoInputSelect = !cameraChangeSupported;
  43. let selectedAudioInputId = settings.micDeviceId;
  44. let selectedAudioOutputId = getAudioOutputDeviceId();
  45. let selectedVideoInputId = settings.cameraDeviceId;
  46. // audio input change will be a problem only when we are in a
  47. // conference and this is not supported, when we open device selection on
  48. // welcome page changing input devices will not be a problem
  49. // on welcome page we also show only what we have saved as user selected devices
  50. if (!conference) {
  51. disableAudioInputChange = false;
  52. disableVideoInputSelect = false;
  53. selectedAudioInputId = userSelectedMic;
  54. selectedAudioOutputId = getUserSelectedOutputDeviceId(state);
  55. selectedVideoInputId = userSelectedCamera;
  56. }
  57. // we fill the device selection dialog with the devices that are currently
  58. // used or if none are currently used with what we have in settings(user selected)
  59. return {
  60. availableDevices: state['features/base/devices'].availableDevices,
  61. disableAudioInputChange,
  62. disableDeviceChange: !JitsiMeetJS.mediaDevices.isDeviceChangeAvailable(),
  63. disableVideoInputSelect,
  64. hasAudioPermission: permissions.audio,
  65. hasVideoPermission: permissions.video,
  66. hideAudioInputPreview: disableAudioInputChange || !JitsiMeetJS.isCollectingLocalStats() || disablePreviews,
  67. hideAudioOutputPreview: !speakerChangeSupported || disablePreviews,
  68. hideAudioOutputSelect: !speakerChangeSupported,
  69. hideVideoInputPreview: !cameraChangeSupported || disablePreviews,
  70. selectedAudioInputId,
  71. selectedAudioOutputId,
  72. selectedVideoInputId
  73. };
  74. }
  75. /**
  76. * Processes device requests from external applications.
  77. *
  78. * @param {Dispatch} dispatch - The redux {@code dispatch} function.
  79. * @param {Function} getState - The redux function that gets/retrieves the redux
  80. * state.
  81. * @param {Object} request - The request to be processed.
  82. * @param {Function} responseCallback - The callback that will send the
  83. * response.
  84. * @returns {boolean} - True if the request has been processed and false otherwise.
  85. */
  86. export function processExternalDeviceRequest( // eslint-disable-line max-params
  87. dispatch: Dispatch<any>,
  88. getState: Function,
  89. request: Object,
  90. responseCallback: Function) {
  91. if (request.type !== 'devices') {
  92. return false;
  93. }
  94. const state = getState();
  95. const settings = state['features/base/settings'];
  96. let result = true;
  97. switch (request.name) {
  98. case 'isDeviceListAvailable':
  99. responseCallback(JitsiMeetJS.mediaDevices.isDeviceListAvailable());
  100. break;
  101. case 'isDeviceChangeAvailable':
  102. responseCallback(
  103. JitsiMeetJS.mediaDevices.isDeviceChangeAvailable(
  104. request.deviceType));
  105. break;
  106. case 'isMultipleAudioInputSupported':
  107. responseCallback(JitsiMeetJS.isMultipleAudioInputSupported());
  108. break;
  109. case 'getCurrentDevices':
  110. dispatch(getAvailableDevices()).then(devices => {
  111. if (areDeviceLabelsInitialized(state)) {
  112. const deviceDescriptions = {
  113. audioInput: undefined,
  114. audioOutput: undefined,
  115. videoInput: undefined
  116. };
  117. const currentlyUsedDeviceIds = new Set([
  118. getAudioOutputDeviceId(),
  119. settings.micDeviceId,
  120. settings.cameraDeviceId
  121. ]);
  122. devices.forEach(device => {
  123. const { deviceId, kind } = device;
  124. if (currentlyUsedDeviceIds.has(deviceId)) {
  125. switch (kind) {
  126. case 'audioinput':
  127. deviceDescriptions.audioInput = device;
  128. break;
  129. case 'audiooutput':
  130. deviceDescriptions.audioOutput = device;
  131. break;
  132. case 'videoinput':
  133. deviceDescriptions.videoInput = device;
  134. break;
  135. }
  136. }
  137. });
  138. responseCallback(deviceDescriptions);
  139. } else {
  140. // The labels are not available if the A/V permissions are
  141. // not yet granted.
  142. dispatch(addPendingDeviceRequest({
  143. type: 'devices',
  144. name: 'getCurrentDevices',
  145. responseCallback
  146. }));
  147. }
  148. });
  149. break;
  150. case 'getAvailableDevices':
  151. dispatch(getAvailableDevices()).then(devices => {
  152. if (areDeviceLabelsInitialized(state)) {
  153. responseCallback(groupDevicesByKind(devices));
  154. } else {
  155. // The labels are not available if the A/V permissions are
  156. // not yet granted.
  157. dispatch(addPendingDeviceRequest({
  158. type: 'devices',
  159. name: 'getAvailableDevices',
  160. responseCallback
  161. }));
  162. }
  163. });
  164. break;
  165. case 'setDevice': {
  166. const { device } = request;
  167. if (!areDeviceLabelsInitialized(state)) {
  168. dispatch(addPendingDeviceRequest({
  169. type: 'devices',
  170. name: 'setDevice',
  171. device,
  172. responseCallback
  173. }));
  174. return true;
  175. }
  176. const { label, id } = device;
  177. const deviceId = label
  178. ? getDeviceIdByLabel(state, device.label, device.kind)
  179. : id;
  180. if (deviceId) {
  181. switch (device.kind) {
  182. case 'audioinput':
  183. dispatch(setAudioInputDeviceAndUpdateSettings(deviceId));
  184. break;
  185. case 'audiooutput':
  186. dispatch(setAudioOutputDevice(deviceId));
  187. break;
  188. case 'videoinput':
  189. dispatch(setVideoInputDeviceAndUpdateSettings(deviceId));
  190. break;
  191. default:
  192. result = false;
  193. }
  194. } else {
  195. result = false;
  196. }
  197. responseCallback(result);
  198. break;
  199. }
  200. default:
  201. return false;
  202. }
  203. return true;
  204. }