流媒体学习之路(mediasoup)——拥塞控制分析(6)

文章目录

  • 流媒体学习之路(mediasoup)——拥塞控制分析(6)
  • 一、TransportCongestionControlClient
    • TransportControllerSend
  • 二、TransportCongestionControlServer
  • 三、Transport-cc拥塞算法
    • 3.1 GCC算法概述
    • 3.2 算法简述
      • 基于延迟:
        • 到达时间滤波器
        • 过载检测器
        • 速率控制器
    • 3.3 GCC算法优劣
  • 四、拥塞控制代码
    • 带宽估计
  • 五、小结

  拥塞控制部分主要有两个类:TransportCongestionControlClient 和 TransportCongestionControlServer。这两个类的作用是分别作为发送端拥塞控制以及接收端拥塞控制。
  WebRTC作为P2P模式对两个点对点的网络传输做了很多抗弱网的工作,发送端和接收端都进行了密切的交互。为了适配这样的交互,在服务端进行两个拥塞控制设计就在所难免了。
  WebRTC的拥塞控制方式主要有以下几个:Transport-cc、BBR-congestion、remb(BBR已被google从webrtc移除了)。mediasoup支持Transport-cc和remb。

一、TransportCongestionControlClient

  在Transport类中继承了TransportCongestionControlClient类,这样每次产生传输事件的时候都会对该链路中的网络进行估算并反馈发送控制。

class TransportCongestionControlClient : public webrtc::PacketRouter,public webrtc::TargetTransferRateObserver,public Timer::Listener{public:struct Bitrates{uint32_t desiredBitrate{ 0u };uint32_t effectiveDesiredBitrate{ 0u };uint32_t minBitrate{ 0u };uint32_t maxBitrate{ 0u };uint32_t startBitrate{ 0u };uint32_t maxPaddingBitrate{ 0u };uint32_t availableBitrate{ 0u };};public:class Listener{public:virtual void OnTransportCongestionControlClientBitrates(RTC::TransportCongestionControlClient* tccClient,RTC::TransportCongestionControlClient::Bitrates& bitrates) = 0;virtual void OnTransportCongestionControlClientSendRtpPacket(RTC::TransportCongestionControlClient* tccClient,RTC::RtpPacket* packet,const webrtc::PacedPacketInfo& pacingInfo) = 0;};public:TransportCongestionControlClient(RTC::TransportCongestionControlClient::Listener* listener,RTC::BweType bweType,uint32_t initialAvailableBitrate);virtual ~TransportCongestionControlClient();public:RTC::BweType GetBweType() const{return this->bweType;}void TransportConnected();void TransportDisconnected();void InsertPacket(webrtc::RtpPacketSendInfo& packetInfo);webrtc::PacedPacketInfo GetPacingInfo();void PacketSent(webrtc::RtpPacketSendInfo& packetInfo, int64_t nowMs);void ReceiveEstimatedBitrate(uint32_t bitrate);void ReceiveRtcpReceiverReport(const webrtc::RTCPReportBlock& report, float rtt, int64_t nowMs);void ReceiveRtcpTransportFeedback(const RTC::RTCP::FeedbackRtpTransportPacket* feedback);void SetDesiredBitrate(uint32_t desiredBitrate, bool force);const Bitrates& GetBitrates() const{return this->bitrates;}uint32_t GetAvailableBitrate() const;void RescheduleNextAvailableBitrateEvent();private:void MayEmitAvailableBitrateEvent(uint32_t previousAvailableBitrate);// jmillan: missing.// void OnRemoteNetworkEstimate(NetworkStateEstimate estimate) override;/* Pure virtual methods inherited from webrtc::TargetTransferRateObserver. */public:void OnTargetTransferRate(webrtc::TargetTransferRate targetTransferRate) override;/* Pure virtual methods inherited from webrtc::PacketRouter. */public:void SendPacket(RTC::RtpPacket* packet, const webrtc::PacedPacketInfo& pacingInfo) override;RTC::RtpPacket* GeneratePadding(size_t size) override;/* Pure virtual methods inherited from RTC::Timer. */public:void OnTimer(Timer* timer) override;private:// Passed by argument.Listener* listener{ nullptr };// Allocated by this.webrtc::NetworkControllerFactoryInterface* controllerFactory{ nullptr };webrtc::RtpTransportControllerSend* rtpTransportControllerSend{ nullptr };RTC::RtpProbationGenerator* probationGenerator{ nullptr };Timer* processTimer{ nullptr };// Others.RTC::BweType bweType;uint32_t initialAvailableBitrate{ 0u };Bitrates bitrates;bool availableBitrateEventCalled{ false };uint64_t lastAvailableBitrateEventAtMs{ 0u };RTC::TrendCalculator desiredBitrateTrend;};

