H264相关代码
H.264格式的视频打包成RTP后进行发送,编译环境为VC6++
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h> #include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")
#include <Windows.h> #define PACKET_BUFFER_END (unsigned int)0x00000000
#define MAX_RTP_PKT_LENGTH 1400
#define DEST_IP "127.0.0.1"
#define DEST_PORT 1234
#define H264 96 FILE *bits = NULL; //!< the bit stream file typedef struct { /**//* byte 0 */
unsigned char csrc_len:; /**//* expect 0 */
unsigned char extension:; /**//* expect 1, see RTP_OP below */
unsigned char padding:; /**//* expect 0 */
unsigned char version:; /**//* expect 2 */ /**//* byte 1 */
unsigned char payload:; /**//* RTP_PAYLOAD_RTSP */
unsigned char marker:; /**//* 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. */ } RTP_FIXED_HEADER; typedef struct { //byte 0
unsigned char TYPE:;
unsigned char NRI:;
unsigned char F:; } NALU_HEADER; /**//* 1 BYTES */ typedef struct {
//byte 0
unsigned char TYPE:;
unsigned char NRI:;
unsigned char F:; } FU_INDICATOR; /**//* 1 BYTES */ typedef struct { //byte 0
unsigned char TYPE:;
unsigned char R:;
unsigned char E:;
unsigned char S:; } FU_HEADER; /**//* 1 BYTES */ typedef struct
{ int startcodeprefix_len; //! 4 for parameter sets and first slice in picture, 3 for everything else (suggested)
unsigned int len; //! Length of the NAL unit (Excluding the start code, which does not belong to the NALU)
unsigned int max_size; //! Nal Unit Buffer size
int forbidden_bit; //! should be always FALSE
int nal_reference_idc; //! NALU_PRIORITY_xxxx
int nal_unit_type; //! NALU_TYPE_xxxx
char *buf; //! contains the first byte followed by the EBSP
unsigned short lost_packets; //! true, if packet loss is detected } NALU_t; //查找开始字符0x000001
static int FindStartCode2 (unsigned char *Buf)
{ if(Buf[]!= || Buf[]!= || Buf[] !=)
return ; //判断是否为0x000001,如果是返回1
else
return ; } //查找开始字符0x00000001
static int FindStartCode3 (unsigned char *Buf)
{ if(Buf[]!= || Buf[]!= || Buf[] != || Buf[] !=)
return ;//判断是否为0x00000001,如果是返回1
else
return ;
} //为NALU_t结构体分配内存空间
NALU_t *AllocNALU(int buffersize) { NALU_t *n;
if ((n = (NALU_t*)calloc (, sizeof (NALU_t))) == NULL)
{
printf("AllocNALU: n");
exit();
} n->max_size=buffersize;
if ((n->buf = (char*)calloc (buffersize, sizeof (char))) == NULL)
{
free (n);
printf ("AllocNALU: n->buf");
exit();
}
return n;
} //释放
void FreeNALU(NALU_t *n)
{
if (n)
{
if (n->buf)
{
free(n->buf);
n->buf=NULL;
}
free (n);
}
} //打开二进制文件串流
void OpenBitstreamFile (char *fn)
{ if (NULL == (bits=fopen(fn, "rb")))
{
printf("open file error\n");
exit();
}
} //这个函数输入为一个NAL结构体,主要功能为得到一个完整的NALU并保存在NALU_t的buf中,获取他的长度,填充F,IDC,TYPE位。
//并且返回两个开始字符之间间隔的字节数,即包含有前缀的NALU的长度
int GetAnnexbNALU (NALU_t *nalu)
{
int pos = ;
int StartCodeFound, rewind;
unsigned char *Buf;
int info2=, info3=;
if ((Buf = (unsigned char*)calloc (nalu->max_size , sizeof(char))) == NULL)
printf ("GetAnnexbNALU: Could not allocate Buf memory\n"); nalu->startcodeprefix_len=;//初始化码流序列的开始字符为3个字节
if ( != fread (Buf, , , bits))//从码流中读3个字节
{
free(Buf);
return ;
}
info2 = FindStartCode2 (Buf);//判断是否为0x000001
if(info2 != ) //如果不是,再读一个字节
{
if( != fread(Buf+, , , bits))//读一个字节
{
free(Buf);
return ;
}
info3 = FindStartCode3 (Buf);//判断是否为0x00000001
if (info3 != )//如果不是,返回-1
{
free(Buf);
return -;
}
else
{ //如果是0x00000001,得到开始前缀为4个字节
pos = ;
nalu->startcodeprefix_len = ;
}
}
else
{ //如果是0x000001,得到开始前缀为3个字节
nalu->startcodeprefix_len = ;
pos = ; } //查找下一个开始字符的标志位
StartCodeFound = ;
info2 = ;
info3 = ;
while (!StartCodeFound)
{
if (feof (bits))//判断是否到了文件尾
{
nalu->len = (pos-)-nalu->startcodeprefix_len;
memcpy (nalu->buf, &Buf[nalu->startcodeprefix_len], nalu->len);
nalu->forbidden_bit = nalu->buf[] & 0x80; //1 bit
nalu->nal_reference_idc = nalu->buf[] & 0x60; // 2 bit
nalu->nal_unit_type = (nalu->buf[]) & 0x1f;// 5 bit
free(Buf);
return pos-;
} Buf[pos++] = fgetc (bits);//读一个字节到BUF中
info3 = FindStartCode3(&Buf[pos-]);//判断是否为0x00000001
if(info3 != )
info2 = FindStartCode2(&Buf[pos-]);//判断是否为0x000001
StartCodeFound = (info2 == || info3 == );
} // Here, we have found another start code (and read length of startcode bytes more than we should
// have. Hence, go back in the file
rewind = (info3 == )? - : -;
if ( != fseek (bits, rewind, SEEK_CUR))//把文件指针指向前一个NALU的末尾
{
free(Buf);
printf("GetAnnexbNALU: Cannot fseek in the bit stream file");
} // Here the Start code, the complete NALU, and the next start code is in the Buf.
// The size of Buf is pos, pos+rewind are the number of bytes excluding the next
// start code, and (pos+rewind)-startcodeprefix_len is the size of the NALU excluding the start code
nalu->len = (pos+rewind)-nalu->startcodeprefix_len; //得到一个NAL单元长度
memcpy (nalu->buf, &Buf[nalu->startcodeprefix_len], nalu->len);//拷贝一个完整NALU,不拷贝起始前缀0x000001或0x00000001
nalu->forbidden_bit = nalu->buf[] & 0x80; //1 bit
nalu->nal_reference_idc = nalu->buf[] & 0x60; // 2 bit
nalu->nal_unit_type = (nalu->buf[]) & 0x1f;// 5 bit
free(Buf);
return (pos+rewind);//返回两个开始字符之间间隔的字节数,即包含有前缀的NALU的长度 } //输出NALU长度和TYPE
void dump(NALU_t *n)
{ if (!n)
return; printf(" len: %d ", n->len);
printf("nal_unit_type: %x\n", n->nal_unit_type); } RTP_FIXED_HEADER *rtp_hdr;
NALU_HEADER *nalu_hdr;
FU_INDICATOR *fu_ind;
FU_HEADER *fu_hdr; int main(int argc, char* argv[])
{
WSADATA Ws;
NALU_t *n;
char* nalu_payload;
char sendbuf[];
unsigned short seq_num =;
int bytes=;
int sockfd;
struct sockaddr_in serv_addr; //int len =sizeof(serv_addr); float framerate=;
unsigned int timestamp_increse=,ts_current=;
timestamp_increse=(unsigned int)(90000.0 / framerate); //+0.5); OpenBitstreamFile("./test.264");//打开264文件,并将文件指针赋给bits,在此修改文件名实现打开别的264文件。 //Init Windows Socket
if ( WSAStartup(MAKEWORD(,), &Ws) != )
{
printf("Init Windows Socket Failed::%d\n",GetLastError());
return -;
} if ((sockfd = socket(AF_INET,SOCK_DGRAM,)) == -)
{
perror("socket");
exit();
} /*设置socketaddr_in结构体中相关参数*/
serv_addr.sin_family=AF_INET;
serv_addr.sin_port=htons(DEST_PORT);
serv_addr.sin_addr.s_addr=inet_addr(DEST_IP); /*调用connect函数主动发起对服务器端的连接*/
if(connect(sockfd,(struct sockaddr *)&serv_addr, sizeof(struct sockaddr))== -)
{
perror("connect");
exit();
} n = AllocNALU();//为结构体nalu_t及其成员buf分配空间。返回值为指向nalu_t存储空间的指针 while(!feof(bits))
{ GetAnnexbNALU(n);//每执行一次,文件的指针指向本次找到的NALU的末尾,下一个位置即为下个NALU的起始码0x000001
dump(n);//输出NALU长度和TYPE memset(sendbuf,,);//清空sendbuf;此时会将上次的时间戳清空,因此需要ts_current来保存上次的时间戳值
//rtp固定包头,为12字节,该句将sendbuf[0]的地址赋给rtp_hdr,以后对rtp_hdr的写入操作将直接写入sendbuf。
rtp_hdr =(RTP_FIXED_HEADER*)&sendbuf[];
//设置RTP HEADER£
rtp_hdr->payload = H264; //负载类型号
rtp_hdr->version = ; //版本号,此版本固定为2
rtp_hdr->marker = ; //标志位,由具体协议规定其值。
rtp_hdr->ssrc = htonl(); //随机指定为10,并且在本RTP会话中全局唯一 // 当一个NALU小于1400字节的时候,采用一个单RTP包发送
if(n->len<=)
{
//设置rtp M 位;
rtp_hdr->marker=;
rtp_hdr->seq_no = htons(seq_num ++); //序列号,每发送一个RTP包增1
//设置NALU HEADER,并将这个HEADER填入sendbuf[12]
nalu_hdr =(NALU_HEADER*)&sendbuf[]; //将sendbuf[12]的地址赋给nalu_hdr,之后对nalu_hdr的写入就将写入sendbuf中;
nalu_hdr->F=n->forbidden_bit;
nalu_hdr->NRI=n->nal_reference_idc>>; //有效数据在n->nal_reference_idc的第6,7位,需要右移5位才能将其值赋给nalu_hdr->NRI。
nalu_hdr->TYPE=n->nal_unit_type; nalu_payload=&sendbuf[]; //同理将sendbuf[13]赋给nalu_payload
memcpy(nalu_payload,n->buf+,n->len-); //去掉nalu头的nalu剩余内容写入sendbuf[13]开始的字符串。
ts_current=ts_current+timestamp_increse;
rtp_hdr->timestamp=htonl(ts_current);
bytes=n->len + ; //获得sendbuf的长度,为nalu的长度(包含NALU头但除去起始前缀)加上rtp_header的固定长度12字节
send( sockfd, sendbuf, bytes, ); //发送rtp包
// Sleep(100); } else if(n->len>)
{
//得到该nalu需要用多少长度为1400字节的RTP包来发送
int k=,l=;
int t=; //用于指示当前发送的是第几个分片RTP包
k=n->len/; //需要k个1400字节的RTP包
l=n->len%; //最后一个RTP包的需要装载的字节数
ts_current=ts_current+timestamp_increse;
rtp_hdr->timestamp=htonl(ts_current);
while(t<=k)
{
rtp_hdr->seq_no = htons(seq_num ++); //序列号,每发送一个RTP包增1
if(!t)//发送一个需要分片的NALU的第一个分片,置FU HEADER的S位
{
//设置rtp M 位;
rtp_hdr->marker=;
//设置FU INDICATOR,并将这个HEADER填入sendbuf[12]
fu_ind =(FU_INDICATOR*)&sendbuf[]; //将sendbuf[12]的地址赋给fu_ind,之后对fu_ind的写入就将写入sendbuf中;
fu_ind->F=n->forbidden_bit;
fu_ind->NRI=n->nal_reference_idc>>;
fu_ind->TYPE=; //设置FU HEADER,并将这个HEADER填入sendbuf[13]
fu_hdr =(FU_HEADER*)&sendbuf[];
fu_hdr->E=;
fu_hdr->R=;
fu_hdr->S=;
fu_hdr->TYPE=n->nal_unit_type; nalu_payload=&sendbuf[]; //同理将sendbuf[14]赋给nalu_payload
memcpy(nalu_payload,n->buf+,); //去掉NALU头 bytes=+; //获得sendbuf的长度,为nalu的长度(除去起始前缀和NALU头)加上rtp_header,fu_ind,fu_hdr的固定长度14字节
send( sockfd, sendbuf, bytes, ); //发送rtp包
t++;
}
//发送一个需要分片的NALU的非第一个分片,清零FU HEADER的S位,如果该分片是该NALU的最后一个分片,置FU HEADER的E位
else if(k==t)//发送的是最后一个分片,注意最后一个分片的长度可能超过1400字节(当l>1386时)。
{ rtp_hdr->marker=; //设置rtp M 位;当前传输的是最后一个分片时该位置1
//设置FU INDICATOR,并将这个HEADER填入sendbuf[12]
fu_ind =(FU_INDICATOR*)&sendbuf[]; //将sendbuf[12]的地址赋给fu_ind,之后对fu_ind的写入就将写入sendbuf中;
fu_ind->F=n->forbidden_bit;
fu_ind->NRI=n->nal_reference_idc>>;
fu_ind->TYPE=; //设置FU HEADER,并将这个HEADER填入sendbuf[13]
fu_hdr =(FU_HEADER*)&sendbuf[];
fu_hdr->R=;
fu_hdr->S=;
fu_hdr->TYPE=n->nal_unit_type;
fu_hdr->E=; nalu_payload=&sendbuf[]; //同理将sendbuf[14]的地址赋给nalu_payload
memcpy(nalu_payload,n->buf+t*+,l-);//将nalu最后剩余的l-1(去掉了一个字节的NALU头)字节内容写入sendbuf[14]开始的字符串。
bytes=l-+; //获得sendbuf的长度,为剩余nalu的长度l-1加上rtp_header,FU_INDICATOR,FU_HEADER三个包头共14字节
send( sockfd, sendbuf, bytes, ); //发送rtp包
t++;
// Sleep(100);
} else if(t<k&&!=t)
{ //设置rtp M 位;
rtp_hdr->marker=;
//设置FU INDICATOR,并将这个HEADER填入sendbuf[12]
fu_ind =(FU_INDICATOR*)&sendbuf[]; //将sendbuf[12]的地址赋给fu_ind,之后对fu_ind的写入就将写入sendbuf中;
fu_ind->F=n->forbidden_bit;
fu_ind->NRI=n->nal_reference_idc>>;
fu_ind->TYPE=; //设置FU HEADER,并将这个HEADER填入sendbuf[13]
fu_hdr =(FU_HEADER*)&sendbuf[];
//fu_hdr->E=0;
fu_hdr->R=;
fu_hdr->S=;
fu_hdr->E=;
fu_hdr->TYPE=n->nal_unit_type; nalu_payload=&sendbuf[]; //同理将sendbuf[14]的地址赋给nalu_payload
memcpy(nalu_payload,n->buf+t*+,); //去掉起始前缀的nalu剩余内容写入sendbuf[14]开始的字符串。
bytes=+; //获得sendbuf的长度,为nalu的长度(除去原NALU头)加上rtp_header,fu_ind,fu_hdr的固定长度14字节
send( sockfd, sendbuf, bytes, ); //发送rtp包
t++;
} } } } send( sockfd, "over",, );//发送结束标志
FreeNALU(n); closesocket(sockfd);
WSACleanup();
return ; }
接收RTP包后,将其保存为H.264格式的视频,编译环境为VC6++
// NALDecoder.cpp : Defines the entry point for the console application.
// #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h> #include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")
#include <Windows.h> #define MAXDATASIZE 1500
#define PORT 1234
#define BUFFER_SIZE 10 FILE * poutfile = NULL;
char * outputfilename = "./receive.264"; 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:; /* expect 0 */
unsigned char extension:; /* expect 1, see RTP_OP below */
unsigned char padding:; /* expect 0 */
unsigned char version:; /* expect 2 */
/* byte 1 */
unsigned char payloadtype:; /* RTP_PAYLOAD_RTSP */
unsigned char marker:; /* 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:;
unsigned char NRI:;
unsigned char F:;
} NALU_HEADER; // 1 BYTE /*
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI| Type |
+---------------+
*/
typedef struct
{
//byte 0
unsigned char TYPE:;
unsigned char NRI:;
unsigned char F:;
} FU_INDICATOR; // 1 BYTE /*
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|S|E|R| Type |
+---------------+
*/
typedef struct
{
//byte 0
unsigned char TYPE:;
unsigned char R:;
unsigned char E:;
unsigned char S:;
} FU_HEADER; // 1 BYTES int OpenBitstreamFile (char *fn)
{
if (NULL == (poutfile = fopen(fn, "wb")))
{
printf("Error: Open input file error\n");
getchar();
}
return ;
} NALU_t *AllocNALU(int buffersize)
{
NALU_t *n; if ((n = (NALU_t*)calloc (, sizeof(NALU_t))) == NULL)
{
printf("AllocNALU Error: Allocate Meory To NALU_t Failed ");
exit();
}
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[];
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 = ; //当前包传出的数据
static int total_recved = ; //一共传输的数据
int fwrite_number = ; //存入文件的数据长度 memcpy(recvbuf,bufIn, len); //复制rtp包
printf("包长度+ rtp头: = %d\n",len); //////////////////////////////////////////////////////////////////////////
//begin rtp_payload and rtp_header p = (RTPpacket_t*)&recvbuf[];
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[];
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())) //为结构体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[]; //网络传输过来的字节序 ,当存入内存还是和文档描述的相反,只要匹配网络字节序和文档描述即可传输正确。
printf("forbidden_zero_bit: %d\n",nalu_hdr->F); //网络传输中的方式为:F->NRI->TYPE.. 内存中存储方式为 TYPE->NRI->F (和nal头匹配)。
n->forbidden_bit= nalu_hdr->F << ; //内存中的字节序。
printf("nal_reference_idc: %d\n",nalu_hdr->NRI);
n->nal_reference_idc = nalu_hdr->NRI << ;
printf("nal 负载类型: %d\n",nalu_hdr->TYPE);
n->nal_unit_type = nalu_hdr->TYPE; //end nal_hdr
//////////////////////////////////////////////////////////////////////////
//开始解包
if ( nalu_hdr->TYPE == )
{
printf("这个包有错误,0无定义\n");
}
else if ( nalu_hdr->TYPE > && nalu_hdr->TYPE < ) //单包
{
printf("当前包为单包\n");
putc(0x00, poutfile);
putc(0x00, poutfile);
putc(0x00, poutfile);
putc(0x01, poutfile); //写进起始字节0x00000001
total_bytes +=;
memcpy(p->payload,&recvbuf[],len-);
p->paylen = len-;
fwrite(nalu_hdr,,,poutfile); //写NAL_HEADER
total_bytes += ;
fwrite_number = fwrite(p->payload,,p->paylen,poutfile); //写NAL数据
total_bytes = p->paylen;
printf("包长度 + nal= %d\n",total_bytes);
}
else if ( nalu_hdr->TYPE == ) //STAP-A 单一时间的组合包
{
printf("当前包为STAP-A\n");
}
else if ( nalu_hdr->TYPE == ) //STAP-B 单一时间的组合包
{
printf("当前包为STAP-B\n");
}
else if (nalu_hdr->TYPE == ) //MTAP16 多个时间的组合包
{
printf("当前包为MTAP16\n");
}
else if ( nalu_hdr->TYPE == ) //MTAP24 多个时间的组合包
{
printf("当前包为MTAP24\n");
}
else if ( nalu_hdr->TYPE == ) //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[]; //分片包用的是FU_INDICATOR而不是NALU_HEADER
printf("FU_INDICATOR->F :%d\n",fu_ind->F);
n->forbidden_bit = fu_ind->F << ;
printf("FU_INDICATOR->NRI :%d\n",fu_ind->NRI);
n->nal_reference_idc = fu_ind->NRI << ;
printf("FU_INDICATOR->TYPE :%d\n",fu_ind->TYPE);
n->nal_unit_type = fu_ind->TYPE; fu_hdr=(FU_HEADER*)&recvbuf[]; //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 == ) //分片包最后一个包
{
printf("当前包为FU-A分片包最后一个包\n");
memcpy(p->payload,&recvbuf[],len - );
p->paylen = len - ;
fwrite_number = fwrite(p->payload,,p->paylen,poutfile); //写NAL数据
total_bytes = p->paylen;
printf("包长度 + FU = %d\n",total_bytes);
}
else if (rtp_hdr->marker == ) //分片包 但不是最后一个包
{
if (fu_hdr->S == ) //分片的第一个包
{
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 += ; F = fu_ind->F << ;
NRI = fu_ind->NRI << ;
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 +=;
memcpy(p->payload,&recvbuf[],len - );
p->paylen = len - ;
fwrite_number = fwrite(p->payload,,p->paylen,poutfile); //写NAL数据
total_bytes = p->paylen;
printf("包长度 + FU_First = %d\n",total_bytes);
}
else //如果不是第一个包
{
printf("当前包为FU-A分片包\n");
memcpy(p->payload,&recvbuf[],len - );
p->paylen= len - ;
fwrite_number = fwrite(p->payload,,p->paylen,poutfile); //写NAL数据
total_bytes = p->paylen;
printf("包长度 + FU = %d\n",total_bytes);
}
}
}
else if ( nalu_hdr->TYPE == ) //FU-B分片包,解码顺序和传输顺序相同
{
if (rtp_hdr->marker == ) //分片包最后一个包
{
printf("当前包为FU-B分片包最后一个包\n"); }
else if (rtp_hdr->marker == ) //分片包 但不是最后一个包
{
printf("当前包为FU-B分片包\n");
}
}
else
{
printf("这个包有错误,30-31 没有定义\n");
}
total_recved += total_bytes;
printf("total_recved = %d\n",total_recved);
memset(recvbuf,,);
free (p->payload);
free (p);
FreeNALU(n);
//结束解包
//////////////////////////////////////////////////////////////////////////
return ;
} int main()
{
WSADATA Ws;
char recvbuf[MAXDATASIZE]; //加上头最大传输数据 1500
SOCKET sockfd, client_fd;
int sin_size;
char sendbuf[BUFFER_SIZE];
struct sockaddr_in server_sockaddr, client_sockaddr; int receive_bytes = ; OpenBitstreamFile(outputfilename); //Init Windows Socket
if ( WSAStartup(MAKEWORD(,), &Ws) != )
{
printf("Init Windows Socket Failed::%d\n",GetLastError());
return -;
} //创建socket
//建立socket链接,数据报socket,IPv4协议
if ((sockfd = socket(AF_INET, SOCK_DGRAM, )) == -)
{
perror("socket");
exit();
}
else
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);
memset(&(server_sockaddr.sin_zero), ,); //填充0以保持与struct sockaddr同样大小 //绑定socket
if (bind(sockfd, (struct sockaddr *) &server_sockaddr,
sizeof(struct sockaddr)) < )
{
perror("ERROR on binding");
exit();
}
else
printf("bind success!\n"); //接收从客户端发来的数据
sin_size = sizeof(struct sockaddr_in);
printf("waiting for client connection...\n");
while((receive_bytes = recvfrom(sockfd, recvbuf, MAXDATASIZE, , (struct sockaddr *)&client_sockaddr, &sin_size)) >)
{
if(strncmp(recvbuf, "over",) == )break; poutfile = fopen(outputfilename,"ab+");
rtp_unpackage(recvbuf,receive_bytes);
fclose(poutfile);
} //sendto( sockfd, sendbuf, BUFFER_SIZE, 0 ,(struct sockaddr *)&client_sockaddr, sin_size); closesocket(client_fd);
closesocket(sockfd);
WSACleanup();
return ;
}
TCP发送端(客户端)发送数据,TCP接收端(服务器端)接收数据,编译环境为VC6++
//Client.c
#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")
#include <Windows.h> #define PORT 4900
#define IP_ADDRESS "127.0.0.1"
#define MAX_PATH 1500 int main(int argc, char* argv[])
{
WSADATA Ws;
SOCKET CientSocket;
struct sockaddr_in ServerAddr;
int Ret = ;
int AddrLen = ;
HANDLE hThread = NULL;
char SendBuffer[MAX_PATH]; //Init Windows Socket
if ( WSAStartup(MAKEWORD(,), &Ws) != )
{
printf("Init Windows Socket Failed::%d\n",GetLastError());
return -;
} ServerAddr.sin_family = AF_INET;
ServerAddr.sin_addr.s_addr = inet_addr(IP_ADDRESS);
ServerAddr.sin_port = htons(PORT);
memset(ServerAddr.sin_zero, 0x00, ); //Create Socket
CientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if ( CientSocket == INVALID_SOCKET )
{
printf("Create Socket Failed::%d\n",GetLastError());
return -;
} //Connect Socket
Ret = connect(CientSocket,(struct sockaddr*)&ServerAddr, sizeof(ServerAddr));
if ( Ret == SOCKET_ERROR )
{
printf("Connect Error::%d\n",GetLastError());
return -;
}
else
{
printf("连接成功,请输入要发送的数据!\n");
}
//进行通信
while ()
{ scanf("%s",SendBuffer);
Ret = send(CientSocket, SendBuffer, (int)strlen(SendBuffer), );
if ( Ret == SOCKET_ERROR )
{ int errorNum=GetLastError();
if (errorNum==||errorNum==)
{
printf("Server close!\n");
} break;
} } closesocket(CientSocket);
WSACleanup(); return ;
}
//Server.c
#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")
#include <Windows.h> #define PORT 4900
#define IP_ADDRESS "127.0.0.1" //IP为客户端的地址,即对方的地址,故有三种形式:127.0.0.1(或本机地址),xxx,xxx,xxx,xxx(对方的地址),或如下所示
#define MAX_PATH 1500 int main(int argc, char* argv[])
{
WSADATA Ws;
SOCKET ServerSocket, CientSocket;
struct sockaddr_in LocalAddr, ClientAddr;
int Ret = ;
int AddrLen = ;
HANDLE hThread = NULL;
AddrLen = sizeof(ClientAddr); //Init Windows Socket
if ( WSAStartup(MAKEWORD(,), &Ws) != )
{
printf("Init Windows Socket Failed::%d\n",GetLastError());
return -;
} LocalAddr.sin_family = AF_INET;
// LocalAddr.sin_addr.s_addr = inet_addr(IP_ADDRESS);
LocalAddr.sin_addr.s_addr =INADDR_ANY;
LocalAddr.sin_port = htons(PORT);
memset(LocalAddr.sin_zero, 0x00, ); //Create Socket
ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if ( ServerSocket == INVALID_SOCKET )
{
printf("Create Socket Failed::%d\n",GetLastError()); return -;
} //Bind Socket
Ret = bind(ServerSocket, (struct sockaddr*)&LocalAddr, sizeof(LocalAddr));
if ( Ret != )
{ printf("Bind Socket Failed::%d\n",GetLastError());
return -;
} //Listen Socket
Ret = listen(ServerSocket, );
if ( Ret != )
{ printf("listen Socket Failed::%d\n",GetLastError());
return -;
} //Accept Socket
CientSocket = accept(ServerSocket, (struct sockaddr*)&ClientAddr, &AddrLen);
if ( CientSocket == INVALID_SOCKET )
{ printf("Accept Failed::%d\n",GetLastError()); }
else
printf("服务端已经启动,准备接收数据\n"); printf("客户端连接::%d:%d\n",inet_ntoa(ClientAddr.sin_addr),ClientAddr.sin_port); //开始通信
while ( )
{ int Ret = ;
char RecvBuffer[MAX_PATH]; //while ( 1)
{
memset(RecvBuffer, 0x00, sizeof(RecvBuffer));
Ret = recv(CientSocket, RecvBuffer, MAX_PATH, );
if ( Ret == || Ret == SOCKET_ERROR )
{
printf("客户端退出!\n");
break;
}
if(strncmp(RecvBuffer, "q",) == )break; printf("接收到客户信息为:%s\n",RecvBuffer);
} } closesocket(ServerSocket);
closesocket(CientSocket);
WSACleanup(); return ;
}
UDP通信的示例,编译环境为VC6++
// UdpClient.cpp : Defines the entry point for the console application.
//
#include <stdio.h>
#include <stdlib.h>
//使用winsock所需的头文件
#include <winsock2.h>
//使用winsock所需的库文件
#pragma comment(lib,"ws2_32.lib")
#include <Windows.h> #define SOCK_VER 2 //程序使用的winsock主版本 //变量和函数的声明
SOCKET g_sock = ; //套接字声明
void ErrMsg(DWORD dwErr);//错误信息打印 int main(int argc, char* argv[])
{ struct sockaddr_in addr = {};
char szBuff[] = "hello UDP!";
char *p=szBuff;
int nSent= ;
struct sockaddr_in saServer={};
int nFromLen=sizeof(saServer); int nRecv= ; //初始化WinSock环境
WSADATA wd = {};
int nStart = WSAStartup(MAKEWORD(SOCK_VER,),&wd);
if ( != nStart)
{
return ;
} if (LOBYTE(wd.wVersion) != )
{
return ;
} //创建一个UDP SOCKET g_sock = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
if (INVALID_SOCKET == g_sock)
{
ErrMsg(WSAGetLastError());
return ;
} printf("socket 创建成功!!\n"); //发送数据的目标地址 addr.sin_family = AF_INET;//IPV4
addr.sin_port = htons();//端口
addr.sin_addr.s_addr = inet_addr("127.0.0.1");//IP
memset(addr.sin_zero, 0x00, ); //在UDP中通过调用connect函数,在数据通信中,就可以用send和recv进行发送或接收数据
/*
if(connect(g_sock,(struct sockaddr_in*)&addr,sizeof(addr))!=0)
{exit(0);}
nSent=send(g_sock,p,strlen(szBuff)+1,0);*/ //发送数据包
nSent = sendto(g_sock,p,strlen(szBuff)+,,(struct sockaddr* )&addr,sizeof(struct sockaddr_in)); if ( == nSent)
{
ErrMsg(WSAGetLastError());
}
else
{
printf("信息成功发送,等待回应。。\n");
} //等待回应
szBuff[] = '\0';//清空缓冲区 nRecv = recvfrom(g_sock,p,,,(struct sockaddr *)&saServer,&nFromLen);
//nRecv = recv(g_sock,p,256,0);
if (SOCKET_ERROR == nRecv)
{
ErrMsg(WSAGetLastError()); }
else
{
printf("收到回应:%s从%s,%d\n",szBuff,inet_ntoa(saServer.sin_addr),ntohs(saServer.sin_port)); } //关闭连接套接字
closesocket(g_sock);
system("pause"); //清理套接字环境
WSACleanup(); return ;
} //打印错误信息函数
void ErrMsg(DWORD dwErr)
{
char szErr[] = {};
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,NULL,dwErr,MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),szErr,,NULL);
printf("%s且错误码为:%d\n",szErr,GetLastError());
}
// UdpServer.cpp : Defines the entry point for the console application.
// #include <stdio.h>
#include <stdlib.h>
//使用winsock所需的头文件
#include <winsock2.h>
//使用winsock所需的库文件
#pragma comment(lib,"ws2_32.lib")
#include <Windows.h>
//程序中要使用的winsock的主版本
#define SOCK_VER 2 //定义变量和函数
SOCKET g_sock = ;
void ErrMsg(DWORD dwErr); int main(int argc, char* argv[])
{
struct sockaddr_in addr;
int nLen = sizeof(addr);
int nBind;
struct sockaddr_in saClient = {};
int nFromLen = sizeof(saClient);
char szBuff[] = {};
int nSent; //初始化环境
WSADATA wd = {};
int nStart = WSAStartup(MAKEWORD(SOCK_VER,),&wd);//函数成功返回0,失败返回错误代码
if ( != nStart)
{//错误处理
return ;
} if ( != LOBYTE(wd.wVersion))
{
return ;
} //创建socket套接字
g_sock = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
if (INVALID_SOCKET == g_sock)
{
ErrMsg(WSAGetLastError());
return ;
} printf("socket 创建成功!!\n"); //绑定 addr.sin_family = AF_INET;
addr.sin_port = htons();
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
memset(addr.sin_zero, 0x00, ); nBind = bind(g_sock,(struct sockaddr *)&addr,sizeof(addr));//成功返回0
if ( != nBind)
{
ErrMsg(WSAGetLastError());
return ;
} //获得已经绑定的端口号 getsockname(g_sock,(struct sockaddr *)&addr,&nLen); printf("socket 成功绑定到端口:%d,等待数据。。。\n",ntohs(addr.sin_port)); //等待并接收数据 recvfrom(g_sock,szBuff,,,(struct sockaddr *)&saClient,&nFromLen);
printf("收到的信息:%s,从%s,%d \n",szBuff,inet_ntoa(saClient.sin_addr),ntohs(saClient.sin_port)); //向客户端发送回应
strcpy(szBuff,"OK!");
nSent = sendto(g_sock,szBuff,strlen(szBuff)+,,(struct sockaddr *)&saClient,sizeof(saClient)); if ( == nSent)
{
ErrMsg(WSAGetLastError());
}
else
{
printf("成功发出回应!!\n"); } //关闭资源
closesocket(g_sock);//关闭套接字
system("pause"); WSACleanup();//清理环境 return ;
} //打印错误信息函数
void ErrMsg(DWORD dwErr)
{
char szErr[] = {};
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,NULL,dwErr,MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),szErr,,NULL);
printf(szErr); }
H264相关代码的更多相关文章
- [ARM] Cortex-M Startup.s启动文件相关代码解释
1. 定义一个段名为CSTACK, 这里: NOROOT表示如何定义的段没有被关联,那么同意会被优化掉,如果不想被优化掉就使用ROOT. 后面的括号里数字表示如下: (1):这个段是2的1次方即2字节 ...
- Kafka Producer相关代码分析【转】
来源:https://www.zybuluo.com/jewes/note/63925 @jewes 2015-01-17 20:36 字数 1967 阅读 1093 Kafka Producer相关 ...
- 命令行方式使用abator.jar生成ibatis相关代码和sql语句xml文件
最近接手一个老项目,使用的是数据库是sql server 2008,框架是springmvc + spring + ibatis,老项目是使用abator插件生成的相关代码,现在需要增加新功能,要添加 ...
- myBatis自动生成相关代码文件配置(Maven)
pom.xml文件添加配置 <build> <finalName>generator</finalName> <plugins> <!-- mav ...
- 临时2级页表的初始化过程 head_32.S 相关代码解释
page_pde_offset = (__PAGE_OFFSET >> 20); /* __PAGE_OFFSET是0xc0000000,page_pde_offset = 3072 = ...
- 使用Mybatis Generator自动生成Mybatis相关代码
本文将简要介绍怎样利用Mybatis Generator自动生成Mybatis的相关代码: 一.构建一个环境: 1. 首先创建一个表: CREATE TABLE pet (name VARCHAR(2 ...
- K:树、二叉树与森林之间的转换及其相关代码实现
相关介绍: 二叉树是树的一种特殊形态,在二叉树中一个节点至多有左.右两个子节点,而在树中一个节点可以包含任意数目的子节点,对于森林,其是多棵树所组成的一个整体,树与树之间彼此相互独立,互不干扰,但其 ...
- js 横屏 竖屏 相关代码 与知识点
<!DOCTYPE html> <html> <head> <title></title> </head> <body&g ...
- 转:关于Latent Dirichlet Allocation及Hierarchical LDA模型的必读文章和相关代码
关于Latent Dirichlet Allocation及Hierarchical LDA模型的必读文章和相关代码 转: http://andyliuxs.iteye.com/blog/105174 ...
随机推荐
- Advanced Installer
Advanced Installer是一款功能强大.可生成符合 MS Windows 认证的 Windows Installer 的 MSI 安装包制作工具,具有友好的图形用户界面,直观而且非常简单的 ...
- Python 中使用optparse进行参数解析
使用过Linux/Unix的人都会知道,命令行下的很多命令都需要参数,在C语言中添加和解析参数比较繁琐.Python中提供了optparse模块可以非常方便地处理命令行参数. 1 命令行参数的样 ...
- sql 联合查询速度慢,需要对其进行分组
分组:SELECT * FROM(SELECT ROW_NUMBER() OVER(ORDER BY ProjID) as row_number,* FROM ( select ProjAppl ...
- Source Insight新建工程文件
options->document options ->document type ->c source file 下 //添加 “.S”结尾的汇编语言支持 project -& ...
- Linux(CentOS)安装rar和unrar以及rar和unrar命令的使用
可以参考此篇博文. http://www.cnblogs.com/linjiqin/archive/2013/03/24/2979736.html 不过我按照其步骤手动安装Linux的rar文件执 ...
- 浅谈Oracle 性能优化
基于大型Oracle数据库应用开发已有6个年头了,经历了从最初零数据演变到目前上亿级的数据存储.在这个经历中,遇到各种各样的性能问题及各种性能优化. 在这里主要给大家分享一下数据库性能优化的一些方法和 ...
- NGUI的原理机制:深入剖析UIPanel,UIWidget,UIDrawCall底层原理
这是我去搜狐畅游面试时,面试官问的一个问题.问NGUI的机制原理是什么?就是这个插件是根据什么写出来的.当时没答上来,下面是我从转载过来的,可以研究研究. 之前项目中用的NGUI的版本是3.0.7 f ...
- Ajax概述
- jFinal中报对应模型不存在的错误(The Table mapping of model: demo.User not exists)
jFinal中报对应模型不存在的错误(The Table mapping of model: demo.User not exists) 贴出错误: java.lang.RuntimeExceptio ...
- OC9_文件操作
// // main.m // OC9_文件操作 // // Created by zhangxueming on 15/6/19. // Copyright (c) 2015年 zhangxuemi ...