1. RTP协议基础回顾:RTP/RTCP协议栈结构、RTP头部格式详解、Payload Type与编解码器映射、SSRC与CSRC机制

各位同学,咱们今天正式开讲。第一节课,我打算带大家把RTP协议的老底翻一翻。你可能会说:“RTP嘛,不就是个传输音视频数据的协议吗?”嗯,话是没错,但WebRTC里对RTP的用法,跟传统流媒体还真不太一样。我当年刚接触WebRTC时,就因为在SSRC冲突问题上栽了个跟头,排查了整整两天……后来才发现,是对基础理解不够透。

所以这节课,咱们把地基打牢。后面29章,你才能盖得起高楼。

1.1 RTP/RTCP协议栈结构

先看整体架构。RTP全称是Real-time Transport Protocol,实时传输协议。它跑在UDP上面,专门用来传音视频数据。RTCP是它的控制兄弟,负责传质量反馈、统计信息。

我习惯把这一层关系画成下面这张图,你一看就明白:

RTP/RTCP 协议栈结构 应用层(WebRTC / 音视频App) 会话层:SDP 协商(编解码器、SSRC、Payload Type) 传输层:RTP(媒体数据) + RTCP(控制/统计/反馈) RTP 端口(偶数) | RTCP 端口(奇数) 网络层:UDP(无连接、低延迟) IP 网络(物理传输) 关键点 RTP 不保证 QoS RTCP 提供反馈 UDP 无重传

说白了,RTP就是负责把摄像头采集到的每一帧画面、麦克风录到的每一段声音,切成一个个小包,打上时间戳和序号,扔到网络里。RTCP则在旁边默默统计:丢包率多少?延迟多大?然后告诉发送方“你该调整码率了”。

这里有个细节我提醒你注意:RTP和RTCP共用一对UDP端口,RTP用偶数端口,RTCP用下一个奇数端口。比如你用端口10000传RTP,那RTCP就是10001。这是RFC 3550规定的,WebRTC里也严格遵循。

核心记忆点:RTP传数据,RTCP传控制。两者缺一不可。没有RTCP,发送方就是个瞎子,不知道网络状况如何。

1.2 RTP头部格式详解

接下来咱们拆开RTP包,看看它的头部长什么样。我当年第一次看RFC 3550里的头部格式图,说实话,有点懵。但后来自己抓包分析了几次,就全通了。

标准的RTP头部固定12字节,结构如下:

偏移(字节) 字段 长度(bit) 说明
0 V(版本) 2 当前为2,RFC 3550定义
0 P(填充) 1 为1时尾部有填充字节
0 X(扩展) 1 为1时头部后有扩展头
0 CC(CSRC计数) 4 表示CSRC标识符数量
1 M(标记) 1 视频帧边界标记
1 PT(负载类型) 7 标识编解码器类型
2-3 序列号 16 每发一个包+1,用于检测丢包和排序
4-7 时间戳 32 采样时刻,用于播放同步
8-11 SSRC 32 同步源标识符,唯一标识一个流
12-...(可选) CSRC列表 32×CC 贡献源列表,混音时使用

嗯,这里我要重点说几个字段,因为WebRTC里经常跟它们打交道:

  • 序列号(Sequence Number):每发一个RTP包就加1。接收方靠它判断有没有丢包。我遇到过一个问题:发送方序列号从0开始,但接收方期望从随机值开始,结果前几个包全被当成乱序包丢弃了……后来发现是没做序列号初始化同步。
  • 时间戳(Timestamp):注意,它不是绝对时间,而是采样时钟的计数。比如音频用48kHz采样率,那每增加1就代表1/48000秒。视频则常用90kHz时钟。
  • M标记位(Marker):视频里,一帧可能拆成多个RTP包。最后一个包会把M位置1,告诉接收方“这一帧齐了”。

我的小技巧:调试RTP问题时,我习惯先用Wireshark抓包,然后过滤 rtp,直接看序列号和时间戳的走势。如果序列号出现大的跳变,基本就是丢包了。如果时间戳长时间不变,那编码器可能卡住了。

1.3 Payload Type与编解码器映射

PT字段只有7个bit,取值范围0-127。其中0-96是静态分配,RFC 3551里写死了。比如:

  • PT=0:PCMU(G.711 μ-law)
  • PT=8:PCMA(G.711 A-law)
  • PT=111:G.722

但WebRTC里用的编解码器,比如VP8、VP9、H.264、Opus,它们的PT值都是动态分配的。什么意思呢?就是在SDP协商阶段,双方商量好:你用PT=96代表VP8,我用PT=100代表Opus。

举个例子,SDP里会这样写:

m=video 9 UDP/TLS/RTP/SAVPF 96 97 98
a=rtpmap:96 VP8/90000
a=rtpmap:97 VP9/90000
a=rtpmap:98 H264/90000

这里96、97、98就是动态PT值,分别映射到VP8、VP9、H.264。时钟频率都是90000Hz,这是视频的标准值。

注意避坑:我曾经遇到一个互操作问题:Chrome发出来的视频流PT=96,但接收方是旧版Firefox,它硬编码认为PT=96是H.263。结果解码全乱码。后来发现是SDP协商时Firefox没正确解析rtpmap。所以,永远不要假设对端能正确理解动态PT,一定要在SDP里明确声明。

音频也一样,Opus通常用PT=111:

m=audio 9 UDP/TLS/RTP/SAVPF 111
a=rtpmap:111 opus/48000/2

48000是采样率,2是声道数(立体声)。

1.4 SSRC与CSRC机制

最后咱们聊聊SSRC和CSRC。这两个概念,说白了就是“谁发的这个包”。

SSRC(同步源):每个RTP流都有一个唯一的32位SSRC标识符。比如你开了一个WebRTC通话,你的摄像头视频流有一个SSRC,你的麦克风音频流有另一个SSRC。接收方靠SSRC区分不同的流。

SSRC是随机生成的,但冲突概率虽然低,不是零。RFC 3550里专门有一节讲SSRC冲突检测和解决。WebRTC里也实现了这个机制:如果检测到冲突,就换一个新的SSRC,并发送RTCP BYE包通知对端。

CSRC(贡献源):这个在混音场景下用。比如一个会议服务器把3个人的音频混成一路RTP流,那SSRC是服务器的,CSRC列表里放那3个人的原始SSRC。接收方看到CSRC列表,就知道这包音频是混了哪几个人的声音。

我画了张图帮你理解:

SSRC 与 CSRC 工作机制 发送端 A SSRC=0xA1B2C3D4 发送端 B SSRC=0xE5F6A7B8 发送端 C SSRC=0xC9D0E1F2 混音器 / MCU SSRC=0x12345678 CSRC列表:[A,B,C] 接收端 识别SSRC和CSRC 关键理解 SSRC = 谁发的流 | CSRC = 原始贡献者列表 混音后SSRC变,CSRC保留原始信息

在WebRTC里,SSRC的分配通常由SFU(选择性转发单元)或MCU(混音单元)来管理。如果是P2P通话,那就简单了,每个媒体轨道一个SSRC。

一句话总结:SSRC标识“这个包属于哪个流”,CSRC标识“这个流是由哪些源混合而成的”。WebRTC里,SSRC冲突是灾难性的,会导致音视频串流。所以框架内部有完善的冲突检测机制,你一般不用操心,但排查问题时得知道有这回事。

好了,这一章的基础内容就到这里。RTP头部那12个字节,你最好能背下来——不是死记硬背,而是理解每个字段为什么存在。后面讲NACK、FEC、带宽估计时,你会反复用到这些知识。