WebRTC中音視頻服務質量QoS之RTT衡量網絡往返時延的加權平均RTT計算機制?詳解

WebRTC中音視頻服務質量QoS之RTT衡量網絡往返時延加權平均RTT計算機制?的詳解

WebRTC中音視頻服務質量QoS之RTT衡量網絡往返時延加權平均RTT計算機制?的詳解

  • WebRTC中音視頻服務質量QoS之RTT衡量網絡往返時延加權平均RTT計算機制?的詳解
  • 前言
  • 一、 RTT 網絡往返時延的原理?
    • 1、基于發送端(SR/RR 模式)
      • ①. ?基本定義?
      • ②. ?計算 RTT 網絡往返時延的原理?
      • ③ 發送 Sender Report (SR) 協議
        • SenderReport 協議的格式
        • 組織SR協議
        • SR和RR中都有ReportBlock數據塊保存 LSR和DLSR的信息
        • SR和RR中都有ReportBlock協議解析
      • ④ 發送ReceiverReport(RR)協議
        • ReceiverReport協議格式
        • 組織 ReceiverReport(RR)數據
        • 終止計算rtt往返時延 加權平均RTT計算機制?
          • 定時計算 WebRTC中默認1秒
    • 2、基于接收端(RTCP XR 模式)
      • 觸發條件?:接收端僅拉流(不發送媒體數據),通過 ?RTCP Extended Reports (XR)? 擴展協議實現 RTT 探測?
  • 二、網絡質量評估算法之時延加權平均RTT計算機制?
  • 三、 rtp和rtcp發送包列表數據保存時間 (WebRTC根據rtt計算的)


WebRTC專題開嗨鴨 !!!

一、 WebRTC 線程模型

1、WebRTC中線程模型和常見線程模型介紹

2、WebRTC網絡PhysicalSocketServer之WSAEventselect模型使用

二、 WebRTC媒體協商

1、WebRTC媒體協商之SDP中JsepSessionDescription類結構分析

2、WebRTC媒體協商之CreatePeerConnectionFactory、CreatePeerConnection、CreateOffer

3、WebRTC之證書(certificate)生成的時機分析

4、WebRTC源碼之RtpTransceiver添加視頻軌道的AddTrack函數中橋接模式的流程分析

三、 WebRTC 音頻數據采集

1、WebRTC源碼之音頻設備播放流程源碼分析

2、WebRTC源碼之音頻設備的錄制流程源碼分析

四、 WebRTC 音頻引擎(編解碼和3A算法)

五、 WebRTC 視頻數據采集

六、 WebRTC 視頻引擎( 編解碼)

七、 WebRTC 網絡傳輸

1、WebRTC的ICE之STUN協議

2、WebRTC的ICE之Dtls/SSL/TLSv1.x協議詳解

八、 WebRTC服務質量(Qos)

1、WebRTC中RTCP協議詳解

2、WebRTC中RTP協議詳解

3、WebRTC之NACK、RTX 在什么時機判斷丟包發送NACK請求和RTX丟包重傳

4、WebRTC源碼之視頻質量統計數據的數據結構分析

5、WebRTC源碼之RTCPReceiver源碼分析

6、WebRTC中音視頻服務質量QoS之RTT衡量網絡往返時延加權平均RTT計算機制?的詳解

九、 NetEQ

十、 Simulcast與SVC

前言

一、 RTT 網絡往返時延的原理?

WebRTC 提供 ?兩種 RTT 計算模式?,適應不同傳輸場景

1、基于發送端(SR/RR 模式)

*** 觸發條件?: 發送端周期性發送 ?Sender Report (SR)?,接收端回應 ?Receiver Report (RR)?? ***

①. ?基本定義?

	?DLSR? 表示自接收端最后一次收到發送端 Sender Report (SR) 到生成當前 Receiver Report (RR) 的時間間隔,單位為 ?1/65536 秒??1。若接收端未收到過 SR 報文,則 DLSR 值為零?1。

