首页 › deep-tech › 慢半拍的智慧:抖动缓冲原理

慢半拍的智慧:抖动缓冲原理

一个反直觉的问题:打电话为什么不重传?

你下载一个文件时丢了一个包,网卡会安安静静把它重传回来,文件最终完好无损。可你打电话时丢了一个包,对方听到的却是"滋——"的一声破音,然后这事就过去了,没人补传。

这不是历史遗留问题,而是一个精心设计的选择。实时语音走的是 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 兜底。一套完整的语音质量方案,是逐跳机制与端到端机制的分工协作。

参考来源

  1. RFC 3550: RTP: A Transport Protocol for Real-Time Applications — RTP/RTCP 协议本体,含帧间到达抖动估计公式与接收报告字段定义
  2. ITU-T G.114: One-way transmission time — 单向时延 150/400ms 分级的出处
  3. Jitter buffer - Wikipedia — 抖动缓冲的定义、固定式与自适应式对比
  4. Packet loss concealment - Wikipedia — 接收端与发送端丢包隐藏技术分类
  5. S. Moon, J. Kurose, D. Towsley《Packet audio playout delay adjustment for performance improvement of voice over IP》 — 说话期/静音期双系数自适应播放调度的经典文献;WebRTC 的 NetEQ 实现可在其官方源码仓(webrtc.googlesource.com)modules/audio_coding/neteq 目录下研读

本文由「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 等场景全覆盖。

51学通信知识星球海报-扫码加入