rtsp分包规则

本文详细介绍了RTP传输视频流的核心代码,包括RTP头部结构、NALU头部、FU指示器和FU头部等。代码实现了根据NAL单元长度判断是否采用单RTP包或FU_A分包发送。同时,针对抓包发现的RTP版本错误问题,提出了检查大小端和结构体对齐的排查思路。
摘要由CSDN通过智能技术生成

理论部分: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个字节对其;
























评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

天未及海宽

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

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

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

打赏作者

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

抵扣说明:

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

余额充值