var OV; // OpenVidu object to initialize a session var session; // Session object where the user will connect var publisher; // Publisher object which the user will publish var sessionId; // Unique identifier of the session var audioEnabled = true; // True if the audio track of publisher is active var videoEnabled = true; // True if the video track of publisher is active var numOfVideos = 0; // Keeps track of the number of videos that are being shown // Check if the URL already has a room window.addEventListener('load', function () { sessionId = window.location.hash.slice(1); // For 'https://myurl/#roomId', sessionId would be 'roomId' if (sessionId) { // The URL has a session id. Join the room right away console.log("Joining to room " + sessionId); showSessionHideJoin(); joinRoom(); } else { // The URL has not a session id. Show welcome page showJoinHideSession(); } }); // Disconnect participant on browser's window closed window.addEventListener('beforeunload', function () { if (session) session.disconnect(); }); function joinRoom() { if (!sessionId) { // If the user is joining to a new room sessionId = randomString(); } // --- 1) Get an OpenVidu object --- OV = new OpenVidu(); // --- 2) Init a session --- session = OV.initSession(); // --- 3) Specify the actions when events take place in the session --- // On every new Stream received... session.on('streamCreated', function (event) { // Subscribe to the Stream to receive it. HTML video will be appended to element with 'subscriber' id var subscriber = session.subscribe(event.stream, 'videos'); // When the new video is added to DOM, update the page layout to fit one more participant subscriber.on('videoElementCreated', function (event) { numOfVideos++; updateLayout(); }); }); // On every new Stream destroyed... session.on('streamDestroyed', function (event) { // Update the page layout numOfVideos--; updateLayout(); }); // --- 4) Connect to the session with a valid user token --- // 'getToken' method is simulating what your server-side should do. // 'token' parameter should be retrieved and returned by your own backend getToken(sessionId).then(token => { // Connect with the token session.connect(token) .then(() => { // --- 5) Set page layout for active call --- // Update the URL shown in the browser's navigation bar to show the session id var path = (location.pathname.slice(-1) == "/" ? location.pathname : location.pathname + "/"); window.history.pushState("", "", path + '#' + sessionId); // Auxiliary methods to show the session's view showSessionHideJoin(); initializeSessionView(); // --- 6) Get your own camera stream with the desired properties --- publisher = OV.initPublisher('publisher', { audioSource: undefined, // The source of audio. If undefined default audio input videoSource: undefined, // The source of video. If undefined default video input publishAudio: true, // Whether to start publishing with your audio unmuted or not publishVideo: true, // Whether to start publishing with your video enabled or not resolution: '640x480', // The resolution of your video frameRate: 30, // The frame rate of your video insertMode: 'APPEND', // How the video is inserted in target element 'video-container' mirror: true // Whether to mirror your local video or not }); // --- 7) Specify the actions when events take place in our publisher --- // When our HTML video has been added to DOM... publisher.on('videoElementCreated', function (event) { // When your own video is added to DOM, update the page layout to fit it numOfVideos++; updateLayout(); $(event.element).prop('muted', true); // Mute local video to avoid feedback }); // --- 8) Publish your stream --- session.publish(publisher); }) .catch(error => { console.log('There was an error connecting to the session:', error.code, error.message); }); }); } function leaveRoom() { // --- 9) Leave the session by calling 'disconnect' method over the Session object --- session.disconnect(); // Back to welcome page window.location.href = window.location.origin + window.location.pathname; } /* AUXILIARY MEHTODS */ function muteAudio() { audioEnabled = !audioEnabled; publisher.publishAudio(audioEnabled); if (!audioEnabled) { $('#mute-audio').removeClass('btn-primary'); $('#mute-audio').addClass('btn-default'); } else { $('#mute-audio').addClass('btn-primary'); $('#mute-audio').removeClass('btn-default'); } } function muteVideo() { videoEnabled = !videoEnabled; publisher.publishVideo(videoEnabled); if (!videoEnabled) { $('#mute-video').removeClass('btn-primary'); $('#mute-video').addClass('btn-default'); } else { $('#mute-video').addClass('btn-primary'); $('#mute-video').removeClass('btn-default'); } } function randomString() { return Math.random().toString(36).slice(2); } // 'Session' page function showSessionHideJoin() { $('#nav-join').hide(); $('#nav-session').show(); $('#join').hide(); $('#session').show(); $('footer').hide(); $('#main-container').removeClass('container'); } // 'Join' page function showJoinHideSession() { $('#nav-join').show(); $('#nav-session').hide(); $('#join').show(); $('#session').hide(); $('footer').show(); $('#main-container').addClass('container'); } // Prepare HTML dynamic elements (URL clipboard input) function initializeSessionView() { // Tooltips $('[data-toggle="tooltip"]').tooltip(); // Input clipboard $('#copy-input').val(window.location.href); $('#copy-button').bind('click', function () { var input = document.getElementById('copy-input'); input.focus(); input.setSelectionRange(0, input.value.length); try { var success = document.execCommand('copy'); if (success) { $('#copy-button').trigger('copied', ['Copied!']); } else { $('#copy-button').trigger('copied', ['Copy with Ctrl-c']); } } catch (err) { $('#copy-button').trigger('copied', ['Copy with Ctrl-c']); } }); // Handler for updating the tooltip message. $('#copy-button').bind('copied', function (event, message) { $(this).attr('title', message) .tooltip('fixTitle') .tooltip('show') .attr('title', "Copy to Clipboard") .tooltip('fixTitle'); }); } // Dynamic layout adjustemnt depending on number of videos function updateLayout() { console.warn('There are now ' + numOfVideos + ' videos'); var publisherDiv = $('#publisher'); var publisherVideo = $("#publisher video"); var subscriberVideos = $('#videos > video'); publisherDiv.removeClass(); publisherVideo.removeClass(); subscriberVideos.removeClass(); switch (numOfVideos) { case 1: publisherVideo.addClass('video1'); break; case 2: publisherDiv.addClass('video2'); subscriberVideos.addClass('video2'); break; case 3: publisherDiv.addClass('video3'); subscriberVideos.addClass('video3'); break; case 4: publisherDiv.addClass('video4'); publisherVideo.addClass('video4'); subscriberVideos.addClass('video4'); break; default: publisherDiv.addClass('videoMore'); publisherVideo.addClass('videoMore'); subscriberVideos.addClass('videoMore'); break; } } /** * -------------------------- * SERVER-SIDE RESPONSIBILITY * -------------------------- * These methods retrieve the mandatory user token from OpenVidu Server. * This behavior MUST BE IN YOUR SERVER-SIDE IN PRODUCTION (by using * the API REST, openvidu-java-client or openvidu-node-client): * 1) Initialize a session in OpenVidu Server (POST /api/sessions) * 2) Generate a token in OpenVidu Server (POST /api/tokens) * 3) The token must be consumed in Session.connect() method */ var OPENVIDU_SERVER_URL = "https://" + location.hostname + ":4443"; var OPENVIDU_SERVER_SECRET = "MY_SECRET"; function getToken(mySessionId) { return createSession(mySessionId).then(sId => createToken(sId)); } function createSession(sId) { // See https://openvidu.io/docs/reference-docs/REST-API/#post-apisessions return new Promise((resolve, reject) => { $.ajax({ type: "POST", url: OPENVIDU_SERVER_URL + "/api/sessions", data: JSON.stringify({ customSessionId: sId }), headers: { "Authorization": "Basic " + btoa("OPENVIDUAPP:" + OPENVIDU_SERVER_SECRET), "Content-Type": "application/json" }, success: response => resolve(response.id), error: (error) => { if (error.status === 409) { resolve(sId); } else { console.warn('No connection to OpenVidu Server. This may be a certificate error at ' + OPENVIDU_SERVER_URL); if (window.confirm('No connection to OpenVidu Server. This may be a certificate error at \"' + OPENVIDU_SERVER_URL + '\"\n\nClick OK to navigate and accept it. ' + 'If no certificate warning is shown, then check that your OpenVidu Server is up and running at "' + OPENVIDU_SERVER_URL + '"')) { location.assign(OPENVIDU_SERVER_URL + '/accept-certificate'); } } } }); }); } function createToken(sId) { // See https://openvidu.io/docs/reference-docs/REST-API/#post-apitokens return new Promise((resolve, reject) => { $.ajax({ type: "POST", url: OPENVIDU_SERVER_URL + "/api/tokens", data: JSON.stringify({ session: sId }), headers: { "Authorization": "Basic " + btoa("OPENVIDUAPP:" + OPENVIDU_SERVER_SECRET), "Content-Type": "application/json" }, success: response => resolve(response.token), error: error => reject(error) }); }); }