Sip Trunk between Janus gateway and Cisco unified communication manager

Hello everyone,
I am a newbie in Janus Gateway. I was actually looking for a solution that will allow me to click a call link and makes calls to an ivr system.
Based some findings on the internet, I installed the janus gateway which has started successfully. The error I am facing is that Janus is asking the Webrtc client to register
Since my goal is to use Webrtc and make concurrent calls to my ivr system (7100@abc.com). I have Cisco expressway edge and core which will route calls from any sip client to my cucm end points

Cucm is a Cisco sip server which does not accept sip registration instead,it uses sip trunk to communicate with another sip server
Is it possible for me to set up sip trunk on Janus gateway to cucm sip server. Janus gateway will receive incoming sip request from Webrtc and router the calls to the cucm sip server

Can someone put me in the right direction?

Thank you in anticipation

The SIP plugin doesn’t currently support trunking, but we have an experimental PR that tries to address that: GitHub - meetecho/janus-gateway at sip-trunk

You can find more information in this presentation I did at Kamailio World:

ok.thanks. i will check the presentation and revert.

I have checked the presentation.I cloned the janus-gateway but still not able to see sip trunk section on sip plugin
I downloaded the zip file from the GitHub on PC. Am using visual code editor. How do I get started with downloaded code?

Hello Sir,
I think the sip plugin requires sip registration first . I was able to make use of your online Sip Gateway Demo to make calls to cucm end points via the Cisco expressway Edge . However ,establishing the sip trunk between Janus and cucm will be the best option.

From the sip.html , I can see 3 Java scripts (sip js,janus.js,settings.js).
I guess the sip js is the one handling the sip registration request to the sip plugin. Which script handles the call?

The link I gave you is for a branch that supports SIP trunking, with no need to send a SIP register. Using the SIP plugin you still register, but it’s a “fake” registration, since you basically just use type = "trunk" (or something like that). It’s all explained in the presentation, so please refer to that. I’ll align the code and create a PR for the branch later today.

I’ve created a PR with code aligned to the latest master branch. It has some info on how to configure/use it too.

Hello Sir, I am actually a newbie on Janus gateway. I have created the trunk on the Janus sip plugin. After setting the register type as “trunk”, it threw an error “wrong state, register first” when I changed the register type to guess, it registered. When initiated the call, I got this [WARN] [3775422298965054] No stream, queueing this trickle as it got here before the SDP…
[3775422298965054] Creating ICE agent (ICE Full mode, controlled)
[janus.plugin.sip-0x75cf800025e0] No WebRTC media anymore
[3775422298965054] WebRTC resources freed; 0x75cfa0001dc0 0x75cfa0004710. currently i am study the SIP Gateway Demo.

sir,can you please take a look at code? I dont need incoming call. Just outgoing call to an ivr system.
let janus = null;
let sipcall = null;
let opaqueId = “sipcall-” + Janus.randomString(12);

let localStream = null;
let remoteStream = null;

let localTracks = {}, localVideos = 0;
let remoteTracks = {}, remoteVideos = 0;

let doAudio = true, doVideo = true;
let isMuted = false;
let isVideoMuted = false;
let helpersCreated = false;

// Array to store helper handles
let helpers = ;

document.addEventListener(‘DOMContentLoaded’, function () {
initializeJanus();
document.getElementById(‘callButton’).addEventListener(‘click’, toggleCall);
document.getElementById(‘muteButton’).addEventListener(‘click’, toggleMute);
document.getElementById(‘videoButton’).addEventListener(‘click’, toggleVideo);
document.querySelectorAll(‘.dtmf’).forEach(button => {
button.addEventListener(‘click’, function () {
sendDTMF(this.getAttribute(‘data-digit’));
});
});
});

function initializeJanus() {
Janus.init({
debug: “all”,
callback: function () {
if (!Janus.isWebrtcSupported()) {
alert("No WebRTC support… ");
return;
}
janus = new Janus({
server: ‘ws://1.1.1.1:8188/’,
success: function () {
attachMainHandle();
},
error: function (error) {
console.error(error);
alert(error);
},
destroyed: function () {
window.location.reload();
}
});
}
});
}

