RTP头的解析及大小端处理的细节

本文介绍了RTP头部的解析方法,并详细探讨了在不同CPU架构下(大端和小端)如何正确处理RTP头部信息。文章通过位域和位移操作两种方式解析RTP头部,同时讲解了如何在小端系统中将多字节数据转换为正确的字节序。

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

RTP头的解析及大小端处理的细节

在看如何解析RTP头之前,先复习下大端和小端的概念,再分析如何解析RTP头

大端和小端

大小端是不同的CPU架构对内存的使用方式不同,将最高有效字节放在内存的低位,为大端序,在最高有效字节放在内存的高位,为小端序。在之前本以为大端和小端只是针对的多字节,其实在一个字节内的比特序也有大小端的概念,这里列几个结论

  1. 进制书写中,最高位在最左端,最低为在最右端
    0x12345678,最高位位0x12。那么在小端系统中解释为0x78563412,大端解释为0x12345678(可以认为系统中的内存顺序为低位在前,高位在后,与书写顺序相反)。
  2. 所有的网络协议都是以大端序(多字节序和比特序)来定义。
  3. 在x86下是小端(多字节序和比特序)。
  4. 以太网中是以小端来发送比特,发送时现转换为小端,接收时网卡会将比特序转换位本机的比特序,所以比特序的转换是在网卡上处理的,应用层无需关心,见下面参考资料1的描述

参考资料

  1. 这篇文章详细的解释了多字节及比特序的大小端
  2. 这篇文章详细介绍了比特序

RTP头的解析

RTP头的定义

这里写图片描述

可以看到协议的定义是以大端顺序定义的

以位域的方式解析RTP Header

按照RTP Header的定义,定义如下位域结构体

struct MBRTPHeader
{
#ifdef RTP_BIG_ENDIAN
	unsigned char version:2;
	unsigned char padding:1;
	unsigned char extension:1;
	unsigned char csrccount:4;

	uint8_t marker:1;
	uint8_t payloadtype:7;
#else // little endian
	unsigned char csrccount:4;
	unsigned char extension:1;
	unsigned char padding:1;
	unsigned char version:2;

	unsigned char payloadtype:7;
	unsigned char marker:1;
#endif // RTP_BIG_ENDIAN

	unsigned short sequencenumber;
	unsigned int timestamp;
	unsigned int ssrc;
};

位域结构体中定义了一个RTP_BIG_ENDIAN宏,用来标识系统是否是大端,如果系统是大端那么位域(比特序)的定义就是按照协议中的字段顺序来定义。如果是小端,应该是按照协议中相反的顺序来定义。x86 PC一般都是小端,下面按照小端解析RTP。

void ProcessRTPHeader(unsigned char* pData)
{
    RTPHeader *pRtpHeader = (RTPHeader*)pData;
    //取version
    int v = pRtpHeader->version;
    //取extension
    int x = pRtpHeader->extension;
    //取payload type
    int pt = pRtpHeader->payloadtype;
    //取marker
    int mark = pRtpHeader->marker;
    
    //取seq
    int seq = ntohs(pRtpHeader->sequencenumber);
    //取ssrc
    int ssrc = ntohl(pRtpHeader->ssrc);
}

在上面的代码示例中,取sequencenumber和ssrc时需要注意,因为其为多字节,所以需要转换为本地字节序(小端多字节序)后,再取值。

通过移位操作解析Rtp Header

定义如下结构体

struct RTPHeader
{
    int version;
    int type;
    int pad, ext, cc, mark;
    int seq, time;
    int ssrc;
};

void ProcessRTPHeader(unsigned char* pData)
{
    RTPHeader rtp;
    /*取version,version在最高位(本机是小端),移到最低位后取值。以下的pad,ext,cc,mark,type字段同理都做了相应的位移操作*/
    rtp.version = (packet[0] >> 6) & 3;
    rtp.pad = (packet[0] >> 5) & 1;
    rtp.ext = (packet[0] >> 4) & 1;
    rtp.cc = packet[0] & 7;
    rtp.mark = (packet[1] >> 7) & 1;
    rtp.type = (packet[1]) & 127;
    
    //rbe16函数作用同ntohs,只是转入的参数不同
    rtp.seq = rbe16(packet + 2);
    //rbe32函数作用同ntohl,只是传入的参数不同
    rtp.time = rbe32(packet + 4);
    rtp.ssrc = rbe32(packet + 8);
}

int rbe16(const unsigned char *p)
{
    int v = p[0] << 8 | p[1];
    return v;
}

int rbe32(const unsigned char *p)
{
    int v = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
    return v;
}

两种方式的更有优势,通过位域的方式代码更简单。通过位运算的方式,兼容性格强,不需判断系统的字节序。

总结

这里介绍了解析RTP Header时对多字节序及比特序的大小端处理所应注意的细节,可以扩展开去,解析其它协议也应注意大小端的处理的不同。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mo4776

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值