②. ?計算 RTT 網絡往返時延的原理?

	在端到端通信中(以端點 A 和 B 為例):?A 發送 SR?:記錄發送時間 t1(即 LSR,Last SR Timestamp)?2。?B 接收 SR?:記錄接收時間 last_recv_time?2。?B 發送 RR?:計算從 last_recv_time 到當前時間的延遲(即 DLSR),并附加到 RR 報文?2。?A 接收 RR?:根據公式 RTT = 當前時間 - LSR - DLSR 計算往返時間。

公式: R T T = T c u r r e n t ? T L S R ? T D L S R 65536 {RTT=T_{current} ? T_ {LSR} ? \frac{T_{DLSR}}{65536}} RTT=Tcurrent??TLSR??65536TDLSR?? (單位:秒)

參數說明?:

?
T L S R T_ {LSR} TLSR? :發送端最后一次 SR 的 NTP 時間戳(中間 32 位)?3。
? T D L S R ? T_{DLSR?} TDLSR??:接收端處理 SR 到生成 RR 的延遲(單位:1/65536 秒)?

③ 發送 Sender Report (SR) 協議

SenderReport 協議的格式

//    Sender report (SR) (RFC 3550).
//     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
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//    |V=2|P|    RC   |   PT=SR=200   |             length            |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//  0 |                         SSRC of sender                        |
//    +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
//  4 |              NTP timestamp, most significant word             |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//  8 |             NTP timestamp, least significant word             |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 12 |                         RTP timestamp                         |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 16 |                     sender's packet count                     |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 20 |                      sender's octet count                     |
// 24 +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
組織SR協議

std::unique_ptr<rtcp::RtcpPacket> RTCPSender::BuildSR(const RtcpContext& ctx) {// Timestamp shouldn't be estimated before first media frame.RTC_DCHECK_GE(last_frame_capture_time_ms_, 0);// The timestamp of this RTCP packet should be estimated as the timestamp of// the frame being captured at this moment. We are calculating that// timestamp as the last frame's timestamp + the time since the last frame// was captured.int rtp_rate = rtp_clock_rates_khz_[last_payload_type_];if (rtp_rate <= 0) {rtp_rate =(audio_ ? kBogusRtpRateForAudioRtcp : kVideoPayloadTypeFrequency) /1000;}// Round now_us_ to the closest millisecond, because Ntp time is rounded// when converted to milliseconds,uint32_t rtp_timestamp =timestamp_offset_ + last_rtp_timestamp_ +((ctx.now_us_ + 500) / 1000 - last_frame_capture_time_ms_) * rtp_rate;rtcp::SenderReport* report = new rtcp::SenderReport();report->SetSenderSsrc(ssrc_);report->SetNtp(TimeMicrosToNtp(ctx.now_us_));report->SetRtpTimestamp(rtp_timestamp);report->SetPacketCount(ctx.feedback_state_.packets_sent);report->SetOctetCount(ctx.feedback_state_.media_bytes_sent);// TODO@chensong  2025-03-15  獲取當前發送 report->SetReportBlocks(CreateReportBlocks(ctx.feedback_state_));return std::unique_ptr<rtcp::RtcpPacket>(report);
}
SR和RR中都有ReportBlock數據塊保存 LSR和DLSR的信息

// From RFC 3550, RTP: A Transport Protocol for Real-Time Applications.
//
// RTCP report block (RFC 3550).
//
//     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
//    +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
//  0 |                 SSRC_1 (SSRC of first source)                 |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//  4 | fraction lost |       cumulative number of packets lost       |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//  8 |           extended highest sequence number received           |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 12 |                      interarrival jitter                      |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 16 |                         last SR (LSR)                         |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 20 |                   delay since last SR (DLSR)                  |
// 24 +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
SR和RR中都有ReportBlock協議解析

last_sr_ :發送端發送時間

delay_since_last_sr_ : 是遠端最后接受SR或者RR包的時間

