1. RTP协议概述:RTP/RTCP协议族介绍、RTP在流媒体传输中的角色、RTP与UDP/TCP的关系、RTP协议栈架构

各位同学,咱们今天正式开讲RTP协议。说实话,我做了十几年流媒体,见过太多人一上来就扎进代码里,结果被丢包、抖动、音画不同步折磨得死去活来。其实很多坑,都是因为没搞懂RTP在协议栈里的位置。

嗯,咱们先搭个框架。把RTP的来龙去脉理清楚,后面实战才不会跑偏。

1.1 RTP/RTCP协议族介绍

RTP,全称是Real-time Transport Protocol,实时传输协议。它还有个兄弟叫RTCP,Real-time Transport Control Protocol。这俩通常成对出现,一个管数据,一个管控制。

我习惯把RTP比作快递员——负责把音视频数据包准时送到。RTCP呢,就像快递公司的客服系统,时不时给你回传一下“包裹到哪了”、“路上丢了几件”、“网速还行不行”。

它们俩的关系,用一张图就能看明白:

RTP/RTCP 协议族架构 应用层(H.264 / AAC / Opus 等编码数据) RTP(实时数据传输) RTCP(质量控制与反馈) 传输层(UDP / TCP) 网络层(IP) RTP与RTCP共用同一个传输层端口号(RTP: 偶数端口,RTCP: 下一个奇数端口)

你看,RTP和RTCP在传输层之上是并行的。RTP负责搬运音视频数据,RTCP负责搬运控制信息。它们共用一对UDP端口,RTP用偶数端口,RTCP用下一个奇数端口。这个设计我一直觉得挺巧妙——一个端口对,搞定所有事。

1.2 RTP在流媒体传输中的角色

RTP在流媒体里到底扮演什么角色?说白了,它就是个“带时间戳的快递员”。

普通的TCP传输,只管数据到没到,不管数据什么时候该到。但音视频不一样——你想想看,视频帧必须在特定时间点播放,早到或晚到都不行。RTP的核心价值就在这里:它给每个数据包打上了时间戳和序列号。

RTP的核心职责:

  • 时间同步:通过时间戳告诉接收端“这个包应该在什么时候播放”
  • 丢包检测:通过序列号发现“中间丢了几个包”
  • 包序恢复:即使包乱序到达,也能按序列号重新排好
  • 载荷类型标识:告诉接收端“这个包里面是H.264还是AAC”

我记得有一次做视频会议项目,对方反馈画面卡顿。我抓包一看,RTP序列号跳了好几个,但接收端没做丢包重传逻辑。嗯,这就是典型的“只用了RTP的数据传输,没用好它的控制能力”。

1.3 RTP与UDP/TCP的关系

这个问题我几乎每次面试都会问。很多人觉得RTP必须跑在UDP上,其实不准确。

特性 RTP over UDP RTP over TCP
实时性 高(无重传延迟) 低(TCP重传会引入延迟)
丢包处理 应用层自行处理 TCP自动重传
典型场景 直播、视频会议、VoIP 点播、RTSP隧道、防火墙穿透
头部开销 小(UDP 8字节 + RTP 12字节) 较大(TCP 20字节 + RTP 12字节)
适用网络 可控内网或QoS保障网络 公网、防火墙严格的环境

我个人习惯:能走UDP尽量走UDP。为什么?因为实时通信最怕的就是“等”。TCP为了保证可靠,丢包后会重传,但重传的包可能已经过了播放时间,到了也没用。

避坑指南:我曾经在一个跨国直播项目里,因为网络防火墙只开放了TCP 80端口,被迫把RTP封装在TCP里。结果延迟从200ms飙到了1.2s。后来用了UDP打洞+应用层FEC前向纠错,才把延迟压回400ms以内。所以,选UDP还是TCP,得看你的网络环境能不能接受UDP。

1.4 RTP协议栈架构

咱们把RTP协议栈从上到下拆开看看。一个完整的RTP包,长这样:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|X|  CC   |M|     PT      |       sequence number         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           timestamp                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           synchronization source (SSRC) identifier            |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|            contributing source (CSRC) identifiers             |
|                             ....                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                   RTP payload (音视频数据)                      |
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

这个头部结构,我建议你背下来。每个字段都有它的用途:

  • V(版本):当前是版本2,固定为2
  • P(填充位):有些加密算法要求数据对齐,会用这个位
  • X(扩展位):需要额外头部信息时置1
  • CC(CSRC计数):混音场景下,告诉你有几个参与者
  • M(标记位):视频帧的结束、音频话路的开始,靠它标记
  • PT(载荷类型):告诉接收端“这是H.264还是AAC”
  • 序列号:丢包检测和排序全靠它
  • 时间戳:RTP的灵魂字段,决定了播放节奏
  • SSRC:每个数据源的唯一ID,防止冲突

注意:RTP头部固定12字节,但如果有CSRC列表或扩展头,长度会变。解析时一定要先读CC和X字段,再决定头部总长度。我见过有人硬编码12字节,结果解析带扩展头的包时直接崩了。

说到SSRC,我踩过一个坑。有一次做多路混音,两个编码器不小心用了相同的SSRC。接收端直接把两路音频当成一路,音质一塌糊涂。后来我加了个SSRC冲突检测,发现冲突就重新随机生成一个。嗯,这个机制在RFC 3550里其实有写,但很多人没注意。

好了,这一章咱们把RTP的定位、协议族关系、传输层选择、包结构都过了一遍。这些都是后面实战的基础。你想想看,如果连RTP头部字段都认不全,后面做丢包重传、做jitter buffer,那不就是空中楼阁吗?