webrtc QOS方法一.2(接收端NACK流程實現)

一、概述

webrtc接收端觸發發送NACK報文有兩處:

1、接收RTP報文,對序列號進行檢測,發現有丟包,立即觸發發送NACK報文。

2、定時檢查nack_list_隊列,發現丟包滿足申請重傳條件,立即觸發發送NACK報文。

二、函數實現

1、接收丟包觸發函數實現

NackModule::OnReceivedPacket
->NackModule::GetNackBatch

函數裏實現,該函數在整個調用棧的位置如下:

 

2、定時檢查觸發函數實現

PlatformThread::StartThread()
->PlatformThread::Run()
->ProcessThreadImpl::Process()
->NackModule::Process()
->NackModule::GetNackBatch

其中NackModule::Process是掛載在接收RTP報文線程的一個定時任務。在RtpVideoStreamReceiver::RtpVideoStreamReceiver函數實現掛載。

NackModule::Process函數的調度週期是kProcessIntervalMs(默認20ms)

3、核心函數

NackModule::AddPacketsToNack、NackModule::GetNackBatch是NACK核心函數。

NackModule::AddPacketsToNack

決定是否將該報文放入NACK隊列

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();
      keyframe_request_sender_->RequestKeyFrame();
      return;
    }
  }

  for (uint16_t seq_num = seq_num_start; seq_num != seq_num_end; ++seq_num) {
    // Do not send nack for packets that are already recovered by FEC or RTX
    if (recovered_list_.find(seq_num) != recovered_list_.end())
      continue;
    NackInfo nack_info(seq_num, seq_num + WaitNumberOfPackets(0.5),
                       clock_->TimeInMilliseconds());
    RTC_DCHECK(nack_list_.find(seq_num) == nack_list_.end());
    nack_list_[seq_num] = nack_info;
  }
}

該函數的中心思想是:

1、nack_list的最大長度爲kMaxNackPackets,即本次發送的nack包至多可以對kMaxNackPackets個丟失的包進行重傳請求。如果丟失的包數量超過kMaxNackPackets,會循環清空nack_list中關鍵幀之前的包,直到其長度小於kMaxNackPackets。也就是說,放棄對關鍵幀首包之前的包的重傳請求,直接而快速的以關鍵幀首包之後的包號作爲重傳請求的開始。

2、nack_list中包號的距離不能超過kMaxPacketAge個包號。即nack_list中的包號始終保持 [cur_seq_num - kMaxPacketAge, cur_seq_num] 這樣的跨度,以保證nack請求列表中不會有太老舊的包號。

NackModule::GetNackBatch

決定是否發生NACK請求重傳該報文。兩種觸發方式都是調用這個函數決定是否發送NACK請求重傳。

std::vector<uint16_t> NackModule::GetNackBatch(NackFilterOptions options) {
  bool consider_seq_num = options != kTimeOnly;
  bool consider_timestamp = options != kSeqNumOnly;
  Timestamp now = clock_->CurrentTime();
  std::vector<uint16_t> nack_batch;
  auto it = nack_list_.begin();
  while (it != nack_list_.end()) {
    TimeDelta resend_delay = TimeDelta::Millis(rtt_ms_);
    if (backoff_settings_) {
      resend_delay = std::max(resend_delay, backoff_settings_->min_retry_interval);
      if (it->second.retries > 1) {
        TimeDelta exponential_backoff =
            std::min(TimeDelta::Millis(rtt_ms_), backoff_settings_->max_rtt) *
            std::pow(backoff_settings_->base, it->second.retries - 1);
        resend_delay = std::max(resend_delay, exponential_backoff);
      }
    }

    bool delay_timed_out =
        now.ms() - it->second.created_at_time >= send_nack_delay_ms_;
    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);
    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();
      if (it->second.retries >= kMaxNackRetries) {
        it = nack_list_.erase(it);
      } else {
        ++it;
      }
      continue;
    }
    ++it;
  }
  return nack_batch;
}

該函數的中心思想是:

1、因爲報文有可能出現亂序抖動情況,不能說檢測出丟包就立即重傳,需要等待send_nack_delay_ms_,當等待時間大於send_nack_delay_ms_,申請重傳。

2、因爲NACK產生的延時主要在RTT環路延時上,所以再次重傳的時間一定要大於rtt_ms_,當兩次發送NACK重傳請求時間大於rtt_ms_時,纔會申請再次重傳。

3、視頻會議場景對實時性要求很高,當報文一直處於丟包狀態,不能持續申請重傳,最大重傳次數爲kMaxNackRetries,超過最大重傳次數,放棄該報文。不再重傳。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章