先看两个场景。
你给朋友发微信,消息晚到了十秒,朋友回一句“刚看到”——一切正常,聊天照旧。但你打电话时,只要对方的声音比你慢上小半秒,对话立刻乱了套:你以为是沉默,开始说话,对方也同时在说,然后两个人一起停下来,一起说“喂?喂?听得到吗?”
这就是交互式实时通信和普通数据传输的根本区别:数据包晚到,叫“慢”;语音包晚到,叫“错”。播放器像一个按乐谱演奏的鼓手,第 20 毫秒的鼓点必须落在第 20 毫秒上。一个迟到了 200 毫秒的语音包,不是“晚点响”,而是已经没有它的位置了——乐章都翻篇了。
由此引出本文的主角:一套看起来“故意不可靠”的传输体系。它不用我们熟悉的可靠传输协议,不重传,不确认,甚至允许丢包。但这套体系恰恰撑起了全世界每一天的几十亿次通话。它的核心设计哲学只有一句话:
对实时语音来说,迟到的正确交付,等于错误的交付。与其把包追回来,不如把空缺藏起来。
本文就拆解这套体系:为什么不能用可靠传输、RTP 协议的三个关键字段各管什么、抖动缓冲如何“用延迟换连续”、丢包之后靠什么瞒过你的耳朵。
人对语音时延的容忍度,是国际上研究了几十年的经典课题。国际电联的经典结论(G.114 建议)大致是:
这 150 毫秒,就是一次通话的“总预算”。而预算表上早就排满了花钱的项目:
$$D_{端到端} = D_{打包} + D_{编码} + D_{空口} + D_{传输} + D_{缓冲} \le 150\,\text{ms}$$
| 环节 | 在花钱做什么 | 典型量级 |
|---|---|---|
| 打包时延 | 攒够 20ms 的音频才凑成一个包 | 20ms |
| 编解码算法时延 | 编码器要看前后帧才能压缩 | 十几毫秒 |
| 空口传输 | 排队调度、混合重传纠错 | 几到几十毫秒 |
| 核心网与承载 | 光纤传输、路由器转发排队 | 几到几十毫秒 |
| 抖动缓冲 | 开播前先攒一层“垫底” | 几十毫秒 |
现在假设我们“聪明地”选择了可靠传输:丢一个包就重传一次。重传一次的代价是多少?至少一个往返时延(RTT)——无线网络里随便就是几十毫秒;如果这次重传又丢了,还要指数退避再等更久。两次重传下来,几百毫秒没了,预算当场爆仓。
更要命的是,语音是一股连续的流。可靠传输(如 TCP)有个特性叫队头阻塞:第 100 号包没送达,第 101、102、103 号包即使先到了也要在接收端排队等着。对文件下载,这无所谓,反正是按序拼装;对语音流,一个包卡住,后面所有包集体迟到——你为了救一个包,搭上了整条流的时延。
所以结论是结构性的,而不是调参能解决的:实时语音从一开始就不该走可靠传输的路。那可靠性问题怎么办?答案是下一节:把“可靠性”这件事整个搬出传输层,交给最懂语音的人自己处理。
实时语音选择在 UDP 之上自建一层轻量协议——RTP(实时传输协议)。它由 IETF 制定,1996 年首次发布,2003 年由现行标准接替,是 VoIP 技术大厦的地基。WebRTC 视频会议、运营商 VoLTE/VoNR 通话、IPTV,底层都跑着它。
RTP 的架构原则有个专门的名字:应用层分帧(Application Layer Framing)——协议该做什么、不该做什么,由应用自己决定,而不是塞进操作系统的协议栈里。为什么?因为只有应用层知道:
于是分工非常清晰:
顺便说清一个常见误解:RTP 通常跑在 UDP 上,不是因为语音“不在乎可靠性”,而是因为语音在乎的是另一种可靠性——时间的可靠性。UDP 恰好把网络从“保证送达”的承诺中解放出来,让每一个包走最快的路,谁也不等谁。丢一两个包,损失 20~40ms 的声音,人耳的短时记忆和隐藏算法完全兜得住;而队头阻塞损失的是整条流的时间秩序,人耳立刻抗议。这笔账,工程师算得很清楚。
RTP 的头最小只有 12 字节,却解决了实时传输的几乎所有“知情权”问题。发送端每 20ms 发一个包,每个包头都写清楚了它的身份和时序。
| 字段 | 大小 | 作用 | 工程师视角 |
|---|---|---|---|
| 序列号 | 16 位 | 每发一包加 1,用于发现丢包和乱序 | 收到 100、101、103——中间丢了 102 |
| 时间戳 | 32 位 | 本包第一个采样值的时刻,用于决定何时播放 | 与到达时刻无关,只与采样节拍有关 |
| 载荷类型 PT | 7 位 | 标识载荷用哪种编码 | 通话前协商好的“暗号”,解码器按它选算法 |
| 同步源 SSRC | 32 位 | 标识这一路媒体流是谁发的 | 判断“单通”的关键线索 |
| 标记位 M | 1 位 | 应用自定义的特殊含义 | 如静音段后第一帧打标 |
| 版本等控制位 | 若干 | 协议版本、填充、扩展指示 | 一般不动 |
三个字段分工微妙,值得单独说透。
序列号管“到了没有”。发送端每发一包,序列号加一;接收端对着编号查缺,立刻知道丢了哪个、有没有乱序。初始值是随机的——这既让重放攻击更难,也意味着序列号本身没有绝对含义,只有“相对差值”有意义。
时间戳管“什么时候播”。它记录的不是发送时刻,而是采样时钟的读数。语音电话的经典采样率是 8000 赫兹(每秒 8000 个采样点),时间戳就按这个时钟走:一个 20ms 的包,无论里面压了多少字节,时间戳永远增加
$$\Delta TS = f_{采样} \times T_{打包} = 8000 \times 0.02 = 160$$
如果是静音、 comfort noise 之类的特殊帧,不携带正常语音,时间戳照样往前走——播放节拍不能因为内容稀疏而乱。视频流的时钟通常用 90kHz,同样是“粒度要足够细”的道理。
序列号与时间戳的分工,可以这么记:序列号回答“物流问题”(发了几件、到了几件、顺序乱没乱),时间戳回答“演出问题”(这一段声音该在乐曲的第几毫秒响起)。接收端用前者记账,用后者排节目单。
RTCP:5% 带宽的体检报告。与 RTP 配套的还有一个控制协议 RTCP,它周期性地发送接收质量报告:丢了多少包、抖动估计多大、往返时延多少。它占用的带宽通常只有媒体流的 5% 左右,却是整条链路唯一的“病人自述”——现网排障时,端点之间的丢包统计往往就是从 RTCP 报告里读出来的。
最后算一笔开销账,你会理解现网的一个优化点。以经典编码 G.711(每采样 1 字节)为例:20ms 打包,载荷 160 字节;加上 12 字节 RTP 头、8 字节 UDP 头、20 字节 IP 头,一个包 200 字节:
$$\text{头部开销} = \frac{200-160}{160} = 25\%$$
四分之一的带宽花在头上。所以无线空口上普遍启用头压缩技术,把 40 字节的协议头压到几字节——这是 VoLTE 能高效承载语音的原因之一。
理想网络里,发送端每 20ms 发一个包,接收端就每 20ms 收到一个包,完美。现实是,包的到达间隔忽长忽短——这个时延的波动,术语叫包时延变化,俗称抖动。
抖动的来源很具体:
对语音接收端来说,抖动带来两个必须解决的时间问题:
第一个问题靠缓冲,第二个问题靠缓冲的自适应调节——这正是接下来最核心的部件。
理解抖动缓冲,用楼顶水塔就够了。
上游水泵供水忽大忽小(网络抖动),但楼下住户拧开水龙头,水流必须恒定(播放连续)。水塔的办法是:在中间存一层水,出水口按固定速率放水。水位高时多吃一点,水位低时少放一点,把上游的波动全部吸收在塔里。
抖动缓冲器干的就是这件事,而且它有一个铁律,值得原文式地记住:
抖动缓冲能对抗的最大抖动,恰好等于它开播前引入的那段缓冲延迟。
想稳住 60ms 的到达波动?那就在开始播放前,先攒 60ms 的音频垫底。天下没有免费的平滑——缓冲深度就是时延预算里的固定开销。
于是出现了一个此消彼长的根本权衡。定义有效丢包率:
$$P_{有效} = P_{网络丢包} + P_{迟到被弃}(D)$$
缓冲调优的本质,就是在这条曲线上找拐点:再加深一毫秒缓冲所换来的“少丢包”,已经不值一毫秒时延了。工程上常见的初始策略是按“平均到达时延 + 三四倍抖动标准差”来定深度——覆盖大部分到达时间的分布尾部,又不过分挥霍预算。
更精巧的是自适应缓冲。网络状况在通话中会变(走进电梯、切换小区、拥塞起伏),静态深度很快过时。自适应策略两条腿走路:
一句话总结这一节:抖动缓冲是用时延买连续性的交易所,VAD 是它的低买高卖窗口。
缓冲解决“迟到”,但真丢了的包怎么办?接收端有三招,按代价从低到高排列。
第一招:藏(丢包隐藏 PLC)。人耳对 20~40ms 内的声音空缺有相当强的掩蔽效应,尤其是有前后文参考时。PLC 算法利用相邻帧的信号特征,合成一段过渡音填进缺口——上一帧是浊音,就顺着基音周期外插;是清音,就填能量衰减的噪声。现代编解码器的 PLC 已经做得相当自然。上文那笔“丢 20ms 无所谓”的账,靠的就是这一招。
第二招:补(前向纠错 FEC)。发送端把第 n 帧的冗余副本搭车放进第 n+1 甚至 n+2 的包里。第 n 个包丢了?等 n+1 到达时从冗余字段里把它解出来。代价是带宽——相当于给语音上了双保险。它对抗的是孤立的单包丢失;连续突发丢包时,邻居包全没了,冗余也一起陪葬。更新的编解码器还引入信道感知编码——对最重要的信息位加大保护、次要位少保护,把冗余花在刀刃上。
第三招:换(重传)。你没看错,语音理论上也能重传——当往返时延足够小(同一局域网、同一机房)时,快速重传一帧的代价可以接受。但跨无线网的移动语音,RTT 几十毫秒起步,重传回来的帧十有八九已经迟到,所以移动语音基本不用这招。当年“能不能用可靠传输”的争论,在这里被实践画上了句号。
三招的分工与代价:
| 手段 | 原理 | 付出的代价 | 时延影响 | 适用场景 |
|---|---|---|---|---|
| PLC 隐藏 | 用相邻帧合成过渡音填补缺口 | 几乎为零(算力) | 无 | 兜底手段,永远开着 |
| FEC 冗余 | 下一包捎带上一帧副本 | 带宽翻倍或部分增加 | 增加一帧解包等待 | 单包随机丢失为主的网络 |
| 重传 | 缺失帧快速重发 | 带宽 + 信令 | 增加至少一个 RTT | 仅超低时延局域环境 |
| 加深缓冲 | 多垫底,减少迟到弃包 | 更多端到端时延 | 直接增加 | 抖动大而预算宽裕的场景 |
值得一提的是,新一代移动语音编解码器 EVS(为 VoLTE/VoNR 开发,支持最高 20kHz 音频带宽,采样率覆盖 8/16/32/48kHz,比特率最低 5.9kbps、最高 128kbps,且每 20ms 可动态切换)在标准层面就把信道感知编码、改进的丢包隐藏、抖动缓冲管理列为核心特性——换句话说,这套“藏、补、调”的实时传输哲学,已经内化进了编解码标准本身。它还向后兼容上一代宽带编码 AMR-WB(比特率 6.6~23.85kbps),保证新老终端能互通。
把前面的部件串起来。你对着手机说了一句话,它经历这样一条流水线:
flowchart TD
A["说话:声波采样"] --> B["声码器:20ms一帧"]
B --> C["RTP封装:12字节头"]
C --> D["UDP:尽快发出"]
D --> E["IP:路由转发"]
E --> F["空口专用承载"]
F --> G["IP骨干网"]
G --> H["对端接收解包"]
H --> I["抖动缓冲"]
I --> J["解码与丢包隐藏"]
J --> K["按本地时钟匀速播放"]
再看时间维度上发生了什么。注意接收端的两个关键动作:序列号查缺和到点播、缺了藏:
sequenceDiagram
participant A as A手机
participant N as 无线与IP网络
participant B as B手机缓冲与播放
A->>N: 每20ms发出序列号n的包
N->>B: n包准时到达进入缓冲
A->>N: 发出n+1包
N--xB: n+1包在网络中丢失
Note over B: 序列号出现空洞先记账
A->>N: 20ms后发出n+2包
N->>B: n+2包晚到但仍在播放时刻前入队
B->>B: 播放时刻到n+1缺席启动PLC合成填充
B->>B: 按本地播放时钟匀速取出解码
最后是接收端缓冲器每时每刻都在做的决策循环——它就是整个实时传输体系的“现场指挥”:
flowchart TD
P["RTP包到达"] --> Q{"序列号连续吗"}
Q -->|连续| R["按序入缓冲"]
Q -->|有空洞| S["记账并启动短暂等待"]
S --> T{"播放前补齐了吗"}
T -->|补齐| R
T -->|超时| U["对缺口启用PLC"]
R --> V{"缓冲水位与目标比"}
V -->|偏高| W["静音段悄悄收紧"]
V -->|偏低| X["放慢取包垫深缓冲"]
U --> V
这套原理不是纸上谈兵,它直接对应你排障时的每一个动作。
抓包看 RTP 流,是语音质量分析的基本功。用 Wireshark 打开一份通话抓包,Telephony 菜单下的 RTP 流分析能直接画出三条曲线:丢包率、抖动、估算 MOS 分。解读思路完全来自本文:
“单通”的判决书写在 SSRC 里。用户投诉“听得见对方、对方听不见我”,先看双向 RTP 流:如果只有一个方向的 SSRC 在跳序列号,说明反向媒体流压根没建立——常见原因是媒体地址/端口协商失败、NAT 或防火墙拦了 UDP。这一眼,省掉一圈无头绪的排障。
打包周期是一场微交易。20ms 一包(ptime=20)是主流;改成 40ms 一包,头开销减半、包速率减半,但打包时延多了 20ms,且一个包丢了损失翻倍。预算紧张的链路倾向大包,时延敏感的场景用小包——没有标准答案,只有预算分配。
空口 QoS 是在给抖动缓冲减负。VoLTE 通话会建立一条专用语音承载,保证比特率、调度优先级最高。它的真正作用之一,是压住空口这一段的时延方差——而空口恰恰是移动链路里抖动最大的源头。专用承载把抖动源头摁住了,终端的缓冲就可以开得浅,端到端时延预算就省下来了。空口 QoS 与媒体面,在这里握手。
跨网互通的“音质刺客”。工程师都遇到过:两端手机都支持新一代编码,通话音质却回到了窄带时代。原因往往在运营商间的互联互通路由按最保守能力对接,通话被降级到老编码——这也是为什么有时换一个拨号路径(例如走另一种互通方式),音质会突然变好。这提醒我们:编解码能力是协商出来的,不是设备单方面决定的。
误解一:“UDP 不可靠,所以 VoIP 音质天生差。”恰恰相反。语音放弃了“包包送达”的可靠性,换来了“时间秩序”的可靠性;丢包的窟窿由 PLC 和 FEC 来补,而队头阻塞的窟窿无人能补。对实时流,UDP 是更可靠的选择。
误解二:“丢包率 0%,音质一定好。”MOS 是时延与丢包的联合判决。一条零丢包但端到端 500ms 的链路,听感同样崩溃。看质量要看两个轴,别只盯丢包率。
误解三:“抖动缓冲开大点总没错。”缓冲深度就是时延。开得越深,抢话越明显,而省下的那点“迟到弃包”未必有人耳能感知。缓冲调优永远是在预算曲线上找拐点,不是越大越好。
Q1:RTP 和 RTCP 到底什么关系?为什么要有两个协议? A:RTP 搬货,RTCP 交体检单。RTP 承载媒体本身(序列号 + 时间戳 + 载荷);RTCP 周期性地向对端汇报接收质量——丢包率、抖动估计、往返时延——占用约 5% 的带宽。没有 RTCP,端点就成了“哑巴打电话”,只能猜对端听得好不好;有了它,码率自适应、质量监控才有数据来源。
Q2:语音为什么是 20ms 一包? A:20ms 是编码帧长与打包周期的主流约定,是三方平衡的结果:太短(如 5ms),包头开销占比暴涨,包处理负担剧增;太长(如 80ms),打包时延吃光预算,且一个包丢失对应一大段声音,PLC 很难藏住。20ms 恰好让头开销(压缩前约 25%)、时延(20ms)和单包损失(20ms)都落在可接受区间。EVS 等新编码保持 20ms 帧长,但支持每帧切换比特率,灵活性体现在帧内而不是帧长上。
Q3:时延和抖动是一回事吗? A:不是。时延是一个包从 A 到 B 花的时间;抖动是多个包时延的波动幅度。缓冲器对付的是抖动,但买缓冲花的钱是时延。网络可以时延很大而抖动很小(恒定 300ms,像卫星链路——能通话但抢话),也可以平均时延很小但抖动巨大(突发毛刺——听感断续)。两者必须分开诊断。
Q4:微信电话这类 OTT 通话和 VoLTE 通话,走的也是这套机制吗? A:媒体面基本同源——IP 语音都建立在 RTP(或其衍生、WebRTC 同族协议)加抖动缓冲加丢包隐藏这套机制上,微信电话的“通话”模式底层逻辑与此同源。差别在承载与优先级:VoLTE 有运营商网络保障的专用承载,时延方差被空口调度压住;OTT 通话跑在普通上网承载里,和视频、下载抢资源,只能靠应用层自己的缓冲与编码自适应硬扛。这就是弱网下两者体验差距的根源。
Q5:RTP 头里的时间戳能用来对时吗? A:不能。RTP 时间戳是相对量——记录本包第一个采样在媒体时钟上的位置,初始值随机,时钟粒度等于采样率。它回答“该在曲目的第几毫秒播放”,不回答“现在几点”。通话两端要对墙钟,得靠别的机制;而 RTP 时间戳的真正用途,是让接收端把网络打乱的时间秩序,重新排回一条均匀的播放时间线。
本文由「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 等场景全覆盖。
