WebRTC中RTCP协议详解

WebRTC、RTCP、Fackback

Posted by chensong on 2021-11-16 23::50::33

WebRTC中RTCP协议详解

@TOC

</font>

<hr style=” border:solid; width:100px; height:1px;” color=#000000 size=1”>

WebRTC中RTCP协议详解 WebRTC专题开嗨鸭 !!!

一、 WebRTC 线程模型

1、WebRTC中线程模型和常见线程模型介绍

2、WebRTC网络PhysicalSocketServer之WSAEventselect模型使用

二、 WebRTC媒体协商

1、WebRTC媒体协商之SDP中JsepSessionDescription类结构分析

三、 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源码之视频质量统计数据的数据结构分析

九、 NetEQ

十、 Simulcast与SVC

前言

<font color=#999AAA >RTCP包 </font>

在这里插入图片描述

一共1500字节(网络最大包的大小)

  1. Mac Header:14字节
  2. Ip Header : 20字节
  3. UDP Header:8字节
  4. Data: 可变长度
  5. Mac Tailer:4字节

<hr style=” border:solid; width:100px; height:1px;” color=#000000 size=1”>

<font color=#999AAA >提示:以下是本篇文章正文内容,下面案例可供参考

一、RTCP Header 头结构

在这里插入图片描述

1、 RTCP Header 对应的数据结构

/* Struct for RTCP common header. */
			struct CommonHeader
			{
#if defined(MS_LITTLE_ENDIAN)
				uint8_t count : 5;  // 一个包中Report Block个数
				uint8_t padding : 1;// 填充标识, 最后一个填充字节是()个数
				uint8_t version : 2;
#elif defined(MS_BIG_ENDIAN)
				uint8_t version : 2;
				uint8_t padding : 1;
				uint8_t count : 5;
#endif
				uint8_t packetType : 8; // 不同RTCP包的类型
				uint16_t length : 16;   // 16位,包长度(包括头)。[数值为(N-1)个4字节]
			};

2、 RTCP Header 说明

在这里插入图片描述

3、 RTCP Header 在代码中解析的流程的为代码

Packet* Packet::Parse(const uint8_t* data, size_t len)
		{
			MS_TRACE();

			// First, Currently parsing and Last RTCP packets in the compound packet.
			Packet* first{ nullptr };
			Packet* current{ nullptr };
			Packet* last{ nullptr };

			while (len > 0u)
			{
				if (!Packet::IsRtcp(data, len))
				{
					MS_WARN_TAG(rtcp, "data is not a RTCP packet");

					return first;
				}

				auto* header     = const_cast<CommonHeader*>(reinterpret_cast<const CommonHeader*>(data));
				// 这一步 ??? 是不是很有问题啊     ========>  数据的 要加上头的大小的哈
				size_t packetLen = static_cast<size_t>(ntohs(header->length) + 1) * 4;

				if (len < packetLen)
				{
					MS_WARN_TAG(
					  rtcp,
					  "packet length exceeds remaining data [len:%zu, "
					  "packet len:%zu]",
					  len,
					  packetLen);

					return first;
				}

				switch (Type(header->packetType))
				{
					case Type::SR:
					{
						// 1. PT= 200 发送反馈包
						current = SenderReportPacket::Parse(data, packetLen);

						if (!current)
							break;

						if (header->count > 0)
						{
							Packet* rr = ReceiverReportPacket::Parse(data, packetLen, current->GetSize());

							if (!rr)
								break;

							current->SetNext(rr);
						}

						break;
					}

					case Type::RR:
					{
						//2. PT = 201  接受多少包发送给对端
						current = ReceiverReportPacket::Parse(data, packetLen);

						break;
					}

					case Type::SDES:
					{
						// 3. PT = 202  对媒体源的描述
						current = SdesPacket::Parse(data, packetLen);

						break;
					}

					case Type::BYE:
					{
						// 4. PT = 203 不需要传输的数据
						current = ByePacket::Parse(data, packetLen);

						break;
					}

					case Type::APP:
					{
						// 5. PT = 204 应用自定义信息
						current = nullptr;

						break;
					}

					case Type::RTPFB:
					{
						// 6. PT = 205 反馈信息
						current = FeedbackRtpPacket::Parse(data, packetLen);

						break;
					}

					case Type::PSFB:
					{
						// 7. PT= 206  负载情况 反馈信息
						current = FeedbackPsPacket::Parse(data, packetLen);

						break;
					}

					case Type::XR:
					{
						// 8. PT = 207  扩展头
						current = ExtendedReportPacket::Parse(data, packetLen);

						break;
					}

					default:
					{
						MS_WARN_TAG(rtcp, "unknown RTCP packet type [packetType:%" PRIu8 "]", header->packetType);

						current = nullptr;
					}
				}

				if (!current)
				{
					std::string packetType = Type2String(Type(header->packetType));

					if (Type(header->packetType) == Type::PSFB)
					{
						packetType +=
						  " " + FeedbackPsPacket::MessageType2String(FeedbackPs::MessageType(header->count));
					}
					else if (Type(header->packetType) == Type::RTPFB)
					{
						packetType +=
						  " " + FeedbackRtpPacket::MessageType2String(FeedbackRtp::MessageType(header->count));
					}

					MS_WARN_TAG(rtcp, "error parsing %s Packet", packetType.c_str());

					return first;
				}

				data += packetLen;
				len -= packetLen;

				if (!first)
					first = current;
				else
					last->SetNext(current);

				last = current->GetNext() != nullptr ? current->GetNext() : current;
			}

			return first;
		}