bool ReportBlock::Parse(const uint8_t* buffer, size_t length) 
{RTC_DCHECK(buffer != nullptr);if (length < ReportBlock::kLength){RTC_LOG(LS_ERROR) << "Report Block should be 24 bytes long";return false;}// 接收到的媒體源ssrcsource_ssrc_ = ByteReader<uint32_t>::ReadBigEndian(&buffer[0]);// TODO@chensong 2022-10-19  丟包率 fraction_lost/**TODO@chensong 2023-03-07  某時刻收到的有序包的數量Count = transmitted-retransmitte,當前時刻為Count2,上一時刻為Count1;接收端以一定的頻率發送RTCP包(RR、REMB、NACK等)時,會統計兩次發送間隔之間(fraction)的接收包信息。接收端發送的RR包中包含兩個丟包:一個是fraction_lost,是兩次統計間隔間的丟包率(以256為基數換算成8bit)。一個是cumulative number of packets lost,是總的累積丟包。 **/fraction_lost_ = buffer[4];// 接收開始丟包總數, 遲到包不算丟包,重傳有可以導致負數cumulative_lost_ = ByteReader<int32_t, 3>::ReadBigEndian(&buffer[5]);// 低16位表示收到的最大seq,高16位表示seq循環次數extended_high_seq_num_ = ByteReader<uint32_t>::ReadBigEndian(&buffer[8]);// rtp包到達時間間隔的統計方差jitter_ = ByteReader<uint32_t>::ReadBigEndian(&buffer[12]);// ntp時間戳的中間32位last_sr_ = ByteReader<uint32_t>::ReadBigEndian(&buffer[16]);// 記錄上一個接收SR的時間與上一個發送SR的時間差delay_since_last_sr_ = ByteReader<uint32_t>::ReadBigEndian(&buffer[20]);return true;
}

④ 發送ReceiverReport(RR)協議

ReceiverReport協議格式

// RTCP receiver report (RFC 3550).
//
//   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
//  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//  |V=2|P|    RC   |   PT=RR=201   |             length            |
//  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//  |                     SSRC of packet sender                     |
//  +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
//  |                         report block(s)                       |
//  |                            ....                               |
組織 ReceiverReport(RR)數據

在RTCPSender類中BuildRR方法中調用 GetFeedbackState方法獲取 ReportBlock數據

調用流程

RTCPSender類BuildRR —> ModuleRtpRtcpImpl::GetFeedbackState獲取 remote_sender_rtp_time_(遠端發送時間)和 last_received_sr_ntp_ (最后一次接受時間)
—>LastReceivedNTP 方法調用NTP方法
–>RTCPReceiver類NTP 獲取 remote_sender_rtp_time_(遠端發送時間)和 last_received_sr_ntp_ (最后一次接受時間)


