解码与丢包恢复 · 花屏了怎么办

帧到点解码、渲染;但丢包会让参考链断裂、画面花屏。这一章讲清 VideoDecoder 解码、关键帧作为恢复锚点、PLI 请求关键帧的时机,以及「冻结等关键帧」还是「显示花屏」的取舍。收尾第 6 部分。

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

解码与丢包恢复 · 花屏了怎么办

接收管线的终点:到点的帧交给解码器还原成像素,渲染到屏幕。顺利时波澜不惊,但网络丢包会让参考链断裂、画面花屏——怎么恢复才是接收侧真正的考验。这一章收尾第 6 部分。

解码:压缩帧 → 像素

解码是编码的逆,由 VideoDecoder(和 VideoEncoder 对称的抽象)完成:吃一个压缩帧,吐一个 VideoFrame(I420 像素),交给渲染。解码器同样由工厂创建、可用硬件解码(手机的解码芯片),同样统一接口、多编解码器实现。正常流程平淡:组好的帧→ 解码 → 渲染。真正复杂的是出了岔子怎么办。

花屏的成因:参考链断了

帧引用那章讲过:预测帧依赖参考帧。丢包毁掉某帧后:

  • 依赖它的后续帧解出来是错的——参考的内容不对,解码器基于错误的基础“预测”,得到的是花屏 / 拖影 / 马赛克。
  • 而且会传染:错误的帧又被后续帧参考,花屏一直蔓延,直到出现一个不依赖任何历史的关键帧“重启”。

所以视频丢包的典型症状,是“花屏一片,持续一两秒,然后突然一下清晰”——那“突然清晰”的一刻,就是收到了新关键帧。

恢复:请求关键帧

丢包让[参考链断裂、无可挽回时,接收端发 PLI(Picture Loss Indication)请求一个关键帧——关键帧自包含、不依赖历史,是重建画面的“锚点”。 发送端收到 PLI,让编码器立即产一个关键帧发过来;接收端解出这个关键帧,画面就“接上”了,花屏结束。

但关键帧的代价不小:它自包含、编码整幅画面,比预测帧大好几倍——发一个关键帧是一次带宽冲击,还要切成很多包。所以不能一丢包就狂发 PLI:那会导致关键帧满天飞、带宽被吃光、拥塞加剧,恶性循环。接收端要权衡:能靠重传补回缺帧就别请求关键帧(重传一个小包,远比一个关键帧便宜);只有重传也救不回、参考链彻底断了,才发 PLI。这个“能修就修、修不了才重来”的判断,靠帧引用查找提供的依赖信息。

更精细的:丢包通知 LNTF

除了“请求整个关键帧”这种重手段,还有更精细的机制 LNTF(Loss Notification):接收端告诉发送端“我具体丢了哪一帧”,发送端可以选择从一个仍可用的旧帧预测新帧(而非发昂贵的关键帧),让损失更小地恢复。这是“介于重传和关键帧之间”的中间手段,在支持的编解码器上能更省带宽地恢复。

取舍:冻结还是花屏

丢包恢复期间,接收端还有个显示策略的取舍:

参考链断了、新帧解出来是花的,该显示“花屏”还是“冻住上一个好帧”? 两种选择:

  • 显示花屏 —— 忠实显示解码结果,但花屏很难看、甚至引起不适。
  • 冻结(freeze) —— 停在最后一个正确的帧,等关键帧来了再更新。画面“卡住”一下,但比花屏观感好。

WebRTC 通常选冻结——宁可短暂卡住,也不显示花屏。用户对“卡一下再恢复”的容忍度,远高于“满屏马赛克”。所以你视频通话时看到的“画面卡住几百毫秒然后恢复”,正是接收端在冻结等关键帧。这又是一个实时媒体“两害相权取其轻”的工程决策:完美不可得时,选那个用户最不反感的降级方式。

小结与第 6 部分回顾

解码由 VideoDecoder(可硬件)把压缩帧还原成像素渲染。丢包毁掉参考帧会导致花屏蔓延,恢复靠请求关键帧(PLI,自包含锚点,但大而贵,故能重传就别请求)或更精细的 LNTF。显示策略上,WebRTC 通常冻结(卡一下)而非显示花屏——观感更好。

第 6 部分「视频管线」到此完整:发送侧总览→帧→采集编码入口→编码器→自适应→联播SVC→码率分配→打包;接收侧抖动缓冲组帧→帧引用→渲染时机→解码恢复。视频从摄像头到屏幕的全程,已经拆透。

视频讲完,音频是另一片天地——它有自己独特的挑战(丢包隐藏、回声消除)。第 7 部分进入音频管线,尤其 NetEq 和 AEC3,是 WebRTC 音频的两颗明珠。👉 第 7 部分即将展开。

参考资料

  • [1] libwebrtc 源码.api/video_codecs/video_decoder.h、video/(解码调度)、modules/video_coding/(loss notification)。
  • [2] libwebrtc 源码.modules/rtp_rtcp/source/rtcp_packet/pli.h(PLI)、loss notification 相关。
  • [3] 关联帧引用、NACK 重传。

本页目录