  上述展示了该类的头文件。可以看到,主要包含了:连接相关、发包控制、feedback处理等函数。

  连接相关(TransportConnected、TransportDisconnected):仅仅校验了该链路是否还在传输。

  发包控制
  GetPacingInfo:获取发送信息。
  成员——rtpTransportControllerSend:真正的发送控制者,通过对feedback信息估算出的带宽来调整发送(调用webrtc中的发送部分)。
  ReceiveEstimatedBitrate:处理接收方带宽估计后的码率。(与接收方带宽反馈相关 —— remb)

TransportControllerSend

  在TransportControllerSend的类中,继承了网络变化的观察者类:

class RtpTransportControllerSend final: public RtpTransportControllerSendInterface,public RtcpBandwidthObserver,public TransportFeedbackObserver,public NetworkStateEstimateObserver

  这些类通过拥塞控制算法以及网络参数来判断有多少数据可以发送到网络中。而传输使用的类就是PacketRouter。

// 接到feedback进行带宽估计OnTransportFeedback
// 接到remb的带宽反馈进行带宽控制OnReceivedEstimatedBitrate

  上面就是TransportControllerSend这个类干的最主要的工作。

二、TransportCongestionControlServer

  TransportCongestionControlServer类是与TransportCongestionControlClient相反的类,它主要工作在接收端。(web发送数据过来的接收处理模块)。

class TransportCongestionControlServer : public webrtc::RemoteBitrateEstimator::Listener,public Timer::Listener{public:class Listener{public:virtual void OnTransportCongestionControlServerSendRtcpPacket(RTC::TransportCongestionControlServer* tccServer, RTC::RTCP::Packet* packet) = 0;};public:TransportCongestionControlServer(RTC::TransportCongestionControlServer::Listener* listener,RTC::BweType bweType,size_t maxRtcpPacketLen);virtual ~TransportCongestionControlServer();public:RTC::BweType GetBweType() const{return this->bweType;}void TransportConnected();void TransportDisconnected();uint32_t GetAvailableBitrate() const{switch (this->bweType){case RTC::BweType::REMB:return this->rembServer->GetAvailableBitrate();default:return 0u;}}void IncomingPacket(uint64_t nowMs, const RTC::RtpPacket* packet);void SetMaxIncomingBitrate(uint32_t bitrate);private:void SendTransportCcFeedback();void MaySendLimitationRembFeedback();/* Pure virtual methods inherited from webrtc::RemoteBitrateEstimator::Listener. */public:void OnRembServerAvailableBitrate(const webrtc::RemoteBitrateEstimator* remoteBitrateEstimator,const std::vector<uint32_t>& ssrcs,uint32_t availableBitrate) override;/* Pure virtual methods inherited from Timer::Listener. */public:void OnTimer(Timer* timer) override;private:// Passed by argument.Listener* listener{ nullptr };// Allocated by this.Timer* transportCcFeedbackSendPeriodicTimer{ nullptr };std::unique_ptr<RTC::RTCP::FeedbackRtpTransportPacket> transportCcFeedbackPacket;webrtc::RemoteBitrateEstimatorAbsSendTime* rembServer{ nullptr };// Others.RTC::BweType bweType;size_t maxRtcpPacketLen{ 0u };uint8_t transportCcFeedbackPacketCount{ 0u };uint32_t transportCcFeedbackSenderSsrc{ 0u };uint32_t transportCcFeedbackMediaSsrc{ 0u };uint32_t maxIncomingBitrate{ 0u };uint64_t limitationRembSentAtMs{ 0u };uint8_t unlimitedRembCounter{ 0u };};

  这个模块中可以看到进行接收方的工作。

// 产生并发送feedbackSendTransportCcFeedback
// 接收包并计算IncomingPacket

