流媒体学习之路(mediasoup)——GCC的Feedback信令回复(8)

流媒体学习之路(mediasoup)——GCC的Feedback信令回复(8)

——
我正在的github给大家开发一个用于做实验的项目 —— github.com/qw225967/Bifrost

目标:可以让大家熟悉各类Qos能力、带宽估计能力,提供每个环节关键参数调节接口并实现一个json全配置,提供全面的可视化算法观察能力。

欢迎大家使用
——


  前面停了很久没有更新mediasoup的介绍文章,最近继续深入地分析几个比较有意思的类。本文把目光集中到GCC的反馈处理类——TransportCongestionControlServer中。它依附于Producer做上行gcc反馈,虽然只是给上行发送端记录feedback的小功能,但是仍然需要解析一下它的思想。

一、简述

  首先,我们可以知道mediasoup中有很多的feedback类型(但是为啥我单独拎出来说这个呢?诶正好我在看,哈哈。后续别的咱再说):
在这里插入图片描述

  准确来说,这个应该是传输的feedback。它的payload是有自己定义的结构的,但头部与正常的RTCP是一致的。

1.1 Feedback包结构

  直接看这个feedback的结构:

/* RTP Extensions for Transport-wide Congestion Control
 * draft-holmer-rmcat-transport-wide-cc-extensions-01

   0               1               2               3
   0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |V=2|P|  FMT=15 |    PT=205     |           length              |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                     SSRC of packet sender                     |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                      SSRC of media source                     |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |      base sequence number     |      packet status count      |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                 reference time                | fb pkt. count |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |          packet chunk         |         packet chunk          |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  .                                                               .
  .                                                               .
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |         packet chunk          |  recv delta   |  recv delta   |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  .                                                               .
  .                                                               .
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |           recv delta          |  recv delta   | zero padding  |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */

  我们可以看到,在整个结构中包含了挺多的数据,那它们到底是做什么用的?我在这里尝试去分析一下它的设计思想:

  1.base sequence number(基础参考seq):携带这个参数的原因可能大家都能理解,假设我们需要去了解对方接收数据的情况,总需要一个可参考的基准。这个就是整个数据结构中统计的第一个序号。
  2.packet status count(反馈状态包数):这个统计了整个反馈中携带状态信息的计数,注意只是当前反馈包所携带的信息的计数,与后面的fb pkt. count 不同。这个的作用在于更方便地去取出状态数据。
  3.reference time(参考时间):整个feedback信息记录的状态都可以根据这个参考时间计算出时间戳。
  4.fb pkt. count(反馈的总包数):该包与上述的状态包计数不同,是一个累计值。它可以用于计算整体的feedback的丢包率。
  5.packet chunk(包状态记录块):这个记录的是每个包的到达状态,可以为:未到达 00、正常到的 01、来慢了 10。为了压缩整个传输的数据包大小,分别设计了两种压缩模式——Run length chunk(行程长度编码数据块)与Status vector chunk(状态矢量编码数据块),packet chunk的第一bit标识chunk类型。
  6.recv delta(到达时间间隔):以250us(0.25ms)为单位,表示RTP包到达时间与前面一个RTP包到达时间的间隔,对于记录的第一个RTP包,该包的时间间隔是相对reference time的。

1.2 数据内容

  上述的几个内容还有两个比较值得展开说的——packet chunk和recv delta。

1.2.1 packet chunk

  别的文章都已经介绍了无数遍了,packet chunk的两种编码模式就是为了更有效地节省数据空间,因此对整个接收状态进行了压缩。packet chunk里的每一块空间都是一个独立的接收状态展示。

  其中第一bit为0的叫做Run length chunk。意味着是对整体长度进行了压缩。怎么理解呢?例如下面的一个情况:

// 有一串字符串 aaabbbcddeeeeee,我们怎么尽量简单地去表示它让它长度变短呢?
// 其实可以写为:3a3bc2d6e —— 也就是按数字表示每个字母的数量,不带数字的就为一个。

// 对应到packet chunk里 结构就是:
0                   1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|T| S |       Run Length        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// T 就为第一个 bit —— 编码类型
// S 两个bit 表示状态 (00未到达、01正常到达、10来慢了)
// Run Length 就是具体的数据

// 例如下面:
0                   1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0|0 0|0 0 0 0 0 1 1 0 1 1 1 0 1|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

