RTP RFC 1889 — 让 UDP 也能跑实时音视频
是什么
RTP(Real-time Transport Protocol,实时传输协议)是给实时音视频包加一层时间戳和序号的协议。日常类比:你给一筐快递盒子贴标签,写上”第几件、几点几分发的”,收件人就能按发货时间排好播放,丢了哪件也一目了然。
RTP 自己不传东西,它跑在 UDP 上面,给每个 UDP 包前面加一个 12 字节的头。这个头里有 4 个关键字段:
- 序号(16 bit):第几个包
- 时间戳(32 bit):这一帧在发送端的”采样时刻”
- SSRC(32 bit):这一路流的唯一标识
- payload type(7 bit):里面装的是什么编码(PCMU 音频?H.261 视频?)
接收端拿到这些字段就能:丢包重组、抖动消除、多人会议分流、按编码解码。
为什么重要
不理解 RTP,下面这些事都没法解释:
- 为什么 Zoom / 腾讯会议 / WebRTC / IP 电话不用 TCP——但又能保证语音连贯
- 为什么视频会议里丢一个包听不出来,丢 TCP 包却会卡 2 秒
- 为什么 WebRTC 的浏览器到浏览器音视频,底层包格式 30 年没换过——就是 RTP
- 为什么实时流媒体的 NAT 穿透总是要单独配(STUN/ICE)——因为 RTP 自己不管
核心要点
三个基本设计选择
-
跑 UDP 不跑 TCP:TCP 像挂号信,丢了就重发,但晚到的语音重发也没用,反而把后面的全堵住(队头阻塞)。UDP 让丢的就丢,RTP 在上层补”知道丢了什么、知道何时播”。
-
时间戳 ≠ 墙上时间:时间戳按”采样率”走。音频 8 kHz 采样,每个 20 ms 包 +160;视频 90 kHz 时钟,每帧 +3000。接收端拿这个时间戳算”应该几点播”,消除网络抖动。
-
数据流和控制流分开:RTP 跑数据,配套的 RTCP(RTP Control Protocol)跑控制——每几秒互相汇报”我收到多少、丢多少、抖动多大”。两者占连续两个端口(偶数数据、奇数控制)。
12 字节头的全貌(简化)
0 1 2 3+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|V=2|P|X| CC |M| PT | sequence number |+---------------+---------------+-------------------------------+| timestamp |+---------------------------------------------------------------+| SSRC |+---------------------------------------------------------------+- V=2 是版本号
- M(marker)标”一帧的最后一个包”——视频解码器靠它知道”该刷屏了”
- CC + 后续 CSRC:mixer 把多路合成一路时,把原始源标识列在这里
RTCP 报告和 5% 带宽规则
RTCP 包不能太多,否则吃带宽。RFC 规定 RTCP 最多占总带宽的 5%,发送间隔随会议人数自动拉长——50 个人开会时每人 5 秒发一次,500 个人时 50 秒发一次。这是个早期且优雅的拥塞自适应设计。
实践案例
案例 1:一通 VoIP 电话的包流
你拨打 IP 电话,麦克风每 20 ms 采样一次。每 20 ms 你的客户端封装一个 RTP 包:
- payload type = 0(PCMU,G.711 µ-law)
- timestamp += 160(8000 Hz × 0.02 s)
- sequence += 1
- payload = 160 字节音频样本
接收端缓冲 60 ms(jitter buffer),按 timestamp 排序,按 20 ms 节奏取出播放。丢了一个包就塞静音或上一帧,听上去几乎察觉不到。
案例 2:5 人视频会议怎么分流
5 个人都往会议桥(mixer)发 RTP,每人一个独立的 SSRC:
Alice SSRC=0xAAAA →Bob SSRC=0xBBBB → [mixer] → 每个参会者Carol SSRC=0xCCCC →...mixer 可以选两种模式:
- 转发:把 5 路原样转给每个人(每人收 4 路),客户端自己解 4 路混音
- 混音:mixer 自己把 5 路解码混成 1 路再编码发出,CSRC 列表保留原始 SSRC,让客户端知道”刚刚那段声音是谁说的”
WebRTC 时代主流改成 SFU(Selective Forwarding Unit)——只转发,不混音,因为 CPU 便宜带宽贵。
案例 3:WebRTC 里你看不见但每天用的 RTP
打开 Chrome 的 chrome://webrtc-internals,开一个 Google Meet,你会看到:
- 多路 RTP 流(音频一路、视频一路,可能还有屏幕共享)
- SSRC、PT、丢包率、抖动、RTT——全是 RTCP 报告里的字段
- 加了 SRTP 加密层(RFC 3711),但帧格式还是 RFC 1889 那 12 字节
30 年前的设计,今天每个浏览器都在跑。
踩过的坑
-
时间戳不是 Unix 时间:很多人第一次写 RTP 解析以为 timestamp 能直接转日期,结果发现是相对的、按采样率走。必须配合 RTCP 的 SR 报告里的 NTP 时间戳才能对齐到墙上时间。
-
序号 16 bit 会回卷:高码率视频几小时就把 65536 用完。接收端要追踪 cycle,常见 bug 是排序时把回卷的 0 当成”乱序很久的旧包”丢掉。
-
RTP 自己不加密:RFC 1889 原版是明文。生产必须套 SRTP(RFC 3711),WebRTC 强制 DTLS-SRTP,否则浏览器拒绝建连。
-
NAT 穿透不在 RTP 范围内:两端在 NAT 后面直接发 RTP 大概率到不了。要先用 STUN 探测公网映射,失败时走 TURN 中继,这套叫 ICE,是另外的协议族。
-
RFC 1889 ≠ 最新版:2003 年 RFC 3550 取代了 1889,加了一些安全和 mixer 细节,但 12 字节头一字未改。看老代码里出现 1889 不要慌。
适用 vs 不适用场景
适用:
- 实时音视频通话(VoIP / 视频会议 / WebRTC)
- 直播流媒体(早期 RTSP 拉流;今天大多换成 HLS/DASH,但内部转码仍可能用 RTP)
- 多源同步(多路摄像头按时间戳对齐)
不适用:
- 文件传输 / 网页 / API 调用 → 必须可靠、不要实时 → 用 TCP / http-2 / quic
- 海量并发直播(百万观众)→ HLS 切片走 CDN 比 RTP 划算
- 严格实时控制(机械臂、自动驾驶 in-vehicle)→ 用 DDS / time-sensitive networking
历史小故事(可跳过)
- 1992 年:Henning Schulzrinne 在 GMD-Fokus 提出最早的”音频包”草稿
- 1996 年 1 月:RFC 1889 发布,作者四人是实时网络名宿——其中 jacobson-1988 的 Van Jacobson 同时还是 TCP 拥塞控制的奠基人
- 2003 年:RFC 3550 取代 1889,是今天的”现行版”
- 2011 年起:WebRTC 把 RTP 搬进浏览器,从协议变成 Web 标准的一部分
学到什么
- 协议分层不是越多越好:RTP 只做”序号+时间戳+源标识”三件事,刻意不做拥塞、加密、重传——这种克制让它撑了 30 年
- 数据面和控制面分离是经典手法:RTCP 占 5% 带宽这个”自适应汇报”设计今天看仍然漂亮
- 抖动 vs 丢包是实时网络的两件事:丢包靠 FEC / 重传补,抖动靠 jitter buffer 补,两条路独立
- RFC 是一种特殊文本:短、密、可执行——一份 RFC 就是一份能让全世界互通的”接口契约”
延伸阅读
- 原始 RFC:RFC 1889(1996,正文约 70 页)
- 现行版:RFC 3550(2003,差异不大)
- WebRTC 入门:High Performance Browser Networking 第 18 章
- 视频教程:Computerphile — How RTP Works
- saltzer-1984-e2e —— 端到端原则解释了”为什么可靠性放上层”
- jacobson-1988 —— 同作者的 TCP 拥塞控制论文
关联
- tcp —— 反例:可靠传输,但实时音视频用不了
- quic —— UDP 上的现代可靠传输,与 RTP 一样选择不走 TCP
- http-2 —— 多路复用思想可以对照 RTP 多 SSRC
- saltzer-1984-e2e —— “端到端”原则让 RTP 敢于不做拥塞控制
- jacobson-1988 —— Van Jacobson 是 RTP 共同作者
- cerf-kahn-1974 —— TCP/IP 论文,整个 IP 协议栈的根