Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a SIP call through nodejs

I'm trying yo make automated calls to my customers, I already have my freepbx setup and working, now I want to be able to fire some nodejs code to make the call, get the audio stream and pass it to dialogflow and play an MP3 (or any other audio type) to the customer based on the dialogflow response.

I've tried with this package:

https://github.com/versatica/JsSIP

And I can stablish the call and get the audio stream. Yup, that's great, if only wasn't in a html on the browser... But then I found this!

https://github.com/versatica/jssip-node-websocket

Looked like the answer to all my questions until I tried to place a call from my nodejs script, surprise, doesn't work, and is not even intended to work, it's just for sending sip signals, but is not capable of make a call because the package relays on WebRTC (Wich's only runs on the browser)

Then I found this question:

Simple SIP phone in nodeJS without WebRTC

Some package called sip was mentioned, I needed to give it a try, and wow, it's pure sip communication, I don't know much about this but still, after a lot of work I manage to connect to my freepbx, authenticate and place a call!! Everything seemed to be fine at that point, but now... Where is the audio??

As I understood after a lot of reading the sip only places the call, all the media transmission is performed by RTP, but my question is, how do I implement this? I need to create some RTP media server to handle this or what do I need to do? Any help, clarification or guide on the right direction would be really appreciated, thanks in advance and sorry for my bad English.

Here is my current code:

const sip = require('sip');
const util = require('util');
const digest = require('sip/digest');

const rstring = () => Math.floor(Math.random() * 1e6).toString();

sip.start({
  publicAddress: 'My public IP Address',
  tcp: false,
  logger: {
    send: (message, address) => {
      debugger;
      util.debug("send\n" + util.inspect(message, false, null));
    },
    recv: (message, address) => {
      debugger;
      util.debug("recv\n" + util.inspect(message, false, null));
    },
    error: (message, address) => {
      debugger;
      util.debug("ERROR\n" + util.inspect(message, false, null));
    },
  },
});

// Making the call
sip.send({
  method: 'INVITE',
  version: '2.0',
  uri: 'sip:[email protected]',
  headers: {
    to: {
      uri: 'sip:[email protected]',
    },
    from: {
      uri: 'sip:[email protected]',
      params: {
        tag: rstring(),
      },
    },
    'call-id': rstring(),
    cseq: {
      method: 'INVITE',
      seq: Math.floor(Math.random() * 1e5),
    },
    'content-type': 'application/sdp',
    contact: [
      {
        uri: '[email protected]',
      },
    ],
  },
  content: `v=0\r\no=- 234 1 IN IP4 PUBLIC.IP.ADDRESS\r\ns=-\r\nc=IN IP4 PUBLIC.IP.ADDRESS\r\nb=TIAS:13888000\r\nt=0 0\r\nm=audio PORT RTP/AVP 114 0 8 9 112 111 101\r\nb=TIAS:64000\r\nb=AS:64\r\na=rtpmap:114 opus/48000/2\r\na=fmtp:114 minptime=10; useinbandfec=1\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:9 G722/8000\r\na=rtpmap:112 ilbc/8000\r\na=rtpmap:111 speex/16000\r\na=rtpmap:101 telephone-event/8000\r\na=fmtp:101 0-15\r\na=sendrecv\r\na=rtcp:11801\r\na=ptime:20\r\n`,
},
(rs) => {
  if (rs.status === 401) {
    // sending ACK
    const rq = {
      method: 'INVITE',
      version: '2.0',
      uri: 'sip:[email protected]',
      headers: {
        to: {
          uri: rs.headers.to.uri,
        },
        from: rs.headers.from,
        'call-id': rs.headers['call-id'],
        cseq: {
          method: 'INVITE',
          seq: rs.headers.cseq.seq + 1,
        },
        'content-type': 'application/sdp',
        contact: [
          {
            uri: '[email protected]',
          },
        ],
      },
      content: `v=0\r\no=- 234 1 IN IP4 PUBLIC.IP.ADDRESS\r\ns=-\r\nc=IN IP4 PUBLIC.IP.ADDRESS\r\nb=TIAS:13888000\r\nt=0 0\r\nm=audio PORT RTP/AVP 114 0 8 9 112 111 101\r\nb=TIAS:64000\r\nb=AS:64\r\na=rtpmap:114 opus/48000/2\r\na=fmtp:114 minptime=10; useinbandfec=1\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:9 G722/8000\r\na=rtpmap:112 ilbc/8000\r\na=rtpmap:111 speex/16000\r\na=rtpmap:101 telephone-event/8000\r\na=fmtp:101 0-15\r\na=sendrecv\r\na=rtcp:11801\r\na=ptime:20\r\n`,
    };
    digest.signRequest(rs.headers['www-authenticate'][0], rq, rs, {
      user: 'username',
      password: 'password',
    });
    sip.send(rq);
  } else {
    process.exit(1);
  }
});
like image 933
lHumanizado Avatar asked Aug 17 '19 02:08

lHumanizado


People also ask

What is Drachtio?

drachtio is the open source SIP application server ecosystem that aims to end all that. It consists of a high-performance and easily-configured SIP server (written in C++), and a Node. js framework that enables applications to control the server and implement application logic.

How does node js work with JavaScript?

It is a used as backend service where javascript works on the server-side of the application. This way javascript is used on both frontend and backend. Node. js runs on chrome v8 engine which converts javascript code into machine code, it is highly scalable, lightweight, fast, and data-intensive.


1 Answers

I have the same problem and I didn't find any solution. So I forked sip.js and changed the source code. It works! I use some node modules instead of native websocket RTC API on the browser. See https://github.com/Winston87245/SIP.js.

According to https://www.rfc-editor.org/rfc/rfc3261 Page 12, you need to understand how SIP (RFC 3261) and SDP (RFC 4566) work.

As far as I know, when client A receives a 200 OK response from B after ringing, client A according to SDP (in an OK response) connects to Client B directly and doesn't need a SIP proxy. In other words, it is a P2P connection. You also need to know ICE (RFC 8445).

I hope this can help you. Feel free to contact me if you have any questions.

like image 196
Winston Syu Avatar answered Sep 19 '22 22:09

Winston Syu