二、 RTCP 有哪些 Type类型

在这里插入图片描述

1、 RTCP SR

在这里插入图片描述

①、 Sender Information block

在这里插入图片描述

②、SR 数据结构

/* Struct for RTCP sender report. */
			struct Header
			{
				uint32_t ssrc;          // 源
				uint32_t ntpSec;        // 网络时间戳, 用于不同源之间的同步 
				uint32_t ntpFrac;
				uint32_t rtpTs;        // 相于时间戳,于RTP包时间戳一致
				uint32_t packetCount;  //总发送包数
				uint32_t octetCount;  // 总发送的数据量
			};

③、 接受端的处理

void RtpStreamSend::ReceiveRtcpReceiverReport(RTC::RTCP::ReceiverReport* report)
	{
		MS_TRACE();

		/* Calculate RTT. */

		// Get the NTP representation of the current timestamp.
		uint64_t nowMs = DepLibUV::GetTimeMs();
		auto ntp       = Utils::Time::TimeMs2Ntp(nowMs);

		// Get the compact NTP representation of the current timestamp.
		uint32_t compactNtp = (ntp.seconds & 0x0000FFFF) << 16;

		compactNtp |= (ntp.fractions & 0xFFFF0000) >> 16;

		uint32_t lastSr = report->GetLastSenderReport();
		uint32_t dlsr   = report->GetDelaySinceLastSenderReport();

		// RTT in 1/2^16 second fractions.
		uint32_t rtt{ 0 };

		// If no Sender Report was received by the remote endpoint yet, ignore lastSr
		// and dlsr values in the Receiver Report.
		if (lastSr && dlsr && (compactNtp > dlsr + lastSr))
			rtt = compactNtp - dlsr - lastSr;

		// RTT in milliseconds.
		this->rtt = static_cast<float>(rtt >> 16) * 1000;
		this->rtt += (static_cast<float>(rtt & 0x0000FFFF) / 65536) * 1000;

		if (this->rtt > 0.0f)
			this->hasRtt = true;

		this->packetsLost  = report->GetTotalLost();
		this->fractionLost = report->GetFractionLost();

		// Update the score with the received RR.
		UpdateScore(report);
	}

2、Receiver report block

在这里插入图片描述

3、 RTCP SDES

包含CNAME项的SDES包必须包含在美国组合RTCP包中。SDES包可能包括其他源描述项,这要根据特别的应用需要,并同时考虑带宽限制

在这里插入图片描述

①、 SDES说明

1、SC: SSRC/CSRC数量 2、Item: 采用TLV存放数据 3、 CNAME: SSRC的规范名

②、 SDES item

在这里插入图片描述 在这里插入图片描述

③、 WebRTC中使用SDES

  1. 媒体协商时,为每个源(SSRC)设置了一个CNAME
....
a=ssrc:3121455836 cname:UrMYhDY/S2YpnPSN
....
  1. 通过RTCP SDES 确认CNAME与SSRC的关系

4、RTCP BYE

不需要ssrc流了 就写ssrc放到ssrc这边 发送方就不会发送哈

在这里插入图片描述

5、 RTCP APP

应用的信息

在这里插入图片描述 在这里插入图片描述

6、 RTCP FB Type

在这里插入图片描述

7、 RTCP RTPFB Type

在这里插入图片描述

8、 RTCP PSFB Type

在这里插入图片描述

9、 RTCP FB Type

在这里插入图片描述

在这里插入图片描述

总结:

WebRTC源码分析地址:https://github.com/chensongpoixs/cwebrtc