// 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 sfutest = null; var opaqueId = "videoroomtest-" + Janus.randomString(12); var myroom = 1234; // Demo room if (getQueryStringValue("room") !== "") myroom = parseInt(getQueryStringValue("room")); var myusername = null; var myid = null; var mystream = null; // We use this other ID just to map our subscriptions to us var mypvtid = null; var remoteFeed = null; var feeds = {}, feedStreams = {}, subStreams = {}, slots = {}, mids = {}, subscriptions = {}; var localTracks = {}, localVideos = 0, remoteTracks = {}; var bitrateTimer = [], simulcastStarted = {}, svcStarted = {}; var doSimulcast = getQueryStringValue("simulcast") === "yes" || getQueryStringValue("simulcast") === "true"; var doSvc = getQueryStringValue("svc"); if (doSvc === "") doSvc = null; var acodec = getQueryStringValue("acodec") !== "" ? getQueryStringValue("acodec") : null; var vcodec = getQueryStringValue("vcodec") !== "" ? getQueryStringValue("vcodec") : null; var subscriber_mode = getQueryStringValue("subscriber-mode") === "yes" || getQueryStringValue("subscriber-mode") === "true"; var use_msid = getQueryStringValue("msid") === "yes" || getQueryStringValue("msid") === "true"; $(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 video room test plugin janus.attach({ plugin: "janus.plugin.videoroom", opaqueId: opaqueId, success: function (pluginHandle) { $("#details").remove(); sfutest = pluginHandle; Janus.log( "Plugin attached! (" + sfutest.getPlugin() + ", id=" + sfutest.getId() + ")" ); Janus.log(" -- This is a publisher/manager"); // Prepare the username registration $("#videojoin").removeClass("hide"); $("#registernow").removeClass("hide"); $("#register").click(registerUsername); $("#username").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 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("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" ); $("#videolocal").parent().parent().unblock(); if (!on) return; $("#publish").remove(); // This controls allows us to override the global room bitrate cap $("#bitrate").parent().parent().removeClass("hide"); $("#bitrate a").click(function () { $(".dropdown-toggle").dropdown("hide"); let id = $(this).attr("id"); let bitrate = parseInt(id) * 1000; if (bitrate === 0) { Janus.log("Not limiting bandwidth via REMB"); } else { Janus.log("Capping bandwidth to " + bitrate + " via REMB"); } $("#bitrateset") .text($(this).text()) .parent() .removeClass("open"); sfutest.send({ message: { request: "configure", bitrate: bitrate }, }); return false; }); }, 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 != undefined && event != null) { if (event === "joined") { // Publisher/manager created, negotiate WebRTC and attach to existing feeds, if any myid = msg["id"]; mypvtid = msg["private_id"]; Janus.log( "Successfully joined room " + msg["room"] + " with ID " + myid ); if (subscriber_mode) { $("#videojoin").addClass("hide"); $("#videos").removeClass("hide"); } else { publishOwnFeed(true); } // Any new feed to attach to? if (msg["publishers"]) { let list = msg["publishers"]; Janus.debug( "Got a list of available publishers/feeds:", list ); let sources = null; for (let f in list) { if (list[f]["dummy"]) continue; let id = list[f]["id"]; let display = list[f]["display"]; let streams = list[f]["streams"]; for (let i in streams) { let stream = streams[i]; stream["id"] = id; stream["display"] = display; } let slot = feedStreams[id] ? feedStreams[id].slot : null; let remoteVideos = feedStreams[id] ? feedStreams[id].remoteVideos : 0; feedStreams[id] = { id: id, display: display, streams: streams, slot: slot, remoteVideos: remoteVideos, }; Janus.debug( " >> [" + id + "] " + display + ":", streams ); if (!sources) sources = []; sources.push(streams); } if (sources) subscribeTo(sources); } } else if (event === "destroyed") { // The room has been destroyed Janus.warn("The room has been destroyed!"); bootbox.alert("The room has been destroyed", function () { window.location.reload(); }); } else if (event === "event") { // Any info on our streams or a new feed to attach to? if (msg["streams"]) { let streams = msg["streams"]; for (let i in streams) { let stream = streams[i]; stream["id"] = myid; stream["display"] = myusername; } feedStreams[myid] = { id: myid, display: myusername, streams: streams, }; } else if (msg["publishers"]) { let list = msg["publishers"]; Janus.debug( "Got a list of available publishers/feeds:", list ); let sources = null; for (let f in list) { if (list[f]["dummy"]) continue; let id = list[f]["id"]; let display = list[f]["display"]; let streams = list[f]["streams"]; for (let i in streams) { let stream = streams[i]; stream["id"] = id; stream["display"] = display; } let slot = feedStreams[id] ? feedStreams[id].slot : null; let remoteVideos = feedStreams[id] ? feedStreams[id].remoteVideos : 0; feedStreams[id] = { id: id, display: display, streams: streams, slot: slot, remoteVideos: remoteVideos, }; Janus.debug( " >> [" + id + "] " + display + ":", streams ); if (!sources) sources = []; sources.push(streams); } if (sources) subscribeTo(sources); } else if (msg["leaving"]) { // One of the publishers has gone away? let leaving = msg["leaving"]; Janus.log("Publisher left: " + leaving); unsubscribeFrom(leaving); } else if (msg["unpublished"]) { // One of the publishers has unpublished? let unpublished = msg["unpublished"]; Janus.log("Publisher left: " + unpublished); if (unpublished === "ok") { // That's us sfutest.hangup(); return; } unsubscribeFrom(unpublished); } else if (msg["error"]) { if (msg["error_code"] === 426) { // This is a "no such room" error: give a more meaningful description bootbox.alert( "

