createInitialLocalTracks()

in conference.js [484:651]


    createInitialLocalTracks(options = {}) {
        const errors = {};

        // Always get a handle on the audio input device so that we have statistics (such as "No audio input" or
        // "Are you trying to speak?" ) even if the user joins the conference muted.
        const initialDevices = config.disableInitialGUM ? [] : [ 'audio' ];
        const requestedAudio = !config.disableInitialGUM;
        let requestedVideo = false;

        if (!config.disableInitialGUM
                && !options.startWithVideoMuted
                && !options.startAudioOnly
                && !options.startScreenSharing) {
            initialDevices.push('video');
            requestedVideo = true;
        }

        if (!config.disableInitialGUM) {
            JitsiMeetJS.mediaDevices.addEventListener(
                JitsiMediaDevicesEvents.PERMISSION_PROMPT_IS_SHOWN,
                browserName =>
                    APP.store.dispatch(
                        mediaPermissionPromptVisibilityChanged(true, browserName))
            );
        }

        JitsiMeetJS.mediaDevices.addEventListener(
            JitsiMediaDevicesEvents.SLOW_GET_USER_MEDIA,
            () => APP.store.dispatch(toggleSlowGUMOverlay(true))
        );

        let tryCreateLocalTracks;

        // On Electron there is no permission prompt for granting permissions. That's why we don't need to
        // spend much time displaying the overlay screen. If GUM is not resolved within 15 seconds it will
        // probably never resolve.
        const timeout = browser.isElectron() ? 15000 : 60000;

        // FIXME is there any simpler way to rewrite this spaghetti below ?
        if (options.startScreenSharing) {
            tryCreateLocalTracks = this._createDesktopTrack()
                .then(([ desktopStream ]) => {
                    if (!requestedAudio) {
                        return [ desktopStream ];
                    }

                    return createLocalTracksF({
                        devices: [ 'audio' ],
                        timeout,
                        firePermissionPromptIsShownEvent: true,
                        fireSlowPromiseEvent: true
                    })
                        .then(([ audioStream ]) =>
                            [ desktopStream, audioStream ])
                        .catch(error => {
                            errors.audioOnlyError = error;

                            return [ desktopStream ];
                        });
                })
                .catch(error => {
                    logger.error('Failed to obtain desktop stream', error);
                    errors.screenSharingError = error;

                    return requestedAudio
                        ? createLocalTracksF({
                            devices: [ 'audio' ],
                            timeout,
                            firePermissionPromptIsShownEvent: true,
                            fireSlowPromiseEvent: true
                        })
                        : [];
                })
                .catch(error => {
                    errors.audioOnlyError = error;

                    return [];
                });
        } else if (!requestedAudio && !requestedVideo) {
            // Resolve with no tracks
            tryCreateLocalTracks = Promise.resolve([]);
        } else {
            tryCreateLocalTracks = createLocalTracksF({
                devices: initialDevices,
                timeout,
                firePermissionPromptIsShownEvent: true,
                fireSlowPromiseEvent: true
            })
                .catch(err => {
                    if (requestedAudio && requestedVideo) {

                        // Try audio only...
                        errors.audioAndVideoError = err;

                        if (err.name === JitsiTrackErrors.TIMEOUT && !browser.isElectron()) {
                            // In this case we expect that the permission prompt is still visible. There is no point of
                            // executing GUM with different source. Also at the time of writing the following
                            // inconsistency have been noticed in some browsers - if the permissions prompt is visible
                            // and another GUM is executed the prompt does not change its content but if the user
                            // clicks allow the user action isassociated with the latest GUM call.
                            errors.audioOnlyError = err;
                            errors.videoOnlyError = err;

                            return [];
                        }

                        return (
                            createLocalTracksF({
                                devices: [ 'audio' ],
                                timeout,
                                firePermissionPromptIsShownEvent: true,
                                fireSlowPromiseEvent: true
                            }));
                    } else if (requestedAudio && !requestedVideo) {
                        errors.audioOnlyError = err;

                        return [];
                    } else if (requestedVideo && !requestedAudio) {
                        errors.videoOnlyError = err;

                        return [];
                    }
                    logger.error('Should never happen');
                })
                .catch(err => {
                    // Log this just in case...
                    if (!requestedAudio) {
                        logger.error('The impossible just happened', err);
                    }
                    errors.audioOnlyError = err;

                    // Try video only...
                    return requestedVideo
                        ? createLocalTracksF({
                            devices: [ 'video' ],
                            firePermissionPromptIsShownEvent: true,
                            fireSlowPromiseEvent: true
                        })
                        : [];
                })
                .catch(err => {
                    // Log this just in case...
                    if (!requestedVideo) {
                        logger.error('The impossible just happened', err);
                    }
                    errors.videoOnlyError = err;

                    return [];
                });
        }

        // Hide the permissions prompt/overlay as soon as the tracks are
        // created. Don't wait for the connection to be made, since in some
        // cases, when auth is required, for instance, that won't happen until
        // the user inputs their credentials, but the dialog would be
        // overshadowed by the overlay.
        tryCreateLocalTracks.then(tracks => {
            APP.store.dispatch(toggleSlowGUMOverlay(false));
            APP.store.dispatch(mediaPermissionPromptVisibilityChanged(false));

            return tracks;
        });

        return {
            tryCreateLocalTracks,
            errors
        };
    },