3. RTP负载类型与编码映射:PT值与编码格式对应关系

聊到RTP,有个东西你绕不开——负载类型(Payload Type,PT)。说白了,它就是RTP包里的一个“标签”,告诉接收方:“嘿,我这包里装的是H.264视频,还是AAC音频?”

我刚开始接触RTP时,总觉得PT就是个数字而已。直到有一次调试一个视频通话系统,画面死活出不来,抓包一看——PT值对不上。嗯,从那以后我再也不敢小看这个字段了。

3.1 PT字段:RTP包的“身份证”

RTP头部里有个7位的PT字段,取值范围0-127。它决定了接收端怎么解析这个包里的数据。

你想想看,如果发送端说“这是H.264”,接收端却按MPEG-2去解码,那画面肯定花掉。所以PT值的映射必须双方约定好。

核心要点:PT值就是编码格式的“代号”。发送端填什么,接收端就得按什么解。两边不一致,通信就崩。

3.2 静态负载类型:0-95,RFC定义的“老规矩”

PT值0-95属于静态负载类型。这些值在RFC 3551里写死了,比如PCMU是0,PCMA是8,GSM是3。你只要看到PT=0,就知道是G.711 μ-law音频,不用猜。

但这里有个坑——静态类型只覆盖了最老的那批编码格式。H.264、AAC、Opus这些“新贵”都不在里面。为什么?因为RFC 3551是2003年的东西,那时候H.264还没普及呢。

PT值 编码格式 时钟频率(Hz) 声道数
0 PCMU (G.711 μ-law) 8000 1
3 GSM 8000 1
4 G.723 8000 1
8 PCMA (G.711 A-law) 8000 1
10 L16 (线性PCM 16位) 44100 2
14 MPA (MPEG-1/2 Audio) 90000 1
26 JPEG (Motion JPEG) 90000 -
31 H.261 90000 -
32 MPV (MPEG-1/2 Video) 90000 -
34 H.263 90000 -

个人经验:我在做VoIP网关时,经常要处理PT=0和PT=8的转换。国内用PCMA多,国外用PCMU多。两边不通时,抓包一看PT值就明白了。

3.3 动态负载类型:96-127,现代编码的“自留地”

PT值96-127是动态负载类型。说白了,就是留给应用自己定义的。H.264、AAC、Opus、VP8、VP9……这些现代编码格式都用这个范围。

为什么叫“动态”?因为同一个PT值在不同会话里可能代表不同编码。比如这次通话PT=96是H.264,下次通话PT=96可能是VP8。全靠SDP协商时约定。

我见过不少新手踩的坑:写死PT=96表示H.264,结果对方用PT=96传AAC,解码直接崩。记住——动态类型必须通过SDP协商,不能硬编码。

PT值范围 类型 特点 典型编码
0-95 静态 RFC定义,全局固定 PCMU、PCMA、GSM、H.261
96-127 动态 SDP协商,会话内有效 H.264、AAC、Opus、VP8

3.4 常用编码格式的PT值:实战中的“熟面孔”

下面这几个PT值,你在实际项目中几乎天天见。我按使用频率排个序:

3.4.1 H.264 — 视频编码的“扛把子”

H.264没有静态PT值,通常用96-102之间的值。我在WebRTC项目里,H.264的PT值最常见的是96或100。但记住——这不是标准,全靠SDP里写:

// SDP中H.264的典型声明
m=video 5004 RTP/AVP 96
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1;profile-level-id=42e01f

这里96就是动态分配的。时钟频率90000是视频的“老传统”,所有视频编码都用这个值。

3.4.2 AAC — 音频编码的“音质担当”

AAC在RTP里通常用96-127之间的值。但要注意——AAC有好几种变体:AAC-LC、AAC-HE、AAC-LD。我在做直播项目时,AAC的PT值常用97或98:

// SDP中AAC的典型声明
m=audio 5006 RTP/AVP 97
a=rtpmap:97 MPEG4-GENERIC/48000/2
a=fmtp:97 streamtype=5;profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3

这里48000是采样率,2是声道数。fmtp那串参数看着复杂,其实就是告诉接收端AAC的打包细节。

避坑指南:我曾经在AAC的PT值上吃过亏。对方SDP里写PT=97是AAC,但实际发的是AAC-LD(低延迟版)。我的解码器只支持AAC-LC,结果音频全是噪声。后来我养成了习惯——不光看PT值,还要看fmtp里的profile-level-id。

3.4.3 Opus — 新时代的“全能选手”

Opus是WebRTC的“亲儿子”,PT值通常用111或112。它的时钟频率固定是48000Hz:

// SDP中Opus的典型声明
m=audio 5008 RTP/AVP 111
a=rtpmap:111 opus/48000/2
a=fmtp:111 minptime=10;useinbandfec=1

Opus有个好处——它自带前向纠错(FEC)。minptime=10表示最小打包时长10ms,useinbandfec=1开启带内FEC。我在弱网环境下特别喜欢用Opus,丢包30%还能听清。

3.5 编码映射的核心逻辑:一张图看懂

下面这张图展示了PT值从SDP协商到RTP收发的完整流程:

RTP负载类型映射流程 阶段1:SDP协商 发送端声明:PT=96 → H.264,PT=111 → Opus 接收端确认:同意映射关系 阶段2:RTP发送 视频包:PT=96,载荷=H.264 NAL单元 音频包:PT=111,载荷=Opus帧 阶段3:RTP接收 读取PT=96 → 查找映射表 → H.264解码器 读取PT=111 → 查找映射表 → Opus解码器 阶段4:音视频输出 视频渲染 + 音频播放

3.6 实战中的PT值选择建议

说了这么多理论,来点实际的。我在多个项目里总结了几条PT值选择的原则:

  • 视频优先用96-100:H.264、H.265、VP8、VP9都往这个范围放。我习惯把H.264放96,VP8放97,这样排查时一眼能认出来。
  • 音频优先用111-116:Opus放111,AAC放112,PCMU/PCMA用静态值0和8。这个范围跟WebRTC默认值一致,兼容性好。
  • 避免用127:有些老设备把127当保留值,用了可能出问题。我吃过这个亏,后来再也不用127了。
  • SDP里必须写rtpmap:动态类型不写rtpmap,接收端根本不知道PT对应什么编码。这是新手最容易犯的错误。

我的习惯:每次搭建新系统,我都会先写一份PT值分配表,贴在项目文档里。比如:PT=96 H.264视频,PT=111 Opus音频,PT=112 AAC音频。团队里所有人都按这个来,省得后期排查时抓瞎。

3.7 常见问题与排查思路

最后分享几个我遇到过的PT值相关故障,你以后八成也会碰到:

现象 可能原因 排查方法
视频黑屏,音频正常 视频PT值映射错误,解码器不匹配 抓包看RTP的PT值,对比SDP中的rtpmap
音频噪声或静音 AAC的profile-level-id不匹配 检查fmtp参数,确认AAC变体一致
双方SDP交换正常,但媒体不通 动态PT值冲突,两个编码用了同一个PT 检查SDP中是否有重复的PT值
WebRTC通话中突然断流 Opus的minptime设置不一致 确认双方minptime相同,建议都用10ms

嗯,PT值这块内容就这些。说白了就是“协商-映射-收发”三个步骤。你只要记住:静态类型看RFC,动态类型看SDP,大部分问题都能解决。