WireShark抓包分析
(RTP/RTCP over TCP)
RTSP Interleaved Frame, Channel: 0x01, 60 bytes
Magic: 0x24
Channel: 0x01 (0x00是RTP数据包)(0x01是RTCP数据包)
Length: 60
Real-time Transport Control Protocol (Receiver Report)
10.. .... = Version: RFC 1889 Version (2)
..0. .... = Padding: False
...0 0001 = Reception report count: 1
Packet type: Receiver Report (201)
Length: 7 (32 bytes)
Sender ***C: 0xfebeb924 (4273912100)
Source 1
Identifier: 0x283e2070 (675160176)
***C contents
Fraction lost: 253 / 256
Cumulative number of packets lost: -1
Extended highest sequence number received: 118657
Interarrival jitter: 1235
Last SR timestamp: 0 (0x00000000)
Delay since last SR timestamp: 0 (0 milliseconds)
Real-time Transport Control Protocol (Source description)
10.. .... = Version: RFC 1889 Version (2)
..0. .... = Padding: False
...0 0001 = Source count: 1
Packet type: Source description (202)
Length: 6 (28 bytes)
Chunk 1, ***C/CSRC 0xFEBEB924
Identifier: 0xfebeb924 (4273912100)
SDES items
Type: CNAME (user and domain) (1)
Length: 15
Text: DESKTOP-KPVK7LH
Type: END (0)
二进制报文:
24 01 00 3c 81 c9 00 07 fe be b9 24 28 3e 20 70
fd ff ff ff 00 01 cf 81 00 00 04 d3 00 00 00 00
00 00 00 00 81 ca 00 06 fe be b9 24 01 0f 44 45
53 4b 54 4f 50 2d 4b 50 56 4b 37 4c 48 00 00 00
Packet type代表该报文的类型RTP与RTCP协议头比较类似,为将二者区分开来,只需要检查报文头部第二个字节的之是否为200~206(或更后)即可,若是,则可以肯定为RTCP报文而非RTP.
FFmpeg源码对应RTCP分析
函数static int rtp_parse_one_packet(RTPDemuxContext *s, AVPacket *pkt, uint8_t **bufptr, int len)
#define RTP_VERSION 2
if ((buf[0] & 0xc0) != (RTP_VERSION << 6))//判断当前RTP版本是否是2
return -1;
/* 枚举RTCP报文类型 */
enum RTCPType {
RTCP_FIR = 192,
RTCP_NACK, // 193
RTCP_SMPTETC,// 194
RTCP_IJ, // 195
RTCP_SR = 200,
RTCP_RR, // 201
RTCP_SDES, // 202
RTCP_BYE, // 203
RTCP_APP, // 204
RTCP_RTPFB,// 205
RTCP_PSFB, // 206
RTCP_XR, // 207
RTCP_AVB, // 208
RTCP_RSI, // 209
RTCP_TOKEN,// 210
};
//判断报文是否是RTCP
#define RTP_PT_IS_RTCP(x) (((x) >= RTCP_FIR && (x) <= RTCP_IJ) || \
((x) >= RTCP_SR && (x) <= RTCP_TOKEN))
if (RTP_PT_IS_RTCP(buf[1])) {
return rtcp_parse_packet(s, buf, len);
}
文献解读Length长度说明
Length: 6 (28 bytes)
Chunk 1, ***C/CSRC 0xFEBEB924
Identifier: 0xfebeb924 (4273912100)
SDES items
Type: CNAME (user and domain) (1)
Length: 15
Text: DESKTOP-KPVK7LH
Type: END (0)
Length: 7 (32 bytes)该数据是如何计算出来的
如下是一些翻译的解析
长度: 16 比特 该 RTCP 包的长度减 1。其单位是 32 比特字,包括头和任何填充字节。
长度:16位,32位字RTCP包长度的一半。
英文原文
length: 16 bits
The length of this RTCP packet in 32-bit words minus one,
including the header and any padding. (The offset of one makes
zero a valid length and avoids a possible infinite loop in
scanning a compound RTCP packet, while counting 32-bit words
avoids a validity check for a multiple of 4.)
该RTCP包长度除以4减去1,除以4是以32比特作为单位,所以长度7,换算回来就是(7+1)*4 = 32
代码
std::string strName = "fengyuzaitu@51.cto";
int nLength = 2 + 2 + 8 + 20 + 4 + 6 + strName.size();
std::string strRTCPBuffer;
//之所以需要增加长度是为了能够在CNAME字符串结尾添加0x0,保证wireshark能够解析出Type :0 (End)标志,%4是为了4个字节对齐
int nRet = (strName.length() + 2) % 4;
if (0 != nRet)
{
nLength = nLength + 4 - nRet;
}
else
{
nLength = nLength + 4;
}
strRTCPBuffer.resize(nLength);
strRTCPBuffer[0] = 0x24;
strRTCPBuffer[1] = 0x01;
//RTCP报文长度,一段是RTCP统计数据内容,一段是RTCP源描述
*(unsigned short*)&strRTCPBuffer[3] = htons(nLength - 4);
//Sender Report
strRTCPBuffer[4] = (char)(2 << 6); // V=2, P=RC=0
strRTCPBuffer[5] = (char)200; // PT=SR=200
*(unsigned short*)&strRTCPBuffer[6] = htons(6); // 7个32比特减一
*(unsigned int*)&strRTCPBuffer[8] = htonl(0x23456);
*(unsigned int*)&strRTCPBuffer[12] = htonl(m_video.sendtime >> 32); // High 32-bits
*(unsigned int*)&strRTCPBuffer[16] = htonl(m_video.sendtime & 0xFFFFFFFF); // Low 32-bits
*(unsigned int*)&strRTCPBuffer[20] = htonl(m_nRtpTime);
*(unsigned int*)&strRTCPBuffer[24] = htonl(m_nSendPacketCount);
*(unsigned int*)&strRTCPBuffer[28] = htonl(m_nSendLength);
//Source description
strRTCPBuffer[32] = (char)(2 << 6) + 1; // V=2, P=0, SC=1
strRTCPBuffer[33] = 202;
//RTCP源描述长度,减去4个字节($+channel+2个字节长度)减去Sender Report报文长度
*(unsigned short*)&strRTCPBuffer[34] = htons((nLength - 4 - 7 * 4) / 4 - 1);
*(unsigned int*)&strRTCPBuffer[39] = htonl(0x23456);
strRTCPBuffer[40] = 1; // CNAME=1
strRTCPBuffer[41] = (char)strName.size();
memcpy(&strRTCPBuffer[42], strName.c_str(), strName.size());
注意:通过htons,htonl等函数转换为网络字节序,否则接收端无法正常解析,这一点可以通过wireshark的分析可以看出来
注意:类似于RTP信息包,每个RTCP信息包以固定部分开始,紧接着的是可变长结构单元,最后以一个32位边界结束
没有进行对齐的报文,WireShark显示异常
RTSP Interleaved Frame, Channel: 0x01, 80 bytes
Magic: 0x24
Channel: 0x01
Length: 80
Real-time Transport Control Protocol (Receiver Report)
10.. .... = Version: RFC 1889 Version (2)
..0. .... = Padding: False
...0 0001 = Reception report count: 1
Packet type: Receiver Report (201)
Length: 7 (32 bytes)
Sender ***C: 0xc009d701 (3221870337)
Source 1
Identifier: 0xbd7266dd (3178391261)
***C contents
Fraction lost: 0 / 256
Cumulative number of packets lost: 0
Extended highest sequence number received: 2175
Sequence number cycles count: 0
Highest sequence number received: 2175
Interarrival jitter: 0
Last SR timestamp: 0 (0x00000000)
Delay since last SR timestamp: 0 (0 milliseconds)
Real-time Transport Control Protocol (Source description)
10.. .... = Version: RFC 1889 Version (2)
..0. .... = Padding: False
...0 0001 = Source count: 1
Packet type: Source description (202)
Length: 11 (48 bytes)
Chunk 1, ***C/CSRC 0xC009D701
Identifier: 0xc009d701 (3221870337)
SDES items
[RTCP frame length check: OK - 80 bytes](实际上这里解析出错)