同前篇类似,修改自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。