c/c++语言开发共享h264文件分析(纯c解析代码)

参考链接:1. 解析H264的SPS信息 https://blog.csdn.net/lizhijian21/article/details/80982403 2. h.264的POC计算 https://www.cnblogs.com/TaigaCon/p/3551001.html 3. 视音频数 …

参考链接:1. 解析h264的sps信息 https://blog.csdn.net/lizhijian21/article/details/80982403
               2. h.264的poc计算 https://www.cnblogs.com/taigacon/p/3551001.html
               3. 视音频数据处理入门:h.264视频码流解析 https://blog.csdn.net/leixiaohua1020/article/details/50534369

代码中的注释, 有对sps,pps,slice的分析,未进行代码分析(有些可能不准确)。

h264文件分析(纯c解析代码)

  1 #include <stdio.h>    2 #include <stdlib.h>    3 #include <string.h>    4 #include <arpa/inet.h>    5     6 #define tab44 "    "    7 #define printf_debug    8     9 #define prtntf_str_len 10   10    11 /************************************************************************************************************   12 **                                        nalu header: 负责将vcl产生的比特字符串适配到各种各样的网络和多元环境中,    13                                                        覆盖了所有片级以上的语法级别(nalu的作用, 方便网络传输)   14 **   15 -------------------------------------------------------------------------------------------------------------   16 **        字段名称               |    长度(bits)    |        有关描述   17 -------------------------------------------------------------------------------------------------------------   18 **        forbidden_bit          |    1             |        编码中默认值为0, 当网络识别此单元中存在比特错误时, 可将其设为1, 以便接收方丢掉该单元   19 **        nal_reference_idc      |    2             |        0~3标识这个nalu的重要级别   20 **        nal_unit_type          |    5             |          nalu的类型(类型1~12是h.264定义的, 类型24~31是用于h.264以外的,    21                                                              rtp负荷规范使用这其中的一些值来定义包聚合和分裂, 其他值为h.264保留)   22    23 ** nal_unit_type:   24     0                未使用   25     1                未使用data partitioning, 非idr图像的slice   26     2                使用data partitioning且为slice a   27     3                使用data partitioning且为slice b   28     4                使用data partitioning且为slice c   29     5                idr图像的slice(立即刷新)   30     6                补充增强信息(sei)   31     7                序列参数集(sequence parameter set, sps)   32     8                图像参数集(picture parameter set, pps)   33     9                分界符   34     10                序列结束   35     11                码流结束   36     12                填充   37     13...23            保留   38     24...31            未使用   39        40 ** sps, pps. slice等信息就不解析了. 为了减少bits, 用了哥伦布编码(自己解析比较麻烦, 但是网上有很多).   41    42 ** sps信息说明:   43         1. 视频宽高, 帧率等信息;   44         2. seq_parameter_set_id, 指明本序列参数集的id号, 这个id号将被picture参数集引用;   45         3. pic_width_in_mbs_minus1, 加1指定以宏块(16*16)为单位的每个解码图像的宽度, 即width = (pic_width_in_mbs_minus1 + 1) * 16   46         4. pic_height_in_map_units_minus1;   47         5. pic_order_cnt_type, 视频的播放顺序序号叫做poc(picture order count), 取值0,1,2;   48         6. time_scale, fixed_frame_rate_flag, 计算帧率(fps).   49            视频帧率信息在sps的vui parameters syntax中, 需要根据time_scale, fixed_frame_rate_flag计算得到: fps = time_scale / num_units_in_tick.   50            但是需要判断参数timing_info_present_flag是否存在, 若不存在表示fps在信息流中无法获取.   51            同时还存在另外一种情况: fixed_frame_rate_flag为1时, 两个连续图像的hdr输出时间频率为单位, 获取的fps是实际的2倍.   52    53 ** pps信息说明:       54         1. pic_parameter_set_id, 用以指定本参数集的序号, 该序号在各片的片头被引用;   55         2. seq_parameter_set_id, 指明本图像参数集所引用的序列参数集的序号;   56         3. 其他高深的暂时还不理解, 指明参考帧队列等.   57            58 ** slice信息说明:   59         1. slice_type, 片的类型;   60         2. pic_parameter_set_id, 引用的图像索引;   61         3. frame_num, 每个参考帧都有一个连续的frame_num作为它们的标识, 它指明了各图像的解码顺序. 非参考帧也有,但没有意义;   62         4. least significant bits;   63         5. 综合三种poc(pic_order_cnt_type), 类型2应该是最省bit的, 因为直接从frame_num获得, 但是序列方式限制最大;   64            类型1, 只需要一定的bit量在sps标志出一些信息还在slice header中表示poc的变化, 但是比类型0要节省bit, 但是其序列并不是随意的, 要周期变化;   65            对于类型0因为要对poc的lsb(pic_order_cnt_lsb, last bit)进行编码所以用到的bit最多, 优点是序列可以随意.   66            ** 自我理解, 不一定准确(这边算显示顺序, 要根据sps中的pic_order_cnt_type, 为2, 意味着码流中没有b帧, frame_num即为显示顺序;   67               为1, 依赖frame_num求解poc; 为0, 把poc的低位编进码流内, 但这只是低位, 而poc的高位picordercntmsb则要求解码器自行计数,   68               计数方式依赖于前一编码帧(prevpicordercntmsb与prevpicordercntlsb.   69                  70            ** 一般的码流分析所见(未仔细证实): pic_order_cnt_type=2, 只有frame_num(无b帧);   71               pic_order_cnt_type=1, 暂未分析到;   72               pic_order_cnt_type=0, pic_order_cnt_lsb指示显示顺序, 一般为偶数增长(0, 2, 4, 6, 据说是什么场方式和帧方式, 场时其实是0 0 2 2 4 4).   73                  74            ** 编码与显示的原因: 视频编码顺序与视频的播放顺序, 并不完全相同, 视频编码时, 如果采用了b帧编码, 由于b帧很多时候都是双向预测得来的,   75               这时会先编码b帧的后向预测图像(p帧), 然后再进行b帧编码, 因此会把视频原来的播放顺序打乱, 以新的编码顺序输出码流,   76               而在解码断接收到码流后, 需要把顺序还原成原本的播放顺序, 以输出正确的视频. 在编解码中, 视频的播放顺序序号叫做poc(picture order count).   77                  78 ** 总结: 1. 码流中有很多sps(序列), 一个序列中有多个图像, 一个图像中有多个片, 一个片中有多个块;   79          2. sps中有seq_parameter_set_id. pps中有pic_parameter_set_id, 并通过seq_parameter_set_id指明关联的序列.   80             slice中有pic_parameter_set_id, 指明关联的图像;   81          3. sps中可计算宽高以及帧率, pic_order_cnt_type(显示顺序的类型);   82             slice header中可算出解码的顺序, 以及根据pic_order_cnt_type算出显示顺序.               83 ************************************************************************************************************/   84 typedef enum e_h264_nalu_priority   85 {   86     nalu_priority_disposable = 0,   87     nalu_priority_low         = 1,   88     nalu_priority_high       = 2,   89     nalu_priority_highest    = 3,   90 } e_h264_nalu_priority;   91    92 typedef enum e_h264_nalu_type   93 {   94     nalu_type_slice    = 1,   95     nalu_type_dpa      = 2,   96     nalu_type_dpb      = 3,   97     nalu_type_dpc      = 4,   98     nalu_type_idr      = 5,   99     nalu_type_sei      = 6,  100     nalu_type_sps      = 7,  101     nalu_type_pps      = 8,  102     nalu_type_aud      = 9,  103     nalu_type_eoseq    = 10,  104     nalu_type_eostream = 11,  105     nalu_type_fill     = 12,  106 } e_h264_nalu_type;  107   108 typedef struct t_h264_nalu_header  109 {  110     unsigned char forbidden_bit:1, nal_reference_idc:2, nal_unit_type:5;  111 } t_h264_nalu_header;  112   113 typedef struct t_h264_nalu  114 {  115     int startcodelen;  116       117     t_h264_nalu_header h264naluheader;  118       119     unsigned int bodylen;  120       121     unsigned char *bodydata;  122 } t_h264_nalu;  123   124 /**********************************************************************************  125  1. h264的起始码: 0x000001(3 bytes)或0x00000001(4 bytes);  126  2. 文件流中用起始码来区分nalu.  127 ***********************************************************************************/  128 static int findstartcode3bytes(unsigned char *scdata)  129 {  130     int isfind = 0;  131   132     if ((0==scdata[0]) && (0==scdata[1]) && (1==scdata[2]))  133     {  134         isfind = 1;  135     }  136       137     return isfind;  138 }  139   140 static int findstartcode4bytes(unsigned char *scdata)  141 {  142     int isfind = 0;  143   144     if ((0==scdata[0]) && (0==scdata[1]) && (0==scdata[2]) && (1 == scdata[3]))  145     {  146         isfind = 1;  147     }  148       149     return isfind;  150 }  151   152 static int getnaludatalen(int startpos, int h264bitssize, unsigned char *h264bits)  153 {  154     int parsepos = 0;  155       156     parsepos = startpos;  157       158     while (parsepos < h264bitssize)  159     {  160         if (findstartcode3bytes(&h264bits[parsepos]))  161         {  162             return parsepos - startpos;  163         }  164         else if (findstartcode4bytes(&h264bits[parsepos]))  165         {  166             return parsepos - startpos;  167         }  168         else  169         {  170             parsepos++;  171         }  172     }  173       174     return parsepos - startpos; // if file is end  175 }  176   177 static void parsenaludata(const unsigned int nalulen, unsigned char* const nuludata)  178 {  179     static int nalunum = 0;  180       181     unsigned char *data = null;  182     unsigned char prioritystr[prtntf_str_len+1] = {0};  183     unsigned char typestr[prtntf_str_len+1] = {0};  184       185     t_h264_nalu_header h264naluheader = {0};  186       187     data = nuludata;  188       189     memset(&h264naluheader, 0x0, sizeof(t_h264_nalu_header));  190       191     h264naluheader.nal_reference_idc = data[0]>>5 & 0x3;  192     h264naluheader.nal_unit_type = data[0] & 0x1f;  193       194     nalunum++;  195       196 #ifdef printf_debug  197     switch (h264naluheader.nal_reference_idc)  198     {  199         case nalu_priority_disposable:  200             sprintf(prioritystr, "dispos");  201             break;  202               203         case nalu_priority_low:  204             sprintf(prioritystr, "low");  205             break;  206   207         case nalu_priority_high:  208             sprintf(prioritystr, "high");  209             break;  210   211         case nalu_priority_highest:  212             sprintf(prioritystr, "highest");  213             break;  214   215         default:  216             break;  217     }  218       219     switch (h264naluheader.nal_unit_type)  220     {  221         case nalu_type_slice:  222             sprintf(typestr,"slice");  223             break;  224               225         case nalu_type_dpa:  226             sprintf(typestr,"dpa");  227             break;  228               229         case nalu_type_dpb:  230             sprintf(typestr,"dpb");  231             break;  232               233         case nalu_type_dpc:  234             sprintf(typestr,"dpc");  235             break;  236               237         case nalu_type_idr:  238             sprintf(typestr,"idr");  239             break;  240               241         case nalu_type_sei:  242             sprintf(typestr,"sei");  243             break;  244               245         case nalu_type_sps:  246             sprintf(typestr,"sps");  247             break;  248               249         case nalu_type_pps:  250             sprintf(typestr,"pps");  251             break;  252               253         case nalu_type_aud:  254             sprintf(typestr,"aud");  255             break;  256               257         case nalu_type_eoseq:  258             sprintf(typestr,"eoseq");  259             break;  260               261         case nalu_type_eostream:  262             sprintf(typestr, "eostream");  263             break;  264               265         case nalu_type_fill:  266             sprintf(typestr, "fill");  267             break;  268           269         default:  270             break;  271     }  272       273     printf("%5d| %7s| %6s| %8d|n",nalunum,prioritystr,typestr,nalulen);  274 #endif  275       276 }  277   278 int main(int argc, char *argv[])  279 {  280     int filelen = 0;  281     int nalulen = 0;  282     int h264bitspos = 0;  283   284     unsigned char *h264bits = null;  285     unsigned char *naludata = null;  286       287     file *fp = null;  288       289     if (2 != argc)  290     {  291         printf("usage: flvparse **.flvn");  292   293         return -1;  294     }  295   296     fp = fopen(argv[1], "rb");  297     if (!fp)  298     {  299         printf("open file[%s] error!n", argv[1]);  300   301         return -1;  302     }  303       304     fseek(fp, 0, seek_end);  305       306     filelen = ftell(fp);  307       308     fseek(fp, 0, seek_set);  309       310     h264bits = (unsigned char*)malloc(filelen);  311     if (!h264bits)  312     {  313         printf("maybe file is too long, or memery is not enough!n");  314           315         fclose(fp);  316       317         return -1;  318     }  319       320     memset(h264bits, 0x0, filelen);  321       322     if (fread(h264bits, 1, filelen, fp) < 0)  323     {  324         printf("read file data to h264bits error!n");  325           326         fclose(fp);  327         free(h264bits);  328           329         h264bits = null;  330           331         return -1;  332     }  333       334     fclose(fp);  335       336     printf("-----+-------- nalu table ------+n");  337     printf(" num |    idc |  type |   len   |n");  338     printf("-----+--------+-------+---------+n");  339   340     while (h264bitspos < (filelen-4))  341     {  342         if (findstartcode3bytes(&h264bits[h264bitspos]))  343         {  344             nalulen = getnaludatalen(h264bitspos+3, filelen, h264bits);  345   346             naludata = (unsigned char*)malloc(nalulen);  347             if (naludata)  348             {  349                 memset(naludata, 0x0, nalulen);  350                   351                 memcpy(naludata, h264bits+h264bitspos+3, nalulen);  352                   353                 parsenaludata(nalulen, naludata);  354                   355                 free(naludata);  356                 naludata = null;  357             }  358               359             h264bitspos += (nalulen+3);  360         }  361         else if (findstartcode4bytes(&h264bits[h264bitspos]))  362         {  363             nalulen = getnaludatalen(h264bitspos+4, filelen, h264bits);  364   365             naludata = (unsigned char*)malloc(nalulen);  366             if (naludata)  367             {  368                 memset(naludata, 0x0, nalulen);  369   370                 memcpy(naludata, h264bits+h264bitspos+4, nalulen);  371   372                 parsenaludata(nalulen, naludata);  373                   374                 free(naludata);  375                 naludata = null;  376             }  377               378             h264bitspos += (nalulen+4);  379         }  380         else  381         {  382             h264bitspos++;  383         }  384     }  385   386     return 0;  387 }

view code

 

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/c-cdevelopment/604859.html

(0)
上一篇 2021年5月12日
下一篇 2021年5月12日

精彩推荐