Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

mediaDeviceHelper.js 8.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. /* global APP, JitsiMeetJS */
  2. import { getAudioOutputDeviceId } from '../../react/features/base/devices';
  3. /**
  4. * Determines if currently selected audio output device should be changed after
  5. * list of available devices has been changed.
  6. * @param {MediaDeviceInfo[]} newDevices
  7. * @returns {string|undefined} - ID of new audio output device to use, undefined
  8. * if audio output device should not be changed.
  9. */
  10. function getNewAudioOutputDevice(newDevices) {
  11. if (!JitsiMeetJS.mediaDevices.isDeviceChangeAvailable('output')) {
  12. return;
  13. }
  14. const selectedAudioOutputDeviceId = getAudioOutputDeviceId();
  15. const availableAudioOutputDevices = newDevices.filter(
  16. d => d.kind === 'audiooutput');
  17. // Switch to 'default' audio output device if we don't have the selected one
  18. // available anymore.
  19. if (selectedAudioOutputDeviceId !== 'default'
  20. && !availableAudioOutputDevices.find(d =>
  21. d.deviceId === selectedAudioOutputDeviceId)) {
  22. return 'default';
  23. }
  24. }
  25. /**
  26. * Determines if currently selected audio input device should be changed after
  27. * list of available devices has been changed.
  28. * @param {MediaDeviceInfo[]} newDevices
  29. * @param {JitsiLocalTrack} localAudio
  30. * @returns {string|undefined} - ID of new microphone device to use, undefined
  31. * if audio input device should not be changed.
  32. */
  33. function getNewAudioInputDevice(newDevices, localAudio) {
  34. const availableAudioInputDevices = newDevices.filter(
  35. d => d.kind === 'audioinput');
  36. const settings = APP.store.getState()['features/base/settings'];
  37. const selectedAudioInputDeviceId = settings.userSelectedMicDeviceId;
  38. const selectedAudioInputDevice = availableAudioInputDevices.find(
  39. d => d.deviceId === selectedAudioInputDeviceId);
  40. // Here we handle case when no device was initially plugged, but
  41. // then it's connected OR new device was connected when previous
  42. // track has ended.
  43. if (!localAudio || localAudio.disposed || localAudio.isEnded()) {
  44. // If we have new audio device and permission to use it was granted
  45. // (label is not an empty string), then we will try to use the first
  46. // available device.
  47. if (selectedAudioInputDevice && selectedAudioInputDeviceId) {
  48. return selectedAudioInputDeviceId;
  49. } else if (availableAudioInputDevices.length
  50. && availableAudioInputDevices[0].label !== '') {
  51. return availableAudioInputDevices[0].deviceId;
  52. }
  53. } else if (selectedAudioInputDevice
  54. && selectedAudioInputDeviceId !== localAudio.getDeviceId()) {
  55. // And here we handle case when we already have some device working,
  56. // but we plug-in a "preferred" (previously selected in settings, stored
  57. // in local storage) device.
  58. return selectedAudioInputDeviceId;
  59. }
  60. }
  61. /**
  62. * Determines if currently selected video input device should be changed after
  63. * list of available devices has been changed.
  64. * @param {MediaDeviceInfo[]} newDevices
  65. * @param {JitsiLocalTrack} localVideo
  66. * @returns {string|undefined} - ID of new camera device to use, undefined
  67. * if video input device should not be changed.
  68. */
  69. function getNewVideoInputDevice(newDevices, localVideo) {
  70. const availableVideoInputDevices = newDevices.filter(
  71. d => d.kind === 'videoinput');
  72. const settings = APP.store.getState()['features/base/settings'];
  73. const selectedVideoInputDeviceId = settings.userSelectedCameraDeviceId;
  74. const selectedVideoInputDevice = availableVideoInputDevices.find(
  75. d => d.deviceId === selectedVideoInputDeviceId);
  76. // Here we handle case when no video input device was initially plugged,
  77. // but then device is connected OR new device was connected when
  78. // previous track has ended.
  79. if (!localVideo || localVideo.disposed || localVideo.isEnded()) {
  80. // If we have new video device and permission to use it was granted
  81. // (label is not an empty string), then we will try to use the first
  82. // available device.
  83. if (selectedVideoInputDevice && selectedVideoInputDeviceId) {
  84. return selectedVideoInputDeviceId;
  85. } else if (availableVideoInputDevices.length
  86. && availableVideoInputDevices[0].label !== '') {
  87. return availableVideoInputDevices[0].deviceId;
  88. }
  89. } else if (selectedVideoInputDevice
  90. && selectedVideoInputDeviceId !== localVideo.getDeviceId()) {
  91. // And here we handle case when we already have some device working,
  92. // but we plug-in a "preferred" (previously selected in settings, stored
  93. // in local storage) device.
  94. return selectedVideoInputDeviceId;
  95. }
  96. }
  97. export default {
  98. /**
  99. * Determines if currently selected media devices should be changed after
  100. * list of available devices has been changed.
  101. * @param {MediaDeviceInfo[]} newDevices
  102. * @param {boolean} isSharingScreen
  103. * @param {JitsiLocalTrack} localVideo
  104. * @param {JitsiLocalTrack} localAudio
  105. * @returns {{
  106. * audioinput: (string|undefined),
  107. * videoinput: (string|undefined),
  108. * audiooutput: (string|undefined)
  109. * }}
  110. */
  111. getNewMediaDevicesAfterDeviceListChanged( // eslint-disable-line max-params
  112. newDevices,
  113. isSharingScreen,
  114. localVideo,
  115. localAudio) {
  116. return {
  117. audioinput: getNewAudioInputDevice(newDevices, localAudio),
  118. videoinput: isSharingScreen ? undefined : getNewVideoInputDevice(newDevices, localVideo),
  119. audiooutput: getNewAudioOutputDevice(newDevices)
  120. };
  121. },
  122. /**
  123. * Tries to create new local tracks for new devices obtained after device
  124. * list changed. Shows error dialog in case of failures.
  125. * @param {function} createLocalTracks
  126. * @param {string} (cameraDeviceId)
  127. * @param {string} (micDeviceId)
  128. * @returns {Promise.<JitsiLocalTrack[]>}
  129. */
  130. createLocalTracksAfterDeviceListChanged(
  131. createLocalTracks,
  132. cameraDeviceId,
  133. micDeviceId) {
  134. let audioTrackError;
  135. let videoTrackError;
  136. const audioRequested = Boolean(micDeviceId);
  137. const videoRequested = Boolean(cameraDeviceId);
  138. if (audioRequested && videoRequested) {
  139. // First we try to create both audio and video tracks together.
  140. return (
  141. createLocalTracks({
  142. devices: [ 'audio', 'video' ],
  143. cameraDeviceId,
  144. micDeviceId
  145. })
  146. // If we fail to do this, try to create them separately.
  147. .catch(() => Promise.all([
  148. createAudioTrack(false).then(([ stream ]) => stream),
  149. createVideoTrack(false).then(([ stream ]) => stream)
  150. ]))
  151. .then(tracks => {
  152. if (audioTrackError) {
  153. APP.UI.showMicErrorNotification(audioTrackError);
  154. }
  155. if (videoTrackError) {
  156. APP.UI.showCameraErrorNotification(videoTrackError);
  157. }
  158. return tracks.filter(t => typeof t !== 'undefined');
  159. }));
  160. } else if (videoRequested && !audioRequested) {
  161. return createVideoTrack();
  162. } else if (audioRequested && !videoRequested) {
  163. return createAudioTrack();
  164. }
  165. return Promise.resolve([]);
  166. /**
  167. *
  168. */
  169. function createAudioTrack(showError = true) {
  170. return (
  171. createLocalTracks({
  172. devices: [ 'audio' ],
  173. cameraDeviceId: null,
  174. micDeviceId
  175. })
  176. .catch(err => {
  177. audioTrackError = err;
  178. showError && APP.UI.showMicErrorNotification(err);
  179. return [];
  180. }));
  181. }
  182. /**
  183. *
  184. */
  185. function createVideoTrack(showError = true) {
  186. return (
  187. createLocalTracks({
  188. devices: [ 'video' ],
  189. cameraDeviceId,
  190. micDeviceId: null
  191. })
  192. .catch(err => {
  193. videoTrackError = err;
  194. showError && APP.UI.showCameraErrorNotification(err);
  195. return [];
  196. }));
  197. }
  198. }
  199. };