OpenVidu / openvidu

OpenVidu Platform main repository
https://openvidu.io
Apache License 2.0
1.87k stars 463 forks source link

Safari issue: getDisplayMedia must be called from a user gesture handler. #773

Closed dennysq closed 1 year ago

dennysq commented 1 year ago

Describe the bug When I am trying to share the screen in Safari is throwing an error

Expected behavior I should be able to share the screen.

Wrong current behavior Error message: Publisher Initialization Failed. GENERIC_ERROR: InvalidAccessError: getDisplayMedia must be called from a user gesture handler. Publisher.ts: 387

OpenVidu tutorial where to replicate the error This is my code example: btnScreen.onclick = async function () {

     if(window.isSecureContext){
        console.log('isSecureContext equals true');
     }
            if (sharingScreenAllowed) {
                if (!screenEnabled) {
                    let screenShareCount = getRemoteStreamScreenCount();
                    if (screenShareCount >= 1) {
                        showCustomModalAlert(localizer('Room.ScreenSharingGoingOnTitle'), localizer('Room.ScreenSharingGoingOnSubtitle'));
                    } else {
                        onMeetScreenSessionObject = null;
                        OVScreen = new OpenVidu();
                        OVScreen.enableProdMode();
                        sessionScreen = OVScreen.initSession();
                        displayBusyIndicator();
                        mySessionId = document.getElementById('sessionId').value;
                        try{
                            const response= await getTokenAsync(mySessionId, SCREEN_VIDEO_TYPE);
                            let connection = response.body;    
                            try{
                                await sessionScreen.connect(connection.token);
                                console.log('screen connected!');              
                                onMeetScreenSessionObject = csvJSON(sessionScreen.connection.data);
                                hideWaitingRoom();
                                const emojiScreen = String.fromCodePoint('0x1F5A5');
                                if(window.isSecureContext){
                                    console.log('isSecureContext equals true');
                                 }
                                screenPublisher = OVScreen.initPublisher(undefined, {
                                    audioSource: null, // The source of audio. If undefined default microphone
                                    videoSource: 'screen', // The source of video. If undefined default webcam
                                    publishAudio: true, // Whether you want to start publishing with your audio unmuted or not
                                    publishVideo: true, // Whether you want to start publishing with your video enabled or not
                                    resolution: '640x480', // The resolution of your video old 640x480
                                    frameRate: 30,          // The frame rate of your video
                                    insertMode: 'APPEND',   // How the video is inserted in the target element MAIN_CONTAINER_ID
                                    mirror: false // Whether to mirror your local video or not

                                }, function (error) {
                                    if(window.isSecureContext){
                                        console.log('isSecureContext equals true');
                                     }                // Function to be executed when the method finishes
                                    if (error) {
                                        console.error(emojiScreen + ' Error while initializing publisher: ', error);
                                        handleError(error);
                                        screenEnabled = false;
                                    } else {
                                        console.log(emojiScreen + ' Publisher successfully initialized');
                                    }
                                });
                                screenPublisher.once('accessDenied', (event) => {
                                    if(window.isSecureContext){
                                        console.log('isSecureContext equals true');
                                     } 
                                    console.warn(emojiScreen + ' ScreenShare: Access Denied');
                                });
                                screenPublisher.once('accessAllowed', (event) => {
                                    console.log(emojiScreen + ' ScreenShare: accessAllowed');
                                    screenEnabled = true;
                                    updateScreenToggleTooltip(screenEnabled, true);
                                    sendEventMessageToRoom(SHARE_SCREEN_START_CMD);
                                    screenPublisher.stream.getMediaStream().getVideoTracks()[0].addEventListener('ended', () => {
                                        console.log(emojiScreen + ' User pressed the "Stop sharing" button method 2');
                                        screenEnabled = false;
                                        updateScreenToggleTooltip(screenEnabled, true);
                                        leaveScreenSession();
                                    });
                                    screenPublisher.stream.getMediaStream().addEventListener('inactive', () => {
                                        console.log(emojiScreen + ' User pressed the "Stop sharing" button method 1');
                                        screenEnabled = false;
                                        updateScreenToggleTooltip(screenEnabled, true);
                                        leaveScreenSession();
                                        // You can send a signal with Session.signal method to warn other participants
                                    });
                                    // --- 7) Specify the actions when events take place in our publisher ---
                                    screenPublisher.on('videoElementCreated', function (event) {
                                        console.log(emojiScreen + ' <=====================screen video element created===========================>');
                                    });
                                    screenPublisher.on('streamCreated', function (event) {
                                        console.log(emojiScreen + ' <=====================screen stream created==================================>');
                                    });
                                    screenPublisher.on('streamDestroyed', function (event) {
                                        sendEventMessageToRoom(SHARE_SCREEN_STOP_CMD);
                                        console.log(emojiScreen + ' <=====================screen stream destroyed==================================>');
                                    });
                                    console.log(emojiScreen + ' ScreenShare: before call publish');
                                    sessionScreen.publish(screenPublisher);
                                    console.log(emojiScreen + ' Screen Publisher successfully initialized');
                                });
                            }catch(error) {
                                console.error(error);
                                handleError(error);
                                onMeetScreenSessionObject = null;
                            }
                            finally {
                                hideBusyIndicator();
                            }

                        }catch(error){
                            console.error(error);
                            handleTokenError(error);
                            onMeetScreenSessionObject = null;
                        }finally{
                            hideBusyIndicator();
                        }
                    }
                } else {
                    leaveScreenSession();
                    updateScreenToggleTooltip(screenEnabled);
                }
            } else { console.log('sharing screen option has been disabled by the host'); }

    }

OpenVidu deployment info I am using Openvidu 2.23 browser client.

Client device info (if applicable) Safari 15.5 (17613.2.7.1.8) image

Screenshots

Additional context I've tried to implement everything function using await, but still is throwing the error.

pabloFuente commented 1 year ago

The error is self-explanatory: Safari won't allow you to access input devices without a proper user gesture. You need to call the function accessing the camera/mic/display from within a user-gesture handler (such as an element click). You have lots of resources online explaining the issue and the solution:

dennysq commented 1 year ago

Hi @pabloFuente I understand your point but is very strange as you can see the code attached is inside the onclick function.