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<uint16_t> 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<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;
最多发送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;
}
- 最多重传 kMaxNackRetries = 10 次
- 比上一次多出 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)
->
->
->
->