最近在做H264裸流封装成PS流的工作,首先一个问题是了解H264基本流的构成及拆解一个H264文件.
H.264 的基本流由一系列NALU (Network Abstraction Layer Unit )组成,不同的NALU数据量各不相同。H.264 草案指出,当数据流是储存在介质上时,在每个NALU 前添加起始码:0x000001或0x00000001,用来指示一个NALU 的起始和终止位置。在这样的机制下,在码流中检测起始码,作为一个NALU得起始标识,当检测到下一个起始码时,当前NALU结束。
H.264 码流中每个帧的开头的3~4个字节是H.264 的start_code(起始码),0x00000001或0x000001。3字节的0x000001只有一种场合下使用,就是一个完整的帧被编为多个slice(片)的时候,从第二个slice开始,包含这些slice的NALU 使用3字节起始码。也就是说,如果NALU对应的slice为一帧的开始就用0x00000001,否则就用0x000001。
有了以上知识就可以进行切帧(NALU)的工作了,思想很简单,就是寻找00 00 00 01的起始码,下面贴出代码
#include<stdio.h>
#include <malloc.h>
//得到中间某一帧的数据
void get_one_frame(FILE *fp,unsigned char *pdata,int start_pos,int frame_len)
{
fseek(fp,start_pos,SEEK_SET);
for(int i=0;i<frame_len;i++)
fread(pdata+i,1,1,fp);
}
//得到最后一帧的数据
void get_last_frame(FILE *fp,unsigned char *pdata,int start_pos)
{
fseek(fp,start_pos,SEEK_SET);
for(int i=0;!feof(fp);i++)
fread(pdata+i,1,1,fp);
}
//寻找起始码
int check_frame(FILE *fp,unsigned char *flag)
{
int i;
unsigned char *ch = (unsigned char *)malloc(sizeof(unsigned char));
unsigned char *ch1 = (unsigned char *)malloc(sizeof(unsigned char));
for(i = 0;!feof(fp);i++)
{
fread(ch,1,1,fp);
if((*ch) == 0) //found 0
{
fread(ch,1,1,fp);
i++;
fread(ch1,1,1,fp);
i++;
if((*ch) == 0&&(*ch1) == 0)//found 0 0 0
{
fread(ch,1,1,fp);
i++;
while((*ch) == 0&&!feof(fp)) //found 0 0 0 0 ...
{
fread(ch,1,1,fp);
i++;
}
if(feof(fp))
{
free(ch);
free(ch1);
return -2;
}
if((*ch) == 1) //found 0 0 0 1
{
fread(flag,1,1,fp);
i++;
free(ch);
free(ch1);
return i-4; //返回起始码第一个00的位置
}
}
}
}
free(ch);
free(ch1);
return -1;
}
int main()
{
unsigned char flag;
int position = 0;
int rel_position = 0;
int frame_len = 0;
int start_pos = 0;
int end_pos = 0;
unsigned char *pdata;
int count = 1;
//Data_Info_s* pPacker; //帧信息的结构体
FILE *fp_cut = fopen("test.h264","r");
FILE *fp_read = fopen("test.h264","r");
if(fp_cut)
{
rel_position = check_frame(fp_cut,&flag);
position +=rel_position;
start_pos = position; //帧开始位置为 start_pos
if(flag ==0x65) //标记关键帧
{
//pPacker->IFrame=1;
}
rel_position = check_frame(fp_cut,&flag);
frame_len = rel_position+5; //帧长为frame_len;
position += rel_position+5;
printf("1th frame,frame length:%d\n",frame_len);
pdata=(unsigned char *)malloc(sizeof(unsigned char)*frame_len);
get_one_frame(fp_read,pdata,start_pos,frame_len);
free(pdata);
}
count++;
while(!feof(fp_cut))
{
start_pos = position; //帧开始位置为 上次找到的末尾起始位置
if(flag ==0x65) //标记关键帧
{
//pPacker->IFrame=1;
}
rel_position = check_frame(fp_cut,&flag);
if(rel_position !=-1 &&rel_position !=-2)
{
frame_len = rel_position+5; //帧长为frame_len;
position += rel_position+5;
printf("%dth frame,frame length:%d\n",count,frame_len);
pdata = (unsigned char *)malloc(sizeof(unsigned char)*frame_len);
get_one_frame(fp_read,pdata,start_pos,frame_len);
free(pdata);
}
else //到达末尾
{
printf("%dth frame,frame length:%d\n",count,frame_len);
printf("reaching the end of file\n");
pdata = (unsigned char *)malloc(sizeof(unsigned char)*25000);
get_last_frame(fp_read,pdata,start_pos);
free(pdata);
}
count++;
}
if(feof(fp_cut))
{
fclose(fp_cut);
fclose(fp_read);
}
return 0;
}
测试结果如下:
最近已经完成从RTSP源或者H264文件中获取数据,实现RTP荷载PS流的相关封装,后续会分享其中的代码.