简述WebRTC中的丢包重传Nack的实现

一 简述

接收端发现序列号不连续,发送RTCP FB Nack包,发送端从历史队列中查找该包,再发送RTP包,但WebRTC用的RTX重发该包,ssrc和原视频流不同,pt也不同

a=rtpmap:96 H264/90000
a=rtcp-fb:96 goog-remb
a=rtcp-fb:96 transport-cc
a=rtcp-fb:96 ccm fir
a=rtcp-fb:96 nack
a=rtcp-fb:96 nack pli
a=fmtp:96 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f
a=rtpmap:97 rtx/90000
a=fmtp:97 apt=96 //表示97是96重传
a=ssrc-group:FID 1753454336 165688474

这是WebRTC中的SDP,原视频流PT是96,重传RTX PT是97, ssrc-group是原视频流ssrc和RTX ssrc。

 

 sender ssrc是发送RTCP NACK包这端发送视频rtp包的ssrc,media ssrc是随机生成的。

if (receiver_only_ || main_ssrc_ != nack.media_ssrc())  // Not to us.

return;

RTCP FB Nack包如上图所示:pt=205,fmt=1,PID是丢包的起始序列号-2字节,BLP表示起始序列号后面16位丢包情况,1是丢包,0是没丢包。读blp的值要反一下。BLP占2字节。这种结构体可以有多个。

二 实现

发送RTCP FB NACK报文

NackModule2发送RTCP FB NACK报文有两种实现方式:一种是基于序列号,另一种基于时间,周期是20ms。两种都有用到。

AddPacketsToNack(uint16_t seq_num_start,uint16_t seq_num_end),参1 !=参2,说明丢包了。

// Do not send nack for packets that are already recovered by FEC or RTX
    if (recovered_list_.find(seq_num) != recovered_list_.end())
      continue;

丢包了,就放到nack_list_[seq_num] = nack_info;中。
std::vector<uint16_t> NackModule2::GetNackBatch(NackFilterOptions options) {
...
//send_nack_delay_ms_可设置,默认为0
bool delay_timed_out =
        now.ms() - it->second.created_at_time >= send_nack_delay_ms_;
    //resend_delay是rtt,基于时间的,要判断rtt
    bool nack_on_rtt_passed =
        now.ms() - it->second.sent_at_time >= resend_delay.ms();
    bool nack_on_seq_num_passed =
        it->second.sent_at_time == -1 &&
        AheadOrAt(newest_seq_num_, it->second.send_at_seq_num);
    //sent_at_time == -1, 重发一次后,值变了,基于序列号的不会重发到第二次
    if (delay_timed_out && ((consider_seq_num && nack_on_seq_num_passed) ||
                            (consider_timestamp && nack_on_rtt_passed))) {
      nack_batch.emplace_back(it->second.seq_num);	
      ++it->second.retries;	
      it->second.sent_at_time = now.ms();
...
}

nack_sender_->SendNack(nack_batch, /*buffering_allowed=*/true);

基于时间的:在构造函数中,起了一个定时任务, nack_sender_->SendNack(nack_batch, /*buffering_allowed=*/false);

一些参数:

const int kMaxNackPackets = 1000;//nack_list_队列的最大长度
const int kDefaultRttMs = 100; //rtt的默认值
const int kMaxNackRetries = 10; //最大重传次数10次
const int kMaxPacketAge = 10000;

丢包太多,会直接请求FIR、关键帧了。

2 发送端用RTX重发RTP包

根据序列号,从packet_history_找到该包。重发。

packet_history_默认大小是600个,可设置的,存储600个RTP包。

static const int kMinSendSidePacketHistorySize = 600;

细节1:发送端重发丢失的包,会判断RTT,时间比RTT小,不发送

RTPSender::ReSendPacket(...)中,会从packet_history_队列中查找,

if (!VerifyRtt(*packet, clock_->TimeInMilliseconds())) {
    // Packet already resent within too short a time window, ignore.
    return nullptr;
  }

PacingController::EnqueuePacket中,会判断优先级,音频、重传、视频。

细节2:RTX和原RTP包不同的地方:payload多了2字节的OSN即原始序列号,具体见 RTPSender::BuildRtxPacket。

3 接收到原视频包或RTX包

根据ssrc判断出是那种。在RtxReceiveStream::OnRtpPacket中,处理RTX包,然后给RtpVideoStreamReceiver2::OnRtpPacket。

三 自问自答

1 RTCP FB NACK包发送原则?发现丢包立即发送?定时发送?

答:不仅仅判断丢包,基于时间的还判断了RTT,RTT是一直在更新的。

代码见:NackModule2::GetNackBatch。不同版本有差别。

2 重发RTP包即RTX包,原则?判断了rtt时间。

发RTCP Nack包,最多重试10次?

基于序列号的,不会重发10次的。sent_at_time == -1 ,还有什么情况能赋值为-1 ?

基于时间存在重试10次的情况。

3 基于时间周期是20ms,期望RTT在20毫秒之内!!!这个值可以优化。

4 基于序列号和时间有冲突吗?没有,++retries。

怎么优化nack,调整那些参数?

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值