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.web.ts 7.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. import { IReduxState } from '../../app/types';
  2. import { IStateful } from '../app/types';
  3. import { toState } from '../redux/functions';
  4. export * from './functions.any';
  5. /**
  6. * Returns the deviceId for the currently used camera.
  7. *
  8. * @param {Object} state - The state of the application.
  9. * @returns {void}
  10. */
  11. export function getCurrentCameraDeviceId(state: IReduxState) {
  12. return getDeviceIdByType(state, 'isVideoTrack');
  13. }
  14. /**
  15. * Returns the deviceId for the currently used microphone.
  16. *
  17. * @param {Object} state - The state of the application.
  18. * @returns {void}
  19. */
  20. export function getCurrentMicDeviceId(state: IReduxState) {
  21. return getDeviceIdByType(state, 'isAudioTrack');
  22. }
  23. /**
  24. * Returns the deviceId for the currently used speaker.
  25. *
  26. * @param {Object} state - The state of the application.
  27. * @returns {void}
  28. */
  29. export function getCurrentOutputDeviceId(state: IReduxState) {
  30. return state['features/base/settings'].audioOutputDeviceId;
  31. }
  32. /**
  33. * Returns the deviceId for the corresponding local track type.
  34. *
  35. * @param {Object} state - The state of the application.
  36. * @param {string} isType - Can be 'isVideoTrack' | 'isAudioTrack'.
  37. * @returns {string}
  38. */
  39. function getDeviceIdByType(state: IReduxState, isType: string) {
  40. const [ deviceId ] = state['features/base/tracks']
  41. .map(t => t.jitsiTrack)
  42. .filter(t => t?.isLocal() && t[isType as keyof typeof t]())
  43. .map(t => t.getDeviceId());
  44. return deviceId || '';
  45. }
  46. /**
  47. * Returns the saved display name.
  48. *
  49. * @param {Object} state - The state of the application.
  50. * @returns {string}
  51. */
  52. export function getDisplayName(state: IReduxState): string {
  53. return state['features/base/settings'].displayName || '';
  54. }
  55. /**
  56. * Searches known devices for a matching deviceId and fall back to matching on
  57. * label. Returns the stored preferred cameraDeviceId if a match is not found.
  58. *
  59. * @param {Object|Function} stateful - The redux state object or
  60. * {@code getState} function.
  61. * @returns {string}
  62. */
  63. export function getUserSelectedCameraDeviceId(stateful: IStateful) {
  64. const state = toState(stateful);
  65. const {
  66. userSelectedCameraDeviceId,
  67. userSelectedCameraDeviceLabel
  68. } = state['features/base/settings'];
  69. const { videoInput } = state['features/base/devices'].availableDevices;
  70. return _getUserSelectedDeviceId({
  71. availableDevices: videoInput,
  72. // Operating systems may append " #{number}" somewhere in the label so
  73. // find and strip that bit.
  74. matchRegex: /\s#\d*(?!.*\s#\d*)/,
  75. userSelectedDeviceId: userSelectedCameraDeviceId,
  76. userSelectedDeviceLabel: userSelectedCameraDeviceLabel,
  77. replacement: ''
  78. });
  79. }
  80. /**
  81. * Searches known devices for a matching deviceId and fall back to matching on
  82. * label. Returns the stored preferred micDeviceId if a match is not found.
  83. *
  84. * @param {Object|Function} stateful - The redux state object or
  85. * {@code getState} function.
  86. * @returns {string}
  87. */
  88. export function getUserSelectedMicDeviceId(stateful: IStateful) {
  89. const state = toState(stateful);
  90. const {
  91. userSelectedMicDeviceId,
  92. userSelectedMicDeviceLabel
  93. } = state['features/base/settings'];
  94. const { audioInput } = state['features/base/devices'].availableDevices;
  95. return _getUserSelectedDeviceId({
  96. availableDevices: audioInput,
  97. // Operating systems may append " ({number}-" somewhere in the label so
  98. // find and strip that bit.
  99. matchRegex: /\s\(\d*-\s(?!.*\s\(\d*-\s)/,
  100. userSelectedDeviceId: userSelectedMicDeviceId,
  101. userSelectedDeviceLabel: userSelectedMicDeviceLabel,
  102. replacement: ' ('
  103. });
  104. }
  105. /**
  106. * Searches known devices for a matching deviceId and fall back to matching on
  107. * label. Returns the stored preferred audioOutputDeviceId if a match is not found.
  108. *
  109. * @param {Object|Function} stateful - The redux state object or
  110. * {@code getState} function.
  111. * @returns {string}
  112. */
  113. export function getUserSelectedOutputDeviceId(stateful: IStateful) {
  114. const state = toState(stateful);
  115. const {
  116. userSelectedAudioOutputDeviceId,
  117. userSelectedAudioOutputDeviceLabel
  118. } = state['features/base/settings'];
  119. const { audioOutput } = state['features/base/devices'].availableDevices;
  120. return _getUserSelectedDeviceId({
  121. availableDevices: audioOutput,
  122. matchRegex: undefined,
  123. userSelectedDeviceId: userSelectedAudioOutputDeviceId,
  124. userSelectedDeviceLabel: userSelectedAudioOutputDeviceLabel,
  125. replacement: undefined
  126. });
  127. }
  128. /**
  129. * A helper function to abstract the logic for choosing which device ID to
  130. * use. Falls back to fuzzy matching on label if a device ID match is not found.
  131. *
  132. * @param {Object} options - The arguments used to match find the preferred
  133. * device ID from available devices.
  134. * @param {Array<string>} options.availableDevices - The array of currently
  135. * available devices to match against.
  136. * @param {Object} options.matchRegex - The regex to use to find strings
  137. * appended to the label by the operating system. The matches will be replaced
  138. * with options.replacement, with the intent of matching the same device that
  139. * might have a modified label.
  140. * @param {string} options.userSelectedDeviceId - The device ID the participant
  141. * prefers to use.
  142. * @param {string} options.userSelectedDeviceLabel - The label associated with the
  143. * device ID the participant prefers to use.
  144. * @param {string} options.replacement - The string to use with
  145. * options.matchRegex to remove identifies added to the label by the operating
  146. * system.
  147. * @private
  148. * @returns {string} The preferred device ID to use for media.
  149. */
  150. function _getUserSelectedDeviceId(options: {
  151. availableDevices: MediaDeviceInfo[] | undefined;
  152. matchRegex?: RegExp;
  153. replacement?: string;
  154. userSelectedDeviceId?: string;
  155. userSelectedDeviceLabel?: string;
  156. }) {
  157. const {
  158. availableDevices,
  159. matchRegex = '',
  160. userSelectedDeviceId,
  161. userSelectedDeviceLabel,
  162. replacement = ''
  163. } = options;
  164. if (userSelectedDeviceId) {
  165. const foundMatchingBasedonDeviceId = availableDevices?.find(
  166. candidate => candidate.deviceId === userSelectedDeviceId);
  167. // Prioritize matching the deviceId
  168. if (foundMatchingBasedonDeviceId) {
  169. return userSelectedDeviceId;
  170. }
  171. }
  172. // If there is no label at all, there is no need to fall back to checking
  173. // the label for a fuzzy match.
  174. if (!userSelectedDeviceLabel) {
  175. return;
  176. }
  177. const strippedDeviceLabel
  178. = matchRegex ? userSelectedDeviceLabel.replace(matchRegex, replacement)
  179. : userSelectedDeviceLabel;
  180. const foundMatchBasedOnLabel = availableDevices?.find(candidate => {
  181. const { label } = candidate;
  182. if (!label) {
  183. return false;
  184. } else if (strippedDeviceLabel === label) {
  185. return true;
  186. }
  187. const strippedCandidateLabel
  188. = label.replace(matchRegex, replacement);
  189. return strippedDeviceLabel === strippedCandidateLabel;
  190. });
  191. return foundMatchBasedOnLabel?.deviceId;
  192. }