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.

actions.js 9.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  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. DEVICE_PERMISSIONS_CHANGED,
  10. NOTIFY_CAMERA_ERROR,
  11. NOTIFY_MIC_ERROR,
  12. REMOVE_PENDING_DEVICE_REQUESTS,
  13. SET_AUDIO_INPUT_DEVICE,
  14. SET_VIDEO_INPUT_DEVICE,
  15. UPDATE_DEVICE_LIST
  16. } from './actionTypes';
  17. import {
  18. areDeviceLabelsInitialized,
  19. getDeviceIdByLabel,
  20. getDeviceLabelById,
  21. getDevicesFromURL,
  22. setAudioOutputDeviceId
  23. } from './functions';
  24. import logger from './logger';
  25. /**
  26. * Maps the WebRTC string for device type to the keys used to store configure,
  27. * within redux, which devices should be used by default.
  28. */
  29. const DEVICE_TYPE_TO_SETTINGS_KEYS = {
  30. audioInput: {
  31. currentDeviceId: 'micDeviceId',
  32. userSelectedDeviceId: 'userSelectedMicDeviceId',
  33. userSelectedDeviceLabel: 'userSelectedMicDeviceLabel'
  34. },
  35. audioOutput: {
  36. currentDeviceId: 'audioOutputDeviceId',
  37. userSelectedDeviceId: 'userSelectedAudioOutputDeviceId',
  38. userSelectedDeviceLabel: 'userSelectedAudioOutputDeviceLabel'
  39. },
  40. videoInput: {
  41. currentDeviceId: 'audioOutputDeviceId',
  42. userSelectedDeviceId: 'userSelectedCameraDeviceId',
  43. userSelectedDeviceLabel: 'userSelectedCameraDeviceLabel'
  44. }
  45. };
  46. /**
  47. * Adds a pending device request.
  48. *
  49. * @param {Object} request - The request to be added.
  50. * @returns {{
  51. * type: ADD_PENDING_DEVICE_REQUEST,
  52. * request: Object
  53. * }}
  54. */
  55. export function addPendingDeviceRequest(request) {
  56. return {
  57. type: ADD_PENDING_DEVICE_REQUEST,
  58. request
  59. };
  60. }
  61. /**
  62. * Configures the initial A/V devices before the conference has started.
  63. *
  64. * @returns {Function}
  65. */
  66. export function configureInitialDevices() {
  67. return (dispatch, getState) => {
  68. const deviceLabels = getDevicesFromURL(getState());
  69. let updateSettingsPromise;
  70. if (deviceLabels) {
  71. updateSettingsPromise = dispatch(getAvailableDevices()).then(() => {
  72. const state = getState();
  73. if (!areDeviceLabelsInitialized(state)) {
  74. // The labels are not available if the A/V permissions are
  75. // not yet granted.
  76. Object.keys(deviceLabels).forEach(key => {
  77. dispatch(addPendingDeviceRequest({
  78. type: 'devices',
  79. name: 'setDevice',
  80. device: {
  81. kind: key.toLowerCase(),
  82. label: deviceLabels[key]
  83. },
  84. // eslint-disable-next-line no-empty-function
  85. responseCallback() {}
  86. }));
  87. });
  88. return;
  89. }
  90. const newSettings = {};
  91. Object.keys(deviceLabels).forEach(key => {
  92. const label = deviceLabels[key];
  93. const deviceId = getDeviceIdByLabel(state, label, key);
  94. if (deviceId) {
  95. const settingsTranslationMap = DEVICE_TYPE_TO_SETTINGS_KEYS[key];
  96. newSettings[settingsTranslationMap.currentDeviceId] = deviceId;
  97. newSettings[settingsTranslationMap.userSelectedDeviceId] = deviceId;
  98. newSettings[settingsTranslationMap.userSelectedDeviceLabel] = label;
  99. }
  100. });
  101. dispatch(updateSettings(newSettings));
  102. });
  103. } else {
  104. updateSettingsPromise = Promise.resolve();
  105. }
  106. return updateSettingsPromise
  107. .then(() => {
  108. const userSelectedAudioOutputDeviceId = getUserSelectedOutputDeviceId(getState());
  109. return setAudioOutputDeviceId(userSelectedAudioOutputDeviceId, dispatch)
  110. .catch(ex => logger.warn(`Failed to set audio output device.
  111. Default audio output device will be used instead ${ex}`));
  112. });
  113. };
  114. }
  115. /**
  116. * Queries for connected A/V input and output devices and updates the redux
  117. * state of known devices.
  118. *
  119. * @returns {Function}
  120. */
  121. export function getAvailableDevices() {
  122. return dispatch => new Promise(resolve => {
  123. const { mediaDevices } = JitsiMeetJS;
  124. if (mediaDevices.isDeviceListAvailable()
  125. && mediaDevices.isDeviceChangeAvailable()) {
  126. mediaDevices.enumerateDevices(devices => {
  127. dispatch(updateDeviceList(devices));
  128. resolve(devices);
  129. });
  130. } else {
  131. resolve([]);
  132. }
  133. });
  134. }
  135. /**
  136. * Signals that an error occurred while trying to obtain a track from a camera.
  137. *
  138. * @param {Object} error - The device error, as provided by lib-jitsi-meet.
  139. * @param {string} error.name - The constant for the type of the error.
  140. * @param {string} error.message - Optional additional information about the
  141. * error.
  142. * @returns {{
  143. * type: NOTIFY_CAMERA_ERROR,
  144. * error: Object
  145. * }}
  146. */
  147. export function notifyCameraError(error) {
  148. return {
  149. type: NOTIFY_CAMERA_ERROR,
  150. error
  151. };
  152. }
  153. /**
  154. * Signals that an error occurred while trying to obtain a track from a mic.
  155. *
  156. * @param {Object} error - The device error, as provided by lib-jitsi-meet.
  157. * @param {Object} error.name - The constant for the type of the error.
  158. * @param {string} error.message - Optional additional information about the
  159. * error.
  160. * @returns {{
  161. * type: NOTIFY_MIC_ERROR,
  162. * error: Object
  163. * }}
  164. */
  165. export function notifyMicError(error) {
  166. return {
  167. type: NOTIFY_MIC_ERROR,
  168. error
  169. };
  170. }
  171. /**
  172. * Remove all pending device requests.
  173. *
  174. * @returns {{
  175. * type: REMOVE_PENDING_DEVICE_REQUESTS
  176. * }}
  177. */
  178. export function removePendingDeviceRequests() {
  179. return {
  180. type: REMOVE_PENDING_DEVICE_REQUESTS
  181. };
  182. }
  183. /**
  184. * Signals to update the currently used audio input device.
  185. *
  186. * @param {string} deviceId - The id of the new audio input device.
  187. * @returns {{
  188. * type: SET_AUDIO_INPUT_DEVICE,
  189. * deviceId: string
  190. * }}
  191. */
  192. export function setAudioInputDevice(deviceId) {
  193. return {
  194. type: SET_AUDIO_INPUT_DEVICE,
  195. deviceId
  196. };
  197. }
  198. /**
  199. * Sets the audio input device id and updates the settings
  200. * so they are persisted across sessions.
  201. *
  202. * @param {string} deviceId - The id of the new audio input device.
  203. * @returns {Function}
  204. */
  205. export function setAudioInputDeviceAndUpdateSettings(deviceId) {
  206. return function(dispatch, getState) {
  207. const deviceLabel = getDeviceLabelById(getState(), deviceId, 'audioInput');
  208. dispatch(setAudioInputDevice(deviceId));
  209. dispatch(updateSettings({
  210. userSelectedMicDeviceId: deviceId,
  211. userSelectedMicDeviceLabel: deviceLabel
  212. }));
  213. };
  214. }
  215. /**
  216. * Updates the output device id.
  217. *
  218. * @param {string} deviceId - The id of the new output device.
  219. * @returns {Function}
  220. */
  221. export function setAudioOutputDevice(deviceId) {
  222. return function(dispatch, getState) {
  223. const deviceLabel = getDeviceLabelById(getState(), deviceId, 'audioOutput');
  224. return setAudioOutputDeviceId(deviceId, dispatch, true, deviceLabel);
  225. };
  226. }
  227. /**
  228. * Signals to update the currently used video input device.
  229. *
  230. * @param {string} deviceId - The id of the new video input device.
  231. * @returns {{
  232. * type: SET_VIDEO_INPUT_DEVICE,
  233. * deviceId: string
  234. * }}
  235. */
  236. export function setVideoInputDevice(deviceId) {
  237. return {
  238. type: SET_VIDEO_INPUT_DEVICE,
  239. deviceId
  240. };
  241. }
  242. /**
  243. * Sets the video input device id and updates the settings
  244. * so they are persisted across sessions.
  245. *
  246. * @param {string} deviceId - The id of the new video input device.
  247. * @returns {Function}
  248. */
  249. export function setVideoInputDeviceAndUpdateSettings(deviceId) {
  250. return function(dispatch, getState) {
  251. const deviceLabel = getDeviceLabelById(getState(), deviceId, 'videoInput');
  252. dispatch(setVideoInputDevice(deviceId));
  253. dispatch(updateSettings({
  254. userSelectedCameraDeviceId: deviceId,
  255. userSelectedCameraDeviceLabel: deviceLabel
  256. }));
  257. };
  258. }
  259. /**
  260. * Signals to update the list of known audio and video devices.
  261. *
  262. * @param {Array<MediaDeviceInfo>} devices - All known available audio input,
  263. * audio output, and video input devices.
  264. * @returns {{
  265. * type: UPDATE_DEVICE_LIST,
  266. * devices: Array<MediaDeviceInfo>
  267. * }}
  268. */
  269. export function updateDeviceList(devices) {
  270. return {
  271. type: UPDATE_DEVICE_LIST,
  272. devices
  273. };
  274. }
  275. /**
  276. * Signals to check new and old devices for newly added devices and notify.
  277. *
  278. * @param {Array<MediaDeviceInfo>} newDevices - Array of the new devices.
  279. * @param {Array<MediaDeviceInfo>} oldDevices - Array of the old devices.
  280. * @returns {{
  281. * type: CHECK_AND_NOTIFY_FOR_NEW_DEVICE,
  282. * newDevices: Array<MediaDeviceInfo>,
  283. * oldDevices: Array<MediaDeviceInfo>
  284. * }}
  285. */
  286. export function checkAndNotifyForNewDevice(newDevices, oldDevices) {
  287. return {
  288. type: CHECK_AND_NOTIFY_FOR_NEW_DEVICE,
  289. newDevices,
  290. oldDevices
  291. };
  292. }
  293. /**
  294. * Signals that the device permissions have changed.
  295. *
  296. * @param {Object} permissions - Object with the permissions.
  297. * @returns {{
  298. * type: DEVICE_PERMISSIONS_CHANGED,
  299. * permissions: Object
  300. * }}
  301. */
  302. export function devicePermissionsChanged(permissions) {
  303. return {
  304. type: DEVICE_PERMISSIONS_CHANGED,
  305. permissions
  306. };
  307. }