← 返回

实时视频(三):解码与断流

系列导航:8 | 9 | 10

浏览器有 VideoDecoder 接口,不代表当前 H.264/H.265 配置一定可用。能力探测、输入格式、解码队列和断流重建必须放在同一条状态链里。当前 demo 采用 Worker 解耦主线程,Rust/WASM 在解码提交前检查码流结构。

能力探测绑定具体配置

收到参数集后构造 VideoDecoderConfig,配置中包含 codec string、description、codedWidth、codedHeight 和必要的硬件提示。先调用 VideoDecoder.isConfigSupported(),成功后再创建解码器。参数集摘要、分辨率或 profile 变化时,旧结果失效,必须重新探测。

stateDiagram-v2 [*] --> Idle Idle --> Probing: 收到参数集 Probing --> WebCodecs: 支持 Probing --> Wasm: 不支持或初始化失败 WebCodecs --> Playing: 关键帧可解码 Wasm --> Playing: Worker 初始化完成 Playing --> WaitingKeyframe: 丢包或配置变化 WaitingKeyframe --> Probing: 新参数集 Playing --> Recovering: 断流或解码错误 Recovering --> Probing: 重建成功 Recovering --> Error: 超过重试预算

探测失败和解码失败是两类信号。前者说明配置不被当前实现接受,后者可能来自 Annex B/长度前缀不匹配、缺失参考帧或坏包。用异常重试代替能力探测会让错误路径反复创建解码器,页面只会越来越卡。

输入格式只转换一次

H.264 可以使用 Annex B 起始码,也可以使用长度前缀格式,具体取决于解码入口和 description。输入层确定一种内部格式,再由适配器做一次转换。每个 EncodedVideoChunk 携带 type、微秒时间戳和数据所有权,不能在 Worker 和主线程之间重复复制同一缓冲。

Rust/WASM 探测器在这一层检查 NAL 数量、VCL 数量、参数集、关键帧和 SPS 元数据。它把结构错误提前反馈给 Worker,避免把明显坏包交给像素解码器。h26x-worker.ts 把探测摘要附在帧消息中,页面用 bitstream 状态区分网络丢包与解码器失败。

队列背压和关键帧恢复

decodeQueueSize 持续上涨表示输入速度超过解码速度。实时预览不能无限排队,过期的 delta 帧可以丢弃,积压超过预算时清空队列并回到 waiting-keyframe。关键帧等待期间只保留适用的参数集和有限数量的候选单元。

每个解码器实例绑定一个 generation。切换分辨率、参数集或回退路径时递增 generation,旧实例迟到的输出直接 close(),不能绘制到新画面。所有 VideoFrame 在渲染完成后关闭;Worker 终止、Canvas 销毁和事件监听都走统一的释放函数。

WASM 回退的真实边界

WASM 适合在浏览器原生能力不满足时提供覆盖面。它的 CPU 和内存成本需要按分辨率、帧率和并发路数测量,多路视频可以设置并发上限或请求低码流。WASM 初始化、资源加载、运行时崩溃和输出格式错误分别记录,不能把它们都显示成“浏览器不支持”。

当前 Rust 模块是 H26x bitstream probe,不是完整像素解码器。像素输出来自 H.264/H.265 库或浏览器原生解码路径。这个边界写进文档和测试,后续替换探测器时不会误改渲染职责。

音画同步使用统一时钟

渲染层只消费微秒级媒体时间。RTP 时间戳到单调时间的转换在输入层完成,音频存在时以音频播放时钟为基准;没有音频时使用首个媒体时间与单调时钟建立映射。视频帧早于目标时刻短暂等待,晚于窗口则丢弃,不能让旧帧把实时预览拖成回放。

诊断记录音频时间、视频时间、队列长度和差值趋势。固定偏移与持续漂移分别处理:固定偏移可调整起点,持续漂移应回到时间戳频率、采样率或 RTCP 映射检查。页面后台运行时定时器会被节流,恢复播放时清掉过期帧并重新等待当前关键帧。

断流诊断要区分阶段

状态至少区分 source-disconnectedtransport-timeoutbitstream-invaliddecoder-errorrender-stalled。每个状态保存开始时间、重试次数、末次序列号、参数集摘要、队列长度和恢复动作。网络断开重连成功后仍需等待关键帧,不能把 WebSocket 重新建立直接当成画面恢复。

恢复预算包括重连次数、等待关键帧时长和 WASM/服务端回退次数。超过预算返回稳定错误并保留诊断摘要,方便用户报障。日志中不记录完整 RTSP URL、用户名和密码,只保存源 ID、协议、编码和会话 ID。

验证顺序

Rust 层运行解析器测试,Web 层运行 Worker、播放器和断流测试,再执行 WASM 构建和生产构建。浏览器检查重点包括:支持与不支持的 codec、参数集变化、队列清空、页面卸载、断线后关键帧等待以及 bitstream 元数据是否从 Worker 到 UI。

浏览器能力检测通过不代表真实摄像头码流可播,WASM 回退可加载也不代表多路并发达到业务帧率。发布前仍需使用目标编码、分辨率和网络条件做端到端测量。

参考资料