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

本文深入探讨了WebRTC中的拥塞控制机制,重点关注TransportCongestionControlClient和TransportCongestionControlServer两个类在发送端和接收端的角色。详细介绍了Transport-cc算法,包括基于丢包和延迟的带宽估算,以及GCC算法的过载检测器、速率控制器的工作原理。同时,展示了带宽估计和拥塞控制代码的关键部分。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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


  拥塞控制部分主要有两个类: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 };
	};

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

// 产生并发送feedback
	SendTransportCcFeedback
// 接收包并计算
	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-media
	UpdateControllerWithTimeInterval();
}

......

// 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());
    else
      pacer_.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算法的调用部分。但是大部分的内容没有进行详细分析,后续也不再记录。只进行基本的阅读。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值