ZLMedia中RTCP协议的处理简要分析(2)--Sender Report


class RtcpHeader { public: #if __BYTE_ORDER == __BIG_ENDIAN //版本号,固定为2 uint32_t version: 2; //padding,固定为0 uint32_t padding: 1; //reception report count uint32_t report_count: 5; #else //reception report count uint32_t report_count: 5; //padding,末尾是否有追加填充 uint32_t padding: 1; //版本号,固定为2 uint32_t version: 2; #endif //rtcp类型,RtcpType uint32_t pt: 8; private: //长度 uint32_t length: 16; public: /** * 解析rtcp并转换网络字节序为主机字节序,返回RtcpHeader派生类列表 * @param data 数据指针 * @param size 数据总长度 * @return rtcp对象列表,无需free */ static std::vector<RtcpHeader *> loadFromBytes(char *data, size_t size); /** * rtcp包转Buffer对象 * @param rtcp rtcp包对象智能指针 * @return Buffer对象 */ static toolkit::Buffer::Ptr toBuffer(std::shared_ptr<RtcpHeader> rtcp); /** * 打印rtcp相关字段详情(调用派生类的dumpString函数) * 内部会判断是什么类型的派生类 * 使用net2Host转换成主机字节序后才可使用此函数 */ std::string dumpString() const; /** * 根据length字段获取rtcp总长度 */ size_t getSize() const; /** * 后面追加padding数据长度 */ size_t getPaddingSize() const; /** * 设置rtcp length字段 * @param size rtcp总长度,单位字节 */ void setSize(size_t size); protected: /** * 打印字段详情 * 使用net2Host转换成主机字节序后才可使用此函数 */ std::string dumpHeader() const; private: /** * 调用派生类的net2Host函数 * @param size rtcp字符长度 */ void net2Host(size_t size); } PACKED;

class ReportItem { public: friend class RtcpSR; friend class RtcpRR; uint32_t ssrc; //Fraction lost uint32_t fraction: 8; //Cumulative number of packets lost uint32_t cumulative: 24; //Sequence number cycles count uint16_t seq_cycles; //Highest sequence number received uint16_t seq_max; //Interarrival jitter uint32_t jitter; //Last SR timestamp, NTP timestamp,(ntpmsw & 0xFFFF) << 16 | (ntplsw >> 16) & 0xFFFF) uint32_t last_sr_stamp; //Delay since last SR timestamp,expressed in units of 1/65536 seconds uint32_t delay_since_last_sr; private: /** * 打印字段详情 * 使用net2Host转换成主机字节序后才可使用此函数 */ std::string dumpString() const; /** * 网络字节序转换为主机字节序 */ void net2Host(); }PACKED;

class RtcpSR : public RtcpHeader { public: friend class RtcpHeader; uint32_t ssrc; // ntp timestamp MSW(in second) uint32_t ntpmsw; // ntp timestamp LSW(in picosecond) uint32_t ntplsw; // rtp timestamp uint32_t rtpts; // sender packet count uint32_t packet_count; // sender octet count uint32_t octet_count; //可能有很多个 ReportItem items; public: /** * 创建SR包,只赋值了RtcpHeader部分(网络字节序) * @param item_count ReportItem对象个数 * @return SR包 */ static std::shared_ptr<RtcpSR> create(size_t item_count); /** * 设置ntpmsw与ntplsw字段为网络字节序 * @param tv 时间 */ void setNtpStamp(struct timeval tv); void setNtpStamp(uint64_t unix_stamp_ms); /** * 返回ntp时间的字符串 * 使用net2Host转换成主机字节序后才可使用此函数 */ std::string getNtpStamp() const; uint64_t getNtpUnixStampMS() const; /** * 获取ReportItem对象指针列表 * 使用net2Host转换成主机字节序后才可使用此函数 */ std::vector<ReportItem*> getItemList(); private: /** * 打印字段详情 * 使用net2Host转换成主机字节序后才可使用此函数 */ std::string dumpString() const; /** * 网络字节序转换为主机字节序 * @param size 字节长度,防止内存越界 */ void net2Host(size_t size); } PACKED;
V(2bit)
Version,表示RTCP版本号,当前规范定义的版本号为2,需要注意的是RTP数据包中的版本号与RTCP数据包的中的版本号是一致的。
P(1bit)
填充位,表示是否需要填充,0表示不填充,其不属于控制信息。在某些情况下(如加密)需要进行填充,在填充的情况下,Padding的最后一个字节用于计算应该忽略多少个字节!
RC(5bit)
接收方报告计数,表示在该数据包中的接收方报告块的数量,该字段0值是有效的,但没有实际意义!
PT(8bit)
RTCP的数据包的分组类型,RTCP包含的分组类型如下:
BYE分组为结束分组,表示关闭一个数据流;
APP分组为特定应用分组,使应用程序能够自定义新的分组类型;
SDES分组为源描述分组,其作用是给出参加会话者的描述;
SR分组为发送端报告分组:发送端每发送一个RTP数据包,就要发送个一个发送端报告分组SR;
RR为接收端分组报告,接收端分组报告 ,每收到一个RTP流,就产生一个RR分组。
length(16bit)
RTCP数据包的大小。该字段中大小的表示比较有意思,使用4个字节为1组,长度共有几个4个字节的组,然后用该长度减去1,即为RTCP包中的长度!举个例子:假设RTCP数据包的长度为32个字节,32/4=8,总共有8组4个字节,8-1=7,此时RTCP数据包中length的值为7。
SSRC(32bit)
同步源标识
NTP时标
NTP时间戳
RTP时标
RTP时间戳
发送者包计数
从开始传输到当前SR包生成的时间段内,发送端发送的RTP数据包的总个数!如果发送者更改其SSRC,则该计数要被重置
发送者数据8位组计数
从开始传输到当前SR包生成的时间段内,发送端发送的总的数据的大小的八位组计数,不包含头信息以及填充信息!如果发送者更改可SSRC,需要重置该值!该字段可以用来估计平均码率!
report block 使用的较少,一些英文单词做简单的介绍:
fraction lost:丢包率
cumulative number of packets lost :累计丢失的数据包数
extended highest sequence number received:接收到的扩展最高序列号
interarrival jitter:到达间隔抖动
profile-specific extensions :特定于配置文件的扩展
丢包数8(8bit)
前一个SR或RR包发送后,到当前的SR包或RR包的间隔内,来自源(用源SSRC标识)发送的数据包的丢失个数
累积丢包数(24bit)
自开始接受源(用源SSRC标识)发送的数据开始,累积丢失的数据包的个数
扩展包序号(32bit)
低16位为当前接收到的来自源的(用源SSRC标识)数据包的最大序列号;高16位表示RT包序列号的循环计数!我们都知道RTP数据包中,表示序列号的长度为2个字节,即最大的RTP序列号为65536,如果序列号超了65536,假设为655537,这个时候RTCP在扩展包序号中对其说明,如果没有超过65536,则高16位为0,如果超过65536,则为1,如果再来一圈,则为3。做个不恰当的比喻,我们跑步一圈为65536,高16位表示我们当前正在跑第几圈,从0开始计数!
间隔抖动(32bit)
RTP数据包间隔时间的统计估计,以时间戳为单位,用无符号整数表示!
LSR(32bit)
last SR timestamp,表示上一个SR数据包的NTP时间戳!由于NTP时间戳为64bit,LSR为32bit,LSR取上一个SR的NTP时间戳的中间32位:如上一个SR数据包的NTP时间戳为“0x 00 01 7d 6e 3b 64 5a 1c ”,则LSR为0x 7d 6e 3b 64!
DLSR(32bit)
发送当前RR包的时间与上一个SR之间的时间间隔,以1/65536为单位,如,本次RR包与上一次SR的时间间隔为264ms,则本字段的值为0.264*65536=17301.54。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)