webrtc 代码学习(二十五)video nack 模块

video nack 模块
作者:LanPZzzz

文章目录

          • 1. video nack 模块 在 modules\video_coding\nack_module.cc 中
          • 2. NackModule::OnReceivedPacket 获取 video rtp 包
          • 3. NackModule::AddPacketsToNack
          • 4. NackModule::GetNackBatch,分2种,根据 seq 和 timestamp 进行选择重发,在OnReceivedPacket 是 seq, 任务线程中是 ts
          • 5. NackModule::Process, 间隔时间 NackModule::TimeUntilNextProcess
          • 6. nack 中的rtt 是由线程驱动传递的
          • 7. nack 中的rtt 是怎么计算的,请看第三十章节

1. video nack 模块 在 modules\video_coding\nack_module.cc 中
2. NackModule::OnReceivedPacket 获取 video rtp 包
int NackModule::OnReceivedPacket(uint16_t seq_num, bool is_keyframe) {
  rtc::CritScope lock(&crit_);
  // TODO(philipel): When the packet includes information whether it is
  //                 retransmitted or not, use that value instead. For
  //                 now set it to true, which will cause the reordering
  //                 statistics to never be updated.
  bool is_retransmitted = true;

  初始化
  if (!initialized_) {
    newest_seq_num_ = seq_num;
    if (is_keyframe)
      keyframe_list_.insert(seq_num);
    initialized_ = true;
    return 0;
  }

  // Since the |newest_seq_num_| is a packet we have actually received we know
  // that packet has never been Nacked.
  if (seq_num == newest_seq_num_)
    return 0;

  if (AheadOf(newest_seq_num_, seq_num)) {
    // An out of order packet has been received.
    auto nack_list_it = nack_list_.find(seq_num);
    int nacks_sent_for_packet = 0;
    if (nack_list_it != nack_list_.end()) {
      nacks_sent_for_packet = nack_list_it->second.retries;
      nack_list_.erase(nack_list_it);
    }
    if (!is_retransmitted)
      UpdateReorderingStatistics(seq_num);
    return nacks_sent_for_packet;
  }
  insert 包
  AddPacketsToNack(newest_seq_num_ + 1, seq_num);
  newest_seq_num_ = seq_num;

  // Keep track of new keyframes.
  if (is_keyframe)
    keyframe_list_.insert(seq_num);

  // And remove old ones so we don't accumulate keyframes.
  auto it = keyframe_list_.lower_bound(seq_num - kMaxPacketAge);
  if (it != keyframe_list_.begin())
    keyframe_list_.erase(keyframe_list_.begin(), it);

  // Are there any nacks that are waiting for this seq_num.
  std::vector nack_batch = GetNackBatch(kSeqNumOnly);
  发送nack 包
  if (!nack_batch.empty())
    nack_sender_->SendNack(nack_batch);

  return 0;
}
3. NackModule::AddPacketsToNack
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();
      RTC_LOG(LS_WARNING) << "NACK list full, clearing NACK"
                             " list and requesting keyframe.";
      keyframe_request_sender_->RequestKeyFrame();
      return;
    }
  }

  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;
  }
}

这里其他的和audio 的 nack 出不多,清理老包(小于 seq_num_end - kMaxPacketAge),把要重传的包放入到nack_list_。区别比较大的就是重新需要关键帧

4. NackModule::GetNackBatch,分2种,根据 seq 和 timestamp 进行选择重发,在OnReceivedPacket 是 seq, 任务线程中是 ts
std::vector NackModule::GetNackBatch(NackFilterOptions options) {
  bool consider_seq_num = options != kTimeOnly;
  bool consider_timestamp = options != kSeqNumOnly;
  int64_t now_ms = clock_->TimeInMilliseconds();
  std::vector 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;
      最多发送10次
      if (it->second.retries >= kMaxNackRetries) {
        RTC_LOG(LS_WARNING) << "Sequence number " << it->second.seq_num
                            << " removed from NACK list due to max retries.";
        it = nack_list_.erase(it);
      } else {
        ++it;
      }
      continue;
    }

    这个 it->second.sent_at_time + rtt_ms_ <= now_ms 上一次大于rtt_ms_的时间后才选择重传
    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) {
        RTC_LOG(LS_WARNING) << "Sequence number " << it->second.seq_num
                            << " removed from NACK list due to max retries.";
        it = nack_list_.erase(it);
      } else {
        ++it;
      }
      continue;
    }
    ++it;
  }
  return nack_batch;
}
  1. 最多重传 kMaxNackRetries = 10 次
  2. 比上一次多出 rtt_ms_ 的时间,才重传
5. NackModule::Process, 间隔时间 NackModule::TimeUntilNextProcess
    if (!nack_batch.empty())
      nack_sender_->SendNack(nack_batch);

直接发送nack 包请求

6. nack 中的rtt 是由线程驱动传递的

-> CallStats::Process (video\call_stats.cc 120)
->VideoReceiveStream::OnRttUpdate (video\video_receive_stream.cc 360)
->RtpVideoStreamReceiver::UpdateRtt (video\rtp_video_stream_receiver.cc 350)
->NackModule::UpdateRtt (modules\video_coding\nack_module.cc 124)
->
->
->
->

7. nack 中的rtt 是怎么计算的,请看第三十章节

你可能感兴趣的:(webrtc学习)