選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

ActiveDeviceDetector.ts 4.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. import { getLogger } from '@jitsi/logger';
  2. import * as JitsiTrackEvents from '../../JitsiTrackEvents';
  3. import JitsiLocalTrack from '../RTC/JitsiLocalTrack';
  4. import RTC from '../RTC/RTC';
  5. import Statistics from '../statistics/statistics';
  6. export interface IActiveDeviceInfo {
  7. deviceId: string;
  8. deviceLabel: string;
  9. }
  10. const logger = getLogger('modules/detection/ActiveDeviceDetector');
  11. // If after 3000 ms the detector did not find any active devices consider that there aren't any usable ones available
  12. // i.e. audioLevel > 0.008
  13. const DETECTION_TIMEOUT = 3000;
  14. /**
  15. * Go through all audio devices on the system and return one that is active, i.e. has audio signal.
  16. *
  17. * @returns Promise<IActiveDeviceInfo> - Object containing information about the found device.
  18. */
  19. export default function getActiveAudioDevice(): Promise<IActiveDeviceInfo> {
  20. return new Promise(resolve => {
  21. RTC.enumerateDevices((devices: MediaDeviceInfo[]) => {
  22. const audioDevices = devices.filter(device => device.kind === 'audioinput');
  23. const devicePromiseArray: Promise<JitsiLocalTrack>[] = [];
  24. for (const micDevice of audioDevices) {
  25. const devicePromise = RTC.obtainAudioAndVideoPermissions({
  26. devices: [ 'audio' ],
  27. micDeviceId: micDevice.deviceId
  28. }).then((tracks: JitsiLocalTrack[]) => {
  29. // We expect a single device to be available when obtained from obtainAudioAndVideoPermissions
  30. // that's why only take p.value[0].
  31. const track = tracks[0];
  32. Statistics.startLocalStats(track, track.setAudioLevel.bind(track));
  33. return track;
  34. });
  35. devicePromiseArray.push(devicePromise);
  36. }
  37. Promise.allSettled(devicePromiseArray).then(outcomeArray => {
  38. const successfulPromises = outcomeArray.filter(p => p.status === 'fulfilled');
  39. const rejectedPromises = outcomeArray.filter(p => p.status === 'rejected');
  40. const availableDevices = successfulPromises.map(p => (p as PromiseFulfilledResult<JitsiLocalTrack>).value);
  41. const rejectReasons = rejectedPromises.map(p => (p as any).value);
  42. for (const reason of rejectReasons) {
  43. logger.error('Failed to acquire audio device with error: ', reason);
  44. }
  45. // Setup event handlers for monitored devices.
  46. for (const device of availableDevices) {
  47. device.on(JitsiTrackEvents.TRACK_AUDIO_LEVEL_CHANGED, audioLevel => {
  48. // This is a very naive approach but works, a more accurate one would be to use rnnoise in
  49. // order to limit the number of false positives. The 0.008 constant is due to how
  50. // LocalStatsCollector from lib-jitsi-meet publishes audio-levels, in this case 0.008 denotes //
  51. // no input.
  52. if (audioLevel > 0.008) {
  53. stopActiveDevices(availableDevices);
  54. resolve({
  55. deviceId: device.deviceId,
  56. deviceLabel: device.track.label
  57. });
  58. }
  59. });
  60. }
  61. // Cancel the detection in case no devices was found with audioLevel > 0 in the set timeout.
  62. setTimeout(() => {
  63. stopActiveDevices(availableDevices);
  64. resolve({
  65. deviceId: '',
  66. deviceLabel: ''
  67. });
  68. }, DETECTION_TIMEOUT);
  69. });
  70. });
  71. });
  72. }
  73. /**
  74. * Stop the streams of the provided JitsiLocalTracks.
  75. *
  76. * @param {Array<JitsiLocalTrack>} deviceList - Array of JitsiLocalTracks to stop.
  77. * @returns {void}
  78. */
  79. function stopActiveDevices(deviceList: JitsiLocalTrack[]): void {
  80. for (const device of deviceList) {
  81. device.stopStream();
  82. }
  83. }