3. RTP协议概述
各位同学,今天我们来聊聊RTP协议里一个看似简单、实则暗藏玄机的话题——负载类型(Payload Type,简称PT)。
说白了,PT值就是RTP包的“身份证”。它告诉接收端:这个包里装的是H.264视频,还是AAC音频,或者是Opus语音。没有这个标识,接收端根本不知道该怎么解码。
我刚开始做流媒体时,就吃过PT值的亏。有一次调试一个视频通话系统,画面死活出不来。查了半天,发现发送端填的PT值是96,接收端却按H.264去解——其实96对应的是动态负载类型,但两边协商没做好。嗯,从那以后我再也不敢小看这个8位的字段了。
3.1 PT值的标准定义
RTP协议在RFC 3550中定义,PT字段占用8个bit,取值范围0-127。其中0-95是静态分配,96-127留给动态协商。
静态PT值由IANA统一注册,常见的有:
| PT值 | 编码格式 | 时钟频率(Hz) | 声道数 | 说明 |
|---|---|---|---|---|
| 0 | PCMU | 8000 | 1 | G.711 μ-law |
| 3 | GSM | 8000 | 1 | 全速率 |
| 8 | PCMA | 8000 | 1 | G.711 A-law |
| 10 | L16 | 44100 | 2 | 立体声PCM |
| 14 | MPA | 90000 | 1 | MPEG音频 |
| 26 | JPEG | 90000 | - | MJPEG视频 |
| 31 | H.261 | 90000 | - | 老古董了 |
| 32 | MPV | 90000 | - | MPEG-1视频 |
| 34 | H.263 | 90000 | - | 早期视频编码 |
关键点:静态PT值只覆盖了最基础的编码格式。像H.264、AAC、Opus这些现代编码,全部使用动态PT值(96-127)。
3.2 动态PT值的协商机制
你可能会问:为什么H.264不用一个固定的PT值?
原因很简单。H.264有几十个profile和level,AAC有LC、HE、HEv2等变种,Opus的比特率可调范围极大。如果每个变种都分配一个静态PT值,IANA那点地址空间早就不够用了。
所以,动态PT值的协商就派上了用场。这个过程通常通过SIP或RTSP的SDP协商来完成。
我个人习惯把协商过程总结为三步:
- 发送方声明:在SDP中列出支持的编码格式及其对应的动态PT值
- 接收方选择:从发送方列出的格式中选择一个或多个
- 建立映射:双方确认PT值与编码格式的对应关系
举个例子,一个典型的SDP片段长这样:
m=video 5004 RTP/AVP 96 97
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1;profile-level-id=42e01f
a=rtpmap:97 H264/90000
a=fmtp:97 packetization-mode=0;profile-level-id=42001f
这里,96和97都映射到H.264,但参数不同。接收端可以根据自身能力选择其中一个。
我的经验:动态PT值协商时,建议把最常用的编码格式放在前面。如果接收端不支持第一个,会尝试第二个。我曾经见过一个设备只解析SDP中的第一个rtpmap,后面的全忽略——虽然这不符合规范,但现实世界就是这么魔幻。
3.3 常见编码的PT值映射
下面这张表是我在实际项目中常用的映射关系,大家可以直接参考:
| 编码格式 | 典型PT值 | 时钟频率 | SDP中的rtpmap | 备注 |
|---|---|---|---|---|
| H.264 | 96 | 90000 | H264/90000 | 最常用 |
| H.265 | 98 | 90000 | H265/90000 | 4K/8K场景 |
| AAC-LC | 97 | 48000 | MP4A-LATM/48000/2 | 注意声道数 |
| Opus | 99 | 48000 | opus/48000/2 | WebRTC首选 |
| VP8 | 100 | 90000 | VP8/90000 | WebRTC视频 |
| VP9 | 101 | 90000 | VP9/90000 | 高压缩率 |
| G.722 | 9 | 8000 | G722/8000 | 静态PT值 |
| iLBC | 102 | 8000 | iLBC/8000 | 旧版VoIP |
避坑指南:我曾经遇到过一个设备,它把AAC的时钟频率写成了44100而不是48000。结果接收端按44100去解码,音频全部变调。记住,AAC的采样率在SDP中必须与实际编码参数一致,不能随便写。
3.4 PT值协商的完整流程
为了让大家更直观地理解,我画了一张流程图:
这个流程看起来简单,但实际项目中坑不少。我挑几个常见的说说:
- PT值冲突:两个不同的编码用了同一个PT值。我遇到过SDP里写了两个rtpmap都指向96,接收端直接崩溃。
- 时钟频率不匹配:视频编码统一用90000,音频则各不相同。写错时钟频率会导致音画不同步。
- fmtp参数遗漏:H.264的packetization-mode、profile-level-id这些参数不写,接收端可能无法正确解码。
核心原则:动态PT值协商的本质是“先沟通,再传输”。发送方不能想当然地认为接收方支持某种编码,必须通过SDP交换能力信息。这也是RTP协议设计精妙之处——它把编码格式的协商留给了上层协议,自己只负责传输。
3.5 实际项目中的PT值选择建议
做了这么多年流媒体,我总结了几条PT值选择的经验:
- 优先使用96-100:大部分设备默认支持这个范围,兼容性最好
- 避免使用127:有些老设备把127当作保留值,可能会出问题
- 音频和视频的PT值不要重叠:虽然理论上可以,但调试时容易混淆
- 同一个编码的不同参数用不同PT值:方便接收端做能力协商
我的习惯:在项目中,我会固定一套PT值映射表。比如H.264主配置用96,H.264基线配置用97,AAC用98,Opus用99。这样不管是调试还是维护,大家都清楚。团队里新来的同事也不用猜。
好了,关于PT值与编码映射的内容就讲到这里。记住,PT值虽然只有8位,但它承载了编码格式协商的全部信息。搞懂了它,RTP协议就算入门了一半。