视频码率分配 · 总预算分给各层
拥塞控制给了总码率预算,联播/SVC 有多条流多层,怎么分?这一章读 video/video_bitrate_allocator.h:VideoBitrateAllocator 如何把总码率分配到各层、遵循「先喂饱低层再给高层」的原则,以及码率不足时怎么砍掉高层。
视频码率分配 · 总预算分给各层
拥塞控制给了这条视频流一个总码率预算,而联播 / SVC 有多条流 / 多层。总预算怎么分到各层?这是 VideoBitrateAllocator(video/video_bitrate_allocator.h)的活。分配策略看似简单,却直接影响体验。
问题:僧多粥少怎么分
假设总预算 1.5 Mbps,一条 SVC 流有三层,各层“吃饱”分别要 300 kbps / 600 kbps / 1500 kbps。怎么分?
核心原则:「先喂饱低层,有剩再给高层」——从基础层开始,逐层往上,喂饱一层才喂下一层。 为什么?因为层是叠加的:基础层是一切的地基,没喂饱基础层,高层再多码率也没用(高层依赖低层解码)。所以分配是逐层填满:
- 总预算 1.5 Mbps,先给基础层 300 kbps(喂饱)→ 剩 1.2 Mbps。
- 再给第二层 600 kbps(喂饱)→ 剩 600 kbps。
- 剩下的 600 kbps 给第三层(它想要 1500 但只有 600,给它 600,这层就质量打折但能用)。
如果总预算只有 500 kbps 呢?只喂得饱基础层(300),第二层只能给 200(不够它的 600)——那第二、三层可能干脆不发(发个半死不活的层没意义)。这就是联播里“码率不足时高档流被关掉”的由来:与其每档都发一点点、每档都糊,不如集中资源保住能保的档。VideoBitrateAllocator 就是执行这套“逐层填、不够就砍高层”逻辑的地方,GetAllocation 产出各层的码率分配。
音频优先:更上层的分配
再往上看,码率分配其实是两级的:
- 通话级 ——
BitrateAllocator(第 9 部分)先在音频流和视频流之间分:音频优先保住(语音断了通话就废了,而画面糊点还能忍),剩下的给视频。 - 流级 —— 视频拿到它那份后,
VideoBitrateAllocator再在这条视频流的各层 / 各档之间分(本章)。
“音频优先于视频”是 WebRTC 一个坚定的价值排序。 想想:视频会议网络突然变差,你更能忍受哪个——画面糊成马赛克但对方声音清楚,还是画面高清但声音断断续续?几乎所有人选前者:听不清等于没法沟通,看不清还能凑合。所以码率紧张时,系统死保音频(音频码率需求本就低,几十 kbps 就够),把稀缺的带宽尽量喂给它,视频再降质 / 砍档。这个“保听觉、让视觉”的优先级,贯穿 WebRTC 的资源分配——是对“实时沟通,声音第一”的深刻理解。
分配的动态性
码率分配不是算一次就完——它随拥塞控制的目标码率实时变。网络变好,总预算涨,VideoBitrateAllocator 就把更多码率分给高层、甚至重新开启之前砍掉的档;网络变差,预算缩,就逐层减、砍高层。这个分配结果又通过 SetRates 告诉编码器“每层编多大”。分配是连接拥塞控制和编码器的最后一环——总预算在这里被翻译成每层的具体码率。
小结与下一章
VideoBitrateAllocator 把总码率预算分到联播 / SVC 的各层,核心原则**「先喂饱低层,有剩再给高层」(层叠加,基础层是地基),不够就砍掉喂不饱的高层**(集中资源保能保的)。更上层,BitrateAllocator(第 9 部分)先在音频 / 视频间分——音频优先死保(声音第一)。分配随目标码率动态变,结果经 SetRates 落到编码器。
编码 + 分配都齐了,压缩帧要打成 RTP 包发出。但不同编解码器的打包方式各异。下一章看视频打包。👉 视频打包。
参考资料
- [1] libwebrtc 源码.
video/video_bitrate_allocator.h、modules/video_coding/utility/simulcast_rate_allocator.h、svc/svc_rate_allocator.h。 - [2] libwebrtc 源码.
call/bitrate_allocator.h(通话级音视频分配,第 9 部分)。 - [3] 关联拥塞控制码率控制。