实时通讯系统的语音通话延迟优化方法

实时通讯系统的语音通话延迟优化方法

你有没有遇到过这种情况:跟远方的朋友打语音电话,你说"喂喂,能听到吗?"那边要隔个一两秒才回你一句"能听到,你刚才说啥?"这种错位的感觉别提多难受了。其实吧,这种延迟在技术圈里有个正经名字,叫"端到端延迟",而把它压到最低,就是我们今天要聊的正事儿。

作为一个天天跟音视频打交道的从业者,我见过太多产品因为延迟问题被用户吐槽。"卡"、"慢"、"糊"基本上稳居投诉榜前三。而这里头最影响体验的,往往就是这个延迟。你想啊,画面稍微模糊一点,咱们可能还能忍,但说话半天没回应,这谁受得了?

那今天我就用比较接地气的方式,聊聊实时通讯系统里语音通话延迟到底是怎么回事,以及那些行内人都在用的优化方法。咱不搞那些云山雾绕的概念,就说实话、聊干货。

延迟到底是从哪儿来的?

在说怎么优化之前,咱们得先搞清楚延迟是怎么来的。你可能觉得,不就是我说话你听到吗,能有多复杂?嘿,这里头门道可多了。

一次普通的语音通话,延迟基本上是由这几部分组成的:

  • 采集与预处理:设备把声音转换成数字信号,这一步现在都比较快了,但也得花个几毫秒
  • 编码:原始的音频数据太大,得压缩一下。主流的编码器像Opus这类,处理时间通常在20到40毫秒左右
  • 网络传输:这是大头。数据要经过物理层的传输,还要经过各种路由转发,这里头的变数太多了
  • 缓冲与抖动处理:网络不可能一直匀速,为了保证音质,得缓冲一下,这又得加上几十毫秒
  • 解码与播放:把压缩的数据解开来,再通过扬声器放出来

这么说吧,一次理想的语音通话,从你说话到对方听到,整个链路的延迟保守估计也得100毫秒起步。注意啊,这是理想情况。现实世界里,网络的波动、设备的性能、系统的调度,分分钟就能把这个数字翻个倍。

那我们做优化的目标,就是把这几个环节的延迟一个一个掰开揉碎了,能压就压,能省就省。

第一关:采集和编码的优化

选对编解码器很关键

编码器选得好,延迟少一半。这话虽然有点夸张,但编解码器确实是影响延迟的核心因素之一。

早年间做语音通话,AMR和G.711这些老前辈是主流。它们的特点是压缩率高,但延迟也大。后来Opus出来了,这玩意儿简直是开了挂。它能根据网络情况自动调整码率,高码率下音质接近CD,低码率下也能保持可懂的语音质量。更重要的是,它的算法延迟可以低到5毫秒左右,比老前辈们强太多了。

那是不是直接无脑选Opus就行了?差不多,但现在也有不少场景在用EVS(Enhanced Voice Services)这个新一代编码器。它在超宽带和全带宽语音下的表现更优秀,如果你做的产品对音质要求比较高,EVS值得考虑。

帧长度的取舍

编码的时候,数据不是一条一条处理的,而是一帧一帧来的。帧长度的选择是个技术活。

简单来说,帧越长,单位时间内处理的次数越少,效率越高,但延迟也越大。常见的帧长有20毫秒、40毫秒、60毫秒这些规格。如果你做的是实时语音通话,20毫秒的帧长几乎是必选项,虽然CPU占用会高一些,但换来的低延迟体验是完全值得的。

预处理别忽视

采集阶段还有一些小细节也能优化。比如回声消除(AEC),这玩意儿如果算法不够先进,会增加不少延迟。现在主流的做法是采用自适应滤波器,能够根据环境实时调整参数,既保证回声消除效果,又不引入额外延迟。

还有噪声抑制(NS)和自动增益控制(AGC),这些预处理模块的算法选择和参数调优都要考虑到延迟因素。做得好的预处理模块,延迟可以控制在1毫秒以内,几乎可以忽略不计。

第二关:网络传输的优化

网络这块儿可以说是整个链路中最不可控的部分。咱们没办法改变用户的网络环境,但可以在传输协议和策略上做文章。

传输协议的选择

