3. RTP负载类型(PT)与编解码器映射
聊完RTP固定头,咱们得面对一个很实际的问题:接收端拿到一串RTP包,怎么知道里面装的是Opus音频还是VP8视频?答案就在PT字段——负载类型(Payload Type)。
这个字段只有7个比特,取值范围0-127。但你别小看这7个bit,它决定了整个媒体会话的编解码方式。我刚开始接触RTP时,总觉得PT就是个编号,后来踩了坑才明白,它背后牵涉到SDP协商、动态映射、以及不同浏览器之间的兼容性问题。
3.1 PT值的定义与分类
PT值本质上是一个标识符。接收端看到PT=111,就知道该用Opus解码;看到PT=96,就知道该用VP8。但这里有个关键点:有些PT值是固定的,有些是动态协商出来的。
| 分类 | 范围 | 特点 |
|---|---|---|
| 静态PT | 0-95 | IANA分配,编解码器固定绑定 |
| 动态PT | 96-127 | 通过SDP协商,应用层自由映射 |
静态PT是RFC 3551定义的。比如PCMU是0,PCMA是8,G.722是9。这些值在互联网诞生初期就定好了,基本不会变。但WebRTC时代,静态PT根本不够用——VP8、VP9、H264、Opus这些新编解码器,都得走动态PT。
核心要点:WebRTC几乎只用动态PT(96-127)。静态PT更多是传统VoIP系统的遗产,你可以在SIP电话里见到它们,但在Chrome和Firefox的WebRTC会话中,PT值基本都是96起步。
3.2 静态PT与动态PT的实战差异
静态PT的好处是简单。你不需要任何信令协商,看到PT=0就知道是PCMU,直接解码就行。但缺点也很明显——扩展性差。IANA分配一个静态PT要走漫长的标准化流程,等批下来,新编解码器黄花菜都凉了。
动态PT就灵活多了。发送端和接收端通过SDP交换m=行和rtpmap属性,把PT值和编解码器名称、时钟频率、声道数绑定在一起。举个例子:
m=audio 5004 RTP/AVP 111
a=rtpmap:111 opus/48000/2
这段SDP的意思是:音频流用PT=111,对应Opus编解码器,采样率48kHz,双声道。接收端看到这个映射关系,就知道怎么处理PT=111的RTP包了。
我曾在项目中遇到过一个坑:两个WebRTC客户端,一个用PT=111映射Opus,另一个用PT=111映射G.722。结果可想而知,音频全是噪音。后来排查发现,是SDP协商时rtpmap属性写错了。所以我的建议是:动态PT的映射关系一定要在SDP中明确声明,不要依赖任何默认值。
3.3 WebRTC中常用的PT值
WebRTC标准中,常用的编解码器都有约定俗成的PT值。虽然理论上你可以随便选(96-127范围内),但实际部署中,大家都会遵循Chrome和Firefox的默认行为。
| 编解码器 | 类型 | 常用PT | 时钟频率 | 备注 |
|---|---|---|---|---|
| Opus | 音频 | 111 | 48000 | WebRTC默认音频编解码器 |
| PCMU | 音频 | 0 | 8000 | 静态PT,兼容传统设备 |
| PCMA | 音频 | 8 | 8000 | 静态PT,欧洲常用 |
| VP8 | 视频 | 96 | 90000 | WebRTC默认视频编解码器 |
| VP9 | 视频 | 98 | 90000 | Google推广,压缩率更高 |
| H264 | 视频 | 102 | 90000 | 硬件编码支持好 |
| RED | 音频 | 112 | 48000 | 冗余编码,抗丢包 |
| CN | 音频 | 13 | 8000 | 舒适噪声,静态PT |
注意视频编解码器的时钟频率都是90000。这不是巧合,而是RTP规范的要求——视频的时钟频率固定为90kHz,用于计算RTP时间戳。音频则不同,Opus用48kHz,PCMU用8kHz,取决于编解码器本身的采样率。
实战技巧:如果你在抓包时看到PT=96,大概率是VP8。但别100%确定——有些实现会把H264也映射到96。唯一可靠的方式是查看SDP中的rtpmap属性。我个人习惯在代码中打印出SDP的完整内容,确认PT映射关系后再开始媒体处理。
3.4 PT映射的SDP协商过程
PT映射不是写死的,而是通过SDP Offer/Answer模型动态协商的。这个过程我简单拆解一下:
- Offer端:列出支持的编解码器及其PT值
- Answer端:从Offer中选择一个子集,确认PT映射
- 媒体流:双方按照协商好的PT值发送RTP包
举个例子,Chrome发起一个Offer:
m=video 5006 RTP/SAVPF 96 98 102
a=rtpmap:96 VP8/90000
a=rtpmap:98 VP9/90000
a=rtpmap:102 H264/90000
a=fmtp:102 packetization-mode=1;profile-level-id=42e01f
这段SDP的意思是:视频流支持三种编解码器,优先级从高到低是VP8(96)、VP9(98)、H264(102)。Answer端(比如Firefox)可能回复:
m=video 5008 RTP/SAVPF 96 102
a=rtpmap:96 VP8/90000
a=rtpmap:102 H264/90000
Firefox去掉了VP9,只保留VP8和H264。最终双方用VP8通信,PT=96。整个过程不需要任何人工干预,完全自动化。
这里有个细节:PT值在Offer和Answer中必须一致。如果Offer把VP8映射到96,Answer就不能把VP8映射到98。否则接收端会解码失败。我见过一些自研的WebRTC网关,在转发SDP时擅自修改了PT值,结果导致媒体流完全不可用。嗯,这种问题排查起来特别痛苦,因为抓包看到PT值是对的,但解码器就是匹配不上。
3.5 动态PT的冲突与避坑
动态PT虽然灵活,但有个潜在问题:冲突。两个不同的编解码器映射到同一个PT值,这在同一个SDP会话中是不允许的。但跨会话或跨厂商时,冲突时有发生。
举个例子:Chrome把Opus映射到111,但某个SIP网关可能把G.722也映射到111。如果这两个系统互通,音频就会乱套。解决办法是:在SDP协商阶段,确保每个PT值只对应一个编解码器。
避坑指南:我曾经在调试一个跨浏览器通话问题时,发现Chrome和Safari的PT映射完全不一样。Chrome用111映射Opus,Safari用111映射AAC。结果音频只有一边能听到。后来我在SDP中强制指定了Opus的PT为111,并移除了AAC的支持,问题才解决。所以我的建议是:尽量使用主流PT值,不要自己发明映射规则。
另外,注意PT值96-127虽然都是动态范围,但有些值有特殊用途。比如112通常用于RED(冗余编码),13是静态的CN(舒适噪声)。如果你把视频编解码器映射到112,可能会和RED冲突。虽然规范允许,但实际部署中容易引起混淆。
3.6 知识体系总览
为了让你更直观地理解PT与编解码器映射的完整流程,我画了一张图:
这张图展示了从发送端选择编解码器,到接收端解析PT并解码的完整链路。核心就是SDP协商这个中间环节——它像一座桥,把两端的PT映射关系对齐。没有这座桥,RTP流就是一堆乱码。
3.7 实战中的PT值选择建议
说了这么多理论,最后给点实际建议。你在开发WebRTC应用时,PT值怎么选?
- 音频优先用Opus:PT=111,这是WebRTC的默认值,兼容性最好
- 视频优先用VP8:PT=96,所有浏览器都支持
- 需要更高压缩率用VP9:PT=98,但注意Safari不支持
- 需要硬件编码用H264:PT=102,但要注意profile-level-id的协商
- 避免使用静态PT:除非你要和传统VoIP系统互通
记住一点:PT值本身不重要,重要的是SDP中声明的映射关系。只要双方协商一致,你用PT=127映射Opus也没问题。但为了减少麻烦,跟着主流走总是没错的。