// 第一个bit是0,表示run length编码
// 后面两个bit是00, 表示未到达包状态
// 后面的数据 0 0 0 0 0 1 1 0 1 1 1 0 1 -> 11011101 换算成十进制就是221个包,存在221个包连续未到达

  当第一个bit为1时,表示为Status Vector Chunk的编码模式,它是直接表示包是否接到的状态的。和上面表示一个连续的数不一样,它后面跟着的数据就是包的状态码:

// 有一串字符串 aaabbbcddeeeeee,我们怎么尽量简单地去表示它让它长度变短呢?
// 其实可以写为:3a3bc2d6e —— 也就是按数字表示每个字母的数量,不带数字的就为一个。

// 对应到packet chunk里 结构就是:
0                   1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|T|S|        symbol list        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// T 就为第一个 bit —— 编码类型
// S 一个bit 表示状态  (注意:只有一位): 0表示后面的数据都按一位来(简单表示到达的状态,所以一般表示0未到达、1已到达)这样后面14个bit可以表示14个包。
//									  1表示后面使用两位来表示(例如:00、01、10完全表示接到的状态,00未到达、01正常到达、10慢了)这样后面只能表示7个包。
// Run Length 就是具体的数据

// 例如下面:
0                   1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|1|0|0 1 1 1 1 1 0 0 0 1 1 1 0 0|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

// 第一个bit是1,表示symbol list编码
// 后面一个bit是0, 表示使用一位表示法
// 后面的数据 01111100011100 ——>表示:第一个包未到达、接着5个包都到达了、随后3个包未到达、三个包到达了、最后两个包未到达 —— 一共14个包可表示。

0                   1
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|1|1|0 0 1 1 0 1 0 1 0 1 0 0 0 0|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

// 第一个bit是1,表示symbol list编码
// 后面一个bit是1, 表示使用两位表示法
// 后面的数据 0 0 1 1 0 1 0 1 0 1 0 0 0 0 ——>表示:第一个包未到达、第二个包 11 到慢了、01连续三个 正常达到、最后两个未到达 —— 一共7个包可表示。

1.2.2 Receive Delta

  以250us(0.25ms)为单位,表示RTP包到达时间与前面一个RTP包到达时间的间隔,对于记录的第一个RTP包,该包的时间间隔是相对reference time的。

  如果在packet chunk记录了一个"Packet received, small delta"状态的包,那么就会在receive delta列表中添加一个无符号1字节长度receive delta,无符号1字节取值范围[0,255],由于Receive Delta以0.25ms为单位,故此时Receive Delta取值范围[0, 63.75]ms

如果在packet chunk记录了一个"Packet received, large or negative delta"状态的包,那么就会在receive delta列表中添加一个有符号2字节长度的receive delta,范围[-8192.0, 8191.75] ms

  如果时间间隔超过了最大限制,那么就会构建一个新的TransportFeedback RTCP包,由于reference time长度为3字节,所以目前的包中3字节长度能够覆盖很大范围了

  以上说明总结起来就是:对于收到的RTP包在TransportFeedback RTCP receive delta列表中通过时间间隔记录到达时间,如果与前面包时间间隔小,那么使用1字节表示,否则2字节,超过最大取值范围,就另起新RTCP包了。

  对于"Packet received, small delta"状态的包来说,receive delta最大值63.75ms,那么一秒时间跨度最少能标识1000/63.75~=16个包。由于receive delta为250us的倍数,所以一秒时间跨度最多能标识4000个包。

  packet chunk以及receive delta的使用是为了尽可能减小RTCP包大小。packet chunk用到了不同编码方式,对于收到的RTP包才添加到达时间信息,而且是通过时间间隔的方式记录到达时间。

  以上关于格式的内容参考了该大佬的文章:https://blog.jianchihu.net/webrtc-research-transport-cc-rtp-rtcp.html

二、代码分析

  介绍完整体的包格式,我们下面开始对mediasoup的TransportCongestionControlServer类做具体的分析。

2.1 怎么开启transport-cc feedback?

  首先我们发送rtp数据的时候需要开启transport-cc拓展头,只有开启了transport-cc的拓展头我们才能采集它的序号信息。因为在传输中,我们可能存在视频包、重传包、音频包等等,如果要把所有的带宽都算出来,那么这些包的拓展头序号是要连续的,这样就是一条独立的链路去做带宽估计,不受别的混乱序号的影响。
  那么在mediasoup中怎么设置transport-cc的拓展头呢(该拓展头的字段如果是协商来的就不需要太担心,但假设你使用了自己自定义的id,那么需要和mediasoup对其id)?
在这里插入图片描述
  上图的位置就是js层修改它id的地方,但是还不够,下层也要记得改:
