// 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(
''
);
Janus.attachMediaStream(
$("#myvideot" + trackId).get(0),
stream
);
}
if (
caller.webrtcStuff.pc.iceConnectionState !== "completed" &&
caller.webrtcStuff.pc.iceConnectionState !== "connected"
) {
$("#videoleft")
.parent()
.block({
message: "Calling...",
css: {
border: "none",
backgroundColor: "transparent",
color: "white",
},
});
}
},
onremotetrack: function (track, mid, on) {
Janus.debug(
"[caller] Remote track (mid=" +
mid +
") " +
(on ? "added" : "removed") +
":",
track
);
if (!on) {
// Track removed, get rid of the stream and the rendering
$("#peervideo" + mid).remove();
if (track.kind === "video") {
remoteVideos--;
if (remoteVideos === 0) {
// No video, at least for now: show a placeholder
if ($("#videoright .no-video-container").length === 0) {
$("#videoright").append(
'
' +
'' +
'No remote video available' +
"
"
);
}
}
}
delete remoteTracks[mid];
return;
}
if ($("#peervideo" + mid).length > 0) return;
// If we're here, a new track was added
$("#spinner").remove();
if (
$("#videoright audio").length === 0 &&
$("#videoright video").length === 0
) {
$("#videos").removeClass("hide");
$("#videoright")
.parent()
.find("h3")
.html(
'Send DTMF: '
);
for (let i = 0; i < 12; i++) {
if (i < 10)
$("#dtmf").append(
'"
);
else if (i == 10)
$("#dtmf").append(
''
);
else if (i == 11)
$("#dtmf").append(
''
);
}
$(".dtmf").click(function () {
// Send DTMF tone (inband)
caller.dtmf({ dtmf: { tones: $(this).text() } });
});
}
if (track.kind === "audio") {
// New audio track: create a stream out of it, and use a hidden