如何通过nodejs拨打SIP电话

How to make a SIP call through nodejs

我正在尝试自动呼叫我的客户,我已经设置并运行了 freepbx,现在我希望能够触发一些 nodejs 代码来拨打电话,获取音频流并将其传递给dialogflow 并根据 dialogflow 响应向客户播放 MP3(或任何其他音频类型)。

我试过这个包:

https://github.com/versatica/JsSIP

并且我可以稳定通话并获取音频流。是的,那太好了,要是不在浏览器的 html 中就好了……但后来我发现了这个!

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

看起来像是我所有问题的答案,直到我尝试从我的 nodejs 脚本发出调用,惊喜,不起作用,甚至不打算工作,它只是用于发送 sip 信号,但不是能够拨打电话,因为包在 WebRTC 上中继(Wich 仅在浏览器上运行)

然后我发现了这个问题:

Simple SIP phone in nodeJS without WebRTC

提到了一些名为 sip 的包,我需要试一试,哇,这是纯 sip 通信,我对此了解不多,但经过大量工作后,我仍然设法连接到我的freepbx,验证并拨打电话!当时一切似乎都很好,但现在...音频在哪里??

据我了解,经过大量阅读后,sip 仅发出呼叫,所有媒体传输均由 RTP 执行,但我的问题是,我该如何实现?我需要创建一些 RTP 媒体服务器来处理这个或者我需要做什么?任何关于正确方向的帮助、澄清或指导将不胜感激,在此先感谢并抱歉我的英语不好。

这是我当前的代码:

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:destination@domain.com',
  headers: {
    to: {
      uri: 'sip:destination@domain.com',
    },
    from: {
      uri: 'sip:origin@domain.com',
      params: {
        tag: rstring(),
      },
    },
    'call-id': rstring(),
    cseq: {
      method: 'INVITE',
      seq: Math.floor(Math.random() * 1e5),
    },
    'content-type': 'application/sdp',
    contact: [
      {
        uri: 'origin@domain.com',
      },
    ],
  },
  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:destination@domain.com',
      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: 'origin@domain.com',
          },
        ],
      },
      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);
  }
});

我有同样的问题,但我没有找到任何解决方案。所以我 fork sip.js 并更改了源代码。有用!我在浏览器上使用一些节点模块而不是本机 websocket RTC API。 参见 https://github.com/Winston87245/SIP.js

根据 https://www.rfc-editor.org/rfc/rfc3261 第 12 页,您需要了解 SIP (RFC 3261) 和 SDP (RFC 4566) 的工作原理。

据我所知,当客户端A在振铃后收到B的200 OK响应时,客户端A根据SDP(在OK响应中)直接连接到客户端B,不需要SIP代理。换句话说,它是一个 P2P 连接。您还需要了解 ICE (RFC 8445)。

希望对您有所帮助。如果您有任何问题,请随时与我联系。