Linux ·

通过C/C++结构体解析RTP/RTCP的包头的方法

RTP包的头构成为:

 0                  1                  2                  3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |V=2|P|X|  CC  |M|    PT      |      sequence number        |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |                          timestamp                          |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |          synchronization source (SSRC) identifier            |
  +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
  |            contributing source (CSRC) identifiers            |
  |                            ....                              |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

通过wireshark解析的形式:

通过C/C++结构体解析RTP/RTCP的包头的方法 Linux 第1张

在C/C++中,对包的头信息解析的方法为,定义一个结构体:

struct RTP_Header
{
 unsigned __int16 csrc_count:4;
 unsigned __int16 extension:1;
 unsigned __int16 padding:1;
 unsigned __int16 version:2;
 unsigned __int16 payloadtype:7; 
 unsigned __int16 marker:1; 

 unsigned __int16 seq;
 unsigned __int32 timestamp;
 unsigned __int32 ssrc;
};

结构体中,冒号后面数字为实际占的位数,注意8位字节的内部,网络序与本地序是相反的,csrc_count在第1字节的后4位,而在结构体声明中,csrc_count需要在前4位,应用的方法,以序列号的获取为例:

int RTPProcessor::getSequenceNumber(PacketBuffer* packet)
{
 RTP_Header* pHeader =  (RTP_Header*)packet->data;
 return ntohs(pHeader->seq);
}

其中packet->data为recv的udp的原始包,通过RTP_Header的强制类型转换,即可直接通过pHeader->seq获取,这里需要网络序转换成字节序,调用ntohs的函数,同理,对序列号的修改如下:

void RTPProcessor::setSequenceNumber(PacketBuffer* packet)
{
 RTP_Header* pHeader =  (RTP_Header*)packet->data;
 pHeader->seq=  htons(m_sequenceNumer);
}

对于RTCP,方法与RTP相似,RTCP的包的头信息为:

    0                  1                  2                  3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |V=2|P|  CC    |      PT      |          length              |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |          synchronization source (SSRC) identifier            |
  +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
  |                    source or chunk                            |
  |                            ....                              |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

RTCP的包的头信息与RTP有点不同,wireshark的解析为:

通过C/C++结构体解析RTP/RTCP的包头的方法 Linux 第2张

在C/C++中,对包的头信息解析的方法为,定义一个结构体:

struct RTCP_Header
{
 unsigned __int16 csrc_count:5;
 unsigned __int16 padding:1;
 unsigned __int16 version:2; //1 char
 unsigned __int16 payloadtype:8; //2 char
 unsigned __int16 length; //3,4 char

 unsigned __int32 ssrc; //5,6,7,8 char
};

应用的方法,也是获取原始数据包的首地址,再通过RTCP_Header进行强制类型转换。

注意,RTCP的包比较小,通常会将多个RTCP包,通过1个UDP包进行发送,那么就需要对多个RTCP包进行拆解,拆解的方法是通过length,获取不同包的首地址。

多个包的组成为:

通过C/C++结构体解析RTP/RTCP的包头的方法 Linux 第3张

C/C++解析的方法为:

bool RTPProcessor::checkRtcp(PacketBuffer* packet)
{
 bool ret =false;
 const int rtcpCount=5;
 int i=0;
 RTCP_Header* pHeader[rtcpCount];
 int length[rtcpCount]={0};
 int ssrc[rtcpCount]={0};
 int offset=0;
 for (i=0;i<rtcpCount;i++)
 {
  if (i > 0)
  {
   offset+=4*(length[i-1]+1);
  }
  if (offset >= packet->dataSize)
  {
   break;
  }
  pHeader[i] =  (RTCP_Header*)(packet->data+offset);
  length[i] = ntohs(pHeader[i]->length);
  ssrc[i] = ntohl(pHeader[i]->ssrc);

 }
 return ret;
}

其中offset为每个包的偏移地址,rtcpCount=5为每个UDP包,预计不超过5个RTCP包。

参与评论