# 视频抖动缓冲与组帧 · 把破碎的包流重建成帧

> 收到的包乱序、有缺、时快时慢，得先缓冲、排序、拼成完整帧，才能解码。这一章读 video/frame_buffer.h：抖动缓冲怎么吸收网络抖动、组帧怎么等齐一帧的所有包，以及「缓冲大小」在流畅和延迟之间的根本权衡。

- 作者：David（道雾轩）
- 专栏：音视频与 WebRTC 详解（https://daiw.org/manual/webrtc.md）
- 最后更新：2026-07-14
- 原文：https://daiw.org/manual/webrtc/video-jitter-buffer
- 转载与引用：请注明出处并附原文链接（https://daiw.org/about/copyright）

# 视频抖动缓冲与组帧 · 把破碎的包流重建成帧

接收侧的第一场硬仗。[网络交给你的](https://daiw.org/manual/webrtc/rtp-demuxer)，是一串**乱序、有缺口、时快时慢**的 [RTP 包](https://daiw.org/manual/webrtc/rtp-packet)——绝不能直接喂[解码器](https://daiw.org/manual/webrtc/video-decode-recovery)。得先**缓冲、排序、拼成完整的帧**，还要**平滑掉网络的抖动**。这是**抖动缓冲（jitter buffer）+ 组帧**的活，`video/frame_buffer.h` 的 `FrameBuffer` 是核心。

## 网络抖动：包到得忽快忽慢

先看要对抗什么。发送端明明是[匀速发的](https://daiw.org/manual/webrtc/pacing)（每帧间隔均匀），但经过网络，包的**到达时间变得参差不齐**——这就是**抖动（jitter）**：

- 有的包一路顺畅、早到；有的排了队、晚到。
- [丢的包](https://daiw.org/manual/webrtc/nack-rtx)可能[重传](https://daiw.org/manual/webrtc/nack-rtx)后姗姗来迟。

如果收到就立即解码播放，画面就会**忽快忽慢地抽搐**。抖动缓冲的使命，就是**把参差的到达，重新变回匀速的播放**。

## 组帧：等齐一帧的所有包

抖动缓冲干两件交织的事。第一件是**组帧**：

<Callout type="info">
  **一帧[被切成了多个包](https://daiw.org/manual/webrtc/video-packetization)，得等它们**都到齐**才能拼成完整的一帧交给[解码](https://daiw.org/manual/webrtc/video-decode-recovery)——缺一个包，这帧就不完整，解不了。** `FrameBuffer` 收到包后，按 [RTP 时间戳](https://daiw.org/manual/webrtc/rtp-packet)把同一帧的包归拢，靠[序号连续 + marker 位](https://daiw.org/manual/webrtc/video-packetization)判断“这帧的包收齐了没”。收齐 → 拼成一帧，可以往下走；没齐（有缺口）→ 等——等[重传](https://daiw.org/manual/webrtc/nack-rtx)补上、或等到超时放弃（触发[关键帧请求](https://daiw.org/manual/webrtc/video-decode-recovery)）。所以抖动缓冲天然是个“等待区”：它容忍一定的乱序和缺失，给[重传 / FEC](https://daiw.org/manual/webrtc/fec) 争取补救的时间。
</Callout>

## 抖动吸收：攒一点再放

第二件事是**吸收抖动**：

<Callout type="info">
  **抖动缓冲**故意攒住**一小段已完成的帧，延迟一点点再播——用这点缓冲“垫底”，抹平网络到达的参差。** 想象水库：上游来水忽大忽小（抖动的到达），水库蓄一点水，下游就能匀速放水（平滑的播放）。缓冲的深度（攒多少）是**动态自适应**的：网络抖动大，就多攒点（缓冲深），扛得住更晚到的包；网络稳，就少攒点（缓冲浅），延迟更低。这个深度由观测到的[抖动统计](https://daiw.org/manual/webrtc/rtcp-reports)决定，`FrameBuffer` 和 [`VCMTiming`](https://daiw.org/manual/webrtc/video-timing) 配合算出“每帧该在何时播”。
</Callout>

## 根本权衡：流畅 vs 延迟

抖动缓冲藏着实时媒体一个**绕不开的权衡**：

<Callout type="warn">
  **缓冲越深，越流畅（能容忍更大抖动、更多[重传](https://daiw.org/manual/webrtc/nack-rtx)），但延迟越大；缓冲越浅，延迟越低，但一有抖动就卡。** 这是**流畅和低延迟的直接对立**：

  - 深缓冲（如 500ms）—— 什么抖动、[丢包重传](https://daiw.org/manual/webrtc/nack-rtx)都从容，画面丝滑，但你和对方的对话有半秒延迟，像卫星电话。
  - 浅缓冲（如 50ms）—— 延迟极低，对话跟手，但网络稍抖就[卡顿 / 丢帧](https://daiw.org/manual/webrtc/video-decode-recovery)。

  **没有两全**，只能按场景取舍：实时对话优先低延迟（浅缓冲，忍受偶尔卡顿），单向观看可以深缓冲（反正不用互动）。WebRTC 的抖动缓冲**动态调整深度**——在“当前网络能给的流畅”和“可接受的延迟”之间，实时找那个最优点。这个权衡，是所有实时媒体系统的宿命，[音频的 NetEq](https://daiw.org/manual/webrtc/source-map)（[第 7 部分](https://daiw.org/manual/webrtc/source-map)）面对的是同一个问题。
</Callout>

## 小结与下一章

接收侧第一关：**抖动缓冲 + 组帧**（`video/frame_buffer.h`）。**组帧**——按[时间戳](https://daiw.org/manual/webrtc/rtp-packet)归拢[一帧的多个包](https://daiw.org/manual/webrtc/video-packetization)，靠[序号 + marker](https://daiw.org/manual/webrtc/video-packetization)判断收齐，等[重传](https://daiw.org/manual/webrtc/nack-rtx)补缺。**吸收抖动**——攒一小段帧再匀速播，把网络参差的到达抹平，缓冲深度动态自适应。核心权衡：**缓冲深则流畅但延迟高、浅则低延迟但易卡**，按场景取舍。

帧拼齐了，但视频帧之间有**参考依赖**（[帧间预测](https://daiw.org/manual/webrtc/video-pipeline-overview)）——解码前必须理清“这帧依赖哪些帧、能不能解”。下一章看帧引用查找。👉 [帧引用查找](https://daiw.org/manual/webrtc/frame-reference-finder)。

## 参考资料

- [1] libwebrtc 源码.`video/frame_buffer.h`、`modules/video_coding/`（组帧、抖动估计 `jitter_estimator.h`）。
- [2] libwebrtc 源码.`modules/video_coding/timing/`（与 timing 配合）。
- [3] 关联[音频 NetEq](https://daiw.org/manual/webrtc/source-map)——同类抖动缓冲问题。
