BitrateAllocator · 总带宽分给各条流
拥塞控制估出一个总带宽,BitrateAllocator 把它分给各条发送流——音频优先保住,视频拿剩下的。这一章读 call/bitrate_allocator.h:流作为 observer 注册、带 min/max 约束,分配器如何在总量变化时重新分配。
BitrateAllocator · 总带宽分给各条流
RtpTransportControllerSend 估出了一个通话的总带宽,但一次通话有多条发送流(音频、视频、联播多档)。这个总量怎么分给它们?是 BitrateAllocator(call/bitrate_allocator.cc)的活。视频码率分配那章讲了“流内分层”,这一章讲更上一级——“流间分配”。
流注册为 observer
BitrateAllocator 用观察者模式工作(call/bitrate_allocator.h):
// call/bitrate_allocator.h(节选)
class BitrateAllocatorObserver {
// 分配变化时被回调:OnBitrateUpdated(分到的码率, ...)
};
struct MediaStreamAllocationConfig {
// 这条流的约束:min_bitrate, max_bitrate, priority, 能否被暂停...
};
void AddObserver(BitrateAllocatorObserver* observer, MediaStreamAllocationConfig config);每条发送流把自己注册为 BitrateAllocator 的 observer,并声明约束(MediaStreamAllocationConfig:最小 / 最大码率、优先级);分配器据总带宽和这些约束,给每条流分一份,通过回调 OnBitrateUpdated 通知它。 流拿到分配后,就通过 SetRates 告诉编码器“编到这个码率”。约束的意义:
min_bitrate—— 这条流至少要这么多才有意义(低于就没法编 / 干脆暂停)。max_bitrate—— 这条流最多用这么多(再多也没用,如视频分辨率上限)。- 优先级 —— 分配时谁先吃饱。
于是分配是一个“带约束的分配问题”:总量有限,每条流有 min/max/优先级,分配器要合理地切这块蛋糕。这和视频流内的层分配是嵌套的两级——BitrateAllocator 先在流间分(音频 vs 视频),视频流拿到份额后,VideoBitrateAllocator 再在层间分。
音频优先:核心原则
分配的核心原则,视频码率分配那章已强调过,这里落到实处:
BitrateAllocator 先喂饱音频(音频的 min_bitrate 优先满足),剩下的才给视频——因为声音比画面重要。 音频码率需求低(几十 kbps 的 Opus就够),所以“死保音频”代价很小,却保住了通话最关键的部分。带宽极度紧张时:先满足音频的 min → 剩下的给视频(可能视频降到很低甚至暂停某些档)。带宽宽裕时:音频吃饱后,富余的大头给视频提画质。这个“保听觉、让视觉”的排序,是WebRTC 资源分配的价值观,在 BitrateAllocator 这里是硬编码的分配逻辑。
动态:总量一变就重分
分配不是一次性的——它随总带宽实时变:
- 拥塞控制估出的总带宽变了(网络好转 / 恶化),
BitrateAllocator就重新分配,通过OnBitrateUpdated通知各流新的份额。 - 流的增减(协商加了 / 撤了一路媒体),也触发重分。
于是完整的码率传导链是:Transport-CC 反馈 → GoogCC 估总带宽 → BitrateAllocator 分到各流 → 各流 SetRates 到编码器 → 编码器调画质。BitrateAllocator 是这条链上“总量 → 每流”的关键一环。
小结与下一章
BitrateAllocator 把通话总带宽分给各发送流(比视频流内层分配高一级)。流注册为 observer 并声明 MediaStreamAllocationConfig(min/max 码率、优先级),分配器据总量和约束分份、OnBitrateUpdated 通知。核心原则音频优先(先喂饱音频 min,剩下给视频——声音比画面重要,音频需求低故代价小)。分配随总带宽动态重分。它是“总量 → 每流”传导的关键一环。
流的编解码,靠媒体引擎把编解码器接进来。下一章看 WebRtcVoiceEngine / WebRtcVideoEngine。👉 媒体引擎。