视频打包 · 压缩帧怎么切成 RTP 包

一帧压缩视频可能几十 KB,而一个 RTP 包只能装一千多字节——得切。这一章讲清各编解码器专属的 RTP 打包格式(VP8/VP9/AV1/H264 各不同)、为什么打包要懂编解码器的内部结构、以及 marker 位如何标记一帧的结束。收尾发送侧。

作者 David更新于 第 58 篇(共 112 篇)

视频打包 · 压缩帧怎么切成 RTP 包

编码器吐出一帧压缩数据(EncodedImage),可能几十甚至上百 KB。而一个 RTP 包受 MTU 限制只能装一千多字节。所以一帧要切成多个 RTP 包——这就是打包(packetization)。看似只是“切分”,却要懂编解码器的内部结构。收尾发送侧。

一帧 → 多包

一帧压缩视频被切成多个 RTP 包,它们共享同一个 RTP 时间戳(都属于这一帧),序号连续,最后一个包设 marker 位标记“这帧到此为止”。 接收端据此重组:看到一批相同时间戳的包,收集齐(靠序号连续、marker 收尾),就能拼回完整的一帧。这也回答了 RTP 包那章关于 marker 位的伏笔——视频里它就是“帧结束”标记。一帧越大,切的包越多(1080p 关键帧可能切几十上百个包),这也是为什么大帧对丢包更脆弱:包越多,丢中一个的概率越大。

打包要懂编解码器

关键难点:切分不能瞎切,要顺着编解码器的内部结构切。 每种编解码器的压缩数据有自己的组织方式,于是每种有专属的 RTP 打包格式(payload format):

编解码器打包格式结构特点
VP8RFC 7741分区(partition),便于部分丢失恢复
VP9draft带 SVC 层信息
AV1RTP AV1OBU 单元,带 SVC 结构
H264/H265RFC 6184/RFC 7798NAL 单元(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] 关联接收侧组帧。

本页目录