在这里插入图片描述

  这样你就可以自己自定义transport-cc的拓展字段id了。

2.2 类解析

  下面展示了头文件,这个类除了具备feedback的能力之外,还有一个老掉牙的rembserver能力。rembserver其实是旧的接收端带宽估计的内容,目前已被移除了,但是待会还是简单介绍一下(之后会写一篇文章特意介绍 gcc——仅限发送端估计)。

// 头文件:#include "RTC/TransportCongestionControlServer.hpp"

namespace RTC
{
	class TransportCongestionControlServer : public webrtc::RemoteBitrateEstimator::Listener,
	                                         public Timer::Listener
	{
	public:
		class Listener
		{
		public:
			virtual ~Listener() = default;

		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;
			}
		}
		double GetPacketLoss() const;
		void IncomingPacket(uint64_t nowMs, const RTC::RtpPacket* packet);
		void SetMaxIncomingBitrate(uint32_t bitrate);

	private:
		void SendTransportCcFeedback();
		void MaySendLimitationRembFeedback();
		void UpdatePacketLoss(double packetLoss);

		/* 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; // feedback 产生函数
		webrtc::RemoteBitrateEstimatorAbsSendTime* rembServer{ nullptr }; // remb服务
		// Others.
		RTC::BweType bweType;
		size_t maxRtcpPacketLen{ 0u }; // 最大rtcp的包长度
		uint8_t transportCcFeedbackPacketCount{ 0u }; // feedback包的计数
		uint32_t transportCcFeedbackSenderSsrc{ 0u }; // 发送的ssrc,在这里没啥用直接给的 0
		uint32_t transportCcFeedbackMediaSsrc{ 0u }; // 媒体的ssrc,比较重要,在发送端接到feedback的时候会根据它来计算
		uint32_t maxIncomingBitrate{ 0u };	// 最大进入码率
		uint64_t limitationRembSentAtMs{ 0u }; // 最大限制码率
		uint8_t unlimitedRembCounter{ 0u }; // 未限制计数
		std::deque<double> packetLossHistory; // 丢包历史队列
		double packetLoss{ 0 }; // 丢包率
	};
} // namespace RTC

  上述头文件中对于feedback比较重要的函数为:IncomingPacket、SendTransportCcFeedback 和 OnRembServerAvailableBitrate。这三个函数基本涵盖了整个tcc-feedback主流程。

IncomingPacket

  首先IncomingPacket函数分成两个方式去处理进入的rtp包。当使用发送端估计时 BweType = TRANSPORT_CC,就进入到feedback生成的内容中;当使用接收端估计时 BweType = REMB,就进入到接收端带宽估计部分。

	// transportCcFeedbackPacket 指针是一个继承了 webrtc中 RTCP 包的类,直接使用该类指针记录当前所有接包的信息就可以发送出去,后续的包重置即可进行下一轮的feedback记录。
	void TransportCongestionControlServer::IncomingPacket(uint64_t nowMs, const RTC::RtpPacket* packet)
	{
		MS_TRACE();

		switch (this->bweType)
		{
			case RTC::BweType::TRANSPORT_CC:
			{
				uint16_t wideSeqNumber;
				// 获取不到seq退出
				if (!packet->ReadTransportWideCc01(wideSeqNumber))
					break;

				// Update the RTCP media SSRC of the ongoing Transport-CC Feedback packet.
				// 对senderssrc无需求,设置为0
				this->transportCcFeedbackSenderSsrc = 0u;
				// 需要mediassrc来区分包,取出备用
				this->transportCcFeedbackMediaSsrc  = packet->GetSsrc();
				
				// 把待发送包的ssrc做记录
				this->transportCcFeedbackPacket->SetSenderSsrc(0u);
				this->transportCcFeedbackPacket->SetMediaSsrc(packet->GetSsrc());

				// Provide the feedback packet with the RTP packet info. If it fails,
				// send current feedback and add the packet info to a new one.
				// 放入feedback类中
				auto result =
				  this->transportCcFeedbackPacket->AddPacket(wideSeqNumber, nowMs, this->maxRtcpPacketLen);

				switch (result)
				{
					// 成功则检测是否当前 feedback报文已满载? 满了就发送包
					case RTC::RTCP::FeedbackRtpTransportPacket::AddPacketResult::SUCCESS:
					{
						// If the feedback packet is full, send it now.
						if (this->transportCcFeedbackPacket->IsFull())
						{
							MS_DEBUG_DEV("transport-cc feedback packet is full, sending feedback now");

							SendTransportCcFeedback();
						}

						break;
					}
					// 在放入的过程中满了就重置包等下一轮发送
					case RTC::RTCP::FeedbackRtpTransportPacket::AddPacketResult::MAX_SIZE_EXCEEDED:
					{
						// Send ongoing feedback packet and add the new packet info to the
						// regenerated one.
						SendTransportCcFeedback();

						this->transportCcFeedbackPacket->AddPacket(wideSeqNumber, nowMs, this->maxRtcpPacketLen);

						break;
					}
					// 失败则直接重置,有可能前面的信息已失效
					case RTC::RTCP::FeedbackRtpTransportPacket::AddPacketResult::FATAL:
					{
						// Create a new feedback packet.
						this->transportCcFeedbackPacket.reset(new RTC::RTCP::FeedbackRtpTransportPacket(
						  this->transportCcFeedbackSenderSsrc, this->transportCcFeedbackMediaSsrc));

						// Use current packet count.
						// NOTE: Do not increment it since the previous ongoing feedback
						// packet was not sent.
						this->transportCcFeedbackPacket->SetFeedbackPacketCount(
						  this->transportCcFeedbackPacketCount);

						break;
					}
				}
				// 检测是否需要发送remb报文
				MaySendLimitationRembFeedback();

				break;
			}

			case RTC::BweType::REMB:
			{
				uint32_t absSendTime;
				// 存在发送的时间则开始接收端计算否则返回
				if (!packet->ReadAbsSendTime(absSendTime))
					break;

				// NOTE: nowMs is uint64_t but we need to "convert" it to int64_t before
				// we give it to libwebrtc lib (althought this is implicit in the
				// conversion so it would be converted within the method call).
				auto nowMsInt64 = static_cast<int64_t>(nowMs);
				// 放入接收端估计逻辑中
				this->rembServer->IncomingPacket(nowMsInt64, packet->GetPayloadLength(), *packet, absSendTime);

				break;
			}
		}
	}




/* ----------------- 补充个额外的类:FeedbackRtpTransportPacket ----------------- */ 
// 这个类继承了 FeedbackRtpPacket,它的add函数中有个细节。
// 它使用的时间戳 this->latestTimestamp 记录时会对精度做一个调整,避免第一个包造成的误差影响后续的统计。

class FeedbackRtpTransportPacket : public FeedbackRtpPacket...
		
FeedbackRtpTransportPacket::AddPacketResult FeedbackRtpTransportPacket::AddPacket(
	uint16_t sequenceNumber, uint64_t timestamp, size_t maxRtcpPacketLen)
{
	...
	// Let's see if we must set our base.
	if (this->latestTimestamp == 0u)
	{
		this->baseSequenceNumber   = sequenceNumber + 1;
		this->referenceTime        = static_cast<int32_t>((timestamp & 0x1FFFFFC0) / 64);
		this->latestSequenceNumber = sequenceNumber;
		this->latestTimestamp      = (timestamp >> 6) * 64; // IMPORTANT: Loose precision. 除64 再乘 64 相当于把后面的精度去掉
		
		return AddPacketResult::SUCCESS;
	}
	...
}
/* ----------------- 补充个额外的类:FeedbackRtpTransportPacket ----------------- */ 

SendTransportCcFeedback

