// 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; // We'll need two handles for this demo: a caller and a callee var caller = null, callee = null; var opaqueId = Janus.randomString(12); // The local and remote tracks only refer to the caller, though (we ignore the callee) var localTracks = {}, localVideos = 0, remoteTracks = {}, remoteVideos = 0; var callstarted = false, videoenabled = true; var srtp = undefined; // use "sdes_mandatory" to test SRTP-SDES $(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 NoSIP plugin as a caller janus.attach({ plugin: "janus.plugin.nosip", opaqueId: "nosiptest-caller-" + opaqueId, success: function (pluginHandle) { $("#details").remove(); caller = pluginHandle; Janus.log( "[caller] Plugin attached! (" + caller.getPlugin() + ", id=" + caller.getId() + ")" ); $("#start") .removeAttr("disabled") .html("Stop") .click(function () { $(this).attr("disabled", true); janus.destroy(); }); // Negotiate WebRTC in a second (just to make sure both caller and callee handles exist) setTimeout(function () { Janus.debug( "[caller] Trying a createOffer too (audio/video sendrecv)" ); // We want bidirectional audio and video by default caller.createOffer({ tracks: [ { type: "audio", capture: true, recv: true }, { type: "video", capture: true, recv: true }, ], success: function (jsep) { Janus.debug("[caller] Got SDP!", jsep); // We now have a WebRTC SDP: to get a barebone SDP legacy // peers can digest, we ask the NoSIP plugin to generate // an offer for us. For the sake of simplicity, no SRTP: // if you need SRTP support, you can use the same syntax // the SIP plugin uses (mandatory vs. optional). We'll // get the result in an event called "generated" here. let body = { request: "generate", srtp: srtp, }; caller.send({ message: body, jsep: jsep }); // Create a spinner waiting for the remote video $("#videoright").html( '
' + '
' + ' Loading...' + "
" + "
" ); }, error: function (error) { Janus.error("WebRTC error:", error); bootbox.alert("WebRTC error... " + error.message); }, }); }, 1000); }, error: function (error) { console.error("[caller] -- Error attaching plugin...", error); bootbox.alert("[caller] Error attaching plugin... " + error); }, consentDialog: function (on) { Janus.debug( "[caller] Consent dialog should be " + (on ? "on" : "off") + " now" ); if (on) { // Darken screen and show hint $.blockUI({ message: '
', baseZ: 3001, css: { border: "none", padding: "15px", backgroundColor: "transparent", color: "#aaa", top: "10px", left: "100px", }, }); } else { // Restore screen $.unblockUI(); } }, iceState: function (state) { Janus.log("[caller] ICE state changed to " + state); }, mediaState: function (medium, on, mid) { Janus.log( "[caller] Janus " + (on ? "started" : "stopped") + " receiving our " + medium + " (mid=" + mid + ")" ); }, webrtcState: function (on) { Janus.log( "[caller] Janus says our WebRTC PeerConnection is " + (on ? "up" : "down") + " now" ); $("#videoleft").parent().unblock(); if (on) { callstarted = true; $("#togglevideo") .removeAttr("disabled") .click(renegotiateVideo); } }, slowLink: function (uplink, lost, mid) { Janus.warn( "[caller] Janus reports problems " + (uplink ? "sending" : "receiving") + " packets on mid " + mid + " (" + lost + " lost packets)" ); }, onmessage: function (msg, jsep) { Janus.debug("[caller] ::: Got a message :::", msg); // Any error? let error = msg["error"]; if (error) { bootbox.alert(error); caller.hangup(); return; } let result = msg["result"]; if (result) { let event = result["event"]; if (event === "generated") { // We got the barebone SDP offer we wanted, let's have // the callee handle it as if it arrived via signalling let sdp = result["sdp"]; $("#localsdp").text("[" + result["type"] + "]\n" + sdp); // This will result in a "processed" event on the callee handle let processOffer = { request: "process", type: result["type"], sdp: result["sdp"], update: result["update"], srtp: srtp, }; callee.send({ message: processOffer }); } else if (event === "processed") { // As a caller, this means the remote, barebone SDP answer // we got from the legacy peer has been turned into a full // WebRTC SDP answer we can consume here, let's do that if (jsep) { Janus.debug("[caller] Handling SDP as well...", jsep); caller.handleRemoteJsep({ jsep: jsep }); // If this was a renegotiation, update the button if (callstarted) { $("#togglevideo") .text(videoenabled ? "Disable video" : "Enable video") .removeAttr("disabled"); } } } } }, 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") { $("#myvideot" + trackId).remove(); localVideos--; if (localVideos === 0) { // No video, at least for now: show a placeholder if ($("#videoleft .no-video-container").length === 0) { $("#videoleft").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; } if ($("#videoleft video").length === 0) { $("#videos").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 ($("#videoleft .no-video-container").length === 0) { $("#videoleft").append( '
' + '' + 'No webcam available' + "
" ); } } } else { // New video track: create a stream out of it localVideos++; $("#videoleft .no-video-container").remove(); let stream = new MediaStream([track]); localTracks[trackId] = stream; Janus.log("Created local stream:", stream); $("#videoleft").append( '