  feedback通过每次接收数据包的时间计算出差值,记录反馈给上行。主要是用于transport-cc的带宽估计使用。(下面再分析带宽估计算法)

三、Transport-cc拥塞算法

3.1 GCC算法概述

  GCC算法主要分成两个部分,一个是基于丢包的拥塞控制,一个是基于延迟的拥塞控制。
  在早期的实现当中,这两个拥塞控制算法分别是在发送端和接收端实现的,接收端的拥塞控制算法所计算出的估计带宽,会通过RTCP的remb反馈到发送端,发送端综合两个控制算法的结果得到一个最终的发送码率,并以此码率发送数据包。

  下图便是展现的该种实现方式:

  从图中可以看到,Loss-Based Controller在发送端负责基于丢包的拥塞控制,它的输入比较简单,只需要根据从接收端反馈的丢包率,就可以做带宽估算;上图右侧比较复杂,做的是基于延迟的带宽估计,这也是本文后面主要介绍的部分。

  在最近的WebRTC实现中,GCC把它的两种拥塞控制算法都移到了发送端来实现,但是两种算法本身并没有改变,只是在发送端需要计算延迟,因而需要一些额外的feedback信息,为此WebRTC扩展了RTCP协议,其中最主要的是增加了Transport-CC Feedback,该包携带了接收端接收到的每个媒体包的到达时间。

  其算法分为几个部分:到达时间滤波器、过载检测器、速率控制器。

  在获得两个拥塞控制算法分别结算到的发送码率之后,GCC最终的发送码率取的是两种算法的最小值。下面我们详细介绍WebRTC的拥塞控制算法GCC。

3.2 算法简述

  ——其过程就是,到达时间滤波器根据包间的到达时延和发送间隔,计算出延迟变化,这里会用到卡尔曼滤波对延迟变化做平滑以消除网络噪音带来的误差;
  ——延迟变化会作为过载检测器的输入,由过载检测器判断当前网络的状态,有三种网络状态返回overuse/underuse/normal,检测的依据是比较延迟变化和一个阈值,其中该阈值非常关键且是动态调整的。
  ——最后根据网络状态的变化,速率控制器根据一个带宽估计公式计算带宽估计值。
  基于丢包:

  WebRTC通过RTCP协议的Receive Report反馈包来获取接收端的丢包率。Receive Report包中有一个lost fraction字段,包含了接收端的丢包率,如下图所示。

  另外,WebRTC通过以下公式来估算发送码率,式中 As(tk) 即为 tk 时刻的带宽估计值,fl(tk)即为 tk 时刻的丢包率:

  简单来说,当丢包率大于10%时则认为网络有拥塞,此时根据丢包率降低带宽,丢包率越高带宽降的越多;
  当丢包率小于2%时,则认为网络状况很好,此时向上提高5%的带宽以探测是否有更多带宽可用;
  2%到10%之间的丢包率,则会保持当前码率不变,这样可以避免一些网络固有的丢包被错判为网络拥塞而导致降低码率,而这部分的丢包则需要通过其他的如NACK或FEC等手段来恢复。

基于延迟:

  在新近的WebRTC的实现中,所有的带宽估计都放在了发送端,也就说发送端除了做基于丢包的带宽估计,同时也做基于延迟梯度的带宽估计。
  为了能够在接受端做基于延迟梯度的带宽估计,WebRTC扩展了RTP/RTCP协议:
  其一是增加了RTP扩展头部,添加了一个session级别的sequence number, 目的是基于一个session做反馈信息的统计,而不紧紧是一条音频流或视频流;
  其二是增加了一个RTCP反馈信息transport-cc-feedback,该消息负责反馈接受端收到的所有媒体包的到达时间。接收端根据包间的接受延迟和发送间隔可以计算出延迟梯度,从而估计带宽。
  如何根据延迟梯度推断当前网络状况, 总体来说分为以下几个步骤:

到达时间滤波器

    延迟梯度的计算:

  如上图所示,用两个数据包的到达时间间隔减去他们的发送时间间隔,就可以得到一个延迟的变化,这里我们称这个延迟的变化为单向延迟梯度(one way delay gradient),其公式可记为:

  在WebRTC的具体实现中,还有一些细节来保证延迟梯度计算的准确性,总结如下:

