目录


概述


NACK


    问题一、数据包真丢了,会一直重传吗?


    问题二、重传次数不到最大限制次数,就会一直等待吗?


    问题三、当大量丢包时,会全部重传吗?


NACK改进


总结


WebRTC丢包重传大解密-LMLPHP

概述

WebRTC之所以可以优秀的完成音视频通讯,和它本身的丢包重传机制是密不可分的,今天我们就来看看其中的奥秘。

本文以M76版本展开,如果你的工程是基于其他版本开发的,也可以参考。


NACK

说到丢包重传就不得不提到NACK技术,那么NACK是什么呢。它的全称是Negative Acknowledgment Packet,意思是否定确认包,说到这里我们应该可以联想到ACK(Acknowledgment Packet,确认包)。没错,二者的意思是相反的。ACK表示通知对方我收到了你发给我的数据包,NACK表示通知对方我没有收到你发给我的数据包。

那么问题来了,为什么会导致对方明明发送了响应的数据包,而我没有收到呢?其中的原因有很多,比如网络问题,因为中间路由器转发丢失,延时较大导致被NACK(可能数据包还在传输中,只是到达时间比较久)等。

基于上述原因,NACK的存在是非常有必要的。它能够及时的通知发送端重传相应的数据包,保证接收端音频和视频的正常播放。NACK其实是RTCP包的一种,用来是对 RTP 数据传输层进行反馈,它包类型是 205。


问题一、数据包真丢了,会一直重传吗?

答案是否定的。会有哪些决定因素呢?首先看最大重传次数,源码中默认是10次。意思是如果相同seq_num的数据包被重传了10次,接收端依然没收到,就不再继续请求重传了。

处理方式是将该数据包从重传列表中移除,具体看源码:


基于问题一,于是我们引入了问题二。


问题二、重传次数不到最大限制次数,就会一直等待吗?

很不幸,答案是肯定的。NACK技术作为WebRTC对抗弱网的核心技术之一,有两种发送模式,一种是基于包序列号的发送,一种是基于时间序列的发送。对于一个包因为不连续而被判为丢失后,接收端会主动请求重传这个数据包。正常情况下,发送端收到通知后,一般是从发送缓存列表中找到这个包并重新发送。可是,如果因为特定的原因,导致上次的RTT(往返延时)很大。源码中默认是100ms。

这里需要说明一点,因为WebRTC判断某个包是否超时需要重传的标准是上次的RTT时间。如果当前等待的数据包时间已经大于RTT了,就认为丢了,从而请求重传。如果小于RTT,就继续等待。那么漏洞出来了,如果上次的RTT很大很大,WebRTC确实会等待,但是出现这种情况的概率是很低的。同时,WebRTC还可以通过其他机制避免出现类似的问题,于是引出了问题三。


问题三、当大量丢包时,会全部重传吗?

答案是否定的。因为WebRTC不仅限制了重传包的次数,而且还限制了重传包的个数。WebRTC每次要求重传包的个数默认是1000个。

如果丢失的包数量超过 1000个,会循环清空 nack_list_ 中关键帧之前的包,直到其长度小于 1000。也就是通过放弃对关键帧首包之前的老旧包的重传请求,直接而快速的请求新近的数据包,相关逻辑源码:

那是不是nack_list_填充到1000个才会请求重传呢?当然不是,只要最大个数不超过1000个,就可以按照kProcessIntervalMs时间间隔请求一次重传包。即使,只丢失了一个包也会在规定的时间进行重传请求。

其中,kProcessIntervalMs = 20ms。这里有些不理解,为什么不直接初始化20ms,却要通过1000/50计算?


NACK改进

这里需要说明WebRTC新引入的一种机制:NACK延时发送机制,通过控制NACK延时发送的时间间隔,避免固定延时网络下无必要的重传请求。比如,如果kDefaultSendNackDelayMs=20ms,如果因为网络的固有延时,造成某些数据包迟到了10ms,而此时没有NACK延时发送机制的话,这些包都会被认为丢了,从而对这些包请求重传。但是如果有20ms的NACK延时发送,这些包就不会被计算为丢失,从而避免了没有必要的重传请求,避免了资源浪费。

而且,kDefaultSendNackDelayMs参数值是支持自定义设置的,通过接口传参field_trial设置WebRTC-SendNackDelayMs的值,WebRTC在底层会解析这个参数,如果没有设置的话,默认是10ms。相关逻辑如下:


总结

好了,不早了,今天就讲到这里吧。下次继续,大家晚安。


本文分享自微信公众号 - 玩转音视频(gh_5da216074f34)。
如有侵权,请联系 [email protected] 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

05-04 20:19