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

基于Node.js+FFmpeg向Yealink SIP-T42G推送RTP流的问题排查

我在Node.js中实现SIP功能,使用node.js-sip库。收到Yealink SIP-T42G发送的INVITE请求后,解析SDP为对象,发送100、180响应后用FFmpeg启动RTP流,但对方无法接收音频。用FFmpeg生成的SDP返回200时对方无法接通,改成Yealink风格SDP能接通但无音频,FFmpeg识别音频为pcm_alaw, 8000 Hz, mono, s16, 64 kb/s,怀疑格式不匹配。


收到的INVITE请求

Received INVITE
INVITE sip:201@192.168.1.2:5060 SIP/2.0
Via: SIP/2.0/UDP 192.168.1.39:5062;branch=z9hG4bK1534941205
From: "Nik" <sip:Nik@192.168.1.2>;tag=564148403
To: <sip:201@192.168.1.2>
Call-ID: 2068254636@192.168.1.39
CSeq: 2 INVITE
Contact: <sip:Nik@192.168.1.39:5062>
Authorization: Digest username="Nik", realm="NRegistrar", nonce="1234abcd", uri="sip:201@192.168.1.2:5060", response="7fba16dafe3d60c270b774bd5bba524c", algorithm=MD5
Content-Type: application/sdp
Allow: INVITE, INFO, PRACK, ACK, BYE, CANCEL, OPTIONS, NOTIFY, REGISTER, SUBSCRIBE, REFER, PUBLISH, UPDATE, MESSAGE
Max-Forwards: 70
User-Agent: Yealink SIP-T42G 29.71.0.120
Supported: replaces
Allow-Events: talk,hold,conference,refer,check-sync
Content-Length: 306

v=0
o=- 20083 20083 IN IP4 192.168.1.39
s=SDP data
c=IN IP4 192.168.1.39
t=0 0
m=audio 11782 RTP/AVP 0 8 18 9 101
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:18 G729/8000
a=fmtp:18 annexb=no
a=rtpmap:9 G722/8000
a=fmtp:101 0-15
a=rtpmap:101 telephone-event/8000
a=ptime:20
a=sendrecv

解析后的SDP对象

{
    "session":{
        "version":"0",
        "origin":"- 20084 20084 IN IP4 192.168.1.39",
        "sessionName":"SDP data"
    },
    "media":[{
        "media":"audio",
        "port":11784,
        "protocol":"RTP/AVP",
        "format":"0",
        "attributes":["rtpmap:0 PCMU/8000","rtpmap:8 PCMA/8000","rtpmap:18 G729/8000","fmtp:18 annexb=no","rtpmap:9 G722/8000","fmtp:101 0-15","rtpmap:101 telephone-event/8000","ptime:20","sendrecv"]
    }]
}

启动RTP流的代码

var port = SDPParser.parse(res.message.body).media[0].port
var s = new STREAMER('output.wav', '192.168.1.39', port)

STREAMER类实现

class Streamer{
    constructor(inputFilePath, rtpAddress, rtpPort){
        this.inputFilePath = 'output.wav';
        this.rtpAddress = rtpAddress;
        this.rtpPort = rtpPort;
    }

    start(){
        return new Promise((resolve) => {
            const ffmpegCommand = `ffmpeg -re -i ${this.inputFilePath} -ar 8000 -f mulaw -f rtp rtp://${this.rtpAddress}:${this.rtpPort}`;
            const ffmpegProcess = spawn(ffmpegCommand, { shell: true });
            
            ffmpegProcess.stdout.on('data', (data) => {
                data = data.toString()
                data = data.replace(new RegExp('127.0.0.1', 'g'), '192.168.1.3');
                resolve(data.toString())
            });
            
            ffmpegProcess.stderr.on('data', (data) => {
              console.log(data.toString())
            });
            
            ffmpegProcess.on('close', (code) => {
              console.log('close')
              console.log(code.toString())
            });
            
            ffmpegProcess.on('error', (error) => {
              console.log(error.toString())
            });
        })
    }
     
}

完整业务代码

Client.on('INVITE', (res) => {
    console.log("Received INVITE")
    var d = Client.Dialog(res).then(dialog => {
        dialog.send(res.CreateResponse(100))
        dialog.send(res.CreateResponse(180))
        var port = SDPParser.parse(res.message.body).media[0].port

        var s = new STREAMER('output.wav', '192.168.1.39', port)
        s.start().then(sdp => {
            console.log(sdp.split('SDP:')[1])
            var ok = res.CreateResponse(200)
            ok.body = sdp.split('SDP:')[1]
            dialog.send(ok)
        })

        dialog.on('BYE', (res) => {
            console.log("BYE")
            dialog.send(res.CreateResponse(200))
            dialog.kill()
        })
    })
})

解决方案

1. 对齐音频编码与SDP格式

从INVITE的SDP看,Yealink支持PCMU(payload 0)、PCMA(payload 8)等编码。当前FFmpeg命令用-f mulaw(对应PCMU),但输入音频被识别为PCMA,编码不匹配导致无声音。修改FFmpeg命令,明确指定编码:

# 使用PCMU编码(对应payload 0)
ffmpeg -re -i ${this.inputFilePath} -ar 8000 -ac 1 -codec:a pcm_mulaw -f rtp rtp://${this.rtpAddress}:${this.rtpPort}

若要使用PCMA,替换为-codec:a pcm_alaw,同时在200响应的SDP里将m=audio的格式改为8。

2. 修正SDP中的IP与端口

  • 确保SDP的c=字段IP是Yealink能访问到的本地服务端IP,不要错误替换成无关IP。
  • 确认解析后的RTP端口与INVITE中Yealink提供的接收端口一致(INVITE里是11782,当前解析结果为11784,需排查解析器逻辑)。

3. 构造兼容Yealink的SDP

手动生成符合Yealink风格的SDP,避免FFmpeg自动生成的字段不兼容:

v=0
o=- 20085 20085 IN IP4 192.168.1.3
s=SDP data
c=IN IP4 192.168.1.3
t=0 0
m=audio 11782 RTP/AVP 0
a=rtpmap:0 PCMU/8000
a=ptime:20
a=sendonly

注意a=sendonly(仅发送音频),端口使用Yealink的接收端口。

4. 验证RTP流状态

  • 查看FFmpeg的stderr输出,确认音频文件读取正常、转码参数生效。
  • 用Wireshark抓包,检查RTP包是否发送到Yealink的IP和端口,以及负载类型是否匹配指定的编码(如PCMU对应payload 0)。

内容的提问来源于stack exchange,提问作者Nik Hendricks

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 05:08:09