  该函数是feedback中的发送环节,很简单:

	inline void TransportCongestionControlServer::SendTransportCcFeedback()
	{
		MS_TRACE();
		// mediasoup发送包之前会对该数据进行可序化,这样就相当于让这个类变成了一个可发送的数据格式了
		// 比如当前的feedback包还是一个正常的类,在外部可序化之后给空间做了限制,这样发送时就可序化了。
		// 这里检测是否可序化过了,可序化过的包就不应该再继续操作而是直接反回
		if (!this->transportCcFeedbackPacket->IsSerializable())
			return;
		
		// 取出序列号和时间
		auto latestWideSeqNumber = this->transportCcFeedbackPacket->GetLatestSequenceNumber();
		auto latestTimestamp     = this->transportCcFeedbackPacket->GetLatestTimestamp();

		// Notify the listener.
		// 发送出去
		this->listener->OnTransportCongestionControlServerSendRtcpPacket(
		  this, this->transportCcFeedbackPacket.get());

		// Create a new feedback packet.
		// 重新创建新的feedback包
		this->transportCcFeedbackPacket.reset(new RTC::RTCP::FeedbackRtpTransportPacket(
		  this->transportCcFeedbackSenderSsrc, this->transportCcFeedbackMediaSsrc));

		// Increment packet count.
		// 设置当前已记录的feedback包数量
		this->transportCcFeedbackPacket->SetFeedbackPacketCount(++this->transportCcFeedbackPacketCount);

		// Pass the latest packet info (if any) as pre base for the new feedback packet.
		// 最后把上一个包的时间先放入feedback里这样后续可以根据它做参考继续计算
		if (latestTimestamp > 0u)
		{
			MS_WARN_TAG(rtcp, "frq test reset feedback packet add packet latestWideSeqNumber:%lld, latestTimestamp:%lld",
			            latestWideSeqNumber, latestTimestamp);
			this->transportCcFeedbackPacket->AddPacket(
			  latestWideSeqNumber, latestTimestamp, this->maxRtcpPacketLen);
		}
	}