Apparently room " + myroom + " (the one this demo uses as a test room) " + "does not exist...

Do you have an updated janus.plugin.videoroom.cfg " + "configuration file? If not, make sure you copy the details of room " + myroom + " " + "from that sample in your current configuration file, then restart Janus and try again." ); } else { bootbox.alert(msg["error"]); } } } } if (jsep) { Janus.debug("Handling SDP as well...", jsep); sfutest.handleRemoteJsep({ jsep: jsep }); // Check if any of the media we wanted to publish has // been rejected (e.g., wrong or unsupported codec) let audio = msg["audio_codec"]; if ( mystream && mystream.getAudioTracks() && mystream.getAudioTracks().length > 0 && !audio ) { // Audio has been rejected toastr.warning( "Our audio stream has been rejected, viewers won't hear us" ); } let video = msg["video_codec"]; if ( mystream && mystream.getVideoTracks() && mystream.getVideoTracks().length > 0 && !video ) { // Video has been rejected toastr.warning( "Our video stream has been rejected, viewers won't see us" ); // Hide the webcam video $("#myvideo").addClass("hide"); $("#videolocal").prepend( '

' + '' + 'Video rejected, no webcam' + "
" ); } } }, onlocaltrack: function (track, on) { Janus.debug(" ::: Got a local track event :::"); 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") { $("#myvideo" + trackId).remove(); localVideos--; if (localVideos === 0) { // No video, at least for now: show a placeholder if ($("#videolocal .no-video-container").length === 0) { $("#videolocal").prepend( '
' + '' + '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; } $("#videos").removeClass("hide"); if ($("#mute").length === 0) { // Add a 'mute' button $("#videolocal").append( '' ); $("#mute").click(toggleMute); // Add an 'unpublish' button $("#videolocal").append( '' ); $("#unpublish").click(unpublishOwnFeed); } 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 ($("#videolocal .no-video-container").length === 0) { $("#videolocal").prepend( '
' + '' + 'No webcam available' + "
" ); } } } else { // New video track: create a stream out of it localVideos++; $("#videolocal .no-video-container").remove(); let stream = new MediaStream([track]); localTracks[trackId] = stream; Janus.log("Created local stream:", stream); Janus.log(stream.getTracks()); Janus.log(stream.getVideoTracks()); $("#videolocal").prepend( '