现在主流的实时语音传输用的是UDP协议,而不是TCP。这不是没有道理的。

TCP为了保证可靠性,会有重传机制。一旦丢包,发送方得等超时了才能重传,这一等就是几十毫秒过去了。实时语音不一样,偶尔丢一两个包,最多就是听不清楚一个字,但延迟大了那是全程难受。所以UDP虽然不管丢包,但换来了低延迟,在实时场景下是更优解。

当然,UDP之上我们也得做些可靠性的工作。比如RTP/rtcP协议,就是专门为实时传输设计的。RTP负责传输数据,rtcP负责反馈网络状况,两者配合起来,既保证了实时性,又能及时感知网络变化。

拥塞控制算法

网络拥塞是导致延迟飙升的主要原因之一。当网络带宽不够的时候,数据包就会排队,延迟自然就上去了。所以一个好的拥塞控制算法非常关键。

传统的拥塞控制算法像GCC(Google Congestion Control)用的是比较保守的策略,发现丢包就开始降速,延迟是低了,但带宽利用率也上不去。这两年比较火的是基于延迟的拥塞控制算法,比如BBR(Bottleneck Bandwidth and Round-trip propagation time)。BBR的思路是不看丢包看延迟,通过探测瓶颈带宽来调整发送速率,在高延迟网络下表现比GCC好很多。

如果你用的是webrtc,它默认的拥塞控制已经比较成熟了。如果你自己实现,可以考虑基于BBR的改进版本,或者更激进的Scream算法——Scream在延迟控制上非常激进,适合对延迟极度敏感的场景。

节点布局和智能路由

这两年做RTC的人都知道,边缘节点(Edge Node)的布局有多重要。用户的请求如果能就近接入到离自己最近的节点,然后再通过优化过的骨干网传输到对端,整体延迟能低不少。

举个简单的例子,如果你在北京打给上海的用户,如果不走专线,数据可能得绕道广州或者杭州的节点,延迟自然就上去了。但如果服务商在华北和华东都有节点,两边各自接入就近的节点,节点之间走专线,这个延迟就能压到很低。

这里不得不提一下,全球化布局对于做出海业务的团队特别重要。比如你的用户在南美和欧洲,那在这两个大洲都得有节点覆盖,不然延迟没法看。这事儿说起来简单,做起来全是钱和技术积累。

第三关:缓冲和抖动的处理

抖动缓冲的平衡艺术

网络传输有个特性,叫"抖动"(Jitter),就是数据包到达的时间间隔不稳定。有的时候连续来好几个,有的时候半天来一个。抖动缓冲(Jitter Buffer)的作用就是把这些不稳定的数据先存起来,匀速取出来播放,保证听到的声音是连续的。

缓冲得越大,抗抖动能力越强,但延迟也越高。缓冲得小一点,延迟低了,但一旦网络抖动,稍微丢几个包就可能卡顿。这就是一个经典的Trade-off。

现在主流的做法是自适应抖动缓冲。什么意思呢?算法会实时监测网络的抖动情况,动态调整缓冲大小。网络平稳的时候,缓冲就小一点,把延迟压下去;网络波动的时候,缓冲就大一点,保证不卡顿。

这个自适应算法怎么做?其实原理不复杂。核心就是维护一个平滑的抖动估计值,比如用指数加权移动平均(EWMA)来计算当前的网络抖动程度,然后再根据这个估计值来计算需要的缓冲大小。当然具体实现细节就多了去了,什么异常值处理、突发抖动应对、缓冲下限保护这些都是坑。

前向纠错和丢包补偿

虽然UDP不管丢包,但我们可以通过应用层的机制来缓解丢包的影响。最常用的两个技术是前向纠错(FEC)和丢包隐藏(PLC)。

FEC的思路是冗余。发送方除了发当前的数据包,还会发一些冗余的校验信息。接收方如果丢了一个包,可以通过校验信息把丢失的内容算出来。这样就不用等重传,延迟自然就低了。当然FEC的问题是增加了带宽开销,冗余数据越多,抗丢包能力越强,但带宽消耗也越大。

