视频管线总览 · 从采集到渲染

一帧视频从摄像头到对方屏幕,要走采集→自适应→编码→打包→传输→抖动缓冲→组帧→解码→渲染一长串。这一章画出发送侧 VideoStreamEncoder 与接收侧 VideoReceiveStream2 两条管线的全貌,以及 video/ 与 modules/video_coding/ 的分工,为第 6 部分建立地图。

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

视频管线总览 · 从采集到渲染

拿到拥塞控制给的目标码率,视频这一路终于要动起来了。一帧视频从你的摄像头,到对方的屏幕,要穿过一长串环节——任何一环出问题,就是「卡、糊、花、飘」。第 6 部分拆解这条管线(video/ 与 modules/video_coding/)。先建地图。

两条管线,一条链路

视频有发送和接收两条管线,首尾相接成一条完整链路:

图表加载中…

发送侧的核心是 VideoStreamEncoder(video/video_stream_encoder.h),接收侧的核心是 VideoReceiveStream2(video/video_receive_stream2.h)。 一发一收,各领一条管线:

接收侧比发送侧还复杂——因为它要对抗网络的混乱(乱序、丢包、抖动),把破碎的包流重建成流畅的画面。VideoReceiveStream2 那个 2 又是现代重写的标记。

视频为什么这么难

音频和视频都要实时传,但视频的挑战大一个量级,原因值得先说清:

  • 数据量巨大 —— 一路 1080p 视频未压缩每秒几百 MB,必须重度压缩(几百倍),这就引入了编解码的全部复杂性。
  • 帧间有依赖 —— 视频压缩靠帧间预测(这一帧只编码「和上一帧的差异」)。于是丢一个包可能毁掉后续一串帧(参考链断了),这是视频对丢包格外敏感的根源。
  • 要适配多变的网络与算力 —— 带宽、CPU 都在变,视频要能实时降质保流畅、分档服务不同接收者。

这三点,决定了视频管线的每个环节都不简单。

分工:video/ 与 video_coding/

第 6 部分的代码横跨两个目录:

目录管什么
video/管线编排:VideoStreamEncoder、VideoReceiveStream2、自适应、码率分配
modules/video_coding/编解码支撑:抖动缓冲 / 组帧、帧引用、FEC、打包、timing

具体的编解码器实现(VP8/VP9/AV1/H264/H265)则在 modules/video_coding/codecs/,通过统一的 VideoEncoder 抽象接入。

本部分路线

第 6 部分按「发送 → 接收」走一遍这条管线:

  1. 视频帧(数据长什么样)→ 采集与编码器输入 → 编码器抽象与编解码器 → 自适应 → 联播与 SVC → 码率分配 → 打包。
  2. 接收:抖动缓冲与组帧 → 帧引用查找 → 渲染时机 → 解码与丢包恢复。

小结与下一章

视频有发送(VideoStreamEncoder:采集→自适应→编码→打包)和接收(VideoReceiveStream2:组帧→理引用→解码→按时渲染)两条管线。视频难在数据量大(须重压缩)、帧间依赖(丢包毁一串)、要适配多变网络与算力。代码横跨 video/(编排)与 modules/video_coding/(编解码支撑)。

一切从「一帧视频在内存里长什么样」开始。下一章看 VideoFrame 与它的像素缓冲。👉 视频帧。

参考资料

  • [1] libwebrtc 源码.video/video_stream_encoder.h、video/video_receive_stream2.h。
  • [2] libwebrtc 源码.modules/video_coding/(编解码支撑)、g3doc/adaptation.md(自适应)。
  • [3] 关联第 5 部分拥塞控制——视频码率的来源。

本页目录