NackModule::OnReceivedPacket
说明
AddPacketsToNack 将一些可疑的丢包插入到NackList nacklist;
GetNackBatch 获取真正的丢包std::vector
RtpVideoStreamReceiver::RtcpFeedbackBuffer::SendNack 处理丢包
源码
int DEPRECATED_NackModule::OnReceivedPacket(uint16_t seq_num,bool is_keyframe) {return OnReceivedPacket(seq_num, is_keyframe, false);}int DEPRECATED_NackModule::OnReceivedPacket(uint16_t seq_num,bool is_keyframe,bool is_recovered) {MutexLock lock(&mutex_);// 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;}// 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);if (is_recovered) {recovered_list_.insert(seq_num);// Remove old ones so we don't accumulate recovered packets.auto it = recovered_list_.lower_bound(seq_num - kMaxPacketAge);if (it != recovered_list_.begin())recovered_list_.erase(recovered_list_.begin(), it);// Do not send nack for packets recovered by FEC or RTX.return 0;}AddPacketsToNack(newest_seq_num_ + 1, seq_num);newest_seq_num_ = seq_num;// Are there any nacks that are waiting for this seq_num.std::vector<uint16_t> nack_batch = GetNackBatch(kSeqNumOnly);if (!nack_batch.empty()) {// This batch of NACKs is triggered externally; the initiator can// batch them with other feedback messages.nack_sender_->SendNack(nack_batch, /*buffering_allowed=*/true);}return 0;}
NackModule::GetNackBatch
说明
const int kMaxNackRetries = 10;
如果最大尝试次数大于等于10,说明这个包不会过来了,需要将该包从nacklist移除掉。
多次循环,然后最后返回真正的丢包std::vector
源码
std::vector<uint16_t> DEPRECATED_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) {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;}
RtcpFeedbackBuffer::SendNack
说明
处理丢包,根据第二个参数buffering_allowed来决定丢包处理。
true 缓存起来不发送。
false 直接发送到对端不缓存,然后等对端重传丢失的包。
源码
void RtpVideoStreamReceiver::RtcpFeedbackBuffer::SendNack(const std::vector<uint16_t>& sequence_numbers,bool buffering_allowed) {RTC_DCHECK(!sequence_numbers.empty());MutexLock lock(&mutex_);nack_sequence_numbers_.insert(nack_sequence_numbers_.end(),sequence_numbers.cbegin(),sequence_numbers.cend());if (!buffering_allowed) {// Note that while *buffering* is not allowed, *batching* is, meaning that// previously buffered messages may be sent along with the current message.SendRtcpFeedback(ConsumeRtcpFeedbackLocked());}}
NackModule::Process
说明
webrtc每隔20ms来调用这个方法,这个20毫秒是动态变化的。
等待一段时间来获取真正的丢包,然后调用RtcpFeedbackBuffer::SendNack直接发送出去。
源码
void DEPRECATED_NackModule::Process() {if (nack_sender_) {std::vector<uint16_t> nack_batch;{MutexLock lock(&mutex_);nack_batch = GetNackBatch(kTimeOnly);}if (!nack_batch.empty()) {// This batch of NACKs is triggered externally; there is no external// initiator who can batch them with other feedback messages.nack_sender_->SendNack(nack_batch, /*buffering_allowed=*/false);}}// Update the next_process_time_ms_ in intervals to achieve// the targeted frequency over time. Also add multiple intervals// in case of a skip in time as to not make uneccessary// calls to Process in order to catch up.int64_t now_ms = clock_->TimeInMilliseconds();if (next_process_time_ms_ == -1) {next_process_time_ms_ = now_ms + kProcessIntervalMs;} else {next_process_time_ms_ = next_process_time_ms_ + kProcessIntervalMs +(now_ms - next_process_time_ms_) /kProcessIntervalMs * kProcessIntervalMs;}}
学习资料
WebRTC QoS | NACK 格式与发送策略
https://mp.weixin.qq.com/s/XXhpwJ__CarAepxQyW9Rew

