You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js中基于SIP.js构建SIP与RTSP音频代理的实现问询

基于SIP.js 0.21.2实现RTP音频代理(对接RTSP G.711 ulaw设备)

核心思路

跳过SIP.js默认的WebRTC媒体处理逻辑,通过自定义MediaHandlerFactory和MediaHandler直接接管RTP数据包收发,同时对接RTSP设备的音频流,实现双向转发。


步骤1:自定义MediaHandler类

该类负责处理SDP协商、RTP套接字管理和数据包转发,Node.js环境下直接用dgram模块处理UDP传输,无需依赖WebRTC API。

const dgram = require('dgram');
const { MediaHandler } = require('sip.js');

class ProxyMediaHandler extends MediaHandler {
  constructor(mediaSession, options) {
    super(mediaSession, options);
    this.callerRtpSocket = null; // 接收呼叫方RTP的套接字
    this.deviceRtpSocket = null; // 与RTSP设备通信的套接字
    this.callerRtpInfo = null; // 呼叫方的RTP地址/端口
    // 预先配置或通过RTSP协商获取设备信息
    this.deviceRtpInfo = { address: 'RTSP设备IP', port: 设备RTP端口 };
  }

  // 生成本地SDP,告知呼叫方支持G.711 ulaw
  async getDescription(options) {
    const sdp = `v=0
o=- 12345 12345 IN IP4 ${你的代理IP}
s=SIP Audio Proxy
c=IN IP4 ${你的代理IP}
t=0 0
m=audio ${代理监听RTP端口} RTP/AVP 0
a=rtpmap:0 PCMU/8000
a=sendrecv`;
    return { type: 'offer', sdp };
  }

  // 解析远端SDP,提取呼叫方的RTP地址和端口
  async setDescription(description) {
    const sdpLines = description.sdp.split('\n');
    const mediaLine = sdpLines.find(line => line.startsWith('m=audio'));
    const [, callerPort] = mediaLine.split(' ');
    const connLine = sdpLines.find(line => line.startsWith('c=IN IP4'));
    const callerAddress = connLine.split(' ')[3];
    
    this.callerRtpInfo = { address: callerAddress, port: parseInt(callerPort) };
    this.initCallerRtpSocket();
    this.initDeviceRtpSocket();
    // 若未建立RTSP会话,此处需先完成RTSP的OPTIONS/DESCRIBE/SETUP/PLAY流程
  }

  // 初始化接收呼叫方RTP的套接字
  initCallerRtpSocket() {
    this.callerRtpSocket = dgram.createSocket('udp4');
    this.callerRtpSocket.bind(代理监听RTP端口, 你的代理IP);

    this.callerRtpSocket.on('message', (msg) => {
      // 转发呼叫方RTP包到RTSP设备
      this.deviceRtpSocket.send(msg, 0, msg.length, this.deviceRtpInfo.port, this.deviceRtpInfo.address);
    });
  }

  // 初始化与RTSP设备通信的套接字
  initDeviceRtpSocket() {
    this.deviceRtpSocket = dgram.createSocket('udp4');
    // 接收设备RTP包并转发给呼叫方
    this.deviceRtpSocket.on('message', (msg) => {
      if (this.callerRtpInfo) {
        this.callerRtpSocket.send(msg, 0, msg.length, this.callerRtpInfo.port, this.callerRtpInfo.address);
      }
    });
    // 绑定本地端口接收设备RTP流
    this.deviceRtpSocket.bind(本地接收设备RTP的端口, 你的代理IP);
  }

  // 清理资源
  close() {
    if (this.callerRtpSocket) this.callerRtpSocket.close();
    if (this.deviceRtpSocket) this.deviceRtpSocket.close();
    super.close();
  }
}

步骤2:实现MediaHandlerFactory

工厂类用于创建自定义MediaHandler实例,替换SIP.js默认的WebRTC处理逻辑。

class ProxyMediaHandlerFactory {
  createMediaHandler(mediaSession, options) {
    return new ProxyMediaHandler(mediaSession, options);
  }
}

步骤3:配置SIP客户端使用自定义工厂

在实例化UserAgent时,指定自定义媒体处理工厂。

const { UserAgent } = require('sip.js');

const userAgent = new UserAgent({
  uri: SIP账号URI,
  authorizationUsername: SIP用户名,
  authorizationPassword: SIP密码,
  transportOptions: { server: SIP服务器地址 },
  mediaHandlerFactory: new ProxyMediaHandlerFactory()
});

// 启动UA并处理呼叫
userAgent.start().then(() => {
  userAgent.delegate = {
    onInvite(invitation) {
      invitation.accept().then(() => {
        console.log('呼叫已接听,开始转发音频');
      });
    }
  };
});

步骤4:RTSP设备对接补充

需先与RTSP设备建立会话:

  • 发送OPTIONS确认设备支持G.711 ulaw
  • 发送DESCRIBE获取设备SDP,确认编码格式
  • 发送SETUP指定RTP传输参数(如UDP端口)
  • 发送PLAY(设备推送音频)或RECORD(向设备发送音频)启动流

可使用第三方RTSP客户端库或自定义HTTP请求实现会话管理,确保设备RTP流能发送到代理指定端口,同时代理能将呼叫方RTP包发送到设备端口。

关键注意事项

  • RTP头部处理:G.711 ulaw包可直接透传,若两端SSRC冲突,需修改转发包的SSRC字段
  • 端口管理:确保代理监听的UDP端口在防火墙中开放
  • 错误处理:添加套接字error事件监听,呼叫结束时及时释放资源

内容的提问来源于stack exchange,提问作者Jim Udall

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 05:33:39