webrtc中rtcp反饋與碼率控制模塊分析
0. 參考文檔1 google congestion control1. 簡介webrtc的帶寬估計(jì)分為兩部分,一部分為發(fā)送端根據(jù)rtcp反饋信息進(jìn)行反饋,另一部分為接收端根據(jù)收到的rtp數(shù)據(jù)進(jìn)行相應(yīng)的碼率估計(jì)[1]。 本文先分析發(fā)送端根據(jù)rtcp反饋信息進(jìn)行碼率調(diào)整的部分代碼。具體計(jì)算公式: 2. 代碼結(jié)構(gòu)2.1 類關(guān)系rtp_stream_receiver中有一個(gè)繼承自抽象類RtpRtcp的ModuleRtpRtcpImpl,ModuleRtpRtcpImpl中有一個(gè)rtcp_receiver。當(dāng)有RTCP包到來時(shí),逐層處理至rtcp_receiver,當(dāng)包是rtcp
receiver
report包,則會(huì)將包解析,然后在ModuleRtpRtcpImpl中再次調(diào)用rtcp_receiver中的TriggerCallbacksFromRTCPPacket函數(shù),觸發(fā)對(duì)應(yīng)rtcp的一些事件,反饋觸發(fā)的主要是_cbRtcpBandwidthObserver的觀察者(RtcpBandwidthObserverImpl),這個(gè)觀察者收到對(duì)應(yīng)的report
block之后會(huì)計(jì)算成帶寬估計(jì)所需要的參數(shù),并調(diào)用屬主bitratecontrolImpl類對(duì)帶寬進(jìn)行估計(jì),這里會(huì)調(diào)用SendSideBandwidthEstimation中的UpdateReceiverBlock進(jìn)行實(shí)際的帶寬評(píng)估。2.2 調(diào)用關(guān)系圖3. 代碼分析3.1 HandleReportBlock這個(gè)函數(shù)中最主要的部分就是RTT的計(jì)算,webrtc中對(duì)于RTT平滑的因子是一個(gè)線性增長的因子。/* 這個(gè)函數(shù)根據(jù)對(duì)應(yīng)的report block生成了一個(gè)新的RTCPReportBlockInformation結(jié)構(gòu)體,
* 并計(jì)算出對(duì)應(yīng)的RTT,多report block在調(diào)用點(diǎn)處執(zhí)行循環(huán)。 */
void RTCPReceiver::HandleReportBlock(
const RTCPUtility::RTCPPacket& rtcpPacket,
RTCPPacketInformation& rtcpPacketInformation,
uint32_t remoteSSRC)
EXCLUSIVE_LOCKS_REQUIRED(_criticalSectionRTCPReceiver) {
// This will be called once per report block in the RTCP packet.
// We filter out all report blocks that are not for us.
// Each packet has max 31 RR blocks.
//
// We can calc RTT if we send a send report and get a report block back.
// |rtcpPacket.ReportBlockItem.SSRC| is the SSRC identifier of the source to
// which the information in this reception report block pertains.
// Filter out all report blocks that are not for us.
if (registered_ssrcs_.find(rtcpPacket.ReportBlockItem.SSRC) ==
registered_ssrcs_.end()) {
// This block is not for us ignore it.
return;
}
RTCPReportBlockInformation* reportBlock =
CreateOrGetReportBlockInformation(remoteSSRC,
rtcpPacket.ReportBlockItem.SSRC);
if (reportBlock == NULL) {
LOG(LS_WARNING) << "Failed to CreateReportBlockInformation("
<< remoteSSRC << ")";
return;
}
// 用于RTCP超時(shí)的計(jì)算。
_lastReceivedRrMs = _clock->TimeInMilliseconds();
// 其他字段的拷貝。
const RTCPPacketReportBlockItem& rb = rtcpPacket.ReportBlockItem;
reportBlock->remoteReceiveBlock.remoteSSRC = remoteSSRC;
reportBlock->remoteReceiveBlock.sourceSSRC = rb.SSRC;
reportBlock->remoteReceiveBlock.fractionLost = rb.FractionLost;
reportBlock->remoteReceiveBlock.cumulativeLost =
rb.CumulativeNumOfPacketsLost;
if (rb.ExtendedHighestSequenceNumber >
reportBlock->remoteReceiveBlock.extendedHighSeqNum) {
// We have successfully delivered new RTP packets to the remote side after
// the last RR was sent from the remote side.
_lastIncreasedSequenceNumberMs = _lastReceivedRrMs;
}
reportBlock->remoteReceiveBlock.extendedHighSeqNum =
rb.ExtendedHighestSequenceNumber;
reportBlock->remoteReceiveBlock.jitter = rb.Jitter;
reportBlock->remoteReceiveBlock.delaySinceLastSR = rb.DelayLastSR;
reportBlock->remoteReceiveBlock.lastSR = rb.LastSR;
if (rtcpPacket.ReportBlockItem.Jitter > reportBlock->remoteMaxJitter) {
reportBlock->remoteMaxJitter = rtcpPacket.ReportBlockItem.Jitter;
}
int64_t rtt = 0;
uint32_t send_time = rtcpPacket.ReportBlockItem.LastSR;
// RFC3550, section 6.4.1, LSR field discription states:
// If no SR has been received yet, the field is set to zero.
// Receiver rtp_rtcp module is not expected to calculate rtt using
// Sender Reports even if it accidentally can.
if (!receiver_only_ && send_time != 0) {
// 當(dāng)RR在SR之前發(fā)送,send_time為0.
// delay計(jì)算:
// Send SR Receive RR
// | delay in RR |
// | || |
// || ||
//
// RTT = total_time - delay_in_RR
// = receiver_rr_time - send_sr_time - delay_in_RR
// 即使中間幾個(gè)SR丟包,但是如果RTT本身是平滑的,那么RTT不會(huì)受到這幾個(gè)丟包的影響
// 因?yàn)镾R->RR之間的delay可以精確計(jì)算。
uint32_t delay = rtcpPacket.ReportBlockItem.DelayLastSR;
// Local NTP time.
uint32_t receive_time = CompactNtp(NtpTime(*_clock));
// RTT in 1/(2^16) seconds.
uint32_t rtt_ntp = receive_time - delay - send_time;
// Convert to 1/1000 seconds (milliseconds).
rtt = CompactNtpRttToMs(rtt_ntp);
if (rtt > reportBlock->maxRTT) {
// Store max RTT.
reportBlock->maxRTT = rtt;
}
if (reportBlock->minRTT == 0) {
// First RTT.
reportBlock->minRTT = rtt;
} else if (rtt < reportBlock->minRTT) {
// Store min RTT.
reportBlock->minRTT = rtt;
}
// Store last RTT.
reportBlock->RTT = rtt;
// store average RTT
// RTT的平滑計(jì)算。
// 如果這個(gè)塊是在CreateOrGetReportBlockInformation新生成的,
// 則權(quán)重會(huì)從0開始隨著受到的report逐漸遞增。
// srtt(i) = i/(i+1)*srtt(i-1) + 1/(i+1)*rtt + 0.5
if (reportBlock->numAverageCalcs != 0) {
float ac = static_cast(reportBlock->numAverageCalcs);
float newAverage =
((ac / (ac + 1)) * reportBlock->avgRTT) + ((1 / (ac + 1)) * rtt);
reportBlock->avgRTT = static_cast(newAverage + 0.5f);
} else {
// First RTT.
reportBlock->avgRTT = rtt;
}
reportBlock->numAverageCalcs++;
}
TRACE_COUNTER_ID1(TRACE_DISABLED_BY_DEFAULT("webrtc_rtp"), "RR_RTT", rb.SSRC,
rtt);
// 添加回rtcpPacketInformation,在ModuleRtpRtcpImpl中會(huì)使用這個(gè)進(jìn)行事件回調(diào)。
rtcpPacketInformation.AddReportInfo(*reportBlock);
}3.2 UpdateMinHistory這個(gè)函數(shù)主要用于更新變量min_bitrate_history_,這個(gè)變量將會(huì)作用于上升區(qū)間,用來作為基數(shù),這里簡單描述下。// Updates history of min bitrates.
// After this method returns min_bitrate_history_.front().second contains the
// min bitrate used during last kBweIncreaseIntervalMs.
// 主要結(jié)合這個(gè)函數(shù)解釋下變量min_bitrate_history_
// 這個(gè)變量的兩個(gè)維度,front記錄的是離當(dāng)前最遠(yuǎn)的時(shí)間,
// 每個(gè)速率都是按照時(shí)間先后順序逐漸push到尾部。
// 因此更新的時(shí)候,需要先將超時(shí)的元素從列表頭剔除。
// 后一個(gè)維度是最小速率值,
// 在相同的時(shí)間區(qū)間內(nèi),保留最小的速率值。
// |-------Interval 1---------|----------Interval 2------|
// | | |
// |--t1 < t2 < t3 < t4 < t5--|--t1 < t2 < t3 < t4 < t5--|
// 這樣的操作較為簡單,不用在每次插入元素時(shí)去判斷對(duì)應(yīng)的時(shí)間區(qū)域,再找到對(duì)應(yīng)時(shí)間區(qū)間的最小值,用部分冗余的內(nèi)存換取操作的快捷。
void SendSideBandwidthEstimation::UpdateMinHistory(int64_t now_ms) {
// Remove old data points from history.
// Since history precision is in ms, add one so it is able to increase
// bitrate if it is off by as little as 0.5ms.
while (!min_bitrate_history_.empty() &&
now_ms - min_bitrate_history_.front().first + 1 >
kBweIncreaseIntervalMs) {
min_bitrate_history_.pop_front();
}
// Typical minimum sliding-window algorithm: Pop values higher than current
// bitrate before pushing it.
while (!min_bitrate_history_.empty() &&
bitrate_ <= min_bitrate_history_.back().second) {
min_bitrate_history_.pop_back();
}
min_bitrate_history_.push_back(std::make_pair(now_ms, bitrate_));
}3.3 UpdateEstimate函數(shù)UpdateReceiverBlock會(huì)根據(jù)當(dāng)前的report block對(duì)當(dāng)前帶寬估計(jì)的一些變量進(jìn)行相應(yīng)的賦值,此外,只有當(dāng)傳輸包的數(shù)量達(dá)到一定數(shù)量才會(huì)再次觸發(fā)帶寬估計(jì)的調(diào)整。函數(shù)UpdateEstimate是主要用于帶寬估計(jì)的函數(shù)。void SendSideBandwidthEstimation::UpdateEstimate(int64_t now_ms) {
// We trust the REMB and/or delay-based estimate during the first 2 seconds if
// we haven't had any packet loss reported, to allow startup bitrate probing.
if (last_fraction_loss_ == 0 && IsInStartPhase(now_ms)) {
uint32_t prev_bitrate = bitrate_;
// bwe_incoming_是remb更新的值,如果當(dāng)前無丟包且在啟動(dòng)階段,直接使用remb的值。
if (bwe_incoming_ > bitrate_)
bitrate_ = CapBitrateToThresholds(now_ms, bwe_incoming_);
...
}
}
UpdateMinHistory(now_ms);
// Only start updating bitrate when receiving receiver blocks.
// TODO(pbos): Handle the case when no receiver report is received for a very
// long time.
if (time_last_receiver_block_ms_ != -1) {
if (last_fraction_loss_ <= 5) {
// Loss < 2%: Increase rate by 8% of the min bitrate in the last
// kBweIncreaseIntervalMs.
// Note that by remembering the bitrate over the last second one can
// rampup up one second faster than if only allowed to start ramping
// at 8% per second rate now. E.g.:
// If sending a constant 100kbps it can rampup immediatly to 108kbps
// whenever a receiver report is received with lower packet loss.
// If instead one would do: bitrate_ *= 1.08^(delta time), it would
// take over one second since the lower packet loss to achieve 108kbps.
//TODO:tjl
// 這里與公式有一定不同:
// 1. 系數(shù)不同,且附帶一定的修正值(向上取整加1kbps)
// 2. 取的是上一個(gè)時(shí)間間隔之內(nèi)最小值,比較平滑。
bitrate_ = static_cast(
min_bitrate_history_.front().second * 1.08 + 0.5);
// Add 1 kbps extra, just to make sure that we do not get stuck
// (gives a little extra increase at low rates, negligible at higher
// rates).
bitrate_ += 1000;
event_log_->LogBwePacketLossEvent(
bitrate_, last_fraction_loss_,
expected_packets_since_last_loss_update_);
} else if (last_fraction_loss_ 10%: Limit the rate decreases to once a kBweDecreaseIntervalMs +
// rtt.
if (!has_decreased_since_last_fraction_loss_ &&
(now_ms - time_last_decrease_ms_) >=
(kBweDecreaseIntervalMs + last_round_trip_time_ms_)) {
time_last_decrease_ms_ = now_ms;
// Reduce rate:
// newRate = rate * (1 - 0.5*lossRate);
// where packetLoss = 256*lossRate;
//TODO:tjl
// 當(dāng)從未開始降低窗口值,且距離上一次衰減的時(shí)間差大于衰減周期加上rtt。
// 其實(shí)當(dāng)前貌似只有這個(gè)case下會(huì)對(duì)這兩個(gè)變量賦值。
// 這里的last_fraction_loss_是一次統(tǒng)計(jì)間隔(一定包數(shù))之間的總丟包率。
// 丟包率的單位是1/256,因此這里是(1 - 丟包率/2) * 當(dāng)前速率
// 與公式相同。
bitrate_ = static_cast(
(bitrate_ * static_cast(512 - last_fraction_loss_)) /
512.0);
has_decreased_since_last_fraction_loss_ = true;
}
event_log_->LogBwePacketLossEvent(
bitrate_, last_fraction_loss_,
expected_packets_since_last_loss_update_);
}
}
// 在有效范圍內(nèi)修正。
bitrate_ = CapBitrateToThresholds(now_ms, bitrate_);
}