Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

JitsiMediaDevices.js 7.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. import EventEmitter from 'events';
  2. import * as MediaType from './service/RTC/MediaType';
  3. import browser from './modules/browser';
  4. import RTC from './modules/RTC/RTC';
  5. import RTCEvents from './service/RTC/RTCEvents';
  6. import Statistics from './modules/statistics/statistics';
  7. import * as JitsiMediaDevicesEvents from './JitsiMediaDevicesEvents';
  8. /**
  9. * Media devices utilities for Jitsi.
  10. */
  11. class JitsiMediaDevices {
  12. /**
  13. * Initializes a {@code JitsiMediaDevices} object. There will be a single
  14. * instance of this class.
  15. */
  16. constructor() {
  17. this._eventEmitter = new EventEmitter();
  18. this._grantedPermissions = {};
  19. RTC.addListener(
  20. RTCEvents.DEVICE_LIST_CHANGED,
  21. devices =>
  22. this._eventEmitter.emit(
  23. JitsiMediaDevicesEvents.DEVICE_LIST_CHANGED,
  24. devices));
  25. RTC.addListener(
  26. RTCEvents.DEVICE_LIST_AVAILABLE,
  27. devices =>
  28. this._logOutputDevice(
  29. this.getAudioOutputDevice(),
  30. devices));
  31. RTC.addListener(
  32. RTCEvents.GRANTED_PERMISSIONS,
  33. grantedPermissions =>
  34. this._handleGrantedPermissions(grantedPermissions));
  35. // Test if the W3C Permissions API is implemented and the 'camera' and
  36. // 'microphone' permissions are implemented. (Testing for at least one
  37. // of them seems sufficient).
  38. this._permissionsApiSupported = new Promise(resolve => {
  39. if (!navigator.permissions) {
  40. resolve(false);
  41. return;
  42. }
  43. navigator.permissions.query({ name: 'camera ' })
  44. .then(() => resolve(true), () => resolve(false));
  45. });
  46. }
  47. /**
  48. * Updated the local granted permissions cache. A permissions might be
  49. * granted, denied, or undefined. This is represented by having its media
  50. * type key set to {@code true} or {@code false} respectively.
  51. *
  52. * @param {Object} grantedPermissions - Array with the permissions
  53. * which were granted.
  54. */
  55. _handleGrantedPermissions(grantedPermissions) {
  56. this._grantedPermissions = {
  57. ...this._grantedPermissions,
  58. ...grantedPermissions
  59. };
  60. }
  61. /**
  62. * Gathers data and sends it to statistics.
  63. * @param deviceID the device id to log
  64. * @param devices list of devices
  65. */
  66. _logOutputDevice(deviceID, devices) {
  67. const device
  68. = devices.find(
  69. d => d.kind === 'audiooutput' && d.deviceId === deviceID);
  70. if (device) {
  71. Statistics.sendActiveDeviceListEvent(
  72. RTC.getEventDataForActiveDevice(device));
  73. }
  74. }
  75. /**
  76. * Executes callback with list of media devices connected.
  77. * @param {function} callback
  78. */
  79. enumerateDevices(callback) {
  80. RTC.enumerateDevices(callback);
  81. }
  82. /**
  83. * Checks if its possible to enumerate available cameras/micropones.
  84. * @returns {Promise<boolean>} a Promise which will be resolved only once
  85. * the WebRTC stack is ready, either with true if the device listing is
  86. * available available or with false otherwise.
  87. */
  88. isDeviceListAvailable() {
  89. return RTC.isDeviceListAvailable();
  90. }
  91. /**
  92. * Returns true if changing the input (camera / microphone) or output
  93. * (audio) device is supported and false if not.
  94. * @param {string} [deviceType] - type of device to change. Default is
  95. * undefined or 'input', 'output' - for audio output device change.
  96. * @returns {boolean} true if available, false otherwise.
  97. */
  98. isDeviceChangeAvailable(deviceType) {
  99. return RTC.isDeviceChangeAvailable(deviceType);
  100. }
  101. /**
  102. * Checks if the permission for the given device was granted.
  103. *
  104. * @param {'audio'|'video'} [type] - type of devices to check,
  105. * undefined stands for both 'audio' and 'video' together
  106. * @returns {Promise<boolean>}
  107. */
  108. isDevicePermissionGranted(type) {
  109. return new Promise(resolve => {
  110. // Shortcut: first check if we already know the permission was
  111. // granted.
  112. if (type in this._grantedPermissions) {
  113. resolve(this._grantedPermissions[type]);
  114. return;
  115. }
  116. // Check using the Permissions API.
  117. this._permissionsApiSupported.then(supported => {
  118. if (!supported) {
  119. resolve(false);
  120. return;
  121. }
  122. const promises = [];
  123. switch (type) {
  124. case MediaType.VIDEO:
  125. promises.push(
  126. navigator.permissions.query({ name: 'camera' }));
  127. break;
  128. case MediaType.AUDIO:
  129. promises.push(
  130. navigator.permissions.query({ name: 'microphone' }));
  131. break;
  132. default:
  133. promises.push(
  134. navigator.permissions.query({ name: 'camera' }));
  135. promises.push(
  136. navigator.permissions.query({ name: 'microphone' }));
  137. }
  138. Promise.all(promises).then(
  139. r => resolve(r.every(Boolean)),
  140. () => resolve(false)
  141. );
  142. });
  143. });
  144. }
  145. /**
  146. * Returns true if it is possible to be simultaneously capturing audio
  147. * from more than one device.
  148. *
  149. * @returns {boolean}
  150. */
  151. isMultipleAudioInputSupported() {
  152. return !browser.isFirefox();
  153. }
  154. /**
  155. * Returns currently used audio output device id, 'default' stands
  156. * for default device
  157. * @returns {string}
  158. */
  159. getAudioOutputDevice() {
  160. return RTC.getAudioOutputDevice();
  161. }
  162. /**
  163. * Sets current audio output device.
  164. * @param {string} deviceId - id of 'audiooutput' device from
  165. * navigator.mediaDevices.enumerateDevices(), 'default' is for
  166. * default device
  167. * @returns {Promise} - resolves when audio output is changed, is rejected
  168. * otherwise
  169. */
  170. setAudioOutputDevice(deviceId) {
  171. const availableDevices = RTC.getCurrentlyAvailableMediaDevices();
  172. if (availableDevices && availableDevices.length > 0) {
  173. // if we have devices info report device to stats
  174. // normally this will not happen on startup as this method is called
  175. // too early. This will happen only on user selection of new device
  176. this._logOutputDevice(
  177. deviceId, RTC.getCurrentlyAvailableMediaDevices());
  178. }
  179. return RTC.setAudioOutputDevice(deviceId);
  180. }
  181. /**
  182. * Adds an event handler.
  183. * @param {string} event - event name
  184. * @param {function} handler - event handler
  185. */
  186. addEventListener(event, handler) {
  187. this._eventEmitter.addListener(event, handler);
  188. }
  189. /**
  190. * Removes event handler.
  191. * @param {string} event - event name
  192. * @param {function} handler - event handler
  193. */
  194. removeEventListener(event, handler) {
  195. this._eventEmitter.removeListener(event, handler);
  196. }
  197. /**
  198. * Emits an event.
  199. * @param {string} event - event name
  200. */
  201. emitEvent(event, ...args) {
  202. this._eventEmitter.emit(event, ...args);
  203. }
  204. /**
  205. * Returns whether or not the current browser can support capturing video,
  206. * be it camera or desktop, and displaying received video.
  207. *
  208. * @returns {boolean}
  209. */
  210. supportsVideo() {
  211. // Defer to the browser capabilities to allow exposure of the api to the
  212. // consumer but prevent other files from having to import
  213. // JitsiMediaDevices.
  214. return browser.supportsVideo();
  215. }
  216. }
  217. export default new JitsiMediaDevices();