一:RTP协议:
1.RTP协议实际上是由实时传输协议RTP(Realtime Transport Protocol)和
实时传输控制协议RTCP(Realtime Transport Control Protocol)两部分组成的;
2.RTP协议基于多播或单播网络为用户提供连续媒体数据的实时传输服务;
RTCP协议是RTP协议的控制部分,用于实时监控数据传输质量,为系统提供拥塞控制和流控制;
3.RTP数据包:由RTP固定包头(Header)和载荷(Payload)两个部分组成;
其中,包头前12个字节的含义是固定的,而载荷数据可以视音频或视频数据。
二:H264视频传输中的RTP头解析:
/*
RTP数据头信息:12字节
*/
typedef struct _RTP_FIXED_HEADER
{
/**//* byte 0 */
unsigned char csrc_len:4; /**//* expect 0 */
unsigned char extension:1; /**//* expect 1, see RTP_OP below */
unsigned char padding:1; /**//* expect 0 */
unsigned char version:2; /**//* expect 2 */
/**//* byte 1 */
unsigned char payload:7; /**//* RTP_PAYLOAD_RTSP */
unsigned char marker:1; /**//* expect 1 */
/**//* bytes 2, 3 */
unsigned short seq_no;
/**//* bytes 4-7 */
unsigned long timestamp;
/**//* bytes 8-11 */
unsigned long ssrc; /**//* stream number is used here. */
} __PACKED__ RTP_FIXED_HEADER;
1.参数说明:
**第0字节
V:
RTP协议的版本号,占2位;
当前协议的版本号为2(根据RFC3984协议,目前使用的RTP版本号应设为0x10);
P:
填充标识,占1位:
如果在该报文的尾部填充一个或多个额外的八位组,且他们不属于有效载荷;
X:
扩展标志,占1位,如果X=1;
则在RTP报头后跟一个扩展报头;
CC:
CSRC计数器,占4位;
指示CSRC标识符的个数;
**第1字节
M:
标记位Marker bit,占1位。
如果当前NALU为一个接入单元最后的那个NALU,那么将M位置1;
或者当前RTP数据包为一个NALU的最后那个分片时,M位置1。
其余情况下M位保持位0.
PT:
载荷类型Payload type,7位;
对于H.264视频格式来说,当前并没有规定一个默认的PT值,因此选用大于95的值即可,此处可设置为0x60(十进制96)。
**第2-3字节
SQ:
序号Sequence number,16位;
序号的起始值为随机值此处设为0,每发送一个RTP数据包,序号值加1,。
**第4-7字节
TS:
时间戳Timestamp,32位,4个字节;
同序号一样,时间戳的起始值也是随机值,此处设为0.
根据RFC3984协议,与时间相应的时钟频率必须为90000HZ。
**第8-11个字节
SSRC:
同步源标识,32位,4个字节;
SSRC应该被随机生成,以使在同一个RTP会话期中没有任何两个同步源具有相同的SSRC识别符。
此处仅有一个同步源,因此将其设为0x12345678;
注:前12个字节是RTP头的基础数据,后续的CRSC特约信源是可有可无的;
CSRC:
特约信源:32位,4个字节;
扩展标志X:决定RTP头是否有CSRC特约信源,值为1则有;
CC计数器 : 决定CSRC特约信源的个数:
三:NALU在网络中的传输方式:
1.对于每一个NALU,根据其包含的数量的不同,其大小也有差异。
在网络中,当要传输的报文大小超过最大传输单元MTU(Maximum Transmission Unit)时就会产生数据包分片的情况。
2.在以太网环境中可传输的最大报文(MTU)的大小为1500字节;
3.如果发送的数据包大于MTU值,数据包就会被拆开来传输,
这样就会产生很多数据包碎片,增加丢包率,降低网络速度。
4.对于视频传输而言,若RTP包大于MTU而由底层协议机制任意拆包,可能会导致接收端播放器的延时播放甚至无法正常播放。
因此对于大于MTU的NALU单元,必须进行拆包处理。
5.RFC3984协议给出了3种不同的RTP打包方案:
a:Single NALU Packet:
在一个RTP包中只封装一个NALU,一般部标协议里小于950字节的均采用此方案;
b:Aggregation Packet:
在一个RTP包中封装多个NALU,对于较小的NALU可以采用这种打包方案,从而提高传输效率;
c:Fragmentation Unit:
一个NALU封装在多个RTP包中,在部标协议中大于950字节的NALU便采用此种方案进行拆包处理。
注:一般只是用a、b方案即可,很少使用b方案;
四:RTP有效载荷(Payload)说明:
typedef struct _FU_INDICATOR
{
//byte 0
unsigned char TYPE:5;
unsigned char NRI:2;
unsigned char F:1;
}__PACKED__ FU_INDICATOR; /**//* 1 BYTES */
typedef struct _FU_HEADER
{
//byte 0
unsigned char TYPE:5;
unsigned char R:1;
unsigned char E:1;
unsigned char S:1;
} __PACKED__ FU_HEADER; /**//* 1 BYTES */
1.对于一个RTP只封装在一个NALU里:
RTP有效载荷:载荷头Indicator + NALU数据;(这里的NALU数据指的是h264格式里的0x000001开头的数据);
完整的RTP数据 = RTP头(12字节) + 载荷头Indicator(1字节) + NALU数据(一帧h.264的数据长度)
2.对于一个NALU封装在多个RTP包里:
RTP有效载荷:载荷头Indicator + 分片信息fu_header + NALU数据;(这里的NALU数据指的是分片的h264数据);
完整的RTP数据 = RTP头(12字节) + 载荷头Indicator(1字节) + 分片信息fu_header(1字节) + NALU数据(一帧h.264分片后的数据长度)
五:RTP组包代码:
#define nalu_sent_len 950
#define RTP_H264 96
#define RTP_AUDIO 97
#define timestamp_increse (90000 / 25) //90KHz (1s 25帧) == 3600
int VENC_Sent(char *buffer,int buflen)
{
int i;
int is=0;
int nChanNum=0;
int ret = 0;
RTP_FIXED_HEADER *rtp_hdr;
FU_INDICATOR *fu_ind;
FU_HEADER *fu_hdr;
char *nalu_payload = NULL;
int nAvFrmLen = 0;
int nIsIFrm = 0;
int nNaluType = 0;
char sendbuf[500*1024+32] = {0};
int bytes = 0;
char fixHeader[4] = {0x0A,0x0A,0x0A,0x0A};
static int frame_count = 0;
for(is = 0; is < MAX_RTSP_CLIENT; is ++)
{
if(g_rtspClients[is].status != RTSP_SENDING)
{
continue;
}
nAvFrmLen = buflen;
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(g_rtspClients[is].rtpport[0]);
server.sin_addr.s_addr = inet_addr(g_rtspClients[is].IP);
rtp_hdr = (RTP_FIXED_HEADER*)&sendbuf[0];
rtp_hdr->payload = RTP_H264;
rtp_hdr->version = 2;
rtp_hdr->marker = 0;
rtp_hdr->ssrc = htonl(10);
if(nAvFrmLen <= nalu_sent_len) //单包发送
{
rtp_hdr->marker = 1;
rtp_hdr->seq_no = htons(g_rtspClients[is].seqnum++);
fu_ind = (FU_INDICATOR*)&sendbuf[12];
fu_ind->F = 0;
fu_ind->NRI = nIsIFrm;
fu_ind->TYPE = nNaluType;
nalu_payload = &sendbuf[13]; //未分片:头信息一共占13字节
memcpy(nalu_payload, buffer, nAvFrmLen);
g_rtspClients[is].tsvid = g_rtspClients[is].tsvid + timestamp_increse;
rtp_hdr->timestamp = htonl(g_rtspClients[is].tsvid);
bytes = nAvFrmLen + 13 ;
sendto(udpfd, sendbuf, bytes, 0, (struct sockaddr *)&server,sizeof(server));
}
else if(nAvFrmLen > nalu_sent_len) // 分包发送
{
//printf("[%s:%d]:[yang] 222 nAvFrmLen = %d\n",__FUNCTION__,__LINE__,nAvFrmLen);
int k = 0, l = 0;
k = nAvFrmLen / nalu_sent_len; //整包数的个数
l = nAvFrmLen % nalu_sent_len; //最后不足一包的数据的字节数
int t = 0;
g_rtspClients[is].tsvid = g_rtspClients[is].tsvid + timestamp_increse;
rtp_hdr->timestamp = htonl(g_rtspClients[is].tsvid);
while(t <= k)
{
rtp_hdr->seq_no = htons(g_rtspClients[is].seqnum++);
if(t == 0) //
{
rtp_hdr->marker = 0;
fu_ind =(FU_INDICATOR*)&sendbuf[12];
fu_ind->F = 0;
fu_ind->NRI = nIsIFrm;
fu_ind->TYPE = 28;
fu_hdr = (FU_HEADER*)&sendbuf[13]; //13
fu_hdr->E = 0;
fu_hdr->R = 0;
fu_hdr->S = 1;
fu_hdr->TYPE = nNaluType;
nalu_payload = &sendbuf[14];
memcpy(nalu_payload,buffer,nalu_sent_len);
bytes = nalu_sent_len + 14; //分片数据:头信息一共占14字节
sendto(udpfd, sendbuf, bytes, 0, (struct sockaddr *)&server,sizeof(server));
t++;
}
else if(k == t) //发送最后不足一包的数据
{
rtp_hdr->marker = 1;
fu_ind =(FU_INDICATOR*)&sendbuf[12];
fu_ind->F= 0 ;
fu_ind->NRI= nIsIFrm ;
fu_ind->TYPE=28;
fu_hdr =(FU_HEADER*)&sendbuf[13]; // 13
fu_hdr->R = 0;
fu_hdr->S = 0;
fu_hdr->TYPE = nNaluType;
fu_hdr->E = 1;
nalu_payload = &sendbuf[14];
memcpy(nalu_payload,buffer + t*nalu_sent_len, l);
bytes = l + 14; //分片数据:头信息一共占14字节
sendto(udpfd, sendbuf, bytes, 0, (struct sockaddr *)&server,sizeof(server));
t++;
}
else if(t < k && t != 0) //发送 1400大小的数据包
{
rtp_hdr->marker=0;
fu_ind = (FU_INDICATOR*)&sendbuf[12];
fu_ind->F = 0;
fu_ind->NRI = nIsIFrm;
fu_ind->TYPE = 28;
fu_hdr = (FU_HEADER*)&sendbuf[13]; // 13
fu_hdr->R = 0;
fu_hdr->S = 0;
fu_hdr->E = 0;
fu_hdr->TYPE = nNaluType;
nalu_payload = &sendbuf[14];
memcpy(nalu_payload, buffer + t*nalu_sent_len, nalu_sent_len);
bytes = nalu_sent_len + 14; //分片数据:头信息一共占14字节
sendto(udpfd, sendbuf, bytes, 0, (struct sockaddr *)&server,sizeof(server));
t++;
}
}
}
}
}
说明:
1.单包发送或分包发送的最后一包r数据的marker(M)为1,其余为0;
rtp_hdr->marker = 1;
2.时间戳遇上一帧相比固定增加3600:即(90000Hz / 25 == 3600)
g_rtspClients[is].tsvid = g_rtspClients[is].tsvid + timestamp_increse;
rtp_hdr->timestamp = htonl(g_rtspClients[is].tsvid);
注:时间戳要转为网络字节序htonl(大端模式);
3.分片发送一个NALU时:
fu_hdr->S = 1; //是否为分片的第一包数据,是则为1
fu_hdr->R = 0;
fu_hdr->E = 0; //是否为分片的最后一包数据,是则为1
注:第一包,则fu_hdr->S必须为1,
最后一包则fu_hdr->E必须为1;
其余情况3位均为0。
4.RTP有效载荷类型: