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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. import JitsiMeetJS from '../lib-jitsi-meet';
  2. import {
  3. getUserSelectedOutputDeviceId,
  4. updateSettings
  5. } from '../settings';
  6. import {
  7. ADD_PENDING_DEVICE_REQUEST,
  8. CHECK_AND_NOTIFY_FOR_NEW_DEVICE,
  9. REMOVE_PENDING_DEVICE_REQUESTS,
  10. SET_AUDIO_INPUT_DEVICE,
  11. SET_VIDEO_INPUT_DEVICE,
  12. UPDATE_DEVICE_LIST
  13. } from './actionTypes';
  14. import {
  15. areDeviceLabelsInitialized,
  16. getDeviceIdByLabel,
  17. getDevicesFromURL,
  18. setAudioOutputDeviceId
  19. } from './functions';
  20. const logger = require('jitsi-meet-logger').getLogger(__filename);
  21. /**
  22. * Maps the WebRTC string for device type to the keys used to store configure,
  23. * within redux, which devices should be used by default.
  24. */
  25. const DEVICE_TYPE_TO_SETTINGS_KEYS = {
  26. audioInput: {
  27. currentDeviceId: 'micDeviceId',
  28. userSelectedDeviceId: 'userSelectedMicDeviceId',
  29. userSelectedDeviceLabel: 'userSelectedMicDeviceLabel'
  30. },
  31. audioOutput: {
  32. currentDeviceId: 'audioOutputDeviceId',
  33. userSelectedDeviceId: 'userSelectedAudioOutputDeviceId',
  34. userSelectedDeviceLabel: 'userSelectedAudioOutputDeviceLabel'
  35. },
  36. videoInput: {
  37. currentDeviceId: 'audioOutputDeviceId',
  38. userSelectedDeviceId: 'userSelectedCameraDeviceId',
  39. userSelectedDeviceLabel: 'userSelectedCameraDeviceLabel'
  40. }
  41. };
  42. /**
  43. * Adds a pending device request.
  44. *
  45. * @param {Object} request - The request to be added.
  46. * @returns {{
  47. * type: ADD_PENDING_DEVICE_REQUEST,
  48. * request: Object
  49. * }}
  50. */
  51. export function addPendingDeviceRequest(request) {
  52. return {
  53. type: ADD_PENDING_DEVICE_REQUEST,
  54. request
  55. };
  56. }
  57. /**
  58. * Configures the initial A/V devices before the conference has started.
  59. *
  60. * @returns {Function}
  61. */
  62. export function configureInitialDevices() {
  63. return (dispatch, getState) => {
  64. const deviceLabels = getDevicesFromURL(getState());
  65. let updateSettingsPromise;
  66. if (deviceLabels) {
  67. updateSettingsPromise = dispatch(getAvailableDevices()).then(() => {
  68. const state = getState();
  69. if (!areDeviceLabelsInitialized(state)) {
  70. // The labels are not available if the A/V permissions are
  71. // not yet granted.
  72. Object.keys(deviceLabels).forEach(key => {
  73. dispatch(addPendingDeviceRequest({
  74. type: 'devices',
  75. name: 'setDevice',
  76. device: {
  77. kind: key.toLowerCase(),
  78. label: deviceLabels[key]
  79. },
  80. // eslint-disable-next-line no-empty-function
  81. responseCallback() {}
  82. }));
  83. });
  84. return;
  85. }
  86. const newSettings = {};
  87. Object.keys(deviceLabels).forEach(key => {
  88. const label = deviceLabels[key];
  89. const deviceId = getDeviceIdByLabel(state, label, key);
  90. if (deviceId) {
  91. const settingsTranslationMap = DEVICE_TYPE_TO_SETTINGS_KEYS[key];
  92. newSettings[settingsTranslationMap.currentDeviceId] = deviceId;
  93. newSettings[settingsTranslationMap.userSelectedDeviceId] = deviceId;
  94. newSettings[settingsTranslationMap.userSelectedDeviceLabel] = label;
  95. }
  96. });
  97. dispatch(updateSettings(newSettings));
  98. });
  99. } else {
  100. updateSettingsPromise = Promise.resolve();
  101. }
  102. return updateSettingsPromise
  103. .then(() => {
  104. const userSelectedAudioOutputDeviceId = getUserSelectedOutputDeviceId(getState());
  105. return setAudioOutputDeviceId(userSelectedAudioOutputDeviceId, dispatch)
  106. .catch(ex => logger.warn(`Failed to set audio output device.
  107. Default audio output device will be used instead ${ex}`));
  108. });
  109. };
  110. }
  111. /**
  112. * Queries for connected A/V input and output devices and updates the redux
  113. * state of known devices.
  114. *
  115. * @returns {Function}
  116. */
  117. export function getAvailableDevices() {
  118. return dispatch => new Promise(resolve => {
  119. const { mediaDevices } = JitsiMeetJS;
  120. if (mediaDevices.isDeviceListAvailable()
  121. && mediaDevices.isDeviceChangeAvailable()) {
  122. mediaDevices.enumerateDevices(devices => {
  123. dispatch(updateDeviceList(devices));
  124. resolve(devices);
  125. });
  126. } else {
  127. resolve([]);
  128. }
  129. });
  130. }
  131. /**
  132. * Remove all pending device requests.
  133. *
  134. * @returns {{
  135. * type: REMOVE_PENDING_DEVICE_REQUESTS
  136. * }}
  137. */
  138. export function removePendingDeviceRequests() {
  139. return {
  140. type: REMOVE_PENDING_DEVICE_REQUESTS
  141. };
  142. }
  143. /**
  144. * Signals to update the currently used audio input device.
  145. *
  146. * @param {string} deviceId - The id of the new audio input device.
  147. * @returns {{
  148. * type: SET_AUDIO_INPUT_DEVICE,
  149. * deviceId: string
  150. * }}
  151. */
  152. export function setAudioInputDevice(deviceId) {
  153. return {
  154. type: SET_AUDIO_INPUT_DEVICE,
  155. deviceId
  156. };
  157. }
  158. /**
  159. * Signals to update the currently used video input device.
  160. *
  161. * @param {string} deviceId - The id of the new video input device.
  162. * @returns {{
  163. * type: SET_VIDEO_INPUT_DEVICE,
  164. * deviceId: string
  165. * }}
  166. */
  167. export function setVideoInputDevice(deviceId) {
  168. return {
  169. type: SET_VIDEO_INPUT_DEVICE,
  170. deviceId
  171. };
  172. }
  173. /**
  174. * Signals to update the list of known audio and video devices.
  175. *
  176. * @param {Array<MediaDeviceInfo>} devices - All known available audio input,
  177. * audio output, and video input devices.
  178. * @returns {{
  179. * type: UPDATE_DEVICE_LIST,
  180. * devices: Array<MediaDeviceInfo>
  181. * }}
  182. */
  183. export function updateDeviceList(devices) {
  184. return {
  185. type: UPDATE_DEVICE_LIST,
  186. devices
  187. };
  188. }
  189. /**
  190. * Signals to check new and old devices for newly added devices and notify.
  191. *
  192. * @param {Array<MediaDeviceInfo>} newDevices - Array of the new devices.
  193. * @param {Array<MediaDeviceInfo>} oldDevices - Array of the old devices.
  194. * @returns {{
  195. * type: CHECK_AND_NOTIFY_FOR_NEW_DEVICE,
  196. * newDevices: Array<MediaDeviceInfo>,
  197. * oldDevices: Array<MediaDeviceInfo>
  198. * }}
  199. */
  200. export function checkAndNotifyForNewDevice(newDevices, oldDevices) {
  201. return {
  202. type: CHECK_AND_NOTIFY_FOR_NEW_DEVICE,
  203. newDevices,
  204. oldDevices
  205. };
  206. }