NackModule::OnReceivedPacket

image.png

说明

AddPacketsToNack 将一些可疑的丢包插入到NackList nacklist
GetNackBatch 获取真正的丢包std::vector nack_batch;
RtpVideoStreamReceiver::RtcpFeedbackBuffer::SendNack 处理丢包

源码

  1. int DEPRECATED_NackModule::OnReceivedPacket(uint16_t seq_num,
  2. bool is_keyframe) {
  3. return OnReceivedPacket(seq_num, is_keyframe, false);
  4. }
  5. int DEPRECATED_NackModule::OnReceivedPacket(uint16_t seq_num,
  6. bool is_keyframe,
  7. bool is_recovered) {
  8. MutexLock lock(&mutex_);
  9. // TODO(philipel): When the packet includes information whether it is
  10. // retransmitted or not, use that value instead. For
  11. // now set it to true, which will cause the reordering
  12. // statistics to never be updated.
  13. bool is_retransmitted = true;
  14. if (!initialized_) {
  15. newest_seq_num_ = seq_num;
  16. if (is_keyframe)
  17. keyframe_list_.insert(seq_num);
  18. initialized_ = true;
  19. return 0;
  20. }
  21. // Since the |newest_seq_num_| is a packet we have actually received we know
  22. // that packet has never been Nacked.
  23. if (seq_num == newest_seq_num_)
  24. return 0;
  25. if (AheadOf(newest_seq_num_, seq_num)) {
  26. // An out of order packet has been received.
  27. auto nack_list_it = nack_list_.find(seq_num);
  28. int nacks_sent_for_packet = 0;
  29. if (nack_list_it != nack_list_.end()) {
  30. nacks_sent_for_packet = nack_list_it->second.retries;
  31. nack_list_.erase(nack_list_it);
  32. }
  33. if (!is_retransmitted)
  34. UpdateReorderingStatistics(seq_num);
  35. return nacks_sent_for_packet;
  36. }
  37. // Keep track of new keyframes.
  38. if (is_keyframe)
  39. keyframe_list_.insert(seq_num);
  40. // And remove old ones so we don't accumulate keyframes.
  41. auto it = keyframe_list_.lower_bound(seq_num - kMaxPacketAge);
  42. if (it != keyframe_list_.begin())
  43. keyframe_list_.erase(keyframe_list_.begin(), it);
  44. if (is_recovered) {
  45. recovered_list_.insert(seq_num);
  46. // Remove old ones so we don't accumulate recovered packets.
  47. auto it = recovered_list_.lower_bound(seq_num - kMaxPacketAge);
  48. if (it != recovered_list_.begin())
  49. recovered_list_.erase(recovered_list_.begin(), it);
  50. // Do not send nack for packets recovered by FEC or RTX.
  51. return 0;
  52. }
  53. AddPacketsToNack(newest_seq_num_ + 1, seq_num);
  54. newest_seq_num_ = seq_num;
  55. // Are there any nacks that are waiting for this seq_num.
  56. std::vector<uint16_t> nack_batch = GetNackBatch(kSeqNumOnly);
  57. if (!nack_batch.empty()) {
  58. // This batch of NACKs is triggered externally; the initiator can
  59. // batch them with other feedback messages.
  60. nack_sender_->SendNack(nack_batch, /*buffering_allowed=*/true);
  61. }
  62. return 0;
  63. }

NackModule::GetNackBatch

image.png
image.png

说明

const int kMaxNackRetries = 10;
如果最大尝试次数大于等于10,说明这个包不会过来了,需要将该包从nacklist移除掉。
多次循环,然后最后返回真正的丢包std::vector nack_batch;

