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

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