# 渲染时机 VCMTiming · 一帧该什么时候显示

> 帧解好了不能立刻显示——要在正确的时刻渲染，才能流畅且与音频同步。这一章读 video_coding/timing/timing.h 的 VCMTiming::RenderTime，讲清渲染时刻怎么由抖动、解码耗时、音视频同步共同决定，以及它如何把参差的到达变成匀速的画面。

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

# 渲染时机 VCMTiming · 一帧该什么时候显示

[帧可解了](https://daiw.org/manual/webrtc/frame-reference-finder)，但**不能解出来就立刻显示**。显示早了、晚了都不行：早了画面跳，晚了对不上[声音](https://daiw.org/manual/webrtc/rtcp-reports)。每一帧都有一个**正确的渲染时刻**，算它的是 `VCMTiming`（`modules/video_coding/timing/timing.h`）。这一章看它。

## RenderTime：算出该显示的时刻

`VCMTiming` 的核心方法很直白：

```cpp
// modules/video_coding/timing/timing.h(节选)
Timestamp RenderTime(uint32_t rtp_timestamp, Timestamp now) const;
```

给它一帧的 [RTP 时间戳](https://daiw.org/manual/webrtc/rtp-packet)和当前时刻，它算出“这帧应该在**什么时候**渲染出去”。

<Callout type="info">
  **`RenderTime` 综合几个因素，算出每帧的目标渲染时刻，让画面**匀速、且和[音频对齐](https://daiw.org/manual/webrtc/rtcp-reports)**：**

  - **[抖动缓冲延迟](https://daiw.org/manual/webrtc/video-jitter-buffer)** —— 为吸收网络抖动要“垫底”的那段延迟。
  - **解码耗时** —— [解码](https://daiw.org/manual/webrtc/video-decode-recovery)这帧要花多久，得**提前**这么多开始解，才能在目标时刻显示。
  - **[音视频同步](https://daiw.org/manual/webrtc/rtcp-reports)偏移** —— 为了和音频对齐口型，视频可能要额外等一点（或提前）。

  把这些一加，就是“这帧从现在起、还要等多久渲染”。于是尽管[包的到达是参差的](https://daiw.org/manual/webrtc/video-jitter-buffer)，每帧被**摆到它该在的时间点**上显示，画面就**匀速流畅**了。`RenderTime` 本质是把“发送端的时间轴”（RTP 时间戳）映射回“接收端的墙上时间轴”，中间加上必要的缓冲和补偿。
</Callout>

## 音视频同步：视频等音频

渲染时机里，**音视频同步（A/V sync）** 是个专门的考量，呼应 [RTCP SR 那章](https://daiw.org/manual/webrtc/rtcp-reports)：

<Callout type="info">
  **音频和视频是[两条独立的流](https://daiw.org/manual/webrtc/rtp-packet)，各有各的[抖动缓冲](https://daiw.org/manual/webrtc/video-jitter-buffer)和延迟，要让它们“口型对得上”，得把两条流的播放时刻**对齐到同一个绝对时间**。** 靠的是 [RTCP SR 里的 RTP↔NTP 时间戳映射](https://daiw.org/manual/webrtc/rtcp-reports)——它让接收端知道“这帧视频”和“这段音频”在**真实世界的同一刻**采集。`VCMTiming` 据此调整视频的渲染时刻，使之和音频的播放对齐。通常是**视频等音频**（音频延迟对听感影响更大、更难调，所以让视频迁就音频）。WebRTC 里 `RtpStreamsSynchronizer` 一类负责协调两条流的延迟，让 `VCMTiming` 算出的视频渲染时刻，和音频播放严丝合缝。**“口型同步”这个用户最容易察觉的质量指标，就落在这里。**
</Callout>

## 平滑与追赶

`VCMTiming` 还要处理**动态变化**：

- 网络突然变差，[抖动变大](https://daiw.org/manual/webrtc/video-jitter-buffer)，它会**加大缓冲**（渲染时刻往后推），用一点延迟换流畅。
- 网络恢复，它会**慢慢把延迟收回来**（逐渐提前渲染，别一下子跳），追回低延迟。

这个“缓冲深度随网络平滑调整”和[抖动缓冲那章的权衡](https://daiw.org/manual/webrtc/video-jitter-buffer)是一体两面——`VCMTiming` 是那个权衡在“每帧渲染时刻”上的落地。它追求的，始终是[实时媒体的核心目标](https://daiw.org/manual/webrtc/video-jitter-buffer)：**在当前网络下，尽量低的延迟 + 尽量流畅的画面 + 和音频同步**。

## 小结与下一章

**`VCMTiming::RenderTime`** 算每帧的**正确渲染时刻**，综合[抖动缓冲延迟](https://daiw.org/manual/webrtc/video-jitter-buffer)、**解码耗时**、[音视频同步](https://daiw.org/manual/webrtc/rtcp-reports)偏移——把[参差的到达](https://daiw.org/manual/webrtc/video-jitter-buffer)摆成**匀速的显示**。**音视频同步**靠 [RTCP SR 的 RTP↔NTP 映射](https://daiw.org/manual/webrtc/rtcp-reports)对齐两条流（通常视频等音频），`RtpStreamsSynchronizer` 协调，决定了“口型对得上”。它随网络[平滑调整缓冲深度](https://daiw.org/manual/webrtc/video-jitter-buffer)，追求低延迟 + 流畅 + 同步。

到点了，终于**解码并显示**。但网络丢包会让解码遇挫——最后一章看解码与丢包恢复，收尾第 6 部分。👉 [解码与丢包恢复](https://daiw.org/manual/webrtc/video-decode-recovery)。

## 参考资料

- [1] libwebrtc 源码.`modules/video_coding/timing/timing.h`（`VCMTiming::RenderTime`）、`inter_frame_delay.h`。
- [2] libwebrtc 源码.`video/rtp_streams_synchronizer2.h`（音视频同步）。
- [3] 关联 [RTCP SR 时间戳映射](https://daiw.org/manual/webrtc/rtcp-reports)。
