123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295 |
- /* global APP, JitsiMeetJS */
-
- import {
- notifyCameraError,
- notifyMicError
- } from '../../react/features/base/devices/actions.web';
- import {
- flattenAvailableDevices,
- getAudioOutputDeviceId
- } from '../../react/features/base/devices/functions.web';
- import { updateSettings } from '../../react/features/base/settings/actions';
- import {
- getUserSelectedCameraDeviceId,
- getUserSelectedMicDeviceId,
- getUserSelectedOutputDeviceId
- } from '../../react/features/base/settings/functions';
-
- /**
- * Determines if currently selected audio output device should be changed after
- * list of available devices has been changed.
- * @param {MediaDeviceInfo[]} newDevices
- * @returns {string|undefined} - ID of new audio output device to use, undefined
- * if audio output device should not be changed.
- */
- function getNewAudioOutputDevice(newDevices) {
- if (!JitsiMeetJS.mediaDevices.isDeviceChangeAvailable('output')) {
- return;
- }
-
- const selectedAudioOutputDeviceId = getAudioOutputDeviceId();
- const availableAudioOutputDevices = newDevices.filter(
- d => d.kind === 'audiooutput');
-
- // Switch to 'default' audio output device if we don't have the selected one
- // available anymore.
- if (selectedAudioOutputDeviceId !== 'default'
- && !availableAudioOutputDevices.find(d =>
- d.deviceId === selectedAudioOutputDeviceId)) {
- return 'default';
- }
-
- const preferredAudioOutputDeviceId = getUserSelectedOutputDeviceId(APP.store.getState());
-
- // if the preferred one is not the selected and is available in the new devices
- // we want to use it as it was just added
- if (preferredAudioOutputDeviceId
- && preferredAudioOutputDeviceId !== selectedAudioOutputDeviceId
- && availableAudioOutputDevices.find(d => d.deviceId === preferredAudioOutputDeviceId)) {
- return preferredAudioOutputDeviceId;
- }
- }
-
- /**
- * Determines if currently selected audio input device should be changed after
- * list of available devices has been changed.
- * @param {MediaDeviceInfo[]} newDevices
- * @param {JitsiLocalTrack} localAudio
- * @param {boolean} newLabel
- * @returns {string|undefined} - ID of new microphone device to use, undefined
- * if audio input device should not be changed.
- */
- function getNewAudioInputDevice(newDevices, localAudio, newLabel) {
- const availableAudioInputDevices = newDevices.filter(
- d => d.kind === 'audioinput');
- const selectedAudioInputDeviceId = getUserSelectedMicDeviceId(APP.store.getState());
- const selectedAudioInputDevice = availableAudioInputDevices.find(
- d => d.deviceId === selectedAudioInputDeviceId);
- const localAudioDeviceId = localAudio?.getDeviceId();
- const localAudioDevice = availableAudioInputDevices.find(
- d => d.deviceId === localAudioDeviceId);
-
- // Here we handle case when no device was initially plugged, but
- // then it's connected OR new device was connected when previous
- // track has ended.
- if (!localAudio || localAudio.disposed || localAudio.isEnded()) {
- // If we have new audio device and permission to use it was granted
- // (label is not an empty string), then we will try to use the first
- // available device.
- if (selectedAudioInputDevice && selectedAudioInputDeviceId) {
- return selectedAudioInputDeviceId;
- } else if (availableAudioInputDevices.length
- && availableAudioInputDevices[0].label !== '') {
- return availableAudioInputDevices[0].deviceId;
- }
- } else if (selectedAudioInputDevice
- && selectedAudioInputDeviceId !== localAudioDeviceId) {
-
- if (newLabel) {
- // If a Firefox user with manual permission prompt chose a different
- // device from what we have stored as the preferred device we accept
- // and store that as the new preferred device.
- APP.store.dispatch(updateSettings({
- userSelectedMicDeviceId: localAudioDeviceId,
- userSelectedMicDeviceLabel: localAudioDevice.label
- }));
- } else {
- // And here we handle case when we already have some device working,
- // but we plug-in a "preferred" (previously selected in settings, stored
- // in local storage) device.
- return selectedAudioInputDeviceId;
- }
- }
- }
-
- /**
- * Determines if currently selected video input device should be changed after
- * list of available devices has been changed.
- * @param {MediaDeviceInfo[]} newDevices
- * @param {JitsiLocalTrack} localVideo
- * @param {boolean} newLabel
- * @returns {string|undefined} - ID of new camera device to use, undefined
- * if video input device should not be changed.
- */
- function getNewVideoInputDevice(newDevices, localVideo, newLabel) {
- const availableVideoInputDevices = newDevices.filter(
- d => d.kind === 'videoinput');
- const selectedVideoInputDeviceId = getUserSelectedCameraDeviceId(APP.store.getState());
- const selectedVideoInputDevice = availableVideoInputDevices.find(
- d => d.deviceId === selectedVideoInputDeviceId);
- const localVideoDeviceId = localVideo?.getDeviceId();
- const localVideoDevice = availableVideoInputDevices.find(
- d => d.deviceId === localVideoDeviceId);
-
- // Here we handle case when no video input device was initially plugged,
- // but then device is connected OR new device was connected when
- // previous track has ended.
- if (!localVideo || localVideo.disposed || localVideo.isEnded()) {
- // If we have new video device and permission to use it was granted
- // (label is not an empty string), then we will try to use the first
- // available device.
- if (selectedVideoInputDevice && selectedVideoInputDeviceId) {
- return selectedVideoInputDeviceId;
- } else if (availableVideoInputDevices.length
- && availableVideoInputDevices[0].label !== '') {
- return availableVideoInputDevices[0].deviceId;
- }
- } else if (selectedVideoInputDevice
- && selectedVideoInputDeviceId !== localVideoDeviceId) {
-
- if (newLabel) {
- // If a Firefox user with manual permission prompt chose a different
- // device from what we have stored as the preferred device we accept
- // and store that as the new preferred device.
- APP.store.dispatch(updateSettings({
- userSelectedCameraDeviceId: localVideoDeviceId,
- userSelectedCameraDeviceLabel: localVideoDevice.label
- }));
- } else {
- // And here we handle case when we already have some device working,
- // but we plug-in a "preferred" (previously selected in settings, stored
- // in local storage) device.
- return selectedVideoInputDeviceId;
- }
- }
- }
-
- export default {
- /**
- * Determines if currently selected media devices should be changed after
- * list of available devices has been changed.
- * @param {MediaDeviceInfo[]} newDevices
- * @param {JitsiLocalTrack} localVideo
- * @param {JitsiLocalTrack} localAudio
- * @returns {{
- * audioinput: (string|undefined),
- * videoinput: (string|undefined),
- * audiooutput: (string|undefined)
- * }}
- */
- getNewMediaDevicesAfterDeviceListChanged( // eslint-disable-line max-params
- newDevices,
- localVideo,
- localAudio,
- newLabels) {
- return {
- audioinput: getNewAudioInputDevice(newDevices, localAudio, newLabels),
- videoinput: getNewVideoInputDevice(newDevices, localVideo, newLabels),
- audiooutput: getNewAudioOutputDevice(newDevices)
- };
- },
-
- /**
- * Checks if the only difference between an object of known devices compared
- * to an array of new devices are only the labels for the devices.
- * @param {Object} oldDevices
- * @param {MediaDeviceInfo[]} newDevices
- * @returns {boolean}
- */
- newDeviceListAddedLabelsOnly(oldDevices, newDevices) {
- const oldDevicesFlattend = flattenAvailableDevices(oldDevices);
-
- if (oldDevicesFlattend.length !== newDevices.length) {
- return false;
- }
- oldDevicesFlattend.forEach(oldDevice => {
- if (oldDevice.label !== '') {
- return false;
- }
- const newDevice = newDevices.find(nd => nd.deviceId === oldDevice.deviceId);
-
- if (!newDevice || newDevice.label === '') {
- return false;
- }
- });
-
- return true;
- },
-
- /**
- * Tries to create new local tracks for new devices obtained after device
- * list changed. Shows error dialog in case of failures.
- * @param {function} createLocalTracks
- * @param {string} (cameraDeviceId)
- * @param {string} (micDeviceId)
- * @returns {Promise.<JitsiLocalTrack[]>}
- */
- createLocalTracksAfterDeviceListChanged(
- createLocalTracks,
- cameraDeviceId,
- micDeviceId) {
- let audioTrackError;
- let videoTrackError;
- const audioRequested = Boolean(micDeviceId);
- const videoRequested = Boolean(cameraDeviceId);
-
- if (audioRequested && videoRequested) {
- // First we try to create both audio and video tracks together.
- return (
- createLocalTracks({
- devices: [ 'audio', 'video' ],
- cameraDeviceId,
- micDeviceId
- })
-
- // If we fail to do this, try to create them separately.
- .catch(() => Promise.all([
- createAudioTrack(false).then(([ stream ]) => stream),
- createVideoTrack(false).then(([ stream ]) => stream)
- ]))
- .then(tracks => {
- if (audioTrackError) {
- APP.store.dispatch(notifyMicError(audioTrackError));
- }
-
- if (videoTrackError) {
- APP.store.dispatch(notifyCameraError(videoTrackError));
- }
-
- return tracks.filter(t => typeof t !== 'undefined');
- }));
- } else if (videoRequested && !audioRequested) {
- return createVideoTrack();
- } else if (audioRequested && !videoRequested) {
- return createAudioTrack();
- }
-
- return Promise.resolve([]);
-
- /**
- *
- */
- function createAudioTrack(showError = true) {
- return (
- createLocalTracks({
- devices: [ 'audio' ],
- cameraDeviceId: null,
- micDeviceId
- })
- .catch(err => {
- audioTrackError = err;
- showError && APP.store.dispatch(notifyMicError(err));
-
- return [];
- }));
- }
-
- /**
- *
- */
- function createVideoTrack(showError = true) {
- return (
- createLocalTracks({
- devices: [ 'video' ],
- cameraDeviceId,
- micDeviceId: null
- })
- .catch(err => {
- videoTrackError = err;
- showError && APP.store.dispatch(notifyCameraError(err));
-
- return [];
- }));
- }
- }
- };
|