以读文件的方式解析H264裸码流
2017-12-25 16:20
435 查看
H264 NALU类型
标识NAL单元中的RBSP数据类型,其中,nal_unit_type为1, 2, 3, 4, 5及12的NAL单元称为VCL的NAL单元,其他类型的NAL单元为非VCL的NAL单元。0:未规定 1:非IDR图像中不采用数据划分的片段 2:非IDR图像中A类数据划分片段 3:非IDR图像中B类数据划分片段 4:非IDR图像中C类数据划分片段 5:IDR图像的片段 6:补充增强信息 (SEI) 7:序列参数集 8:图像参数集 9:分割符 10:序列结束符 11:流结束符 12:填充数据 13 – 23:保留 24 – 31:未规定
思路
思路是将每次填入的这帧数据提取出来,然后通过RTP实时发送。*cam >> frame_in; if(frame_in.empty()){ cout<<"frame_in blank frame"<<endl; return false; } int start_pos = get_fileSize(name); writer.write(frame_in); /* 将这一帧数据取出来放到buf里 */ /*思路是将每次填入的这帧数据提取出来,然后通过RTP实时发送*/ int end_pos = get_fileSize(name); file=fopen(name.c_str(), "rb+"); fseek(file, start_pos, SEEK_SET); if(end_pos-start_pos>0){ fread (buf, 1, end_pos-start_pos, file); transmitter->sendFrame(buf, end_pos-start_pos); }
sendFrame
void imageTransmitter :: sendFrame(unsigned char* buf,int len){ NALU_HEADER* nalu_hdr; FU_INDICATOR* fu_ind; FU_HEADER* fu_hdr; char* nalu_payload; unsigned char rltBuf[800000]; char sendbuf[1500]; int rlt = GetAnNALU(buf,len,rltBuf,n); if(rlt<0){ printf("get null error!\n"); return; } /* 输出NALU长度和TYPE */ dump(n); /*当一个NALU小于MAX_RTP_PKT_LENGTH字节的时候,采用一个单RTP包发送 */ if(n->len<=MAX_RTP_PKT_LENGTH){ nalu_hdr =(NALU_HEADER*)&sendbuf[0]; nalu_hdr->F=n->forbidden_bit; nalu_hdr->NRI=n->nal_reference_idc>>5; nalu_hdr->TYPE=n->nal_unit_type; nalu_payload=&sendbuf[1]; memcpy(nalu_payload,n->buf+1,n->len-1);//去掉nalu头的nalu剩余内容写入sendbuf[13]开始的字符串。 this->sendPacket(sendbuf,n->len); }else if(n->len>MAX_RTP_PKT_LENGTH){ int k=0,l=0; k=n->len/MAX_RTP_PKT_LENGTH;//需要k个MAX_RTP_PKT_LENGTH字节的RTP包 l=n->len%MAX_RTP_PKT_LENGTH;//最后一个RTP包的需要装载的字节数 int t=0;//用于指示当前发送的是第几个分片RTP包 while(t<=k){ if(!t)//发送一个需要分片的NALU的第一个分片,置FU HEADER的S位 { memset(sendbuf,0,1500); fu_ind =(FU_INDICATOR*)&sendbuf[0]; fu_ind->F=n->forbidden_bit; fu_ind->NRI=n->nal_reference_idc>>5; fu_ind->TYPE=28; fu_hdr =(FU_HEADER*)&sendbuf[1]; fu_hdr->E=0; fu_hdr->R=0; fu_hdr->S=1; fu_hdr->TYPE=n->nal_unit_type; nalu_payload=&sendbuf[2]; memcpy(nalu_payload,n->buf+1,MAX_RTP_PKT_LENGTH);//去掉NALU头 this->sendPacket(sendbuf,MAX_RTP_PKT_LENGTH+2); t++; }else if(k==t)//发送的是最后一个分片,注意最后一个分片的长度可能超过 { memset(sendbuf,0,1500); fu_ind =(FU_INDICATOR*)&sendbuf[0]; fu_ind->F=n->forbidden_bit; fu_ind->NRI=n->nal_reference_idc>>5; fu_ind->TYPE=28; fu_hdr =(FU_HEADER*)&sendbuf[1]; fu_hdr->R=0; fu_hdr->S=0; fu_hdr->E=1; fu_hdr->TYPE=n->nal_unit_type; nalu_payload=&sendbuf[2]; memcpy(nalu_payload,n->buf+t*MAX_RTP_PKT_LENGTH+1,l-1);//将nalu最后剩余的 this->sendPacket(sendbuf,l+1); t++; }else if(t<k&&0!=t){ memset(sendbuf,0,1500); fu_ind =(FU_INDICATOR*)&sendbuf[0]; fu_ind->F=n->forbidden_bit; fu_ind->NRI=n->nal_reference_idc>>5; fu_ind->TYPE=28; fu_hdr =(FU_HEADER*)&sendbuf[1]; fu_hdr->R=0; fu_hdr->S=0; fu_hdr->E=0; fu_hdr->TYPE=n->nal_unit_type; nalu_payload=&sendbuf[2]; memcpy(nalu_payload,n->buf+t*MAX_RTP_PKT_LENGTH+1,MAX_RTP_PKT_LENGTH); this->sendPacket(sendbuf,MAX_RTP_PKT_LENGTH+2); t++; } } } if(rlt>0){ sendFrame(rltBuf, len-rlt); } }
GetAnNALU
/*主要功能为得到一个完整的NALU并保存在NALU_t的buf中,获取他的长度,填充F,IDC,TYPE位。 * 并判断这个buf中是否还存在其他的NALU单元。 * parms: * buf:为一帧原始码流 * len:原始码流长度, * retBuf: 如果有多个NALU,则将已经获取的第一个NALU去掉,其余部分存储在retBuf中 * nalu: 获取的一个NALU * 返回值:-1没有获取的NALU,0获取到NALU并且只有一个,大于0说明不止一个NALU,并且值代表被取出的NALU的长度包含前缀 */ int GetAnNALU(unsigned char* buf,int len, unsigned char* retBuf, NALU_t *nalu){ int pos = 0; int StartCodeFound; if(len<4){ return -1; } nalu->startcodeprefix_len=3;//初始化码流序列的开始字符为3个字节 info2 = FindStartCode2 (buf);//判断是否为0x000001 if(info2 != 1) { info3 = FindStartCode3 (buf);//判断是否为0x00000001 if (info3 != 1)//如果不是,返回-1 { return -1; } else { //如果是0x00000001,得到开始前缀为4个字节 pos = 4; nalu->startcodeprefix_len = 4; } } else { //如果是0x000001,得到开始前缀为3个字节 pos = 3; nalu->startcodeprefix_len = 3; } //查找下一个开始字符的标志位 StartCodeFound = 0; info2 = 0; info3 = 0; while (!StartCodeFound) { if(pos >= len-1){ break; } pos++; info3 = FindStartCode3(&buf[pos-4]);//判断是否为0x00000001 if(info3 != 1) info2 = FindStartCode2(&buf[pos-3]);//判断是否为0x000001 StartCodeFound = (info2 == 1 || info3 == 1); } if(!StartCodeFound){ nalu->len = (len)-nalu->startcodeprefix_len; }else{ if(info2 == 1){ pos = pos - 3; }else{ pos = pos - 4; } nalu->len = pos-nalu->startcodeprefix_len; } memcpy (nalu->buf, &buf[nalu->startcodeprefix_len], nalu->len);//拷贝一个完整NALU,不拷贝起始前缀0x000001或0x00000001 nalu->forbidden_bit = nalu->buf[0] & 0x80; //1 bit nalu->nal_reference_idc = nalu->buf[0] & 0x60; // 2 bit nalu->nal_unit_type = (nalu->buf[0]) & 0x1f;// 5 bit if(!StartCodeFound){ return 0; }else{ memcpy(retBuf, buf+pos, len-pos); return pos; } }
相关文章推荐
- 使用PULL方式解析XML资源文件下面的xml文件
- 用递归方式解析XML的任意文件
- Android数据解析:markdown文件的解析方式
- DOM方式解析XML配置文件,将数据保存成数组
- Windows快捷方式文件格式解析
- struts2文件上传的采用的三种方式解析
- java通过ftp方式读取文件,并解析入库
- 用HttpListener做web服务器,简单解析post方式过来的参数、上传的文件
- Java之xml文件解析三(JDOM方式解析xml文件)
- 多重方式解析二进制文件
- 用dom4j的方式解析和生成xml文件
- Java解析word, ppt , execl 等文件的方式
- android XML 文件解析~SAX方式!
- Android基础 基本的xml文件解析的方式
- JAVA 解析excel文件 poi方式
- xml文件--DOM方式解析xml
- 【慕课笔记】2-3 应用DOM方式解析XML—使用 DOM 解析XML 文件的节点名和节点值
- 【转】Windows快捷方式文件格式解析(中文)
- XFire对其配置文件的解析方式
- XML文件解析(DOM方式)