	/* ----------------- 在这里插入OnTimer函数说一下间隔问题 ----------------- */
	// 初始间隔100ms发送一个
	static constexpr uint64_t TransportCcFeedbackSendInterval{ 100u }; // In ms.
	
	inline void TransportCongestionControlServer::OnTimer(Timer* timer)
	{
		MS_TRACE();

		if (timer == this->transportCcFeedbackSendPeriodicTimer)
			SendTransportCcFeedback();
	}
	/* ----------------- 在这里插入OnTimer函数说一下间隔问题 ----------------- */

OnRembServerAvailableBitrate(remb回调函数)

  OnRembServerAvailableBitrate这个函数是rembserver的一个观察者回调函数,当计算完成后会触发回调:

/* ----------------- 在这里插入讲一下 remb 发送 ----------------- */
// 当 BweType = REMB 时,进入到IncomingPacket 函数的数据就会进入到 rembserver 这个对象中进行接收端带宽估计。

	// 在头文件中我们可以看到该类还继承了 webrtc::RemoteBitrateEstimator::Listener 监听者,意味着当计算出remb的码率后会触发一个回调	
	inline void TransportCongestionControlServer::OnRembServerAvailableBitrate(
	  const webrtc::RemoteBitrateEstimator* /*rembServer*/,
	  const std::vector<uint32_t>& ssrcs,
	  uint32_t availableBitrate)
	{
		MS_TRACE();

		// Limit announced bitrate if requested via API.
		// 取出接收码率与估计码率的最小值
		if (this->maxIncomingBitrate != 0u)
			availableBitrate = std::min(availableBitrate, this->maxIncomingBitrate);

#if MS_LOG_DEV_LEVEL == 3
		std::ostringstream ssrcsStream;

		if (!ssrcs.empty())
		{
			std::copy(ssrcs.begin(), ssrcs.end() - 1, std::ostream_iterator<uint32_t>(ssrcsStream, ","));
			ssrcsStream << ssrcs.back();
		}

		MS_DEBUG_DEV(
		  "sending RTCP REMB packet [bitrate:%" PRIu32 ", ssrcs:%s]",
		  availableBitrate,
		  ssrcsStream.str().c_str());
#endif
		
		// 封包
		RTC::RTCP::FeedbackPsRembPacket packet(0u, 0u);

		packet.SetBitrate(availableBitrate);
		packet.SetSsrcs(ssrcs);
		packet.Serialize(RTC::RTCP::Buffer);

		// Notify the listener.
		// 发送
		this->listener->OnTransportCongestionControlServerSendRtcpPacket(this, &packet);
	}

// 另外在设置最大进入码率的函数中也会触发发送的逻辑
	void TransportCongestionControlServer::SetMaxIncomingBitrate(uint32_t bitrate)
	{
		MS_TRACE();

		auto previousMaxIncomingBitrate = this->maxIncomingBitrate;

		this->maxIncomingBitrate = bitrate;

		if (previousMaxIncomingBitrate != 0u && this->maxIncomingBitrate == 0u)
		{
			// This is to ensure that we send N REMB packets with bitrate 0 (unlimited).
			this->unlimitedRembCounter = UnlimitedRembNumPackets;

			MaySendLimitationRembFeedback();
		}
	}
/* ----------------- 在这里插入讲一下 remb 发送 ----------------- */

三、结语

  本文讲了mediasoup的拥塞控制接收端类,讲得稍微细一些。其实主要就两个功能:产生发送端带宽估计的feedback报文、产生接收端估计的remb报文(已废弃)。remb和transport-cc两个在mediasoup中无法同时使用,所以这俩只有一个会在工作时运行,之后我们分析一下webrtc的gcc算法和发送控制类——TransportCongestionControlClient。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值