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.3KB

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