WebRTC 直播码率与关键帧:0.5/1.5/5 Mbps 三档

作者:

在

结论:直播码率自适应是三档电平控制:默认 0.5/1.5/5 Mbps,每 0.2 秒采样 RTCP 接收报告,丢包率 ≥10% 立即降一档、≥5% 累积五次降档,零丢包累积五次升档且降档后升档等待次数翻倍。关键帧请求与码率都是全局 Params 电平,不含相机 ID、请求 ID 或逐路确认,多路会话共享同一请求位。时间维度上,WebRTC PTS 取的是发送时的 monotonic_ns 换算为 90 kHz,并不等于相机采样的 SOF/EOF。以下为固定源码快照 c8fb906 及固定版本 teleoprtc 归档、并对原码率类与视频轨道做隔离探针的静态分析,未运行真实相机、编码器或网络丢包测试。

关键函数与跨进程调用

入口 源码行为 边界
LivestreamBitrateController.__init__()、run() 默认三档 0.5/1.5/5 Mbps(最高档可由 STREAM_BITRATE 覆盖);初始化就把最高档写 LivestreamEncoderBitrate。每 0.2 秒按接收报告调档。 只采样 get_stats() 字典的第一份报告,并非多轨道最坏/加权网络状态;未观察或报告完全不变则保持当前控制状态。
_sample()、set_quality() fraction_lost / 256 转换丢包比例;手动 high/med/low 直接写 Params 并停止自动控制,auto 恢复自动。 手动模式不更新内部 level、counter、up_samples;回到 auto 后首次调档仍以先前自动状态为基准。代码未核验编码器是否实际应用码率。
StreamSession.run()、message_handler() 会话开始置 LivestreamRequestKeyframe=True;数据通道可切相机、调质量、暂停/恢复视频、启用计时 SEI。 livestreamCameraSwitch 直接更换唯一轨道的订阅源,没有重置轨道 _seen_keyframe 或显式新关键帧请求。
LiveStreamVideoStreamTrack.recv()、switch_camera() 订阅主题采用 conflate=True;首次看到关键帧标记 _seen_keyframe=True 并清全局请求,但收到非空消息都会返回视频包。PTS 用发送时 monotonic_ns 换算为 90 kHz。 _seen_keyframe 是请求清除门,不是发帧门;入流可能先送非关键帧。多轨道共用一个关键帧 Params 位。
_build_frame_data() 普通模式拼接 EncodeData.header + data;可选计时 SEI 额外放入 SOF→EOF、EOF→日志、日志→本机当前时间三段毫秒延迟及 wall-clock 毫秒。 SEI 里同时存在单调时间差与墙钟绝对时间;跨时钟域比较必须说明校时、漂移和跳变。

码率状态机与探针结论

run() 只有丢包率 ≥5% 或 ≤0% 两类调档分支;0% 到 5% 间的正丢包率不清零 counter。因此「连续五次」更准确地说是相关分支累计五次采样,不保证中间完全没有低丢包样本。_sample() 只把整个报告元组相同视为没有新样本,不能证明报告对应新的媒体时间窗口。

五项原 LivestreamBitrateController 类隔离探针核对了:10% 报告立即降档并把升档窗口从 5 翻倍到 10;5% 报告中穿插 1% 样本仍累计到五次降档;降档后需十个零丢包样本才升档;手动 low 切回 auto 时内部仍在 high,后续零丢包不回写 high,Params 继续留在手动 low;完全相同的报告元组不会重复采样。这些探针用人工 RTCP 报告和 Params 记录器执行原类方法,不验证真实 WebRTC 统计刷新、编码器应用或网络体验。

关键帧请求与轨道行为

RTCP PLI 调轨道 request_keyframe(),写全局 Params;每个直播编码器每帧读取码率/关键帧参数再编码。Params 是全局电平触发控制,不含相机 ID、请求 ID、过期时刻或逐路 ACK;软件编码后端只记录不支持设置码率/请求关键帧。多路会话共享 LivestreamRequestKeyframe 和 LivestreamEncoderBitrate,而各轨道各自处理媒体,一个轨道清除全局关键帧请求可能使另一个仍未见关键帧的轨道失去持续请求。

