// We import the settings.js file to know which address we should contact // to talk to Janus, and optionally which STUN/TURN servers should be // used as well. Specifically, that file defines the "server" and // "iceServers" properties we'll pass when creating the Janus session. /* global iceServers:readonly, Janus:readonly, server:readonly */ var janus = null; var screentest = null; var opaqueId = "screensharingtest-" + Janus.randomString(12); var myusername = null; var myid = null; var capture = null; var role = null; var room = null; var source = null; var localTracks = {}, localVideos = 0, remoteTracks = {}, remoteVideos = 0; $(document).ready(function () { // Initialize the library (all console debuggers enabled) Janus.init({ debug: "all", callback: function () { // Use a button to start the demo $("#start").one("click", function () { $(this).attr("disabled", true).unbind("click"); // Make sure the browser supports WebRTC if (!Janus.isWebrtcSupported()) { bootbox.alert("No WebRTC support... "); return; } // Create session janus = new Janus({ server: server, iceServers: iceServers, // Should the Janus API require authentication, you can specify either the API secret or user token here too // token: "mytoken", // or // apisecret: "serversecret", success: function () { // Attach to VideoRoom plugin janus.attach({ plugin: "janus.plugin.videoroom", opaqueId: opaqueId, success: function (pluginHandle) { $("#details").remove(); screentest = pluginHandle; Janus.log( "Plugin attached! (" + screentest.getPlugin() + ", id=" + screentest.getId() + ")" ); // Prepare the username registration $("#screenmenu").removeClass("hide"); $("#createnow").removeClass("hide"); $("#create").click(preShareScreen); $("#joinnow").removeClass("hide"); $("#join").click(joinScreen); $("#desc").focus(); $("#start") .removeAttr("disabled") .html("Stop") .click(function () { $(this).attr("disabled", true); janus.destroy(); }); }, error: function (error) { Janus.error(" -- Error attaching plugin...", error); bootbox.alert("Error attaching plugin... " + error); }, consentDialog: function (on) { Janus.debug( "Consent dialog should be " + (on ? "on" : "off") + " now" ); if (on) { // Darken screen $.blockUI({ message: "", baseZ: 3001, css: { border: "none", padding: "15px", backgroundColor: "transparent", color: "#aaa", }, }); } else { // Restore screen $.unblockUI(); } }, iceState: function (state) { Janus.log("ICE state changed to " + state); }, mediaState: function (medium, on, mid) { Janus.log( "Janus " + (on ? "started" : "stopped") + " receiving our " + medium + " (mid=" + mid + ")" ); }, webrtcState: function (on) { Janus.log( "Janus says our WebRTC PeerConnection is " + (on ? "up" : "down") + " now" ); $("#screencapture").parent().parent().unblock(); if (on) { bootbox.alert( "Your screen sharing session just started: pass the " + room + " session identifier to those who want to attend." ); } else { bootbox.alert( "Your screen sharing session just stopped.", function () { janus.destroy(); window.location.reload(); } ); } }, slowLink: function (uplink, lost, mid) { Janus.warn( "Janus reports problems " + (uplink ? "sending" : "receiving") + " packets on mid " + mid + " (" + lost + " lost packets)" ); }, onmessage: function (msg, jsep) { Janus.debug(" ::: Got a message (publisher) :::", msg); let event = msg["videoroom"]; Janus.debug("Event: " + event); if (event) { if (event === "joined") { myid = msg["id"]; $("#session").html(room); $("#title").html(escapeXmlTags(msg["description"])); Janus.log( "Successfully joined room " + msg["room"] + " with ID " + myid ); if (role === "publisher") { // This is our session, publish our stream Janus.debug( "Negotiating WebRTC stream for our screen (capture " + capture + ")" ); // Safari expects a user gesture to share the screen: see issue #2455 if ( Janus.webRTCAdapter.browserDetails.browser === "safari" ) { bootbox.alert( "Safari requires a user gesture before the screen can be shared: close this dialog to do that. See issue #2455 for more details", function () { screentest.createOffer({ // We want to capture the screen and audio, but sendonly tracks: [ { type: "audio", capture: true, recv: false }, { type: "screen", capture: true, recv: false }, ], success: function (jsep) { Janus.debug("Got publisher SDP!", jsep); let publish = { request: "configure", audio: true, video: true, }; screentest.send({ message: publish, jsep: jsep, }); }, error: function (error) { Janus.error("WebRTC error:", error); bootbox.alert( "WebRTC error... " + error.message ); }, }); } ); } else { // Other browsers should be fine, we try to call getDisplayMedia directly screentest.createOffer({ // We want sendonly audio and screensharing tracks: [ { type: "audio", capture: true, recv: false }, { type: "screen", capture: true, recv: false }, ], success: function (jsep) { Janus.debug("Got publisher SDP!", jsep); let publish = { request: "configure", audio: true, video: true, }; screentest.send({ message: publish, jsep: jsep }); }, error: function (error) { Janus.error("WebRTC error:", error); bootbox.alert("WebRTC error... " + error.message); }, }); } } else { // We're just watching a session, any feed to attach to? if (msg["publishers"]) { let list = msg["publishers"]; Janus.debug( "Got a list of available publishers/feeds:", list ); for (let f in list) { if (list[f]["dummy"]) continue; let id = list[f]["id"]; let display = list[f]["display"]; Janus.debug(" >> [" + id + "] " + display); newRemoteFeed(id, display); } } } } else if (event === "event") { // Any feed to attach to? if (role === "listener" && msg["publishers"]) { let list = msg["publishers"]; Janus.debug( "Got a list of available publishers/feeds:", list ); for (let f in list) { if (list[f]["dummy"]) continue; let id = list[f]["id"]; let display = list[f]["display"]; Janus.debug(" >> [" + id + "] " + display); newRemoteFeed(id, display); } } else if (msg["leaving"]) { // One of the publishers has gone away? let leaving = msg["leaving"]; Janus.log("Publisher left: " + leaving); if (role === "listener" && msg["leaving"] === source) { bootbox.alert( "The screen sharing session is over, the publisher left", function () { window.location.reload(); } ); } } else if (msg["error"]) { bootbox.alert(msg["error"]); } } } if (jsep) { Janus.debug("Handling SDP as well...", jsep); screentest.handleRemoteJsep({ jsep: jsep }); } }, onlocaltrack: function (track, on) { Janus.debug( "Local track " + (on ? "added" : "removed") + ":", track ); // We use the track ID as name of the element, but it may contain invalid characters let trackId = track.id.replace(/[{}]/g, ""); if (!on) { // Track removed, get rid of the stream and the rendering let stream = localTracks[trackId]; if (stream) { try { let tracks = stream.getTracks(); for (let i in tracks) { let mst = tracks[i]; if (mst) mst.stop(); } // eslint-disable-next-line no-unused-vars } catch (e) {} } if (track.kind === "video") { $("#screenvideo" + trackId).remove(); localVideos--; if (localVideos === 0) { // No video, at least for now: show a placeholder if ( $("#screencapture .no-video-container").length === 0 ) { $("#screencapture").append( '
' + '' + 'No webcam available' + "
" ); } } } delete localTracks[trackId]; return; } // If we're here, a new track was added let stream = localTracks[trackId]; if (stream) { // We've been here already return; } $("#screenmenu").addClass("hide"); $("#room").removeClass("hide"); if (track.kind === "audio") { // We ignore local audio tracks, they'd generate echo anyway if (localVideos === 0) { // No video, at least for now: show a placeholder if ($("#screencapture .no-video-container").length === 0) { $("#screencapture").append( '
' + '' + 'No webcam available' + "
" ); } } } else { // New video track: create a stream out of it localVideos++; $("#screencapture .no-video-container").remove(); let stream = new MediaStream([track]); localTracks[trackId] = stream; Janus.log("Created local stream:", stream); $("#screencapture").append( '