PLC则是在丢包之后做文章。如果一个包丢了,PLC会用前面的数据包来推测丢失的内容,生成一个"听起来差不多"的填充数据。好的PLC算法填充出来的声音如果不仔细听,几乎听不出是丢失过的。Opus编码器自带的PLC效果就很不错,这也是Opus受欢迎的原因之一。

实际使用中,FEC和PLC通常是配合使用的。轻度丢包用PLC,重度丢包用FEC,两边都扛不住了才重传。这样既保证了通话质量,又控制了延迟。

第四关:系统层面的优化

线程优先级和调度

这是个很容易被忽视的问题。语音数据的处理需要在独立的线程里,而且这个线程的优先级要设置得比较高。如果处理线程被系统调度到后面去了,延迟就会增加。

在Android上,可以用nice值来调整进程优先级,或者直接用RealtimeThread。在iOS上,AVFoundation框架对音频线程有比较好的优先级保障。Windows和Linux也各有相应的机制来实现实时调度。

另外,处理线程要尽量避免被其他业务逻辑阻塞。常见的做法是把音频处理单独放在一个线程里,跟业务逻辑彻底分开。业务层只管喂数据和处理结果,具体怎么编码怎么传,音频线程自己搞定。

硬件加速

现在的手机和PC都有专门的DSP(数字信号处理器),音频的编解码在这些专用硬件上做效率更高,延迟也更低。特别是Opus和EVS这种计算量比较大的编码器,硬件加速的效果很明显。

调用硬件编码器需要注意兼容性问题。不同芯片厂商的API不一样,需要做适配。另外硬件编码器通常有功耗优势,对于手机端的场景很重要——谁也不想打个电话把手机烧得烫手。

端到端的延迟能优化到什么程度?

说了这么多优化手段,那实际效果能到什么水平呢?我给大家列个表,看看不同场景下的典型延迟是多少:


场景端到端延迟说明
局域网内同机房30-50ms最理想的状况,网络几乎无延迟
跨城专线50-80ms同一区域内走专线,延迟较低
跨国跨洲150-300ms受限于物理距离和网络互联质量
移动网络弱覆盖200-500ms信号差、基站拥塞等都会导致延迟飙升

这个表只是一个大致参考。实际延迟还跟很多因素有关,比如用户的设备性能、网络波动、服务商的节点覆盖等等。

就拿我们自己的经验来说,通过上述这些优化手段的叠加使用,在正常的4G或WiFi环境下,把语音通话延迟压到100毫秒以内是可以做到的。业内顶尖的水平,比如声网这种专门做实时音视频的服务商,在某些场景下甚至能把最佳耗时做到600毫秒以内,当然这个600毫秒可能包含的是从发起呼叫到对方接听的完整时间。

延迟这东西,越往下优化越难。100毫秒到50毫秒,可能需要投入大量的技术和资源。50毫秒再往下,每减少1毫秒都是硬骨头。所以具体要优化到什么程度,还是要看业务场景的需求。

写在最后

聊了这么多,我想强调一点:延迟优化不是某一个环节做好了就行,它是一个系统工程。从采集到编码,从传输到播放,每一个环节都要考虑到,每一毫秒都要去抠。

技术方案的选择也要结合实际业务场景。如果是做语音客服,可能对延迟的要求没那么极致,但稳定性一定要好。如果是做社交1V1,那延迟就是核心指标,必须往死里优化。如果是做语音直播,推流端稍微慢一点问题不大,但拉流端的体验一定要保证。

另外,测试环节也很重要。实验室里的数据再好看,放到真实网络环境下可能完全是另一回事。还是要多做一些弱网测试,看看在网络波动、丢包、抖动的情况下系统表现怎么样。

实时通讯这个领域,技术更新迭代挺快的。编解码器、传输协议、算法策略每年都有新东西出来。保持学习,持续优化,才能给用户带来最好的体验。

好了,今天就聊到这儿。如果你正在做相关的项目,希望这些内容能给你一些参考。有问题咱们可以继续交流。

上一篇即时通讯SDK的负载均衡的策略优化
下一篇 实时通讯系统的语音通话延迟测试报告

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱:

工作时间:周一至周五,9:00-17:30,节假日休息
关注微信
微信扫一扫关注我们

微信扫一扫关注我们

手机访问
手机扫一扫打开网站

手机扫一扫打开网站

返回顶部