LiveStreamVideoStreamTrack.enable(False) 重置 _seen_keyframe,但 switch_camera() 不重置它。即使 _seen_keyframe=False,recv() 也不会阻挡非关键帧,只等待有消息;消费者能否解码取决于编码器/封装及新关键帧到达,不能由该标志保证。五项轨道与时间探针确认:订阅采用 conflate=True、切相机仅替换 socket、首次关键帧前的非关键帧仍返回、PTS 由本机取帧时单调时钟换算为 90 kHz、首次关键帧清全局请求。探针使用假基类、假帧与假时钟,不证明真实编码包可解码、相机切换连续或跨设备时钟已对齐。

帧时间与 SEI 字段

PTS 使用取帧时间计算,不使用 EncodeData.idx.timestampSof/Eof;网络等待、conflate 丢旧包或暂停/恢复都可能让媒体时间轴与相机采样时间轴不同。计时 SEI 中三个毫秒差依次为 SOF→EOF、EOF→日志、日志→读取时刻,第四项为墙钟毫秒。插件层需同时保留采样、编码、传输与显示时间。现有 WebRTC 会话测试检查单轨道 recv() 的 PTS 单调趋势与空包字节,并测试消息代理/非 JSON 请求;未见码率状态机、多轨道关键帧确认、切相机或 SEI 时间字段的原套件专门回归。

时间字段 来源 不能代替什么
WebRTC PTS 发送时 monotonic_ns 换算为 90 kHz 相机采样 SOF/EOF;反映的是取帧/发送节奏。
SEI SOF→EOF 编码内部两段单调时间差 完整采样时间消息契约。
SEI 日志→本机当前时间 单调时间差 跨设备已校时;仍需说明漂移与跳变。
SEI 第四项 wall-clock 绝对毫秒 可直接与其他单调时钟字段比较。

新硬件与插件接口验收

编码插件至少应声明 codec/profile、每路输入时钟、最大分辨率/码率、关键帧间隔、可变码率是否受支持及实际应用后的回执;关键帧请求用 camera_id + request_id + deadline 逐路确认已输出可解码关键帧。码率控制器应以每条流的实际 RTCP 统计及编码器应用回执为依据,手动/自动切换时同步内部档位并明确计数器重置规则,无报告、报告停滞与网络失联要分开。回放/台架应覆盖多路同时启动、一条流无关键帧、相机切换、暂停恢复、PLI 连发、无接收报告、0/中/高丢包、软件编码后端不支持动态码率,以及 wall clock 跳变时 SEI 的解释。

常见问题

WebRTC 直播码率有哪几档,怎么切换?

默认三档 0.5/1.5/5 Mbps,最高档可由 STREAM_BITRATE 覆盖。控制器每 0.2 秒按 RTCP 接收报告调档:丢包率 ≥10% 立即降一档、≥5% 累积五次降档,零丢包累积五次升档且降档后升档等待次数翻倍。

关键帧请求是逐路确认的吗?

不是。关键帧请求与码率都是全局 Params 电平,不含相机 ID、请求 ID 或逐路 ACK;多路会话共享同一个请求位,一个轨道清除全局请求可能使另一个仍未见关键帧的轨道失去持续请求。

切相机会自动请求新关键帧吗?

不会。switch_camera() 只更换唯一轨道的订阅源,不重置 _seen_keyframe,也没有显式新关键帧请求;而且即使 _seen_keyframe=False,recv() 也不会阻挡非关键帧。成功切换后的解码连续性需实测。

WebRTC 的 PTS 就是相机采样时间吗?

不是。PTS 用发送时的 monotonic_ns 换算为 90 kHz,不使用 EncodeData.idx.timestampSof/Eof;网络等待、conflate 丢旧包或暂停恢复都可能让媒体时间轴与采样时间轴不同。

延伸阅读

参考资料

  • 项目 02 —— openpilot-research/WEBRTC_BITRATE_KEYFRAME_TIMING_DEEP_DIVE.md(码率状态机、关键帧请求、PTS/SEI 时间字段与探针结论)。
  • 项目 02 —— openpilot-research/ 同目录远程 RPC 与视频编码专题(会话边界与编码路径作为相邻边界参考)。

说明:本文为对固定源码快照的静态结构分析,未运行完整测试套件、未连接硬件、未验证目标车型的真实参数组合。文中所有阈值、行号索引与配置均出自源文档,标注为未验证的结论保留其原有边界。

更新记录

首次发布 2026-10-11 最近核对:2026-10-11

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注