您的位置:首页 > 其它

以读文件的方式解析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;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  h264 NALU 裸码 解析