std::unique_ptr<rtcp::RtcpPacket> RTCPSender::BuildRR(const RtcpContext& ctx) {rtcp::ReceiverReport* report = new rtcp::ReceiverReport();report->SetSenderSsrc(ssrc_);// TODO@chensong 2025-03-15  rtp_rtcp_impl.cc ->  ModuleRtpRtcpImpl::GetFeedbackStatereport->SetReportBlocks(CreateReportBlocks(ctx.feedback_state_));return std::unique_ptr<rtcp::RtcpPacket>(report);
}// TODO(pbos): Handle media and RTX streams separately (separate RTCP
// feedbacks).
RTCPSender::FeedbackState ModuleRtpRtcpImpl::GetFeedbackState() {RTCPSender::FeedbackState state;// This is called also when receiver_only is true. Hence below// checks that rtp_sender_ exists.if (rtp_sender_) {StreamDataCounters rtp_stats;StreamDataCounters rtx_stats;rtp_sender_->GetDataCounters(&rtp_stats, &rtx_stats);state.packets_sent =rtp_stats.transmitted.packets + rtx_stats.transmitted.packets;state.media_bytes_sent = rtp_stats.transmitted.payload_bytes +rtx_stats.transmitted.payload_bytes;state.send_bitrate = rtp_sender_->BitrateSent();}state.module = this;// TODO@chensong 2025-03-15 獲取遠端發送信息包時間 和當前最后接收一包記錄時間LastReceivedNTP(&state.last_rr_ntp_secs, &state.last_rr_ntp_frac,&state.remote_sr);state.last_xr_rtis = rtcp_receiver_.ConsumeReceivedXrReferenceTimeInfo();return state;
}bool RTCPReceiver::NTP(uint32_t* received_ntp_secs,uint32_t* received_ntp_frac,uint32_t* rtcp_arrival_time_secs,uint32_t* rtcp_arrival_time_frac,uint32_t* rtcp_timestamp) const {rtc::CritScope lock(&rtcp_receiver_lock_);if (!last_received_sr_ntp_.Valid()) {return false;}//   TODO@chensong 2025-03-15  last_rr_ntp_frac 發送時間戳// NTP from incoming SenderReport.if (received_ntp_secs) {*received_ntp_secs = remote_sender_ntp_time_.seconds();}if (received_ntp_frac) {*received_ntp_frac = remote_sender_ntp_time_.fractions();}// Rtp time from incoming SenderReport.// TODO@chensong 2025-03-15 遠端接受最后一個rtp包的時間if (rtcp_timestamp) {*rtcp_timestamp = remote_sender_rtp_time_;}// Local NTP time when we received a RTCP packet with a send block.// TODO@chensong 2025-03-15 本地接受最后一個rtcp包的時間if (rtcp_arrival_time_secs) {*rtcp_arrival_time_secs = last_received_sr_ntp_.seconds();}if (rtcp_arrival_time_frac) {*rtcp_arrival_time_frac = last_received_sr_ntp_.fractions();}return true;
}
// 接收SenderReport包信息
void RTCPReceiver::HandleSenderReport(const CommonHeader& rtcp_block,PacketInformation* packet_information) {rtcp::SenderReport sender_report;if (!sender_report.Parse(rtcp_block)) {++num_skipped_packets_;return;}const uint32_t remote_ssrc = sender_report.sender_ssrc();packet_information->remote_ssrc = remote_ssrc;UpdateTmmbrRemoteIsAlive(remote_ssrc);// Have I received RTP packets from this party?if (remote_ssrc_ == remote_ssrc) {// Only signal that we have received a SR when we accept one.packet_information->packet_type_flags |= kRtcpSr;// TODO@chensong 2025-03-15   SR => RR remote_sender_ntp_time_ = sender_report.ntp();remote_sender_rtp_time_ = sender_report.rtp_timestamp();last_received_sr_ntp_ = TimeMicrosToNtp(clock_->TimeInMicroseconds());} else {// We will only store the send report from one source, but// we will store all the receive blocks.packet_information->packet_type_flags |= kRtcpRr;}for (const rtcp::ReportBlock& report_block : sender_report.report_blocks()) {HandleReportBlock(report_block, packet_information, remote_ssrc);}
}
終止計算rtt往返時延 加權平均RTT計算機制?
定時計算 WebRTC中默認1秒

在ModuleRtpRtcpImpl類中Process方法中統計 加權平均RTT計算機制?


