理论部分:https://blog.csdn.net/shixin_0125/article/details/78798238
以下是rtsp传输视频流时的核心代码
//下面为“小端”结构体写法,所以version不在首位
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. */
};
struct _NALU_HEADER
{
//byte 0
unsigned char TYPE:5;
unsigned char NRI:2;
unsigned char F:1;
}; /**//* 1 BYTES */
struct _FU_INDICATOR
{
//byte 0
unsigned char TYPE:5;
unsigned char NRI:2;
unsigned char F:1;
}; /**//* 1 BYTES */
struct _FU_HEADER
{
//byte 0
unsigned char TYPE:5;
unsigned char R:1;
unsigned char E:1;
unsigned char S:1;
}; /**//* 1 BYTES */
struct _AU_HEADER
{
//byte 0, 1
unsigned short au_len;
//byte 2,3
unsigned short frm_len:13;
unsigned char au_index:3;
}; /**//* 1 BYTES */
typedef struct _RTP_FIXED_HEADER RTP_FIXED_HEADER;
typedef struct _NALU_HEADER NALU_HEADER;
typedef struct _FU_INDICATOR FU_INDICATOR;
typedef struct _FU_HEADER FU_HEADER;
typedef struct _AU_HEADER AU_HEADER;
HI_S32 VENC_Sent(char *buff,int buflen, unsigned int pts)
{
int is=0;
char *buffer = NULL;
RTP_FIXED_HEADER *rtp_hdr = NULL;
NALU_HEADER *nalu_hdr = NULL;
FU_INDICATOR *fu_ind = NULL;
FU_HEADER *fu_hdr = NULL;
char* nalu_payload = NULL;
int nAvFrmLen = 0;
int nIsIFrm = 0;
int nNaluType = 0;
char sendbuf[nalu_sent_len + 20 +32];
struct sockaddr_in server;
int bytes = 0;
/*为啥减5?
一个原始的NALU单元结构为: [startCode][NALU Header][Header Payload]三部分;
startCode是一个Nalu单元的开始,必须是00 00 00 01;
nalu header结构为:
| 1bit | 2bit | 5bit |
固定0 0~3 表重要性 nalu单元的类型
举例:00 00 00 01 06: SEI信息
00 00 00 01 67: 0x67 & 0x1f = 0x07 :SPS
00 00 00 01 68: 0x68 & 0x1f = 0x08 :PPS
00 00 00 01 65: 0x65 & 0x1f = 0x05: IDR Slice
*/
nAvFrmLen = buflen - 5;
nNaluType = buff[4] & 0x1f;
nIsIFrm = (buff[4] & 0x60) >> 5;
buffer = buff + 5;
/*
RTP头(12byte) + NAL头 + RBSP(SPS+SEI+PPS+I片+图像定界符+P片+p片)
*/
rtp_hdr =(RTP_FIXED_HEADER*)&sendbuf[0];
rtp_hdr->payload = RTP_H264;
rtp_hdr->version = 2; //固定为2
rtp_hdr->marker = 0;
/* RTP打包发送h264数据
(1) 当一个NALU小于MTU字节的时候,采用一个单RTP包发送;
(2) 当一个NALU大于等于MTU字节的时候,采用FU_A分发送;
RTP Header + FU_INDICATOR + FU_HEADER
当发送第一个的FU时,S=1 E=0 R=0 并设置rtp M位为0;
当发送中间的FU时, S=0 E=0 R=0 ;
当发送最后一个FU时,S=0 E=1 R=0;并设置rtp M位为1;
*/
if(nAvFrmLen <= nalu_sent_len)
{
rtp_hdr->marker=1; //设置rtp M位; 相当于最后一个包;
nalu_hdr = (NALU_HEADER*)&sendbuf[12];
nalu_hdr->F = 0;
nalu_hdr->NRI = nIsIFrm;
nalu_hdr->TYPE = nNaluType;
rtp_hdr->timestamp = htonl( pts);//htonl(g_rtspClients[is].tsvid);
//nalu_payload指向&sendbuf[13] 将【码流-5】的数据放到&sendbuf[13]
nalu_payload=&sendbuf[13];
memcpy(nalu_payload,buffer,nAvFrmLen);
// g_rtspClients[is].tsvid = pts;//g_rtspClients[is].tsvid+timestamp_increse;
/*为啥加13?
RTP头(12byte) + NAL头(1byte)(去掉了00 00 00 01部分)
bytes为 总的我们需要发送的字节数;
*/
bytes=nAvFrmLen+ 13 ;
for(is=0;is<MAX_RTSP_CLIENT;is++)
{
if(g_rtspClients[is].status == RTSP_SENDING)
{
pthread_mutex_lock(&g_sendmutex);
rtp_hdr->seq_no = htons(g_rtspClients[is].seqnum++); //序列号,每发送一个rtp包增加1
rtp_hdr->ssrc = htonl(10 + is); //随机指定为10,并且在RTP会话中全局唯一;
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);
//printf("g_rtspClients[%d].IP = %s, g_rtspClients[is].rtpport[0] = %d\n",is, g_rtspClients[is].IP, g_rtspClients[is].rtpport[0]);
sendto(udpfd, sendbuf, bytes, 0, (struct sockaddr *)&server,sizeof(server));
pthread_mutex_unlock(&g_sendmutex);
}
}
}
else if(nAvFrmLen>nalu_sent_len)
{
int k=0,l=0;
k=nAvFrmLen/nalu_sent_len;
l=nAvFrmLen%nalu_sent_len;
int t=0;
rtp_hdr->timestamp = htonl( pts);
// rtp_hdr->timestamp=htonl(g_rtspClients[is].tsvid);
// g_rtspClients[is].tsvid = g_rtspClients[is].tsvid + timestamp_increse;
while(t<=k)
{
//第一个FU
if(t==0)
{
rtp_hdr->marker=0; //第一个FU需要设置rtp M位为0;
//设置FU INDICATOR,并将这个HEADER填入sendbuf[12]
/*FU_INDICATOR结构
|F(bit)|NRI(2bit)|Type(5bit)|
其中F和NRI与NALU相同;Type不一样,具体如下;
0 没有定义
1~23 NAL单元 单个NAL单元包
24 STAP-A 单一时间的组合包;
25 STAP-B 单一时间的组合包;
26 MTAP16 多个时间的组合包;
27 MTAP24 多个时间的组合包;
28 FU-A 分片的单元
29 FU-B 分片的单元
30~31 没有定义
*/
fu_ind = (FU_INDICATOR*)&sendbuf[12];
fu_ind->F = 0;
fu_ind->NRI = nIsIFrm;
fu_ind->TYPE= 28;
//设置FU HEADER,并将这个HEADER填入sendbuf[13]
fu_hdr = (FU_HEADER*)&sendbuf[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);
//RTP Header(12byte) + FU_INDICATOR(1byte) + FU_HEADER(1byte)
bytes=nalu_sent_len+14;
for(is=0;is<MAX_RTSP_CLIENT;is++)
{
if(g_rtspClients[is].status == RTSP_SENDING)
{
pthread_mutex_lock(&g_sendmutex);
rtp_hdr->seq_no = htons(g_rtspClients[is].seqnum++);
rtp_hdr->ssrc = htonl(10 + is);
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);
// printf("g_rtspClients[%d].IP = %s, g_rtspClients[is].rtpport[0] = %d\n",is, g_rtspClients[is].IP, g_rtspClients[is].rtpport[0]);
sendto( udpfd, sendbuf, bytes, 0, (struct sockaddr *)&server,sizeof(server));
pthread_mutex_unlock(&g_sendmutex);
}
}
t++;
}
else if(k==t) //最后一个FU
{
//设置rtp M 位;当前传输的是最后一个分片时该位置1
rtp_hdr->marker = 1;
fu_ind = (FU_INDICATOR*)&sendbuf[12];
fu_ind->F = 0 ;
fu_ind->NRI = nIsIFrm ;
fu_ind->TYPE = 28;
//设置FU HEADER,并将这个HEADER填入sendbuf[13]
fu_hdr = (FU_HEADER*)&sendbuf[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;
for(is=0;is<MAX_RTSP_CLIENT;is++)
{
if(g_rtspClients[is].status == RTSP_SENDING)
{
pthread_mutex_lock(&g_sendmutex);
rtp_hdr->seq_no = htons(g_rtspClients[is].seqnum++);
rtp_hdr->ssrc = htonl(10 + is);
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);
// printf("g_rtspClients[%d].IP = %s, g_rtspClients[is].rtpport[0] = %d\n",is, g_rtspClients[is].IP, g_rtspClients[is].rtpport[0]);
sendto(udpfd, sendbuf, bytes, 0, (struct sockaddr *)&server,sizeof(server));
pthread_mutex_unlock(&g_sendmutex);
}
}
t++;
}
else if(t<k && t!=0) //中间FU
{
//设置rtp M 位;
rtp_hdr->marker=0;
//设置FU INDICATOR,并将这个HEADER填入sendbuf[12]
fu_ind = (FU_INDICATOR*)&sendbuf[12];
fu_ind->F = 0;
fu_ind->NRI = nIsIFrm;
fu_ind->TYPE= 28;
fu_hdr = (FU_HEADER*)&sendbuf[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;
for(is=0;is<MAX_RTSP_CLIENT;is++)
{
if(g_rtspClients[is].status == RTSP_SENDING)
{
pthread_mutex_lock(&g_sendmutex);
rtp_hdr->seq_no = htons(g_rtspClients[is].seqnum++);
rtp_hdr->ssrc = htonl(10 + is);
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);
// printf("g_rtspClients[%d].IP = %s, g_rtspClients[is].rtpport[0] = %d\n",is, g_rtspClients[is].IP, g_rtspClients[is].rtpport[0]);
sendto(udpfd, sendbuf, bytes, 0, (struct sockaddr *)&server,sizeof(server));
pthread_mutex_unlock(&g_sendmutex);
}
}
t++;
}
}
}
}
2023-1-9:
问题描述: rtsp同一份代码在其他板子上能正常推流,现在的开发板推流到vlc后不显示画面;
抓包发现:Unknown RTP version 3 ,正常情况下RTP版本应该为2; 而且此时udp数据一直在传送中,但是不显示画面;
问题排查:(1) struct _RTP_FIXED_HEADER注意大小端写法,首先确认开发板属于大端还是小端?
(2) struct _RTP_FIXED_HEADER结构体中有long型变量,需注意开发板属于64位还是32位;若是64位,那么结构体大小变化,会导致错乱;
(3) 涉及bit的结构体需要1个字节对其;