H264 RTP解包

同前篇类似,修改自vc下的程序,跟打包相反,原理就是从udp接收一个数据包,判断这个数据包是单个发送还是分片发送,进而获取rtp包中的h264NAL数据,并加上0x00000001起始字节一并写进文件中即可。

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <sys/socket.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#include <unistd.h>

#include <errno.h>

#include <sys/types.h>

#include <fcntl.h>

typedef struct
{
	unsigned char version;          	//!< Version, 2 bits, MUST be 0x2
	unsigned char padding;			 	//!< Padding bit, Padding MUST NOT be used
	unsigned char extension;			//!< Extension, MUST be zero
	unsigned char cc;       	   		//!< CSRC count, normally 0 in the absence of RTP mixers 		
	unsigned char marker;			   	//!< Marker bit
	unsigned char pt;			   		//!< 7 bits, Payload Type, dynamically established
	unsigned int seq_no;			   	//!< RTP sequence number, incremented by one for each sent packet 
	unsigned int timestamp;	       //!< timestamp, 27 MHz for H.264
	unsigned int ssrc;			   //!< Synchronization Source, chosen randomly
	unsigned char * payload;      //!< the payload including payload headers
	unsigned int paylen;		   //!< length of payload in bytes
} RTPpacket_t;

typedef struct 
{
	/*  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             |
	|                             ....                              |
	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    */
	//intel 的cpu 是intel为小端字节序(低端存到底地址) 而网络流为大端字节序(高端存到低地址)
	/*intel 的cpu : 高端->csrc_len:4 -> extension:1-> padding:1 -> version:2 ->低端
	 在内存中存储 :
	 低->4001(内存地址)version:2
	     4002(内存地址)padding:1
		 4003(内存地址)extension:1
	 高->4004(内存地址)csrc_len:4

     网络传输解析 : 高端->version:2->padding:1->extension:1->csrc_len:4->低端  (为正确的文档描述格式)

	 存入接收内存 :
	 低->4001(内存地址)version:2
	     4002(内存地址)padding:1
	     4003(内存地址)extension:1
	 高->4004(内存地址)csrc_len:4
	 本地内存解析 :高端->csrc_len:4 -> extension:1-> padding:1 -> version:2 ->低端 ,
	 即:
	 unsigned char csrc_len:4;        // expect 0 
	 unsigned char extension:1;       // expect 1
	 unsigned char padding:1;         // expect 0 
	 unsigned char version:2;         // expect 2 
	*/
	/* 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 payloadtype:7;     /* RTP_PAYLOAD_RTSP */
	 unsigned char marker:1;          /* expect 1 */
	/* bytes 2,3 */
	 unsigned int seq_no;            
	/* bytes 4-7 */
	 unsigned int timestamp;        
	/* bytes 8-11 */
	 unsigned int ssrc;              /* stream number is used here. */
} RTP_FIXED_HEADER;


typedef struct
{
	unsigned char forbidden_bit;           //! Should always be FALSE
	unsigned char nal_reference_idc;       //! NALU_PRIORITY_xxxx
	unsigned char nal_unit_type;           //! NALU_TYPE_xxxx  
	unsigned int startcodeprefix_len;      //! 前缀字节数
	unsigned int len;                      //! 包含nal 头的nal 长度,从第一个00000001到下一个000000001的长度
	unsigned int max_size;                 //! 做多一个nal 的长度
	unsigned char * buf;                   //! 包含nal 头的nal 数据
	unsigned int lost_packets;             //! 预留
} NALU_t;

/*
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI|  Type   |
+---------------+
*/
typedef struct 
{
	//byte 0
	unsigned char TYPE:5;
	unsigned char NRI:2;
	unsigned char F:1;        
} NALU_HEADER; // 1 BYTE 

/*
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI|  Type   |
+---------------+
*/
typedef struct 
{
	//byte 0
	unsigned char TYPE:5;
	unsigned char NRI:2; 
	unsigned char F:1;              
} FU_INDICATOR; // 1 BYTE 

/*
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|S|E|R|  Type   |
+---------------+
*/
typedef struct 
{
	//byte 0
	unsigned char TYPE:5;
	unsigned char R:1;
	unsigned char E:1;
	unsigned char S:1;    
} FU_HEADER;   // 1 BYTES 

#define  MAXDATASIZE 1500
#define PORT	1234
#define BUFFER_SIZE	10

