7、丢包隐藏(PLC):音频 PLC 技术(波形替换、静音插入)、视频 PLC 技术(帧冻结、错误隐藏)
丢包隐藏,英文叫 Packet Loss Concealment,简称 PLC。
说白了,就是网络丢包了,但你不能让用户感觉到「卡」或者「断」。你得想办法把丢失的数据「补」上,让听感或视感尽量平滑。
我最早接触 PLC 是在做 VoIP 网关的时候。那时候带宽有限,丢包率动不动就 5% 以上。如果不做 PLC,用户听到的就是「咔咔咔」的爆音,体验极差。
这一章,我们重点聊音频 PLC 和视频 PLC 的核心思路。
7.1 音频 PLC:让声音「接」上
音频丢包,最直接的后果就是声音中断。人耳对中断非常敏感,尤其是连续语音中突然的静音,一听就不对劲。
音频 PLC 的目标就是:用算法把丢失的那段声音「猜」出来,或者用某种方式掩盖掉。
7.1.1 波形替换
波形替换是最经典的音频 PLC 方法。它的原理很简单:
- 假设语音信号在短时间内是平稳的
- 用丢包前的一小段波形,直接复制过来填补
- 或者用前后波形做交叉衰减,避免拼接处出现「咔嗒」声
我在项目中遇到过一个问题:波形替换对浊音(比如元音)效果很好,但对清音(比如「s」、「f」这种摩擦音)效果很差。因为清音是随机噪声,复制一段过去,听起来像「重复的嘶嘶声」,反而更奇怪。
7.1.2 静音插入
静音插入是最简单粗暴的方法。丢包了?直接插一段静音进去。
你可能会想:这能行吗?
嗯,在某些场景下,确实可以。比如非连续语音中的停顿处,或者背景噪声很低的场景。但如果是连续对话,静音插入会让用户感觉「对方突然不说话了」,体验很差。
7.1.3 音频 PLC 的进阶思路
除了波形替换和静音插入,还有更高级的方法:
- 基音周期重复: 检测语音的基音周期,然后按周期重复波形。适合浊音段。
- 时域拉伸: 用 WSOLA 或类似算法,把前后波形拉伸后拼接。效果更好,但计算量也更大。
- 基于模型的 PLC: 用 LPC(线性预测编码)模型预测丢失的语音。这是 3GPP 和 ITU 标准里用的方法。
我记得有一次做 WebRTC 的音频引擎优化,发现它的 PLC 默认用的是简单的波形重复。后来我改成了基音周期重复 + 交叉衰减,丢包率 10% 的情况下,MOS 分提升了 0.3 左右。
7.2 视频 PLC:让画面「停」住
视频丢包,后果更直观:画面花屏、马赛克、或者直接黑掉。
视频 PLC 的目标是:在丢包发生时,让用户看到的画面尽量「正常」,而不是一堆乱码。
7.2.1 帧冻结
帧冻结是最简单的视频 PLC 方法。丢了一帧?那就把上一帧再显示一遍。
你想想看,如果画面变化不大(比如新闻主播在说话),帧冻结的效果其实还行。用户只会觉得画面「卡了一下」,但不会看到花屏。
但如果画面运动剧烈(比如球赛、游戏直播),帧冻结就会导致明显的「跳帧感」。用户会感觉画面一顿一顿的。
7.2.2 错误隐藏
错误隐藏,英文叫 Error Concealment。它比帧冻结更智能。
核心思路是:利用视频编码的帧间预测特性,从参考帧中「修复」丢失的宏块或切片。
具体做法:
- 时域错误隐藏: 用前一帧对应位置的运动矢量,把参考帧的像素搬过来。说白了就是「运动补偿」。
- 空域错误隐藏: 用当前帧周围未丢失的像素,通过插值来填补丢失区域。适合帧内编码的块。
- 混合方法: 同时使用时域和空域信息,根据丢包位置和运动强度动态选择。
我曾经在一个视频会议项目中,遇到过一个问题:错误隐藏算法在低分辨率(360p)下效果很好,但到了 1080p,运动矢量不准,修复出来的画面反而有「鬼影」。后来我加了一个运动矢量可信度判断,低可信度的块改用空域插值,问题才解决。
7.2.3 视频 PLC 的局限性
视频 PLC 不是万能的。它有几个硬伤:
- 无法修复 I 帧丢失: I 帧是独立编码的,没有参考帧。如果 I 帧丢了,后续所有 P/B 帧都无法解码。这时候只能请求关键帧刷新。
- 累积误差: 如果连续丢包,错误隐藏会不断累积误差,画面会越来越模糊。
- 计算开销: 复杂的错误隐藏算法(比如基于光流的修复)计算量很大,移动端可能扛不住。
7.3 本章知识体系
下面这张图,是我梳理的音频和视频 PLC 的核心技术脉络。你可以把它当作一个快速参考。
这张图把音频和视频 PLC 的核心方法都列出来了。你可以看到,无论是音频还是视频,PLC 的本质都是「用已知信息推测未知信息」。区别只在于:音频是时域信号,视频是空时域信号。
好了,这一章就到这里。PLC 是实时通信中「看不见」但「感受得到」的技术。做得好,用户觉得流畅;做得不好,用户直接挂电话。希望这一章的内容对你有帮助。
公众号:蓝海资料掘金营,微信deep3321