_onDeviceListChanged()

in conference.js [2678:2846]


    _onDeviceListChanged(devices) {
        const oldDevices = APP.store.getState()['features/base/devices'].availableDevices;
        const localAudio = getLocalJitsiAudioTrack(APP.store.getState());
        const localVideo = getLocalJitsiVideoTrack(APP.store.getState());

        APP.store.dispatch(updateDeviceList(devices));

        // Firefox users can choose their preferred device in the gUM prompt. In that case
        // we should respect that and not attempt to switch to the preferred device from
        // our settings.
        const newLabelsOnly = mediaDeviceHelper.newDeviceListAddedLabelsOnly(oldDevices, devices);
        const newDevices
            = mediaDeviceHelper.getNewMediaDevicesAfterDeviceListChanged(
                devices,
                this.isSharingScreen,
                localVideo,
                localAudio,
                newLabelsOnly);
        const promises = [];
        const audioWasMuted = this.isLocalAudioMuted();
        const videoWasMuted = this.isLocalVideoMuted();
        const requestedInput = {
            audio: Boolean(newDevices.audioinput),
            video: Boolean(newDevices.videoinput)
        };

        if (typeof newDevices.audiooutput !== 'undefined') {
            const { dispatch } = APP.store;
            const setAudioOutputPromise
                = setAudioOutputDeviceId(newDevices.audiooutput, dispatch)
                    .catch(); // Just ignore any errors in catch block.


            promises.push(setAudioOutputPromise);
        }

        // Handles the use case when the default device is changed (we are always stopping the streams because it's
        // simpler):
        // If the default device is changed we need to first stop the local streams and then call GUM. Otherwise GUM
        // will return a stream using the old default device.
        if (requestedInput.audio && localAudio) {
            localAudio.stopStream();
        }

        if (requestedInput.video && localVideo) {
            localVideo.stopStream();
        }

        // Let's handle unknown/non-preferred devices
        const newAvailDevices
            = APP.store.getState()['features/base/devices'].availableDevices;
        let newAudioDevices = [];
        let oldAudioDevices = [];

        if (typeof newDevices.audiooutput === 'undefined') {
            newAudioDevices = newAvailDevices.audioOutput;
            oldAudioDevices = oldDevices.audioOutput;
        }

        if (!requestedInput.audio) {
            newAudioDevices = newAudioDevices.concat(newAvailDevices.audioInput);
            oldAudioDevices = oldAudioDevices.concat(oldDevices.audioInput);
        }

        // check for audio
        if (newAudioDevices.length > 0) {
            APP.store.dispatch(
                checkAndNotifyForNewDevice(newAudioDevices, oldAudioDevices));
        }

        // check for video
        if (!requestedInput.video) {
            APP.store.dispatch(
                checkAndNotifyForNewDevice(newAvailDevices.videoInput, oldDevices.videoInput));
        }

        // When the 'default' mic needs to be selected, we need to
        // pass the real device id to gUM instead of 'default' in order
        // to get the correct MediaStreamTrack from chrome because of the
        // following bug.
        // https://bugs.chromium.org/p/chromium/issues/detail?id=997689
        const hasDefaultMicChanged = newDevices.audioinput === 'default';

        // This is the case when the local video is muted and a preferred device is connected.
        if (requestedInput.video && this.isLocalVideoMuted()) {
            // We want to avoid creating a new video track in order to prevent turning on the camera.
            requestedInput.video = false;
            APP.store.dispatch(updateSettings({ // Update the current selected camera for the device selection dialog.
                cameraDeviceId: newDevices.videoinput
            }));
            delete newDevices.videoinput;

            // Removing the current video track in order to force the unmute to select the preferred device.
            logger.debug('_onDeviceListChanged: Removing the current video track.');
            this.useVideoStream(null);

        }

        promises.push(
            mediaDeviceHelper.createLocalTracksAfterDeviceListChanged(
                    createLocalTracksF,
                    newDevices.videoinput,
                    hasDefaultMicChanged
                        ? getDefaultDeviceId(APP.store.getState(), 'audioInput')
                        : newDevices.audioinput)
                .then(tracks => {
                    // If audio or video muted before, or we unplugged current
                    // device and selected new one, then mute new track.
                    const muteSyncPromises = tracks.map(track => {
                        if ((track.isVideoTrack() && videoWasMuted)
                            || (track.isAudioTrack() && audioWasMuted)) {
                            return track.mute();
                        }

                        return Promise.resolve();
                    });

                    return Promise.all(muteSyncPromises)
                        .then(() =>
                            Promise.all(Object.keys(requestedInput).map(mediaType => {
                                if (requestedInput[mediaType]) {
                                    const useStream
                                        = mediaType === 'audio'
                                            ? this.useAudioStream.bind(this)
                                            : this.useVideoStream.bind(this);
                                    const track = tracks.find(t => t.getType() === mediaType) || null;

                                    // Use the new stream or null if we failed to obtain it.
                                    return useStream(track)
                                        .then(() => {
                                            if (track?.isAudioTrack() && hasDefaultMicChanged) {
                                                // workaround for the default device to be shown as selected in the
                                                // settings even when the real device id was passed to gUM because of
                                                // the above mentioned chrome bug.
                                                track._realDeviceId = track.deviceId = 'default';
                                            }
                                            mediaType === 'audio'
                                                ? this._updateAudioDeviceId()
                                                : this._updateVideoDeviceId();
                                        });
                                }

                                return Promise.resolve();
                            })));
                })
                .then(() => {
                    // Log and sync known mute state.
                    if (audioWasMuted) {
                        sendAnalytics(createTrackMutedEvent(
                            'audio',
                            'device list changed'));
                        logger.log('Audio mute: device list changed');
                        muteLocalAudio(true);
                    }

                    if (!this.isSharingScreen && videoWasMuted) {
                        sendAnalytics(createTrackMutedEvent(
                            'video',
                            'device list changed'));
                        logger.log('Video mute: device list changed');
                        muteLocalVideo(true);
                    }
                }));

        return Promise.all(promises)
            .then(() => {
                APP.UI.onAvailableDevicesChanged(devices);
            });
    },