// 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 streaming = null; var opaqueId = "streamingtest-" + Janus.randomString(12); var remoteTracks = {}, remoteVideos = 0, dataMid = null; var bitrateTimer = {}; var simulcastStarted = {}, svcStarted = {}; var streamsList = {}; var selectedStream = null; $(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 Streaming plugin janus.attach({ plugin: "janus.plugin.streaming", opaqueId: opaqueId, success: function (pluginHandle) { $("#details").remove(); streaming = pluginHandle; Janus.log( "Plugin attached! (" + streaming.getPlugin() + ", id=" + streaming.getId() + ")" ); // Setup streaming session $("#update-streams").click(updateStreamsList); updateStreamsList(); $("#start") .removeAttr("disabled") .html("Stop") .click(function () { $(this).attr("disabled", true); for (let i in bitrateTimer) clearInterval(bitrateTimer[i]); bitrateTimer = {}; janus.destroy(); $("#streamslist").attr("disabled", true); $("#watch").attr("disabled", true).unbind("click"); $("#start") .attr("disabled", true) .html("Bye") .unbind("click"); }); }, error: function (error) { Janus.error(" -- Error attaching plugin... ", error); bootbox.alert("Error attaching plugin... " + error); }, iceState: function (state) { Janus.log("ICE state changed to " + state); }, webrtcState: function (on) { Janus.log( "Janus says our WebRTC PeerConnection is " + (on ? "up" : "down") + " now" ); }, 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 :::", msg); let result = msg["result"]; if (result) { if (result["status"]) { let status = result["status"]; if (status === "starting") $("#status") .removeClass("hide") .text("Starting, please wait...") .removeClass("hide"); else if (status === "started") $("#status") .removeClass("hide") .text("Started") .removeClass("hide"); else if (status === "stopped") stopStream(); } else if (msg["streaming"] === "event") { // Does this event refer to a mid in particular? let mid = result["mid"] ? result["mid"] : "0"; // Is simulcast in place? let substream = result["substream"]; let temporal = result["temporal"]; if ( (substream !== null && substream !== undefined) || (temporal !== null && temporal !== undefined) ) { if (!simulcastStarted[mid]) { simulcastStarted[mid] = true; addSimulcastButtons(mid); } // We just received notice that there's been a switch, update the buttons updateSimulcastButtons(mid, substream, temporal); } // Is VP9/SVC in place? let spatial = result["spatial_layer"]; temporal = result["temporal_layer"]; if ( (spatial !== null && spatial !== undefined) || (temporal !== null && temporal !== undefined) ) { if (!svcStarted[mid]) { svcStarted[mid] = true; addSvcButtons(mid); } // We just received notice that there's been a switch, update the buttons updateSvcButtons(mid, spatial, temporal); } } } else if (msg["error"]) { bootbox.alert(msg["error"]); stopStream(); return; } if (jsep) { Janus.debug("Handling SDP as well...", jsep); let stereo = jsep.sdp.indexOf("stereo=1") !== -1; // Offer from the plugin, let's answer streaming.createAnswer({ jsep: jsep, // We only specify data channels here, as this way in // case they were offered we'll enable them. Since we // don't mention audio or video tracks, we autoaccept them // as recvonly (since we won't capture anything ourselves) tracks: [{ type: "data" }], customizeSdp: function (jsep) { if (stereo && jsep.sdp.indexOf("stereo=1") == -1) { // Make sure that our offer contains stereo too jsep.sdp = jsep.sdp.replace( "useinbandfec=1", "useinbandfec=1;stereo=1" ); } }, success: function (jsep) { Janus.debug("Got SDP!", jsep); let body = { request: "start" }; streaming.send({ message: body, jsep: jsep }); $("#watch") .html("Stop") .removeAttr("disabled") .unbind("click") .click(stopStream); }, error: function (error) { Janus.error("WebRTC error:", error); bootbox.alert("WebRTC error... " + error.message); }, }); } }, onremotetrack: function (track, mid, on, metadata) { Janus.debug( "Remote track (mid=" + mid + ") " + (on ? "added" : "removed") + (metadata ? " (" + metadata.reason + ") " : "") + ":", track ); let mstreamId = "mstream" + mid; if ( streamsList[selectedStream] && streamsList[selectedStream].legacy ) mstreamId = "mstream0"; if (!on) { // Track removed, get rid of the stream and the rendering $("#remotevideo" + mid).remove(); if (track.kind === "video") { remoteVideos--; if (remoteVideos === 0) { // No video, at least for now: show a placeholder if ( $("#" + mstreamId + " .no-video-container").length === 0 ) { $("#" + mstreamId).append( '
' + '' + 'No remote video available' + "
" ); } } } delete remoteTracks[mid]; return; } if ($("#remotevideo" + mid).length > 0) return; // If we're here, a new track was added $("#spinner" + mid).remove(); let stream = null; if (track.kind === "audio") { // New audio track: create a stream out of it, and use a hidden