这篇文章是对webrtc 中Nack包发送机制的梳理,主要包括三个部分:
第一部分,介绍RTCP包中,Nack包的规范。
第二部分,介绍在WEBRTC中,Nack发送机制的数据流程图。
第三部分,介绍在WEBRTC中,Nack处理的一些关键的代码。
一:RTCP Nack报文解析
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|V=2|P| FMT | PT | length |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| SSRC of packet sender |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| SSRC of media source |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+: Feedback Control Information (FCI) :: Feedback message type (FMT): 5 bits0: unassigned1: Generic NACK2-30: unassigned31: reserved for future expansion of the identifier number spacePayload type (PT): 8 bitsThis is the RTCP packet type that identifies the packet as beingan RTCP FB message. Two values are defined by the IANA:Name | Value | Brief Description----------+-------+------------------------------------RTPFB | 205 | Transport layer FB messagePSFB | 206 | Payload-specific FB messagessrc packet sender: 构造发送当前消息包的端的SSRCssrc source: NACK消息部分,也是SSRC值,在此可称为媒体源标识符The Feedback Control Information (FCI) field has the following Syntax0 1 2 30 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| PID | BLP |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
参考链接:rfc4585
- 如果是一个NACK message
-
PT=RTPFB
-
FMT=1
-
丢包为10,20,30,40,50。用3个int可以表示清楚,用rfc 4585的通用协议表示如下
Real-time Transport Control Protocol (Generic RTP Feedback): NACK: 2 frames lost: NACK: 2 frames lost: NACK: 1 frames lost10.. .... = Version: RFC 1889 Version (2)..0. .... = Padding: False...0 0001 = RTCP Feedback message type (FMT): Generic negative acknowledgement (NACK) (1)Packet type: Generic RTP Feedback (205)Length: 5 (24 bytes)Sender SSRC: 0x00123456 (1193046)Media source SSRC: 0x00c0ffed (12648429)RTCP Transport Feedback NACK PID: 10RTCP Transport Feedback NACK BLP: 0x0200 (Frames 20 lost)RTCP Transport Feedback NACK PID: 30RTCP Transport Feedback NACK BLP: 0x0200 (Frames 40 lost)RTCP Transport Feedback NACK PID: 50RTCP Transport Feedback NACK BLP: 0x0000 (No additional frames lost)
PID为10,表示sequence为10的包,BLP的第10位为1,表示10之后第10个包,即sequence num为20的包,所以一个int可以表示17个连续包的丢包情况。
-
二:Nack包发送流程图
三:Nack在webrtc中的计算
VideoStream接收端的处理
-
添加Nack 包到nack_list_中去
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();LOG(LS_WARNING) << "NACK list full, clearing NACK"" list and requesting keyframe.";keyframe_request_sender_->RequestKeyFrame();return;}}//在Start 和 End之间的RTP包,就是需要发Nack的包。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;}}
-
根据不同的kSeqNumOnly, kTimeOnly模式,来选择需要重发的包
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;if (it->second.retries >= kMaxNackRetries) {LOG(LS_VERBOSE) << "Sequence number " << it->second.seq_num<< " removed from NACK list due to max retries."<< " newest_seq_num " << newest_seq_num_<< " it->second.send_at_seq_num " << it->second.send_at_seq_num;it = nack_list_.erase(it);} else {++it;}continue;}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) {LOG(LS_VERBOSE) << "Sequence number " << it->second.seq_num<< " removed from NACK list due to max retries."<< " rtt_ms " << rtt_ms_;it = nack_list_.erase(it);} else {++it;}continue;}++it;}return nack_batch;}
VideoSendStream端的处理
-
收到OnReceivedNack事件
int32_t RTPSender::ReSendPacket(uint16_t packet_id, int64_t min_resend_time) {// 根据RTT,去从队列里找到还能发送的RTP包std::unique_ptr<RtpPacketToSend> packet =packet_history_.GetPacketAndSetSendTime(packet_id, min_resend_time, true);if (!packet) {// Packet not found.LOG(LS_VERBOSE) << "ResendPacket packet not found "<< " pktid " << packet_id<< " min_resend_time " << min_resend_time;return 0;}。。。。。。if (paced_sender_) {// Convert from TickTime to Clock since capture_time_ms is based on// TickTime.int64_t corrected_capture_tims_ms =packet->capture_time_ms() + clock_delta_ms_;// 添加到Pace Sender里去发送 paced_sender_->InsertPacket(RtpPacketSender::kNormalPriority,packet->Ssrc(), packet->SequenceNumber(),corrected_capture_tims_ms,packet->payload_size(), true);。。。。。。