视频打包 · 压缩帧怎么切成 RTP 包
一帧压缩视频可能几十 KB,而一个 RTP 包只能装一千多字节——得切。这一章讲清各编解码器专属的 RTP 打包格式(VP8/VP9/AV1/H264 各不同)、为什么打包要懂编解码器的内部结构、以及 marker 位如何标记一帧的结束。收尾发送侧。
视频打包 · 压缩帧怎么切成 RTP 包
编码器吐出一帧压缩数据(EncodedImage),可能几十甚至上百 KB。而一个 RTP 包受 MTU 限制只能装一千多字节。所以一帧要切成多个 RTP 包——这就是打包(packetization)。看似只是“切分”,却要懂编解码器的内部结构。收尾发送侧。
一帧 → 多包
打包要懂编解码器
关键难点:切分不能瞎切,要顺着编解码器的内部结构切。 每种编解码器的压缩数据有自己的组织方式,于是每种有专属的 RTP 打包格式(payload format):
| 编解码器 | 打包格式 | 结构特点 |
|---|---|---|
| VP8 | RFC 7741 | 分区(partition),便于部分丢失恢复 |
| VP9 | draft | 带 SVC 层信息 |
| AV1 | RTP AV1 | OBU 单元,带 SVC 结构 |
| H264/H265 | RFC 6184/RFC 7798 | NAL 单元(NALU) |
为什么打包要懂编解码器结构?因为切分点和携带的元信息,直接影响丢包时能恢复多少。 以 H264 为例,它的压缩数据由 NAL 单元(NALU) 组成(每个 NALU 是一个语义单元,如一个片 slice)。打包器会尽量按 NALU 边界切——这样丢一个包,受损的是完整的某个 NALU,而非“半个 NALU + 半个另一个”,解码器更容易跳过 / 局部恢复。VP8 的分区、AV1 的 OBU 也是类似考量。此外,打包格式还携带编解码器专属的元信息——如 VP9/AV1 每个包属于哪个 SVC 空间 / 时间层,这样 SFU 不解码就能靠 RTP 层信息丢弃高层。打包不是无脑切字节,而是“懂内容地切”——这层编解码器专属逻辑,在 modules/rtp_rtcp/source/ 的各个 rtp_packetizer_xxx 里。
与打包对应:接收侧解包
发送侧打包,接收侧就要解包(depacketization)——把一批 RTP 包还原成一帧压缩数据,交给解码器。解包器(rtp_depacketizer_xxx)是打包器的逆,同样每种编解码器一个。它和抖动缓冲配合:抖动缓冲负责“等齐一帧的所有包、排好序”,解包器负责“把这些包的负载拼成一帧”。这是接收侧的第一步,下一章展开。
发送侧全景回顾
到这里,视频发送侧完整了。把前几章串起来:
从摄像头到网络,视频经采集 → 自适应调度 → 编码 → 打包,一路把“大而恒定的原始画面”压成“贴合网络的包流”。
小结与下一章
打包把一帧压缩视频切成多个 RTP 包(共享时间戳、序号连续、marker 位标帧尾)。关键是懂编解码器结构地切——每种编解码器有专属打包格式(H264 按 NALU、VP8 按分区、VP9/AV1 带 SVC 层信息),按语义边界切才利于丢包恢复,层信息还让 SFU 不解码就能丢层。接收侧对应解包。发送侧至此完整。
接下来转到接收侧。收到的包乱序、有缺、抖动,第一关是抖动缓冲 + 组帧——把破碎的包流重建成完整、有序的帧。下一章。👉 视频抖动缓冲与组帧。
参考资料
- [1] libwebrtc 源码.
modules/rtp_rtcp/source/rtp_packetizer_*.h(各编解码器打包器)、rtp_format.h。 - [2] IETF RFC 7741(VP8)、RFC 6184(H264)、RFC 7798(H265)、AV1 RTP。
- [3] 关联接收侧组帧。