FILE * poutfile =  NULL;	

char * outputfilename = "./receive.264";

int  OpenBitstreamFile (char *fn)
{
	if (NULL == (poutfile = fopen(fn, "wb")))
	{
		printf("Error: Open input file error\n");
		getchar();
	}
	return 1;
}

NALU_t *AllocNALU(int buffersize)
{
	NALU_t *n;

	if ((n = (NALU_t*)calloc (1, sizeof(NALU_t))) == NULL)
	{
		printf("AllocNALU Error: Allocate Meory To NALU_t Failed ");
		exit(0);
	}
	return n;
}

void FreeNALU(NALU_t *n)
{
	if (n)
	{
		free (n);
	}
}

/*
*bufIn:rtppackage
*len: the lengthe of rtppackage
*/
void rtp_unpackage(char *bufIn,int len)
{
    unsigned char recvbuf[1500];
	RTPpacket_t *p = NULL; 
	RTP_FIXED_HEADER * rtp_hdr = NULL;
	NALU_HEADER * nalu_hdr = NULL;
	NALU_t * n  = NULL;
	FU_INDICATOR	*fu_ind = NULL;
	FU_HEADER		*fu_hdr= NULL;
	int total_bytes = 0;                 //当前包传出的数据
	static int total_recved = 0;         //一共传输的数据
	int fwrite_number = 0;               //存入文件的数据长度

	memcpy(recvbuf,bufIn, len);          //复制rtp包 
	printf("包长度+ rtp头:   = %d\n",len);

	//////////////////////////////////////////////////////////////////////////
	//begin rtp_payload and rtp_header

	p = (RTPpacket_t*)&recvbuf[0];
	if ((p = malloc (sizeof (RTPpacket_t)))== NULL)
	{
		printf ("RTPpacket_t MMEMORY ERROR\n");
	}
	if ((p->payload = malloc (MAXDATASIZE))== NULL)
	{
		printf ("RTPpacket_t payload MMEMORY ERROR\n");
	}

	if ((rtp_hdr = malloc(sizeof(RTP_FIXED_HEADER))) == NULL)
	{
		printf("RTP_FIXED_HEADER MEMORY ERROR\n");
	}
    
	rtp_hdr =(RTP_FIXED_HEADER*)&recvbuf[0]; 
	printf("版本号 	: %d\n",rtp_hdr->version);
	p->version  = rtp_hdr->version;
	p->padding  = rtp_hdr->padding;
	p->extension  = rtp_hdr->extension;
	p->cc = rtp_hdr->csrc_len;
	printf("标志位 	: %d\n",rtp_hdr->marker);
	p->marker = rtp_hdr->marker;
	printf("负载类型	:%d\n",rtp_hdr->payloadtype);
	p->pt = rtp_hdr->payloadtype;
	printf("包号   	: %d \n",rtp_hdr->seq_no);
	p->seq_no = rtp_hdr->seq_no;
	printf("时间戳 	: %d\n",rtp_hdr->timestamp);
	p->timestamp = rtp_hdr->timestamp;
	printf("帧号   	: %d\n",rtp_hdr->ssrc);
	p->ssrc = rtp_hdr->ssrc;
	
	//end rtp_payload and rtp_header
	//////////////////////////////////////////////////////////////////////////
	//begin nal_hdr
	if (!(n = AllocNALU(800000)))          //为结构体nalu_t及其成员buf分配空间。返回值为指向nalu_t存储空间的指针
	{
		printf("NALU_t MMEMORY ERROR\n");
	}
	if ((nalu_hdr = malloc(sizeof(NALU_HEADER))) == NULL)
	{
		printf("NALU_HEADER MEMORY ERROR\n");
	}

	nalu_hdr =(NALU_HEADER*)&recvbuf[12];                        //网络传输过来的字节序 ,当存入内存还是和文档描述的相反,只要匹配网络字节序和文档描述即可传输正确。
	printf("forbidden_zero_bit: %d\n",nalu_hdr->F);              //网络传输中的方式为:F->NRI->TYPE.. 内存中存储方式为 TYPE->NRI->F (和nal头匹配)。
	n->forbidden_bit= nalu_hdr->F << 7;                          //内存中的字节序。
	printf("nal_reference_idc:  %d\n",nalu_hdr->NRI);
	n->nal_reference_idc = nalu_hdr->NRI << 5;                      
	printf("nal 负载类型:       %d\n",nalu_hdr->TYPE);
	n->nal_unit_type = nalu_hdr->TYPE;

	//end nal_hdr
	//////////////////////////////////////////////////////////////////////////
	//开始解包
	if ( nalu_hdr->TYPE  == 0)
	{
		printf("这个包有错误,0无定义\n");
	}
	else if ( nalu_hdr->TYPE >0 &&  nalu_hdr->TYPE < 24)  //单包
	{
		printf("当前包为单包\n");
		putc(0x00, poutfile);
		putc(0x00, poutfile);
		putc(0x00, poutfile);
		putc(0x01, poutfile);	//写进起始字节0x00000001
		total_bytes +=4;
		memcpy(p->payload,&recvbuf[13],len-13);	
		p->paylen = len-13;
	 	fwrite(nalu_hdr,1,1,poutfile);	//写NAL_HEADER
		total_bytes += 1;
		fwrite_number = fwrite(p->payload,1,p->paylen,poutfile);	//写NAL数据
		total_bytes = p->paylen;
		printf("包长度 + nal= %d\n",total_bytes);
	}
	else if ( nalu_hdr->TYPE == 24)                    //STAP-A   单一时间的组合包
	{
		printf("当前包为STAP-A\n");
	}
	else if ( nalu_hdr->TYPE == 25)                    //STAP-B   单一时间的组合包
	{
		printf("当前包为STAP-B\n");
	}
	else if (nalu_hdr->TYPE == 26)                     //MTAP16   多个时间的组合包
	{
		printf("当前包为MTAP16\n");
	}
	else if ( nalu_hdr->TYPE == 27)                    //MTAP24   多个时间的组合包
	{
		printf("当前包为MTAP24\n");
	}
     else if ( nalu_hdr->TYPE == 28)                    //FU-A分片包,解码顺序和传输顺序相同
	{
		if ((fu_ind = malloc(sizeof(FU_INDICATOR))) == NULL)
		{
			printf("FU_INDICATOR MEMORY ERROR\n");
		}
		if ((fu_hdr = malloc(sizeof(FU_HEADER))) == NULL)
		{
			printf("FU_HEADER MEMORY ERROR\n");
		}

		fu_ind=(FU_INDICATOR*)&recvbuf[12];		//分片包用的是FU_INDICATOR而不是NALU_HEADER
		printf("FU_INDICATOR->F     :%d\n",fu_ind->F);
		n->forbidden_bit = fu_ind->F << 7;
		printf("FU_INDICATOR->NRI   :%d\n",fu_ind->NRI);
		n->nal_reference_idc = fu_ind->NRI << 5;                      
		printf("FU_INDICATOR->TYPE  :%d\n",fu_ind->TYPE);
		n->nal_unit_type = fu_ind->TYPE;

		fu_hdr=(FU_HEADER*)&recvbuf[13];		//FU_HEADER赋值
		printf("FU_HEADER->S        :%d\n",fu_hdr->S);
		printf("FU_HEADER->E        :%d\n",fu_hdr->E);
		printf("FU_HEADER->R        :%d\n",fu_hdr->R);
		printf("FU_HEADER->TYPE     :%d\n",fu_hdr->TYPE);
		n->nal_unit_type = fu_hdr->TYPE;               //应用的是FU_HEADER的TYPE

		if (rtp_hdr->marker == 1)                      //分片包最后一个包
		{
			printf("当前包为FU-A分片包最后一个包\n");
			memcpy(p->payload,&recvbuf[14],len - 14);
			p->paylen = len - 14;
			fwrite_number = fwrite(p->payload,1,p->paylen,poutfile);	//写NAL数据
			total_bytes = p->paylen;
			printf("包长度 + FU = %d\n",total_bytes);	
		}
		else if (rtp_hdr->marker == 0)                 //分片包 但不是最后一个包
		{
			if (fu_hdr->S == 1)                        //分片的第一个包
			{
				unsigned char F;
				unsigned char NRI;
				unsigned char TYPE;
				unsigned char nh;
				printf("当前包为FU-A分片包第一个包\n");
				putc(0x00, poutfile);
				putc(0x00, poutfile);
				putc(0x00, poutfile);
				putc(0x01, poutfile);				//写起始字节码0x00000001
				total_bytes += 4;
                
				F = fu_ind->F << 7;
				NRI = fu_ind->NRI << 5;
				TYPE = fu_hdr->TYPE;                                            //应用的是FU_HEADER的TYPE
				//nh = n->forbidden_bit|n->nal_reference_idc|n->nal_unit_type;  //二进制文件也是按 大字节序存储
				nh = F | NRI | TYPE;

				putc(nh,poutfile);				//写NAL HEADER

				total_bytes +=1;
				memcpy(p->payload,&recvbuf[14],len - 14);
				p->paylen = len - 14;
				fwrite_number = fwrite(p->payload,1,p->paylen,poutfile);	//写NAL数据
				total_bytes = p->paylen;
				printf("包长度 + FU_First = %d\n",total_bytes);	
			}
			else                                      //如果不是第一个包
			{
				printf("当前包为FU-A分片包\n");
				memcpy(p->payload,&recvbuf[14],len - 14);
				p->paylen= len - 14;
				fwrite_number = fwrite(p->payload,1,p->paylen,poutfile);	//写NAL数据
				total_bytes = p->paylen;
				printf("包长度 + FU = %d\n",total_bytes);	
			}	
		}
	}
	else if ( nalu_hdr->TYPE == 29)                //FU-B分片包,解码顺序和传输顺序相同
	{
		if (rtp_hdr->marker == 1)                  //分片包最后一个包
		{
			printf("当前包为FU-B分片包最后一个包\n");

		}
		else if (rtp_hdr->marker == 0)             //分片包 但不是最后一个包
		{
			printf("当前包为FU-B分片包\n");
		}
	}
	else
	{
		printf("这个包有错误,30-31 没有定义\n");
	}
	total_recved += total_bytes;
	printf("total_recved = %d\n",total_recved);
	memset(recvbuf,0,1500);
	free (p->payload);
	free (p);
	FreeNALU(n);
	//结束解包
	//////////////////////////////////////////////////////////////////////////
    return ;
}


