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;
RtcpHeader
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;
ReportItem
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;
RtcpSR

 

 

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。

posted @ 2022-07-27 15:07  泽良_小涛  阅读(279)  评论(0编辑  收藏  举报