webrtc丢包率与jitter计算
RR報文格式: fraction lost cumulative number of packets lost interarrival jitter extended highest sequence number received:
0 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+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ header |V=2|P| RC | PT=RR=201 | length |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| SSRC of packet sender |+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ report | SSRC_1 (SSRC of first source) | block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+1 | fraction lost | cumulative number of packets lost |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| extended highest sequence number received |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| interarrival jitter |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| last SR (LSR) |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| delay since last SR (DLSR) |+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ report | SSRC_2 (SSRC of second source) | block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+2 : ... :+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+| profile-specific extensions |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+丟包計算
-
transmitted,接收到的RTP包的總數(shù);
-
retransmitted,接收到重傳RTP包的數(shù)量;
2.某時刻收到的有序包的數(shù)量Count = transmitted-retransmitte ,當前時刻為Count2,上一時刻為Count1;
3.接收端以一定的頻率發(fā)送RTCP包(RR、REMB、NACK等)時,會統(tǒng)計兩次發(fā)送間隔之間(fraction)的接收包信息:
//兩次發(fā)送間隔之間理論上應該收到的包數(shù)量=當前接收到的最大包序號-上個時刻最大有序包序號
uint16_t exp_since_last = (received_seq_max_ - last_report_seq_max_);//兩次發(fā)送間隔之間實際接收到有序包的數(shù)量=當前時刻收到的有序包的數(shù)量-上一個時刻收到的有序包的數(shù)量
uint32_t rec_since_last = Count2 - Count1//丟包數(shù)=理論上應收的包數(shù)-實際收到的包數(shù)
int32_t missing = exp_since_last - rec_since_lastmissing即為兩次發(fā)送間隔之間的丟包數(shù)量,會累加并通過RR包通知發(fā)送端
丟包統(tǒng)計代碼分析
void StreamStatisticianImpl::UpdateCounters(const RTPHeader& header,size_t packet_length,bool retransmitted) {rtc::CritScope cs(&stream_lock_);......//接收到的RTP包的總數(shù)receive_counters_.transmitted.AddPacket(packet_length, header);//接收到重傳RTP包的數(shù)量if (!in_order && retransmitted) {receive_counters_.retransmitted.AddPacket(packet_length, header);}......// New max.received_seq_max_ = header.sequenceNumber;...... // Count only the new packets received. That is, if packets 1, 2, 3, 5, 4, 6// are received, 4 will be ignored.if (in_order) {last_received_timestamp_ = header.timestamp;last_receive_time_ntp_ = receive_time;last_receive_time_ms_ = clock_->TimeInMilliseconds();}...... }更新transmitted, retransmitted
RtcpStatistics StreamStatisticianImpl::CalculateRtcpStatistics() {RtcpStatistics stats;if (last_report_inorder_packets_ == 0) {// First time we send a report.last_report_seq_max_ = received_seq_first_ - 1;}//last_report_seq_max_ 上一次發(fā)送rr時的seq_max//received_seq_max_ 當前的seq_max//exp_since_last 期望能收到多少包// Calculate fraction lost.uint16_t exp_since_last = (received_seq_max_ - last_report_seq_max_);......// 當前實時刻收到的有序包的數(shù)量receive_counters_.transmitted.packets - receive_counters_.retransmitted.packets// 上一次發(fā)送rr時收到包的數(shù)量 last_report_old_packets_// 實際收到包的數(shù)量 rec_since_last// Number of received RTP packets since last report, counts all packets but// not re-transmissions.uint32_t rec_since_last =(receive_counters_.transmitted.packets -receive_counters_.retransmitted.packets) - last_report_inorder_packets_;// With NACK we don't know the expected retransmissions during the last// second. We know how many "old" packets we have received. We just count// the number of old received to estimate the loss, but it still does not// guarantee an exact number since we run this based on time triggered by// sending of an RTP packet. This should have a minimum effect.// With NACK we don't count old packets as received since they are// re-transmitted. We use RTT to decide if a packet is re-ordered or// re-transmitted.//計算從上一次rr到當前這段時間內(nèi),收到的重傳包總數(shù)uint32_t retransmitted_packets =receive_counters_.retransmitted.packets - last_report_old_packets_;//實際丟包數(shù)加上重傳丟包數(shù)rec_since_last += retransmitted_packets;// 計算丟包數(shù):期望收到的包總數(shù)exp_since_last - 實際收到的包總數(shù)rec_since_lastint32_t missing = 0;if (exp_since_last > rec_since_last) {missing = (exp_since_last - rec_since_last);}//丟包率 = 255 * 丟包數(shù) / 預期收到的包總數(shù)uint8_t local_fraction_lost = 0;if (exp_since_last) {// Scale 0 to 255, where 255 is 100% loss.local_fraction_lost =static_cast<uint8_t>(255 * missing / exp_since_last);}stats.fraction_lost = local_fraction_lost;// We need a counter for cumulative loss too.// TODO(danilchap): Ensure cumulative loss is below maximum value of 2^24.// 累加丟包總數(shù)cumulative_loss_ += missing;stats.cumulative_lost = cumulative_loss_; ...... }抖動計算
jitter定義
如果Si代表第i個包的發(fā)送時間戳,Ri代表第i個包的接收時間戳。Sj、Rj同理。
抖動(i, j) = |(Rj - Ri) - (Sj - Si)| = |(Rj - Sj) - (Ri - Si)|
WebRTC為了統(tǒng)一抖動,并且為了很好的降噪、降低突發(fā)抖動的影響,把上面的抖動(i, j)定義為D(i, j),抖動J(i)定義為:
J(i) = J(i-1) + (|D(i-1, i)| - J(i - 1)) / 16
我雖然看不出J(i)和D(i)的關(guān)系,但是D(i-1, j)是唯一引起J(i)變化的因素,是需要重點關(guān)注的。
代碼分析
void StreamStatisticianImpl::UpdateCounters(const RTPHeader& header,size_t packet_length,bool retransmitted) {if (in_order) {......// If new time stamp and more than one in-order packet received, calculate// new jitter statistics.if (header.timestamp != last_received_timestamp_ &&(receive_counters_.transmitted.packets -receive_counters_.retransmitted.packets) > 1) {//更新JitterUpdateJitter(header, receive_time);}......}...... }更新jitter
void StreamStatisticianImpl::UpdateJitter(const RTPHeader& header,NtpTime receive_time) {//receive_time_rtp對應Rj last_receive_time_rtp對應Ri//header.timestamp對應Sj last_received_timestamp_對應Si//`抖動(i, j)` = `|(Rj - Ri) - (Sj - Si)|`uint32_t receive_time_rtp =NtpToRtp(receive_time, header.payload_type_frequency);uint32_t last_receive_time_rtp =NtpToRtp(last_receive_time_ntp_, header.payload_type_frequency);int32_t time_diff_samples = (receive_time_rtp - last_receive_time_rtp) -(header.timestamp - last_received_timestamp_);time_diff_samples = std::abs(time_diff_samples);// lib_jingle sometimes deliver crazy jumps in TS for the same stream.// If this happens, don't update jitter value. Use 5 secs video frequency// as the threshold.if (time_diff_samples < 450000) {// Note we calculate in Q4 to avoid using float.J(i) = J(i-1) + (|D(i-1, i)| - J(i - 1)) / 16int32_t jitter_diff_q4 = (time_diff_samples << 4) - jitter_q4_;jitter_q4_ += ((jitter_diff_q4 + 8) >> 4);}...... }jitter更新計算
擴展jitter計算
上面jitter計算存在的問題:每一幀的視頻數(shù)據(jù)放進多個RTP包之后,這些RTP包的頭部timestamp字段都是一樣的(都是幀的capture time),但是實際發(fā)送時間不一樣,到達時間也不同。
3) 如何正確計算抖動:
計算D(i, j)時,Si不能只使用RTP timestamp,而是應該使用該RTP實際發(fā)送到網(wǎng)絡的時間戳。這種抖動被命名為jitter_q4_transmission_time_offset,意為考慮了transmission_time_offset的jitter。
- a. transmission_time_offset是什么?
transmission_time_offset是一段時間間隔,該時間間隔代表屬于同一幀的RTP的實際發(fā)送時間距離幀的capture time的 偏移量 。下圖是對transmission_offset_time的解釋:
其中,箭頭代表一個RTP,發(fā)送端的豎線代表時間軸,虛線代表幀的capture time。
最開始三個RTP包在距離capture time offset1時間之后發(fā)送到網(wǎng)絡,因此這三個RTP包的transmission_time_offset應該是offset1。同理第四個RTP包的transmission_time_offset應該是offset2,第五個RTP包的transmission_time_offset應該是offset3。
- b. transmission_time_offset在RTP包的哪里放著?
transmission_time_offset存在于RTP的擴展頭部,設置該擴展頭可以參考RTPSender::SendToNetwork函數(shù),但使用之前該擴展頭之前需要注冊,否則在設置transmission_time_offset擴展頭會失敗。
下面的代碼段是WebRTC中D(i, j)的計算:
void StreamStatisticianImpl::UpdateJitter(const RTPHeader& header,NtpTime receive_time) { ...... // Extended jitter report, RFC 5450.// Actual network jitter, excluding the source-introduced jitter.int32_t time_diff_samples_ext =(receive_time_rtp - last_receive_time_rtp) -((header.timestamp +header.extension.transmissionTimeOffset) -(last_received_timestamp_ +last_received_transmission_time_offset_));time_diff_samples_ext = std::abs(time_diff_samples_ext);if (time_diff_samples_ext < 450000) {int32_t jitter_diffQ4TransmissionTimeOffset =(time_diff_samples_ext << 4) - jitter_q4_transmission_time_offset_;jitter_q4_transmission_time_offset_ +=((jitter_diffQ4TransmissionTimeOffset + 8) >> 4);}其中:
- receive_time_rtp 代表當前RTP的到達時間戳;
- last_receive_time_rtp 是上一個RTP到達時記錄的時間戳;
- header.timestamp + header.extension.transmissionTimeOffset 前者是capture time,后者是對應的transmission time offset,兩者相加代表該RTP實際發(fā)送到網(wǎng)絡的時間戳;
- last_received_timestamp_ + last_received_transmission_time_offset_ 含義同上,但是代表的是上一個RTP的實際發(fā)送到網(wǎng)絡的時間戳;
References
https://www.dazhuanlan.com/2020/01/20/5e253c28c1d67/?cf_chl_jschl_tk=a03572ddaaf7860d9c14cc0c8b7c2c112960d0c2-1602660124-0-AbYBnlAUDWglFW5OROTlf3OkRMVHkPSyiXg5Z8Hxcxw86GiwKJsWfxkvRcQVfBinIGMAQuge574_IG2twwif2YVF1D_6bLL0r6xjX84-TmAG0ZAt9ynEBpHmLX4ZBjkfUlB7IcTVowzkt0WTsgKReUrejuTpROkhxZ2glmy8ks1jLkIEyj8_EovtJwafgL0CNFjt9Q-6nfAY8YiwPjmad5kXBx1zOmX_5kMlg8u1AGxYXo_cb1NcO7_stPvjiBHbG7Iz4YJWQ1HzlKcrLut760Mq5OC1jdrgy6Lyr-LTV_1bMNHvSsEPxVqaT-cQkS4Qzg
總結(jié)
以上是生活随笔為你收集整理的webrtc丢包率与jitter计算的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 我是如何做到自律的?
- 下一篇: 游戏直播平台新赛程:负重前行与危中求生