你下载一个文件时丢了一个包,网卡会安安静静把它重传回来,文件最终完好无损。可你打电话时丢了一个包,对方听到的却是"滋——"的一声破音,然后这事就过去了,没人补传。
这不是历史遗留问题,而是一个精心设计的选择。实时语音走的是 UDP,不是 TCP;丢了的语音帧,宁可不要,也不补传。原因算一笔账就清楚了:语音编码器每 20 毫秒产出一帧,而网络往返时延(RTT)在现网轻松就有几十到两百毫秒。也就是说,等一个丢掉的包重传回来,说话人已经又说完了 3 到 10 帧内容——重传到的那个音再插回去,就像你听人讲话时,中途倒带重放半秒钟,对话节奏全毁。更糟的是 TCP 的队头阻塞:一个包丢了,排在后面的所有包都得等它,语音会变成一段一段的顿挫。
那不重传,丢包导致的空洞怎么办?答案是两条腿走路:用"慢半拍"换取确定性,用"编瞎话"填补空洞。这两条腿,就是本文的主角——抖动缓冲(Jitter Buffer)和丢包隐藏(PLC,Packet Loss Concealment)。它们是语音质量链路上最不显眼、却决定用户"感受"的两块装置。
物理教材里的抖动(Jitter)指相位抖动,而 VoIP 语境里的抖动是一个统计概念:数据包到达间隔的波动。
理想网络像地铁:每 20 毫秒一帧,风雨无阻。真实网络像早高峰的公交:这一班 5 毫秒就到了,下一班堵了 80 毫秒。这种"忽快忽慢"就是抖动。注意,抖动不等于时延——时延是你等多久,抖动是"你等多久"这件事本身忽大忽小。
抖动从哪来?排队。每个路由器、每个基站调度器、每个防火墙的端口里都有队列,负载一重,排队时延就会剧烈波动。此外还有包乱序、路由切换导致的路径变化,都会表现为到达间隔的抖动。
对文件下载,抖动无所谓,TCP 的接收窗口会兜底。对实时语音,抖动是致命的:解码器必须以恒定的 20ms 节奏吃帧,快一帧没有内容可解,慢一帧缓冲耗尽——两种情况都会产生人耳极易察觉的断续。
解决思路朴素得像生活经验:要赶一场重要的会议,你会提前一班车出门,宁可在楼下等三分钟。等待的三分钟,就是缓冲。
接收端收到第一个包时不立即播放,而是先攒一小段,然后设定一个确定性的播放时刻表:
$$T_{play}(i) = T_{first} + \hat{d} + k \cdot \hat{J}$$
其中 $T_{first}$ 是本次话突(talkspurt,一段连续说话)首包的到达时刻,$\hat{d}$ 是估计的平均网络时延,$\hat{J}$ 是抖动估计值,$k$ 是一个 1 到 4 之间的保险系数。$k$ 越大越保险,但延迟也越大——这就是语音工程里永恒的天平。
整流的原理可以这样理解:缓冲把"到达时间的方差"吃进了自己肚子里。包早到就在队列里多排一会儿,包晚到就消耗队列的库存。只要网络时延的波动不超过缓冲深度,解码器看到的就是一个完美的恒定节奏。
但缓冲有两个失效模式,值得每个工程师背下来:
| 状态 | 表现 | 听感 | 根因 |
|---|---|---|---|
| 缓冲过浅(欠载) | 解码器断粮 | 顿挫、机器音、断续 | 目标水位低于实际抖动 |
| 缓冲过深(过载) | 时延膨胀 | 对讲机感、"隔空喊话"感、抢话 | 水位目标过于保守 |
还有第三个更隐蔽的失效模式——时钟频差。发送端和接收端的采样时钟是两颗物理晶振,永远存在百万分之几十的频差。哪怕网络完美无抖动,缓冲水位也会被这颗"看不见的手"缓慢推向枯竭或溢出。这决定了缓冲器必须是自适应的,静态配置永远只是权宜之计。
自适应抖动缓冲的核心是两个循环。
第一个循环:估计。 持续估计网络时延的均值和波动。RTP 协议在包头里带了序号和时间戳,接收端用到达时间和时间戳之差的变化量来估计抖动。RFC 3550 给出的经典指数滑动平均是:
$$J \leftarrow J + \frac{|D_i| - J}{16}$$
其中 $D_i$ 是相邻两包"到达时间之差"与"时间戳之差"的偏离量。除以 16 意味着这是约 1/16 权重的低通滤波——单个包的异常不会惊动估计值,趋势变化又能较快跟上。
第二个循环:调整。 有了估计值就动态升降目标水位。这里藏着整个设计中最精妙的一个洞察:
播放时刻表只能在说话停顿期间大幅调整。
为什么?说话期间每一帧都被解码器"实时"吃掉,此刻提前播放等于跳过内容,推后播放等于重复内容——都会产生可闻的失真。而静音期没人说话,删一段静音或插一段静音,人耳毫无知觉。所以经典算法(如 Moon、Kurose 与 Towsley 在上世纪九十年代末的经典文献)在通话期使用接近 1 的极慢调整系数(约 0.998 量级),在静音期使用快得多的系数(约 0.99 量级)——通话期稳如老狗,静音期偷偷挪表。
flowchart TD
A["检测到说话停顿"] --> B{"缓冲水位偏满<br/>还是偏空"}
B -->|偏满| C["删掉一段静音<br/>播放时刻表提前"]
B -->|偏空| D["补插一段静音<br/>播放时刻表推后"]
C --> E["水位回落到目标区"]
D --> E
A2["正在说话中"] --> F["播放时刻表锁死<br/>不许动"]
F --> G["最多做细微变速<br/>让人耳察觉不到"]
如果静音期等不到,水位在说话中途就必须纠正呢?现代实现不再粗暴丢帧或重复帧,而是变速不变调:把一小段已解码音频做时间上的拉伸或压缩(WSOLA 类时域波形相似叠加算法),语速变化百分之几,人耳几乎无感,缓冲水位却被悄悄扳了回来。WebRTC 的 NetEQ 模块把这条路走到了极致——它干脆不设传统的"缓冲队列",而是由一个决策器在每一帧上选择正常播放、加速、减速、扩展静音或拼接过渡,缓冲深度变成了一个连续可调的连续量。
缓冲能治抖动,治不了真丢包——超过了最深缓冲的等待极限,包就彻底迟到了。这时候轮到 PLC 上场:解码器凭空合成一段"像样"的语音,把空洞填上。
接收端 PLC 的手段按缺口大小分档:
flowchart TD
A["该来的一帧没有来"] --> B{"缺口有多大"}
B -->|只缺一帧<br/>约20ms| C["波形外推<br/>把尾音渐弱着重复"]
B -->|缺几帧| D["合成舒适噪声<br/>模拟说话间隙"]
B -->|缺一大段| E["按通话中断处理<br/>不再硬撑"]
单帧缺失的掩盖效果最好——20 毫秒在人耳的"听觉暂留"之内,外推一段逐渐衰减的波形,多数场景完全无感。缺口越长,合成内容越假,所以发送端配合很重要:
| 手段 | 在哪一端 | 原理 | 代价 |
|---|---|---|---|
| 波形外推 / 参数外推 | 接收端 | 用前几帧的波形或线性预测系数顺势外推 | 无带宽代价,长缺口穿帮 |
| 舒适噪声 | 接收端 | 缺口落在静音段时填环境噪声 | 几乎零代价 |
| 冗余打包 | 发送端 | 一包捎带前一帧的压缩副本 | 带宽翻倍量级 |
| 前向纠错 FEC | 发送端 | 用异或校验包重建缺失帧 | 带宽增加,成块丢失时失效 |
| 交织 | 发送端 | 打散突发错误,化整为零交给 PLC | 引入整卷时延,实时语音慎用 |
你可能会问:VoLTE 空口不是有 HARQ 和非确认模式的无线链路控制层兜底吗?没错,但那套机制只覆盖基站到手机这一段。语音的 RTP 包从媒体网关出发,穿越 IP 承载网、防火墙、SBC,中间任何一个路由器队列抖动、任何一段链路丢包,链路层的重传都无能为力——这段"无人管"的长途,正是端到端 PLC 存在的理由。
国际电联 G.114 建议给实时语音划了条红线:单向时延 150 毫秒以内,大多数应用感觉自然;150 到 400 毫秒勉强可用;超过 400 毫秒不可接受。这条预算要摊给全链路,缓冲深度是其中最大的一块弹性开支:
| 环节 | 典型值 | 说明 |
|---|---|---|
| 声码器算法时延 | 20ms 上下 | 帧长加前视窗,编码器固有的 |
| 打包延迟 | 20ms | 一帧一包,攒帧的时间 |
| 网络 + 空口 | 40~80ms | 随负载波动,抖动的源头 |
| 抖动缓冲深度 | 30~100ms | 最大弹性项,直接可调 |
| 解码 + PLC | <10ms | 可忽略 |
| 合计 | ≈150ms | 撞到 G.114 红线 |
缓冲每加深 10 毫秒,用户端"抢话"和"对讲机感"就重一分;而且时延还会连累回声——端到端时延越大,自己的声音绕一圈回来的重叠感越明显,回声消除器的搜窗压力也越大。时延、抖动、回声这三件事在语音质量里是连坐的。
为什么时延的杀伤力这么大?国际电联 E 模型给出过被广泛引用的量化。单向时延 $d$(毫秒)带来的损伤项(Cole–Rosenbluth 简化式):
$$I_d = 0.024\,d + 0.11\,(d - 177.3)\,H(d - 177.3)$$
其中 $H$ 是阶跃函数——177 毫秒是一个拐点,过了这个拐点时延惩罚开始陡增。损伤折算成质量分:
$$R = 93.2 - I_d - I_e, \qquad MOS = 1 + 0.035R + 7\times10^{-6}R(R-60)(100-R)$$
$I_e$ 是编码器与丢包的联合损伤。这套公式的工程含义直白:缓冲深度不是"越大越保险"的技术参数,而是直接从 MOS 分里扣钱的成本项。
理解了原理,排查思路就清晰了。用户投诉"电话卡",第一步永远是分诊,区分三种病:
第一步,看 RTCP 接收报告。 RTP 的伴生协议 RTCP 周期性携带两个关键字段:丢包率(fraction lost)和抖动(interarrival jitter,就是上文那个 1/16 滤波后的估计值)。丢包率高 → 病在网络丢包,往承载网和空口查;丢包率低但抖动大 → 病在排队,往拥塞节点和传输质量查。
第二步,区分"真丢"与"迟丢"。 严密的实现会把统计分开记:一种是包彻底没到(lost),一种是包到了但晚于播放时刻被接收端主动丢弃(discarded)。后者在包计数上"丢包率为零",用户却在骂街——这种案子里真凶是抖动或缓冲配置,不是丢包。只看丢包率会把排查引向死胡同。
第三步,警惕时钟漂移。 如果投诉模式是"通话时间越长越卡",高度怀疑两端采样钟频差:缓冲被缓慢抽干或撑爆,自适应机制被迫频繁变速。抓包验证方法:对比 RTP 时间戳的增长速率与抓包时钟的流逝速率,两者应当一致,偏差就是频差。
两个常见调试陷阱。其一,现网部署了 SRTP 加密后,抓包看不到语音载荷,只能依赖包头信息和包间隔统计——别指望还原音频听一听了事。其二,MOS 打点测试仪(基于 PESQ/POLQA 算法)自身的缓冲配置会影响得分,两台不同配置的探针对同一段网络打出不同 MOS 是常有的事,横向对比前先对齐探针配置。
最后留一个观念纠偏:抖动缓冲治不了回声。回声是声学与线路反射问题,缓冲是时延整流问题,两者症状都是"通话难受",但病灶不同——把回声投诉归因于"缓冲太大",是新手最常见的方向性错误。
实时语音的传输哲学可以压缩成三句话:可靠性靠预测(缓冲)而不是重传,连续性靠掩盖(PLC)而不是补发,自适应靠静音期悄悄挪表而不是随时乱动。这三大设计与 TCP 世界"错了就重来"的逻辑彻底分道扬镳,根源只有一个——在实时会话里,晚到的好消息就是坏消息。
flowchart TD
A["声码器每20ms出帧"] --> B["RTP打包<br/>载荷约30字节"]
B --> C["头开销40字节<br/>比载荷还大"]
C --> D["IP网络<br/>排队产生抖动"]
D --> E{"按播放时刻表<br/>检查到达"}
E -->|早到| F["进缓冲排队<br/>等待整流"]
E -->|迟到| G["判定丢弃<br/>交PLC合成"]
F --> H["恒定节奏解码"]
G --> H
H --> I["扬声器平滑播放"]
Q: 抖动缓冲和 TCP 重传为什么不能都要? A: 两者服务的是不同的时间观。TCP 重传保证"数据最终都到",但不管什么时候到;实时语音需要"按时到",迟到的语音帧没有任何价值。重传一个 20ms 的语音帧要付出的等待(半个 RTT 起步)远超它的播放窗口,还会阻塞后续帧,所以实时语音从协议选型(UDP/RTP)到接收端设计(缓冲+PLC)整体绕开了重传。
Q: 抖动和丢包怎么区分? A: 抖动是到达间隔的波动,丢包是包根本没到。抓包看序号连续性:序号断档是真丢;序号连续但到达间隔忽大忽小是抖动。RTCP 的 fraction lost 对应前者,interarrival jitter 对应后者。两者对听感的破坏路径不同——抖动过深会转化为接收端"迟丢",这也是很多"零丢包却卡顿"案例的真相。
Q: 为什么视频会议画面卡成幻灯片还能忍,声音一卡就烦? A: 人耳对人声断续和时延极其敏感,因为人类对话依赖"轮替节奏"——你说完我接话,超过几百毫秒的错位就会让双方开始互相抢话。视觉有显著性补偿,掉几帧画面大脑自动脑补;而语音的每一帧都携带时间结构,空洞直接可闻。这就是为什么语音系统的时延预算(150ms 级)比视频(数百毫秒到秒级缓冲)苛刻得多。
Q: WebRTC 的 NetEQ 和传统抖动缓冲的区别是什么? A: 传统缓冲是一个"队列 + 固定目标水位",调整靠静音期删除/插入静音,粒度粗。NetEQ 取消了刚性队列,用逐帧决策替代:每 10ms 决定正常播放、加速、减速、扩展或拼接,用 WSOLA 变速实现水位微调,让缓冲深度变成连续控制的量。思想一脉相承——都是"估计时延、维护播放时刻表、在感知不敏感处调整"——只是控制粒度从"帧级开关"进化到了"每帧连续微操"。
Q: 空口有 HARQ 和 RLC,为什么端到端还要 PLC? A: HARQ 与无线链路层重传只覆盖手机到基站这一跳,且为了时延通常采用非确认模式、重传次数极少。RTP 媒体流出基站后还要穿越承载网、防火墙、SBC、媒体锚点,这些段落在链路层之上、没有逐包重传,它们产生的丢包和抖动只能由接收端的缓冲与 PLC 兜底。一套完整的语音质量方案,是逐跳机制与端到端机制的分工协作。
本文由「51学通信」技术团队出品。51学通信由通信行业18年老兵爱卫生(微信ID: gprshome201101)维护,聚焦5G/6G核心网、3GPP标准解读、通信前沿技术深度分析。 出版作品有《5G核心网原理与实践》《IMS原理与实践》等4本通信专业书籍。知识星球「51学通信」提供深度技术答疑和行业洞察。
[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。