function attachMainHandle() {
janus.attach({
plugin: “janus.plugin.sip”,
opaqueId: opaqueId,
success: function (pluginHandle) {
sipcall = pluginHandle;
console.log(“Master session ID:”, sipcall.getId());
console.log(“Plugin attached! (” + sipcall.getPlugin() + “, id=” + sipcall.getId() + “)”);
registerUsername(sipcall);
},
error: function (error) {
console.error(" – Error attaching plugin…", error);
alert(“Error attaching plugin… " + error);
},
consentDialog: function (on) {
console.log(“Consent dialog should be " + (on ? “on” : “off”) + " now”);
},
mediaState: function (medium, on) {
console.log(“Janus " + (on ? “started” : “stopped”) + " receiving our " + medium);
},
webrtcState: function (on) {
console.log(“Janus says our WebRTC PeerConnection is " + (on ? “up” : “down”) + " now”);
},
onmessage: handleMessage,
onlocaltrack: handleLocalTrack,
onremotetrack: handleRemoteTrack,
oncleanup: function () {
console.log(” ::: Got a cleanup notification :::”);
localStream = null;
remoteStream = null;
}
});
}

function createHelpers(count, masterId) {
console.log(“Creating helpers with master ID:”, masterId);
for (let i = 0; i < 10; i++) { // Create only 10 helpers{
janus.attach({
plugin: “janus.plugin.sip”,
opaqueId: opaqueId + “-helper-” + i,
success: function (pluginHandle) {
helpers.push(pluginHandle);
console.log(“Helper plugin attached! (id=” + pluginHandle.getId() + “)”);
registerUsername(pluginHandle, true, masterId);
},
error: function (error) {
console.error(" – Error attaching helper plugin…“, error);
},
onmessage: handleMessage,
onlocaltrack: handleLocalTrack,
onremotetrack: handleRemoteTrack,
oncleanup: function () {
console.log(” ::: Got a cleanup notification for helper :::");
}
});
}
}

function registerUsername(handle, isHelper = false, masterId = null) {
let formData = JSON.parse(localStorage.getItem(‘formData’));
if (formData && formData.name && formData.phone) {
let register = {
request: “register”,
type: isHelper ? “helper” : “guest”,
username: “sip:” + formData.name + (isHelper ? “-helper” : “”) + “@abc…com”,
display_name: formData.name + " " + formData.phone + (isHelper ? " (Helper)" : “”),
secret: “cisco123”,
};

    if (isHelper && masterId) {
        register["master_id"] = masterId;
    }

    handle.send({ message: register });
} else {
    alert("User data not found. Please go back and submit the form.");
}

}

function handleMessage(msg, jsep) {
console.log(" ::: Got a message :::", msg);
let result = msg[“result”];
if (result) {
if (result[“event”]) {
let event = result[“event”];
if (event === ‘registered’) {
console.log("Successfully registered as " + result[“username”] + “!”);
document.getElementById(‘callButton’).disabled = false;
} else if (event === ‘calling’) {
console.log(“Calling…”);
document.getElementById(‘callButton’).textContent = ‘Calling…’;
document.getElementById(‘callButton’).disabled = true;
} else if (event === ‘incomingcall’) {
console.log("Incoming call from " + result[“username”] + “!”);
} else if (event === ‘hangup’) {
console.log(“Call hung up (” + result[“code”] + " " + result[“reason”] + “)!”);
document.getElementById(‘callButton’).textContent = ‘Call’;
document.getElementById(‘callButton’).disabled = false;
}
}
}

if (jsep) {
  console.log("Handling SDP as well...", jsep);
  sipcall.handleRemoteJsep({ jsep: jsep });
}

}

function handleLocalTrack(track, on) {
console.log("Local track " + (on ? “added” : “removed”) + “:”, track);
let localVideo = document.getElementById(‘localVideo’);
if (on) {
if (localStream === null) {
localStream = new MediaStream();
}
localStream.addTrack(track);
Janus.attachMediaStream(localVideo, localStream);
} else {
localStream.removeTrack(track);
if (localStream.getTracks().length === 0) {
Janus.attachMediaStream(localVideo, null);
}
}
}

function handleRemoteTrack(track, mid, on) {
console.log(“Remote track (mid=” + mid + ") " + (on ? “added” : “removed”) + “:”, track);
let remoteVideo = document.getElementById(‘remoteVideo’);
if (on) {
if (remoteStream === null) {
remoteStream = new MediaStream();
}
remoteStream.addTrack(track);
Janus.attachMediaStream(remoteVideo, remoteStream);
} else {
remoteStream.removeTrack(track);
if (remoteStream.getTracks().length === 0) {
Janus.attachMediaStream(remoteVideo, null);
}
}
}

function toggleCall() {
if (sipcall.webrtcStuff.pc) {
let hangup = { request: “hangup” };
sipcall.send({ message: hangup });
sipcall.hangup();
document.getElementById(‘callButton’).textContent = ‘Call’;
} else {
let sipUri = ‘sip:0000@abc…com’;
navigator.mediaDevices.getUserMedia({ audio: true, video: true })
.then(stream => {
localStream = stream;
// Create offer after local stream is available
sipcall.createOffer({
streams: [localStream],
success: function(jsep) {
let body = { request: “call”, uri: sipUri };
sipcall.send({ message: body, jsep: jsep });
},
error: function(error) {
console.error(“WebRTC error…”, error);
}
});
})
.catch(error => {
console.error(“Error accessing user media:”, error);
});
}
}

function toggleMute() {
let muted = sipcall.isAudioMuted();
if(muted) {
sipcall.unmuteAudio();
document.getElementById(‘muteButton’).textContent = ‘Mute’;
} else {
sipcall.muteAudio();
document.getElementById(‘muteButton’).textContent = ‘Unmute’;
}
}

function toggleVideo() {
let videoMuted = sipcall.isVideoMuted();
if(videoMuted) {
sipcall.unmuteVideo();
document.getElementById(‘videoButton’).textContent = ‘Video Off’;
} else {
sipcall.muteVideo();
document.getElementById(‘videoButton’).textContent = ‘Video On’;
}
}

function sendDTMF(digit) {
if(sipcall.dtmf) {
sipcall.dtmf({dtmf: { tones: digit }});
}
}