源码

  1. std::vector<uint16_t> DEPRECATED_NackModule::GetNackBatch(
  2. NackFilterOptions options) {
  3. bool consider_seq_num = options != kTimeOnly;
  4. bool consider_timestamp = options != kSeqNumOnly;
  5. Timestamp now = clock_->CurrentTime();
  6. std::vector<uint16_t> nack_batch;
  7. auto it = nack_list_.begin();
  8. while (it != nack_list_.end()) {
  9. TimeDelta resend_delay = TimeDelta::Millis(rtt_ms_);
  10. if (backoff_settings_) {
  11. resend_delay =
  12. std::max(resend_delay, backoff_settings_->min_retry_interval);
  13. if (it->second.retries > 1) {
  14. TimeDelta exponential_backoff =
  15. std::min(TimeDelta::Millis(rtt_ms_), backoff_settings_->max_rtt) *
  16. std::pow(backoff_settings_->base, it->second.retries - 1);
  17. resend_delay = std::max(resend_delay, exponential_backoff);
  18. }
  19. }
  20. bool delay_timed_out =
  21. now.ms() - it->second.created_at_time >= send_nack_delay_ms_;
  22. bool nack_on_rtt_passed =
  23. now.ms() - it->second.sent_at_time >= resend_delay.ms();
  24. bool nack_on_seq_num_passed =
  25. it->second.sent_at_time == -1 &&
  26. AheadOrAt(newest_seq_num_, it->second.send_at_seq_num);
  27. if (delay_timed_out && ((consider_seq_num && nack_on_seq_num_passed) ||
  28. (consider_timestamp && nack_on_rtt_passed))) {
  29. nack_batch.emplace_back(it->second.seq_num);
  30. ++it->second.retries;
  31. it->second.sent_at_time = now.ms();
  32. if (it->second.retries >= kMaxNackRetries) {
  33. RTC_LOG(LS_WARNING) << "Sequence number " << it->second.seq_num
  34. << " removed from NACK list due to max retries.";
  35. it = nack_list_.erase(it);
  36. } else {
  37. ++it;
  38. }
  39. continue;
  40. }
  41. ++it;
  42. }
  43. return nack_batch;
  44. }

RtcpFeedbackBuffer::SendNack

image.png

说明

处理丢包,根据第二个参数buffering_allowed来决定丢包处理。
true 缓存起来不发送。
false 直接发送到对端不缓存,然后等对端重传丢失的包。

源码

  1. void RtpVideoStreamReceiver::RtcpFeedbackBuffer::SendNack(
  2. const std::vector<uint16_t>& sequence_numbers,
  3. bool buffering_allowed) {
  4. RTC_DCHECK(!sequence_numbers.empty());
  5. MutexLock lock(&mutex_);
  6. nack_sequence_numbers_.insert(nack_sequence_numbers_.end(),
  7. sequence_numbers.cbegin(),
  8. sequence_numbers.cend());
  9. if (!buffering_allowed) {
  10. // Note that while *buffering* is not allowed, *batching* is, meaning that
  11. // previously buffered messages may be sent along with the current message.
  12. SendRtcpFeedback(ConsumeRtcpFeedbackLocked());
  13. }
  14. }

NackModule::Process

image.png

说明

webrtc每隔20ms来调用这个方法,这个20毫秒是动态变化的。
等待一段时间来获取真正的丢包,然后调用RtcpFeedbackBuffer::SendNack直接发送出去。

源码

  1. void DEPRECATED_NackModule::Process() {
  2. if (nack_sender_) {
  3. std::vector<uint16_t> nack_batch;
  4. {
  5. MutexLock lock(&mutex_);
  6. nack_batch = GetNackBatch(kTimeOnly);
  7. }
  8. if (!nack_batch.empty()) {
  9. // This batch of NACKs is triggered externally; there is no external
  10. // initiator who can batch them with other feedback messages.
  11. nack_sender_->SendNack(nack_batch, /*buffering_allowed=*/false);
  12. }
  13. }
  14. // Update the next_process_time_ms_ in intervals to achieve
  15. // the targeted frequency over time. Also add multiple intervals
  16. // in case of a skip in time as to not make uneccessary
  17. // calls to Process in order to catch up.
  18. int64_t now_ms = clock_->TimeInMilliseconds();
  19. if (next_process_time_ms_ == -1) {
  20. next_process_time_ms_ = now_ms + kProcessIntervalMs;
  21. } else {
  22. next_process_time_ms_ = next_process_time_ms_ + kProcessIntervalMs +
  23. (now_ms - next_process_time_ms_) /
  24. kProcessIntervalMs * kProcessIntervalMs;
  25. }
  26. }

学习资料

WebRTC QoS | NACK 格式与发送策略
https://mp.weixin.qq.com/s/XXhpwJ__CarAepxQyW9Rew