  由于延迟梯度的测量精度很小,为了避免网络噪音带来的误差,利用了卡尔曼滤波来平滑延迟梯度的测量结果。
WebRTC的实现中,并不是单纯的测量单个数据包彼此之间的延迟梯度,而是将数据包按发送时间间隔和到达时间间隔分组,计算组间的整体延迟梯度。分组规则是:
  发送时间间隔小于5ms的数据包被归为一组,这是由于WebRTC的发送端实现了一个平滑发送模块,该模块的发送间隔是5ms发送一批数据包。
  到达时间间隔小于5ms的数据包被归为一组,这是由于在wifi网络下,某些wifi设备的转发模式是,在某个固定时间片内才有机会转发数据包,这个时间片的间隔可能长达100ms,造成的结果是100ms的数据包堆积,并在发送时形成burst,这个busrt内的所有数据包就会被视为一组。
  为了计算延迟梯度,除了接收端要反馈每个媒体包的接受状态,同时发送端也要记录每个媒体包的发送状态,记录其发送的时间值。在这个情况下abs-send-time扩展不再需要。

过载检测器

到达时间滤波器计算出每组数据包的延迟梯度之后,就要据此判断当前的网络拥塞状态,通过和某个阈值的比较,高过某个阈值就认为时网络拥塞,低于某个阈值就认为网路状态良好,因此如何确定阈值就至关重要。

  这就是过载检测器的主要工作,它主要有两部分,一部分是确定阈值的大小,另一部分就是依据延迟梯度和阈值的判断,估计出当前的网络状态,一共有三种网络状态: overuse underuse normal,我们先看网络状态的判断。
  网络状态判断
    判断依据入下图所示:

  为延迟梯度。

   mt表示一个延迟梯度。
  γt 表示的是一个判断阈值,这个阈值是自适应的, 后面还会介绍他是怎么动态调整的,这里先只看如何根据这两个值判断当前网络状态。
  从上图可以看出,这里的判断方法是:

  在实际WebRTC的实现中,虽然每个数据包组(前面提到了如何分组)的到达都会触发这个探测过程,但是使用的m(ti)这个值并不是直接使用每组数据到来时的计算值,而是将这个值放大了60倍。这么做的目的可能是m(ti)这个值通常情况下很小,理想网络下基本为0,放大该值可以使该算法不会应为太灵敏而波动太大。
在判断是否overuse时,不会一旦超过阈值就改变当前状态,而是要满足延迟梯度大于阈值至少持续100ms,才会将当前网络状态判断为overuse。

  阈值自适应
  上面的公式就是GCC提出的阈值自适应算法,其中:

  每组数据包会触发一次探测,同时更新一次阈值,这里 T 的意义就是距上次更新阈值时的时间间隔。
  k(γ)t是一个变化率,或者叫增长率,当然也有可能是负增长。增长的基值是:当前的延迟梯度和上一个阈值的差值为:

  其具体的取值如下:

  其中:ku = 0.01; kd = 0.00018
  从这个式子中可以看出,当延迟梯度减小时,阈值会以一个更慢的速率减小; 延迟梯度增加时,阈值也会以一个更慢的速度增加;不过相对而言,阈值的减小速度要小于增加速度。

速率控制器

速率控制器主要实现了一个状态机的变迁,并根据当前状态来计算当前的可用码率,状态机如下图所示:

  速率控制器根据过载探测器输出的信号(overuse underusenormal)驱动速率控制状态机, 从而估算出当前的网络速率。

  从上图可以看出,当网络拥塞时,会收到overuse信号,状态机进入“decrease”状态,发送速率降低;当网络中排队的数据包被快速释放时,会受到underuse信号,状态机进入“hold”状态。网络平稳时,收到normal信号,状态机进入“increase”状态,开始探测是否可以增加发送速率。

  在Google的paper[3]中,计算带宽的公式如下:

  其中 = 1.05, =0.85。从该式中可以看到,当需要Increase时,以前一次的估算码率乘以1.05作为当前码率;当需要Decrease时,以当前估算的接受端码率(Rr(ti))乘以0.85作为当前码率;Hold状态不改变码率。
  最后,将基于丢包的码率估计值和基于延迟的码率估计值作比较,其中最小的码率估价值将作为最终的发送码率。

3.3 GCC算法优劣

