这篇文章是对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 bits
0: unassigned
1: Generic NACK
2-30: unassigned
31: reserved for future expansion of the identifier number space
Payload type (PT): 8 bits
This is the RTCP packet type that identifies the packet as being
an RTCP FB message. Two values are defined by the IANA:
Name | Value | Brief Description
----------+-------+------------------------------------
RTPFB | 205 | Transport layer FB message
PSFB | 206 | Payload-specific FB message
ssrc packet sender: 构造发送当前消息包的端的SSRC
ssrc source: NACK消息部分,也是SSRC值,在此可称为媒体源标识符
The Feedback Control Information (FCI) field has the following Syntax
0 1 2 3
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 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 lost 10.. .... = 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: 10 RTCP Transport Feedback NACK BLP: 0x0200 (Frames 20 lost) RTCP Transport Feedback NACK PID: 30 RTCP Transport Feedback NACK BLP: 0x0200 (Frames 40 lost) RTCP Transport Feedback NACK PID: 50 RTCP 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); 。。。。。。