// Process any pending tasks such as timeouts (non time critical events).
void ModuleRtpRtcpImpl::Process() {const int64_t now = clock_->TimeInMilliseconds();next_process_time_ = now + kRtpRtcpMaxIdleTimeProcessMs;if (rtp_sender_) {if (now >= last_bitrate_process_time_ + kRtpRtcpBitrateProcessTimeMs) {rtp_sender_->ProcessBitrate();last_bitrate_process_time_ = now;next_process_time_ =std::min(next_process_time_, now + kRtpRtcpBitrateProcessTimeMs);}}bool process_rtt = now >= last_rtt_process_time_ + kRtpRtcpRttProcessTimeMs;if (rtcp_sender_.Sending()) {// Process RTT if we have received a report block and we haven't// processed RTT for at least |kRtpRtcpRttProcessTimeMs| milliseconds.if (rtcp_receiver_.LastReceivedReportBlockMs() > last_rtt_process_time_ &&process_rtt) {std::vector<RTCPReportBlock> receive_blocks;rtcp_receiver_.StatisticsReceived(&receive_blocks);int64_t max_rtt = 0;for (std::vector<RTCPReportBlock>::iterator it = receive_blocks.begin();it != receive_blocks.end(); ++it) {int64_t rtt = 0;rtcp_receiver_.RTT(it->sender_ssrc, &rtt, NULL, NULL, NULL);max_rtt = (rtt > max_rtt) ? rtt : max_rtt;}// Report the rtt.if (rtt_stats_ && max_rtt != 0)rtt_stats_->OnRttUpdate(max_rtt);}// Verify receiver reports are delivered and the reported sequence number// is increasing.if (rtcp_receiver_.RtcpRrTimeout()) {RTC_LOG_F(LS_WARNING) << "Timeout: No RTCP RR received.";} else if (rtcp_receiver_.RtcpRrSequenceNumberTimeout()) {RTC_LOG_F(LS_WARNING) << "Timeout: No increase in RTCP RR extended ""highest sequence number.";}if (remote_bitrate_ && rtcp_sender_.TMMBR()) {unsigned int target_bitrate = 0;std::vector<unsigned int> ssrcs;if (remote_bitrate_->LatestEstimate(&ssrcs, &target_bitrate)) {if (!ssrcs.empty()) {target_bitrate = target_bitrate / ssrcs.size();}rtcp_sender_.SetTargetBitrate(target_bitrate);}}} else {// Report rtt from receiver.if (process_rtt) {int64_t rtt_ms;if (rtt_stats_ && rtcp_receiver_.GetAndResetXrRrRtt(&rtt_ms)) {rtt_stats_->OnRttUpdate(rtt_ms);}}}// Get processed rtt.if (process_rtt) {last_rtt_process_time_ = now;next_process_time_ = std::min(next_process_time_, last_rtt_process_time_ + kRtpRtcpRttProcessTimeMs);if (rtt_stats_) {// TODO@chensong 2025-03-15  1秒更新一次 rtt    公式/*TODO@chensong 2025-03-15 加權平均RTT計算機制?在實時通信場景(如WebRTC)中,RTT(往返時延)的平滑計算對網絡狀態感知和擁塞控制至關重要。通過 ?加權移動平均(Weighted Moving Average)? 對RTT值進行動態調整,可有效平衡歷史數據與實時測量值的影響,抑制短期波動帶來的干擾。以下是核心實現邏輯:?1. 公式定義??計算方式?:新平均RTT由 ?歷史平均值(old_avg)? 與 ?最新測量值(new_sample)? 按權重合成,公式為:textCopy Codeavg_rtt = 0.7 * old_avg + 0.3 * new_sample  其中,歷史數據權重為70%(0.7),新樣本權重為30%(0.3)?23。?數學意義?:?舊值主導(70%)?:確保長期趨勢穩定,避免偶發延遲突變(如網絡抖動)對整體估計的過度影響?23。?新值補充(30%)?:快速響應網絡狀態的漸進變化(如帶寬增減或路由切換)?*/// Make sure we have a valid RTT before setting.int64_t last_rtt = rtt_stats_->LastProcessedRtt();if (last_rtt >= 0)set_rtt_ms(last_rtt);}}if (rtcp_sender_.TimeToSendRTCPReport())rtcp_sender_.SendRTCP(GetFeedbackState(), kRtcpReport);if (TMMBR() && rtcp_receiver_.UpdateTmmbrTimers()) {rtcp_receiver_.NotifyTmmbrUpdated();}
}

2、基于接收端(RTCP XR 模式)

觸發條件?:接收端僅拉流(不發送媒體數據),通過 ?RTCP Extended Reports (XR)? 擴展協議實現 RTT 探測?

實現步驟?:

  1. 網關發送 ?RRTR 報文?(含 NTP 時間戳 T R R T R T_{RRTR} TRRTR?)

  2. 接收端回復 ?DLRR 報文?,包含

    • T R R T R T_{RRTR} TRRTR? (即為LRR)
    • 處理延遲 T D L S R T_{DLSR} TDLSR?(接收 RRTR 到發送 DLRR 的時間)
  3. 網關計算公式

    R T T = T c u r r e n t RTT = {T_{current}} RTT=Tcurrent? - T T R R {T_{TRR}} TTRR? - T D L S R {T_{DLSR}} TDLSR?

二、網絡質量評估算法之時延加權平均RTT計算機制?

加權平均RTT計算機制?
在實時通信場景(如WebRTC)中,RTT(往返時延)的平滑計算對網絡狀態感知和擁塞控制至關重要。通過 ?加權移動平均(Weighted Moving Average)?
對RTT值進行動態調整,可有效平衡歷史數據與實時測量值的影響,抑制短期波動帶來的干擾。以下是核心實現邏輯:

?1. 公式定義??計算方式?:新平均RTT由 ?歷史平均值(old_avg)? 與 ?最新測量值(new_sample)? 按權重合成,公式為:avg_rtt = 0.7 * old_avg + 0.3 * new_sample  其中,歷史數據權重為70%(0.7),新樣本權重為30%(0.3)?23。?數學意義?:?舊值主導(70%)?:確保長期趨勢穩定,避免偶發延遲突變(如網絡抖動)對整體估計的過度影響?23。?新值補充(30%)?:快速響應網絡狀態的漸進變化(如帶寬增減或路由切換)?

三、 rtp和rtcp發送包列表數據保存時間 (WebRTC根據rtt計算的)

void RtpPacketHistory::CullOldPackets(int64_t now_ms) 
{//TODO@chensong 2025-03-15 比如NACK(否定確認)或ARQ(自動重傳請求)中的緩沖區管理策略有關。//  根據 rtt 放棄 rtp包 // 公式 : 淘汰時間 = 3 × max(基準時間, 3 × 當前RTT)// 基準時間通常為 1000ms(兜底值,防止 RTT 過小導致緩存不足)int64_t packet_duration_ms = std::max(kMinPacketDurationRtt * rtt_ms_, kMinPacketDurationMs);while (!packet_history_.empty()){auto stored_packet_it = packet_history_.find(*start_seqno_);RTC_DCHECK(stored_packet_it != packet_history_.end());if (packet_history_.size() >= kMaxCapacity /* 9600*/) {// We have reached the absolute max capacity, remove one packet// unconditionally.RemovePacket(stored_packet_it);continue;}const StoredPacket& stored_packet = stored_packet_it->second;if (!stored_packet.send_time_ms) {// Don't remove packets that have not been sent.return;}if (*stored_packet.send_time_ms + packet_duration_ms > now_ms) {// Don't cull packets too early to avoid failed retransmission requests.return;}if (packet_history_.size() >= number_to_store_ ||(mode_ == StorageMode::kStoreAndCull && *stored_packet.send_time_ms + (packet_duration_ms * kPacketCullingDelayFactor) <= now_ms)) {// Too many packets in history, or this packet has timed out. Remove it// and continue.RemovePacket(stored_packet_it);}else {// No more packets can be removed right now.return;}}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/897950.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/897950.shtml
英文地址,請注明出處:http://en.pswp.cn/news/897950.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

odbus TCP轉Modbus RTU網關快速配置案例

Modbus TCP 轉Modbus RTU網關快速配置案例 在工業自動化領域&#xff0c;Modbus 協議以其簡潔和高效而著稱&#xff0c;成為眾多設備通信的首選。 隨著技術的發展和應用場景的變化&#xff0c;Modbus 協議也發展出了不同的版本&#xff0c;其中 Modbus TCP 和 Modbus RTU 是兩種…

《高效遷移學習:Keras與EfficientNet花卉分類項目全解析》

從零到精通的遷移學習實戰指南&#xff1a;以Keras和EfficientNet為例 一、為什么我們需要遷移學習&#xff1f; 1.1 人類的學習智慧 想象一下&#xff1a;如果一個已經會彈鋼琴的人學習吉他&#xff0c;會比完全不懂音樂的人快得多。因為TA已經掌握了樂理知識、節奏感和手指…

WSL2 Ubuntu安裝GCC不同版本

WSL2 Ubuntu安裝GCC不同版本 介紹安裝gcc 7.1方法 1&#xff1a;通過源碼編譯安裝 GCC 7.1步驟 1&#xff1a;安裝編譯依賴步驟 2&#xff1a;下載 GCC 7.1 源碼步驟 3&#xff1a;配置和編譯步驟 4&#xff1a;配置環境變量步驟 5&#xff1a;驗證安裝 方法 2&#xff1a;通過…

淘寶API vs 爬蟲:合規獲取實時商品數據的成本與效率對比

以下是淘寶 API 和爬蟲在合規獲取實時商品數據方面的成本與效率對比&#xff1a; 成本對比 淘寶 API 開發成本&#xff1a;需要申請開發者賬號并獲取 API 權限&#xff0c;部分敏感或高頻訪問的接口可能需要額外的審核或付費。開發過程中需要按照平臺規定進行編程&#xff0c;相…

Android 手機啟動過程

梳理 為了梳理思路&#xff0c;筆者畫了一幅關于 Android 手機啟動的過程圖片內容純屬個人見解&#xff0c;如有錯誤&#xff0c;歡迎各位指正

【Linux】:封裝線程

朋友們、伙計們&#xff0c;我們又見面了&#xff0c;本期來給大家帶來封裝線程相關的知識點&#xff0c;如果看完之后對你有一定的啟發&#xff0c;那么請留下你的三連&#xff0c;祝大家心想事成&#xff01; C 語 言 專 欄&#xff1a;C語言&#xff1a;從入門到精通 數據結…

正則表達式全解析 + Java常用示例

目錄 一、正則表達式基礎&#xff08;一&#xff09;元字符&#xff08;二&#xff09;字符集&#xff08;三&#xff09;量詞 二、正則表達式常用示例&#xff08;一&#xff09;驗證郵箱格式&#xff08;二&#xff09;驗證電話號碼格式&#xff08;三&#xff09;提取網頁中…

LoRa數傳、點對點通信、Mesh網絡、ZigBee以及圖傳技術的區別和特點

以下是LoRa數傳、點對點通信、Mesh網絡、ZigBee以及圖傳技術的區別和特點&#xff1a; 1.LoRa數傳? 特點&#xff1a;LoRa是一種基于擴頻技術的低功耗廣域網&#xff08;LPWAN&#xff09;通信技術&#xff0c;具有傳輸距離遠&#xff08;城市環境可達2-5公里&#xff0c;鄉村…

星越L_三角指示牌及危險警示燈使用

目錄 1.打開危險警告燈 2.取出反光背心穿上 3.取出指示牌 4.放置三角指示牌。 1.打開危險警示燈 2.取出反光背心穿上 3.取出指示牌

AI與人的智能,改變一生的思維模型【7】易得性偏差

目錄 **易得性偏差思維模型&#xff1a;大腦的「熱搜算法」與反操縱指南****病毒式定義&#xff1a;你的大腦正在被「熱搜」劫持****四大核心攻擊路徑與史詩級案例****1. 信息過載時代的「認知短路」****2. 媒體放大器的「恐怖濾鏡」****3. 個人經驗的「數據暴政」****4. 社交繭…

Jmeter的簡單使用

前置工作 確保java8 版本以上jmeter下載路徑&#xff08;選擇Binaries&#xff09;&#xff1a;https://jmeter.apache.org/download_jmeter.cgi直接解壓&#xff0c;找到bin下面的文件&#xff1a;jmeter.bat&#xff08;可選&#xff09;漢化&#xff0c;修改 jmeter.proper…

MyBatis源碼分析の配置文件解析

文章目錄 前言一、SqlSessionFactoryBuilder1.1、XMLConfigBuilder1.2、parse 二、mappers標簽的解析2.1、cacheElement2.1.1、緩存策略 2.2、buildStatementFromContext2.2.1、sql的解析 前言 本篇主要介紹MyBatis源碼中的配置文件解析部分。MyBatis是對于傳統JDBC的封裝&…

golang快速上手基礎語法

變量 第一種&#xff0c;指定變量類型&#xff0c;聲明后若不賦值&#xff0c;使用默認值0 package mainimport "fmt"func main() {var a int //第一種&#xff0c;指定變量類型&#xff0c;聲明后若不賦值&#xff0c;使用默認值0。fmt.Printf(" a %d\n"…

Java中的訪問修飾符有哪些

在 Java 中&#xff0c;訪問修飾符&#xff08;Access Modifiers&#xff09;用于控制類、方法、變量和構造器的訪問權限。Java 提供了四種訪問修飾符&#xff0c;分別是&#xff1a; publicprotecteddefault&#xff08;包私有&#xff0c;沒有顯式修飾符&#xff09;private…

【公務員考試】高效備考指南

高效備考指南&#xff1a;從計劃制定到心態調整的全面攻略 公務員考試競爭激烈&#xff0c;備考過程既需要科學規劃&#xff0c;也需要持之以恒的努力。結合多位高分考生的經驗與專業機構的指導&#xff0c;本文整理了一套系統化的備考策略&#xff0c;涵蓋目標設定、學習方法…

工程實踐:如何使用SU17無人機來實現室內巡檢任務

阿木實驗室最近發布了科研開發者版本的無人機SU17&#xff0c;該無人機上集成了四目視覺&#xff0c;三維激光雷達&#xff0c;云臺吊艙&#xff0c;高算力的機載計算機&#xff0c;是一個非常合適的平臺用于室內外巡檢場景。同時阿木實驗室維護了多個和無人機相關的開源項目。…

強大的CSS變量

在 CSS 中&#xff0c;變量&#xff08;Custom Properties&#xff09; 允許你定義可重用的值&#xff0c;方便在整個樣式表中使用和修改。CSS 變量的基本語法如下&#xff1a; 1. 定義 CSS 變量 CSS 變量通常在 :root 偽類中定義&#xff0c;以便它們可用于整個文檔&#xf…

藍橋杯嵌入式賽道復習筆記1(led點亮)

前言 基礎的文件創建&#xff0c;參賽資源代碼的導入&#xff0c;我就不說了&#xff0c;直接說CubeMX的配置以及代碼邏輯思路的書寫&#xff0c;在此我也預祝大家人人拿國獎 理論講解 原理圖簡介 1.由于存在PC8引腳到PC15引腳存在沖突&#xff0c;那么官方硬件給的解決方案…

Linux進程1.0--task_struct

1.硬件&#xff1a;馮諾依曼體系結構&#xff1a; 單個分析&#xff1a;、 數據流向&#xff1a;數據必須先進入輸入設備&#xff0c;再到存儲器&#xff0c;然后由存儲器給控制器&#xff0c;控制器收到以后進行相應的處理后&#xff0c;再傳回存儲器&#xff0c;存儲器最終傳…

本地部署Jina AI Reader:用Docker打造你的智能解析引擎

本地部署Jina AI Reader&#xff1a;用Docker打造你的智能解析引擎 &#x1f31f; 引言&#xff1a;為什么需要本地部署&#xff1f;&#x1f4cc; 場景應用圖譜&#x1f527; 部署指南&#xff08;Linux環境&#xff09;1. 環境準備2. Docker部署3. 驗證服務狀態 &#x1f680…