从H264裸流文件中拆解帧的方法

最近在做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流的相关封装,后续会分享其中的代码.

猜你喜欢

转载自blog.csdn.net/lf960731/article/details/82668734
今日推荐