WEBRTC浅析(五)视频Nack包的发送判断逻辑以及数据流

10 篇文章 11 订阅

这篇文章是对webrtc 中Nack包发送机制的梳理,主要包括三个部分:
第一部分,介绍RTCP包中,Nack包的规范。
第二部分,介绍在WEBRTC中,Nack发送机制的数据流程图。
第三部分,介绍在WEBRTC中,Nack处理的一些关键的代码。

一:RTCP Nack报文解析

    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |V=2|P|   FMT   |       PT      |          length               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                  SSRC of packet sender                        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                  SSRC of media source                         |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   :            Feedback Control Information (FCI)                 :
   :              


   Feedback message type (FMT): 5 bits
	   0:    unassigned
	   1:    Generic NACK
	   2-30: unassigned
	   31:   reserved for future expansion of the identifier number space


   Payload type (PT): 8 bits
      This is the RTCP packet type that identifies the packet as being
      an RTCP FB message.  Two values are defined by the IANA:

            Name   | Value | Brief Description
         ----------+-------+------------------------------------
            RTPFB  |  205  | Transport layer FB message
            PSFB   |  206  | Payload-specific FB message

   ssrc packet sender: 构造发送当前消息包的端的SSRC
   ssrc source:  NACK消息部分,也是SSRC值,在此可称为媒体源标识符

   The Feedback Control Information (FCI) field has the following Syntax
   
    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |            PID                |             BLP               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

参考链接:rfc4585

  • 如果是一个NACK message
    • PT=RTPFB

    • FMT=1

    • 丢包为10,20,30,40,50。用3个int可以表示清楚,用rfc 4585的通用协议表示如下

        Real-time Transport Control Protocol (Generic RTP Feedback): NACK: 2 frames lost: NACK: 2 frames lost: NACK: 1 frames lost
        10.. .... = Version: RFC 1889 Version (2)
        ..0. .... = Padding: False
        ...0 0001 = RTCP Feedback message type (FMT): Generic negative acknowledgement (NACK) (1)
        Packet type: Generic RTP Feedback (205)
        Length: 5 (24 bytes)
        Sender SSRC: 0x00123456 (1193046)
        Media source SSRC: 0x00c0ffed (12648429)
        RTCP Transport Feedback NACK PID: 10
        RTCP Transport Feedback NACK BLP: 0x0200 (Frames 20 lost)
        RTCP Transport Feedback NACK PID: 30
        RTCP Transport Feedback NACK BLP: 0x0200 (Frames 40 lost)
        RTCP Transport Feedback NACK PID: 50
        RTCP Transport Feedback NACK BLP: 0x0000 (No additional frames lost)
      

      PID为10,表示sequence为10的包,BLP的第10位为1,表示10之后第10个包,即sequence num为20的包,所以一个int可以表示17个连续包的丢包情况。

二:Nack包发送流程图

NACK_FLOW.jpg

三:Nack在webrtc中的计算

VideoStream接收端的处理
  • 添加Nack 包到nack_list_中去

      	void NackModule::AddPacketsToNack(uint16_t seq_num_start,
      	                                  uint16_t seq_num_end) {
      	  // Remove old packets.
      	  auto it = nack_list_.lower_bound(seq_num_end - kMaxPacketAge);
      	  nack_list_.erase(nack_list_.begin(), it);
      	
      	  // If the nack list is too large, remove packets from the nack list until
      	  // the latest first packet of a keyframe. If the list is still too large,
      	  // clear it and request a keyframe.
      	  uint16_t num_new_nacks = ForwardDiff(seq_num_start, seq_num_end);
      	  if (nack_list_.size() + num_new_nacks > kMaxNackPackets) {
      	    while (RemovePacketsUntilKeyFrame() &&
      	           nack_list_.size() + num_new_nacks > kMaxNackPackets) {
      	    }
      	
      	    if (nack_list_.size() + num_new_nacks > kMaxNackPackets) {
      	      nack_list_.clear();
      	      LOG(LS_WARNING) << "NACK list full, clearing NACK"
      	                         " list and requesting keyframe.";
      	      keyframe_request_sender_->RequestKeyFrame();
      	      return;
      	    }
      	  }
      	
      	  //在Start 和 End之间的RTP包,就是需要发Nack的包。
      	  for (uint16_t seq_num = seq_num_start; seq_num != seq_num_end; ++seq_num) {
      	    NackInfo nack_info(seq_num, seq_num + WaitNumberOfPackets(0.5));
      	    RTC_DCHECK(nack_list_.find(seq_num) == nack_list_.end());
      	    nack_list_[seq_num] = nack_info;
      	  }
      	}
    
  • 根据不同的kSeqNumOnly, kTimeOnly模式,来选择需要重发的包

      std::vector<uint16_t> NackModule::GetNackBatch(NackFilterOptions options) {
        bool consider_seq_num = options != kTimeOnly;
        bool consider_timestamp = options != kSeqNumOnly;
        int64_t now_ms = clock_->TimeInMilliseconds();
        std::vector<uint16_t> nack_batch;
        auto it = nack_list_.begin();
        while (it != nack_list_.end()) {
          if (consider_seq_num && it->second.sent_at_time == -1 &&
              AheadOrAt(newest_seq_num_, it->second.send_at_seq_num)) {
            nack_batch.emplace_back(it->second.seq_num);
            ++it->second.retries;
            it->second.sent_at_time = now_ms;
            if (it->second.retries >= kMaxNackRetries) {
              LOG(LS_VERBOSE) << "Sequence number " << it->second.seq_num
                              << " removed from NACK list due to max retries."
                              << " newest_seq_num " << newest_seq_num_
                              << " it->second.send_at_seq_num " << it->second.send_at_seq_num
                                 ;
              it = nack_list_.erase(it);
            } else {
              ++it;
            }
            continue;
          }
      
          if (consider_timestamp && it->second.sent_at_time + rtt_ms_ <= now_ms) {
            nack_batch.emplace_back(it->second.seq_num);
            ++it->second.retries;
            it->second.sent_at_time = now_ms;
            if (it->second.retries >= kMaxNackRetries) {
              LOG(LS_VERBOSE) << "Sequence number " << it->second.seq_num
                              << " removed from NACK list due to max retries."
                              << " rtt_ms " << rtt_ms_
                              ;
              it = nack_list_.erase(it);
            } else {
              ++it;
            }
            continue;
          }
          ++it;
        }
        return nack_batch;
      }
    
VideoSendStream端的处理
  • 收到OnReceivedNack事件

      int32_t RTPSender::ReSendPacket(uint16_t packet_id, int64_t min_resend_time) {
      
        // 根据RTT,去从队列里找到还能发送的RTP包
        std::unique_ptr<RtpPacketToSend> packet =
            packet_history_.GetPacketAndSetSendTime(packet_id, min_resend_time, true);
        if (!packet) {
          // Packet not found.
          LOG(LS_VERBOSE) << "ResendPacket packet not found "
                          << " pktid " << packet_id
                          << " min_resend_time " << min_resend_time
                             ;
          return 0;
        }
      	
      	。。。。。。
      
        if (paced_sender_) {
          // Convert from TickTime to Clock since capture_time_ms is based on
          // TickTime.
          int64_t corrected_capture_tims_ms =
              packet->capture_time_ms() + clock_delta_ms_;
              
              
          // 添加到Pace Sender里去发送    
          paced_sender_->InsertPacket(RtpPacketSender::kNormalPriority,
                                      packet->Ssrc(), packet->SequenceNumber(),
                                      corrected_capture_tims_ms,
                                      packet->payload_size(), true);
    
      	。。。。。。
    
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值