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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. // @flow
  2. import JitsiMeetJS from '../lib-jitsi-meet';
  3. import { updateSettings } from '../settings';
  4. import { parseURLParams } from '../util';
  5. import logger from './logger';
  6. declare var APP: Object;
  7. const webrtcKindToJitsiKindTranslator = {
  8. audioinput: 'audioInput',
  9. audiooutput: 'audioOutput',
  10. videoinput: 'videoInput'
  11. };
  12. /**
  13. * Detects the use case when the labels are not available if the A/V permissions
  14. * are not yet granted.
  15. *
  16. * @param {Object} state - The redux state.
  17. * @returns {boolean} - True if the labels are already initialized and false
  18. * otherwise.
  19. */
  20. export function areDeviceLabelsInitialized(state: Object) {
  21. // TODO: Replace with something that doesn't use APP when the conference.js logic is reactified.
  22. if (APP.conference._localTracksInitialized) {
  23. return true;
  24. }
  25. for (const type of [ 'audioInput', 'audioOutput', 'videoInput' ]) {
  26. if ((state['features/base/devices'].availableDevices[type] || []).find(d => Boolean(d.label))) {
  27. return true;
  28. }
  29. }
  30. return false;
  31. }
  32. /**
  33. * Get device id of the audio output device which is currently in use.
  34. * Empty string stands for default device.
  35. *
  36. * @returns {string}
  37. */
  38. export function getAudioOutputDeviceId() {
  39. return JitsiMeetJS.mediaDevices.getAudioOutputDevice();
  40. }
  41. /**
  42. * Finds the real device id of the default device of the given type.
  43. *
  44. * @param {Object} state - The redux state.
  45. * @param {*} kind - The type of the device. One of "audioInput",
  46. * "audioOutput", and "videoInput". Also supported is all lowercase versions
  47. * of the preceding types.
  48. * @returns {string|undefined}
  49. */
  50. export function getDefaultDeviceId(state: Object, kind: string) {
  51. const kindToSearch = webrtcKindToJitsiKindTranslator[kind] || kind;
  52. const defaultDevice = (state['features/base/devices'].availableDevices[kindToSearch] || [])
  53. .find(d => d.deviceId === 'default');
  54. // Find the device with a matching group id.
  55. const matchingDevice = (state['features/base/devices'].availableDevices[kindToSearch] || [])
  56. .find(d => d.deviceId !== 'default' && d.groupId === defaultDevice.groupId);
  57. if (matchingDevice) {
  58. return matchingDevice.deviceId;
  59. }
  60. }
  61. /**
  62. * Finds a device with a label that matches the passed label and returns its id.
  63. *
  64. * @param {Object} state - The redux state.
  65. * @param {string} label - The label.
  66. * @param {string} kind - The type of the device. One of "audioInput",
  67. * "audioOutput", and "videoInput". Also supported is all lowercase versions
  68. * of the preceding types.
  69. * @returns {string|undefined}
  70. */
  71. export function getDeviceIdByLabel(state: Object, label: string, kind: string) {
  72. const kindToSearch = webrtcKindToJitsiKindTranslator[kind] || kind;
  73. const device
  74. = (state['features/base/devices'].availableDevices[kindToSearch] || [])
  75. .find(d => d.label === label);
  76. if (device) {
  77. return device.deviceId;
  78. }
  79. }
  80. /**
  81. * Finds a device with a label that matches the passed id and returns its label.
  82. *
  83. * @param {Object} state - The redux state.
  84. * @param {string} id - The device id.
  85. * @param {string} kind - The type of the device. One of "audioInput",
  86. * "audioOutput", and "videoInput". Also supported is all lowercase versions
  87. * of the preceding types.
  88. * @returns {string|undefined}
  89. */
  90. export function getDeviceLabelById(state: Object, id: string, kind: string) {
  91. const kindToSearch = webrtcKindToJitsiKindTranslator[kind] || kind;
  92. const device
  93. = (state['features/base/devices'].availableDevices[kindToSearch] || [])
  94. .find(d => d.deviceId === id);
  95. if (device) {
  96. return device.label;
  97. }
  98. }
  99. /**
  100. * Returns the devices set in the URL.
  101. *
  102. * @param {Object} state - The redux state.
  103. * @returns {Object|undefined}
  104. */
  105. export function getDevicesFromURL(state: Object) {
  106. const urlParams
  107. = parseURLParams(state['features/base/connection'].locationURL);
  108. const audioOutput = urlParams['devices.audioOutput'];
  109. const videoInput = urlParams['devices.videoInput'];
  110. const audioInput = urlParams['devices.audioInput'];
  111. if (!audioOutput && !videoInput && !audioInput) {
  112. return undefined;
  113. }
  114. const devices = {};
  115. audioOutput && (devices.audioOutput = audioOutput);
  116. videoInput && (devices.videoInput = videoInput);
  117. audioInput && (devices.audioInput = audioInput);
  118. return devices;
  119. }
  120. /**
  121. * Converts an array of media devices into an object organized by device kind.
  122. *
  123. * @param {Array<MediaDeviceInfo>} devices - Available media devices.
  124. * @private
  125. * @returns {Object} An object with the media devices split by type. The keys
  126. * are device type and the values are arrays with devices matching the device
  127. * type.
  128. */
  129. export function groupDevicesByKind(devices: Object[]): Object {
  130. return {
  131. audioInput: devices.filter(device => device.kind === 'audioinput'),
  132. audioOutput: devices.filter(device => device.kind === 'audiooutput'),
  133. videoInput: devices.filter(device => device.kind === 'videoinput')
  134. };
  135. }
  136. /**
  137. * Filters audio devices from a list of MediaDeviceInfo objects.
  138. *
  139. * @param {Array<MediaDeviceInfo>} devices - Unfiltered media devices.
  140. * @private
  141. * @returns {Array<MediaDeviceInfo>} Filtered audio devices.
  142. */
  143. export function filterAudioDevices(devices: Object[]): Object {
  144. return devices.filter(device => device.kind === 'audioinput');
  145. }
  146. /**
  147. * We want to strip any device details that are not very user friendly, like usb ids put in brackets at the end.
  148. *
  149. * @param {string} label - Device label to format.
  150. *
  151. * @returns {string} - Formatted string.
  152. */
  153. export function formatDeviceLabel(label: string) {
  154. let formattedLabel = label;
  155. // Remove braked description at the end as it contains non user friendly strings i.e.
  156. // Microsoft® LifeCam HD-3000 (045e:0779:31dg:d1231)
  157. const ix = formattedLabel.lastIndexOf('(');
  158. if (ix !== -1) {
  159. formattedLabel = formattedLabel.substr(0, ix);
  160. }
  161. return formattedLabel;
  162. }
  163. /**
  164. * Returns a list of objects containing all the microphone device ids and labels.
  165. *
  166. * @param {Object} state - The state of the application.
  167. * @returns {Object[]}
  168. */
  169. export function getAudioInputDeviceData(state: Object) {
  170. return state['features/base/devices'].availableDevices.audioInput.map(
  171. ({ deviceId, label }) => {
  172. return {
  173. deviceId,
  174. label
  175. };
  176. });
  177. }
  178. /**
  179. * Returns a list of objectes containing all the output device ids and labels.
  180. *
  181. * @param {Object} state - The state of the application.
  182. * @returns {Object[]}
  183. */
  184. export function getAudioOutputDeviceData(state: Object) {
  185. return state['features/base/devices'].availableDevices.audioOutput.map(
  186. ({ deviceId, label }) => {
  187. return {
  188. deviceId,
  189. label
  190. };
  191. });
  192. }
  193. /**
  194. * Returns a list of all the camera device ids.
  195. *
  196. * @param {Object} state - The state of the application.
  197. * @returns {string[]}
  198. */
  199. export function getVideoDeviceIds(state: Object) {
  200. return state['features/base/devices'].availableDevices.videoInput.map(({ deviceId }) => deviceId);
  201. }
  202. /**
  203. * Returns true if there are devices of a specific type or on native platform.
  204. *
  205. * @param {Object} state - The state of the application.
  206. * @param {string} type - The type of device: VideoOutput | audioOutput | audioInput.
  207. *
  208. * @returns {boolean}
  209. */
  210. export function hasAvailableDevices(state: Object, type: string) {
  211. if (state['features/base/devices'] === undefined) {
  212. return true;
  213. }
  214. return state['features/base/devices'].availableDevices[type].length > 0;
  215. }
  216. /**
  217. * Set device id of the audio output device which is currently in use.
  218. * Empty string stands for default device.
  219. *
  220. * @param {string} newId - New audio output device id.
  221. * @param {Function} dispatch - The Redux dispatch function.
  222. * @param {boolean} userSelection - Whether this is a user selection update.
  223. * @param {?string} newLabel - New audio output device label to store.
  224. * @returns {Promise}
  225. */
  226. export function setAudioOutputDeviceId(
  227. newId: string = 'default',
  228. dispatch: Function,
  229. userSelection: boolean = false,
  230. newLabel: ?string): Promise<*> {
  231. logger.debug(`setAudioOutputDevice: ${String(newLabel)}[${newId}]`);
  232. return JitsiMeetJS.mediaDevices.setAudioOutputDevice(newId)
  233. .then(() => {
  234. const newSettings = {
  235. audioOutputDeviceId: newId,
  236. userSelectedAudioOutputDeviceId: undefined,
  237. userSelectedAudioOutputDeviceLabel: undefined
  238. };
  239. if (userSelection) {
  240. newSettings.userSelectedAudioOutputDeviceId = newId;
  241. newSettings.userSelectedAudioOutputDeviceLabel = newLabel;
  242. } else {
  243. // a flow workaround, I needed to add 'userSelectedAudioOutputDeviceId: undefined'
  244. delete newSettings.userSelectedAudioOutputDeviceId;
  245. delete newSettings.userSelectedAudioOutputDeviceLabel;
  246. }
  247. return dispatch(updateSettings(newSettings));
  248. });
  249. }