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模型动态协商的。这个过程我简单拆解一下:

  1. Offer端:列出支持的编解码器及其PT值
  2. Answer端:从Offer中选择一个子集,确认PT映射
  3. 媒体流:双方按照协商好的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与编解码器映射的完整流程,我画了一张图:

RTP负载类型(PT)与编解码器映射流程 发送端应用 选择编解码器 PT值映射 SDP Offer/Answer 接收端应用 匹配编解码器 PT值解析 RTP媒体流 关键说明: 1. 发送端先选择编解码器,再分配PT值 2. SDP Offer/Answer协商PT映射关系 3. RTP流中携带PT值,接收端据此选择解码器 4. 动态PT(96-127)需在SDP中明确声明rtpmap 5. 静态PT(0-95)可省略rtpmap,但WebRTC中很少用

这张图展示了从发送端选择编解码器,到接收端解析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也没问题。但为了减少麻烦,跟着主流走总是没错的。