  gcc算法采用丢包+延迟双检验机制,同时进行双端信息捕捉控制提高可靠性高、稳定性。但运算逻辑复杂、开发成本高。
  同时,在网络间歇性丢包情况下,GCC 可能收敛的速度比较慢,在一定程度上有可能会造成 REMB 很难反馈给发送端,容易出现发送端流控失效。

四、拥塞控制代码

  webrtc的发送模块中存在一个高精度的发送定时器:

void TransportCongestionControlClient::OnTimer(Timer* timer){MS_TRACE();if (timer == this->processTimer){// Time to call RtpTransportControllerSend::Process().this->rtpTransportControllerSend->Process();// Time to call PacedSender::Process().this->rtpTransportControllerSend->packet_sender()->Process();/* clang-format off */this->processTimer->Start(std::min<uint64_t>(// Depends on probation being done and WebRTC-Pacer-MinPacketLimitMs field trial.this->rtpTransportControllerSend->packet_sender()->TimeUntilNextProcess(),// Fixed value (25ms), libwebrtc/api/transport/goog_cc_factory.cc.this->controllerFactory->GetProcessInterval().ms()));/* clang-format on */MayEmitAvailableBitrateEvent(this->bitrates.availableBitrate);}}

  该定时器定时唤醒发送,时间大约为5ms。每次唤醒后更新时间,随后调用sender的发送。最后计算下一个数据发送的时间,这样就达到了发送控制的效果。

void RtpTransportControllerSend::Process()
{// TODO (ibc): Must really check if we need this ugly periodic timer which is called// every 5ms.// NOTE: Yes, otherwise the pssss scenario does not work:// https://bitbucket.org/versatica/mediasoup/issues/12/no-probation-if-no-real-mediaUpdateControllerWithTimeInterval();
}......// pacedsender中的发送void PacedSender::Process() {int64_t now_us = DepLibUV::GetTimeUsInt64();int64_t elapsed_time_ms = UpdateTimeAndGetElapsedMs(now_us);if (paused_)return;if (elapsed_time_ms > 0) {int target_bitrate_kbps = pacing_bitrate_kbps_;media_budget_.set_target_rate_kbps(target_bitrate_kbps);UpdateBudgetWithElapsedTime(elapsed_time_ms);}if (!prober_.IsProbing())return;PacedPacketInfo pacing_info;absl::optional<size_t> recommended_probe_size;pacing_info = prober_.CurrentCluster();recommended_probe_size = prober_.RecommendedMinProbeSize();size_t bytes_sent = 0;// MS_NOTE: Let's not use a useless vector.RTC::RtpPacket* padding_packet{ nullptr };// Check if we should send padding.while (true){size_t padding_bytes_to_add =PaddingBytesToAdd(recommended_probe_size, bytes_sent);if (padding_bytes_to_add == 0)break;// TODO: REMOVE// MS_DEBUG_DEV(//   "[recommended_probe_size:%zu, padding_bytes_to_add:%zu]",//   *recommended_probe_size, padding_bytes_to_add);padding_packet =packet_router_->GeneratePadding(padding_bytes_to_add);// TODO: REMOVE.// MS_DEBUG_DEV("sending padding packet [size:%zu]", padding_packet->GetSize());packet_router_->SendPacket(padding_packet, pacing_info);bytes_sent += padding_packet->GetSize();if (recommended_probe_size && bytes_sent > *recommended_probe_size)break;}if (bytes_sent != 0){auto now = DepLibUV::GetTimeUsInt64();OnPaddingSent(now, bytes_sent);prober_.ProbeSent((now + 500) / 1000, bytes_sent);}
}

带宽估计

  在上述的pacedsender类中的发送函数里,有一个set_target_rate_kbps函数,该函数把目标的带宽值传入。而带宽值则是类内成员——pacing_bitrate_kbps_——进行记录的。而它则是在PostUpdates进行更新的。

void RtpTransportControllerSend::PostUpdates(NetworkControlUpdate update) {if (update.congestion_window) {if (update.congestion_window->IsFinite())pacer_.SetCongestionWindow(update.congestion_window->bytes());elsepacer_.SetCongestionWindow(PacedSender::kNoCongestionWindow);}if (update.pacer_config) {pacer_.SetPacingRates(update.pacer_config->data_rate().bps(),update.pacer_config->pad_rate().bps());}// TODO: REMOVE: this removes any probation.// update.probe_cluster_configs.clear();for (const auto& probe : update.probe_cluster_configs) {int64_t bitrate_bps = probe.target_data_rate.bps();pacer_.CreateProbeCluster(bitrate_bps, probe.id);}if (update.target_rate) {control_handler_->SetTargetRate(*update.target_rate);UpdateControlState();}
}

  而该函数在各个状态发生变化时都户触发。

  在这里我们仅对feedback的情况进行分析。

NetworkControlUpdate GoogCcNetworkController::OnTransportPacketsFeedback(TransportPacketsFeedback report) {if (report.packet_feedbacks.empty()) {// TODO(bugs.webrtc.org/10125): Design a better mechanism to safe-guard// against building very large network queues.return NetworkControlUpdate();}if (congestion_window_pushback_controller_) {congestion_window_pushback_controller_->UpdateOutstandingData(report.data_in_flight.bytes());}TimeDelta max_feedback_rtt = TimeDelta::MinusInfinity();TimeDelta min_propagation_rtt = TimeDelta::PlusInfinity();Timestamp max_recv_time = Timestamp::MinusInfinity();std::vector<PacketResult> feedbacks = report.ReceivedWithSendInfo();for (const auto& feedback : feedbacks)max_recv_time = std::max(max_recv_time, feedback.receive_time);for (const auto& feedback : feedbacks) {TimeDelta feedback_rtt =report.feedback_time - feedback.sent_packet.send_time;TimeDelta min_pending_time = feedback.receive_time - max_recv_time;TimeDelta propagation_rtt = feedback_rtt - min_pending_time;max_feedback_rtt = std::max(max_feedback_rtt, feedback_rtt);min_propagation_rtt = std::min(min_propagation_rtt, propagation_rtt);}if (max_feedback_rtt.IsFinite()) {feedback_max_rtts_.push_back(max_feedback_rtt.ms());const size_t kMaxFeedbackRttWindow = 32;if (feedback_max_rtts_.size() > kMaxFeedbackRttWindow)feedback_max_rtts_.pop_front();// TODO(srte): Use time since last unacknowledged packet.bandwidth_estimation_->UpdatePropagationRtt(report.feedback_time,min_propagation_rtt);}if (packet_feedback_only_) {if (!feedback_max_rtts_.empty()) {int64_t sum_rtt_ms = std::accumulate(feedback_max_rtts_.begin(),feedback_max_rtts_.end(), 0);int64_t mean_rtt_ms = sum_rtt_ms / feedback_max_rtts_.size();if (delay_based_bwe_)delay_based_bwe_->OnRttUpdate(TimeDelta::ms(mean_rtt_ms));}TimeDelta feedback_min_rtt = TimeDelta::PlusInfinity();for (const auto& packet_feedback : feedbacks) {TimeDelta pending_time = packet_feedback.receive_time - max_recv_time;TimeDelta rtt = report.feedback_time -packet_feedback.sent_packet.send_time - pending_time;// Value used for predicting NACK round trip time in FEC controller.feedback_min_rtt = std::min(rtt, feedback_min_rtt);}if (feedback_min_rtt.IsFinite()) {bandwidth_estimation_->UpdateRtt(feedback_min_rtt, report.feedback_time);}expected_packets_since_last_loss_update_ +=report.PacketsWithFeedback().size();for (const auto& packet_feedback : report.PacketsWithFeedback()) {if (packet_feedback.receive_time.IsInfinite())lost_packets_since_last_loss_update_ += 1;}if (report.feedback_time > next_loss_update_) {next_loss_update_ = report.feedback_time + kLossUpdateInterval;bandwidth_estimation_->UpdatePacketsLost(lost_packets_since_last_loss_update_,expected_packets_since_last_loss_update_, report.feedback_time);expected_packets_since_last_loss_update_ = 0;lost_packets_since_last_loss_update_ = 0;}}absl::optional<int64_t> alr_start_time =alr_detector_->GetApplicationLimitedRegionStartTime();if (previously_in_alr_ && !alr_start_time.has_value()) {int64_t now_ms = report.feedback_time.ms();acknowledged_bitrate_estimator_->SetAlrEndedTime(report.feedback_time);probe_controller_->SetAlrEndedTimeMs(now_ms);}previously_in_alr_ = alr_start_time.has_value();acknowledged_bitrate_estimator_->IncomingPacketFeedbackVector(report.SortedByReceiveTime());auto acknowledged_bitrate = acknowledged_bitrate_estimator_->bitrate();for (const auto& feedback : report.SortedByReceiveTime()) {if (feedback.sent_packet.pacing_info.probe_cluster_id !=PacedPacketInfo::kNotAProbe) {probe_bitrate_estimator_->HandleProbeAndEstimateBitrate(feedback);}}absl::optional<DataRate> probe_bitrate =probe_bitrate_estimator_->FetchAndResetLastEstimatedBitrate();if (fall_back_to_probe_rate_ && !acknowledged_bitrate)acknowledged_bitrate = probe_bitrate_estimator_->last_estimate();bandwidth_estimation_->SetAcknowledgedRate(acknowledged_bitrate,report.feedback_time);bandwidth_estimation_->IncomingPacketFeedbackVector(report);if (network_estimator_) {network_estimator_->OnTransportPacketsFeedback(report);estimate_ = network_estimator_->GetCurrentEstimate();}NetworkControlUpdate update;bool recovered_from_overuse = false;bool backoff_in_alr = false;DelayBasedBwe::Result result;result = delay_based_bwe_->IncomingPacketFeedbackVector(report, acknowledged_bitrate, probe_bitrate, estimate_,alr_start_time.has_value());if (result.updated) {if (result.probe) {bandwidth_estimation_->SetSendBitrate(result.target_bitrate,report.feedback_time);}// Since SetSendBitrate now resets the delay-based estimate, we have to// call UpdateDelayBasedEstimate after SetSendBitrate.bandwidth_estimation_->UpdateDelayBasedEstimate(report.feedback_time,result.target_bitrate);// Update the estimate in the ProbeController, in case we want to probe.MaybeTriggerOnNetworkChanged(&update, report.feedback_time);}recovered_from_overuse = result.recovered_from_overuse;backoff_in_alr = result.backoff_in_alr;if (recovered_from_overuse) {probe_controller_->SetAlrStartTimeMs(alr_start_time);auto probes = probe_controller_->RequestProbe(report.feedback_time.ms());update.probe_cluster_configs.insert(update.probe_cluster_configs.end(),probes.begin(), probes.end());} else if (backoff_in_alr) {// If we just backed off during ALR, request a new probe.auto probes = probe_controller_->RequestProbe(report.feedback_time.ms());update.probe_cluster_configs.insert(update.probe_cluster_configs.end(),probes.begin(), probes.end());}// No valid RTT could be because send-side BWE isn't used, in which case// we don't try to limit the outstanding packets.if (rate_control_settings_.UseCongestionWindow() &&max_feedback_rtt.IsFinite()) {UpdateCongestionWindowSize(/*time_since_last_packet*/ TimeDelta::Zero());}if (congestion_window_pushback_controller_ && current_data_window_) {congestion_window_pushback_controller_->SetDataWindow(*current_data_window_);} else {update.congestion_window = current_data_window_;}return update;
}

  当我们接到feedback包时,我们会对feedback包中携带的rtt信息进行解析,随后进行 第三 部分提到的流程计算出一个网络状态变更的对象返回。

五、小结

  本章展示了整个mediasoup的拥塞控制流程,还简单介绍了transport-cc算法的调用部分。但是大部分的内容没有进行详细分析,后续也不再记录。只进行基本的阅读。

流媒体学习之路(mediasoup)——拥塞控制分析(6)相关推荐

  1. 流媒体学习之路(WebRTC)——GCC分析(1)

    流媒体学习之路(WebRTC)--GCC整体分析(1) 文章目录 流媒体学习之路(WebRTC)--GCC整体分析(1) 一.简介 二.类分析 2.1 RtpTransportControllerSe ...

  2. 流媒体学习之路(WebRTC)——GCC分析(2)

    背景   事实上,带宽估计的算法有很多,有三种比较经典方案:   GCC 算法[ https://datatracker.ietf.org/doc/html/draft-ietf-rmcat-gcc- ...

  3. 流媒体学习之路(BBR算法应用)——BBR算法简介

    流媒体学习之路(BBR算法应用)--BBR算法简介 文章目录 流媒体学习之路(BBR算法应用)--BBR算法简介 一.弱网优化简介 1.1 补包 1.2 前向纠错 1.3 自适应 二.BBR算法 2. ...

  4. python 爬虫 包_python爬虫学习之路-抓包分析

    利用浏览器抓包,是爬虫中的很实用的技能.在爬虫编程之前,我们要对抓取的目标页面有所了解,比如浏览器的这个请求这个页面中间都经历了什么,数据是怎么发送和返回的. 抓包的作用 我把抓包分析的作用简单列一下 ...

  5. 流媒体服务器——Licode Janus-gateway Mediasoup Medooze 分析

    目录 前言 Licode Janus-gateway Mediasoup Medooze 前言 已知的多方通信框架有:Mesh MCU SFU 三种.<三种方案的详细介绍> 其中SFU是目 ...

  6. 外设芯片学习之路_CD4051原理分析和仿真实验

    CD4051原理分析和仿真实验 1.芯片概述 2.芯片引脚分析 3.Proteus仿真实验 1.芯片概述 CD4051是单端8通道多路开关,它有3个通道选择输入端C.B.A 和一个禁止输入端INH.C ...

  7. 【音视频第7天】mediasoup拥塞控制【未完待续】

    WebRTC的拥塞控制方式主要有以下几个:Transport-cc.BBR-congestion.remb(BBR已被google从webrtc移除了).mediasoup支持Transport-cc ...

  8. [EntLib]微软企业库5.0 学习之路——第七步、Cryptographer加密模块简单分析、自定义加密接口及使用—上篇...

    在完成了后,今天开始介绍企业库中的新模块:Cryptographer(加密模块),这个模块在日常的大多数项目的作用非常重要,例如:网站会员密码.身份证号.网站配置等,通过对信息进行加密可以保证项目数据 ...

  9. FPGA学习之路—应用程序—原码二位乘法器及Verilog代码分析

    FPGA学习之路--原码二位乘法器及Verilog代码分析 原理 原码乘法可以分为原码一位乘和原码二位乘,两者在实现规则上大同小异.原码一位乘每次判断乘数的最低位,对被乘数和部分积进行相应操作.而原码 ...

最新文章

  1. ShardingSphere-Proxy分库分表以及多租户安装使用
  2. BZOJ 2337: [HNOI2011]XOR和路径( 高斯消元 )
  3. 空的宏定义作用及常见用法
  4. 工作流技术JBPM开发入门
  5. udp重发机制_UDP 协议
  6. TO C AND TO B IN TERMS OF CUSTOMER
  7. 给基于SAP Spartacus 3.4.1 版本的 Storefront 添加对服务器端渲染的支持
  8. 【转】DCMTK各模块说明!!!!!!!
  9. 后盾网经典原创视频教程php,《后盾网经典原创视频教程:PHP》139集
  10. c mysql 工具类_Jave工具——servlet+jsp编程中mysql数据库连接及操作通用工具类
  11. pytorch中的反卷积的output_padding参数
  12. leetCode 203. Remove Linked List Elements 链表
  13. python控制台编写_Python:为控制台prin编写unittest
  14. 二叉树遍历算法(递归实现+层次遍历)
  15. 稳压管Ir、Izt、Izk、Izm释义
  16. android文件恢复功能,安卓手机误删文件恢复?快速恢复办法
  17. 短代码的java小游戏_java编写的简单移动方块小游戏代码
  18. FZU 2213 Common Tangents(公切线)
  19. ubuntu 16.04 nivida显卡驱动更新步骤
  20. centos7配置 console口_7.5. Configuring the Linux Console

热门文章

  1. 从oracle数据表导到sql语句,oracle导出表结构到sql文件
  2. CloudFoundry User Account and Authentication (UAA) Server Group
  3. gcc编译多文件项目(包含静态库和动态库)
  4. java8类型推导,鲜为人知的Java8特性:泛化目标类型推断
  5. Windows 远程控制 Mac 的解决方案
  6. SpringCloud笔记(一)微服务基础
  7. 浙江工业大学计算机教授,安恒信息董事长范渊受聘浙江工业大学教授 安恒教育基金助力未来网安人才培养...
  8. VS2019调试时快捷键不起作用,可以这样解决
  9. 安卓使用Glide加载图片使宽度充满手机屏幕,高度随宽度等比缩放
  10. 如何制作苹果系统dmg映像文件