int main()
{
	char recvbuf[MAXDATASIZE];  //加上头最大传输数据 1500
	int sockfd;  
	int client_fd;  
	int sin_size;  
	char sendbuf[BUFFER_SIZE];
	struct sockaddr_in server_sockaddr, client_sockaddr;  

	int	receive_bytes = 0;

	OpenBitstreamFile(outputfilename); 
    
	//////////////////////////////////////////////////////////////////////////
	//socket 操作
	
	if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
	{
		perror("socket");
		exit(1);
	}//建立socket链接,数据报socket,IPv4协议
	printf("create socket success!\n");

	server_sockaddr.sin_family = AF_INET;  
	server_sockaddr.sin_addr.s_addr = INADDR_ANY;//0.0.0.0不确定地址  
	server_sockaddr.sin_port = htons(PORT);  
	bzero(&(server_sockaddr.sin_zero), 8);  //填充0以保持与struct sockaddr同样大小

	if (bind(sockfd, (struct sockaddr *) &server_sockaddr,  
		  sizeof(struct sockaddr)) < 0)   
	{
		perror("ERROR on binding");
		exit(1); 
	} 
	printf("bind success!\n");

	sin_size = sizeof(struct sockaddr_in);
	printf("waiting for client connection...\n");
	//接收从客户端发来的数据
	while((receive_bytes = recvfrom(sockfd, recvbuf, MAXDATASIZE, 0, (struct sockaddr *)&client_sockaddr, &sin_size)) >0)
	{
		if(strncmp(recvbuf, "over",4) == 0)

		{

			break;

		}
		poutfile = fopen(outputfilename,"ab+");
		rtp_unpackage(recvbuf,receive_bytes);
		fclose(poutfile);
	}
	strcpy(sendbuf, "success");
	sendto( sockfd, sendbuf, BUFFER_SIZE, 0 ,(struct sockaddr *)&client_sockaddr, sin_size);
	close(client_fd);
	close(sockfd);
	return 0;
}
因为这里用的是recvfrom,因此将上一篇的打包发送中socket的send修改成sendto即可。还有一点是udp发送的速度很快,这里因为要将数据写进文件中,速度肯定没有接受数据快,因此在打包发送端每发送完一个rtp数据包延迟一段时间才行,这里我延时了10ms。

你可能感兴趣的:(H264 RTP解包)