• 【转载】 H264的I/P/B帧类型判断


    http://blog.csdn.net/zhuweigangzwg/article/details/44152239

    这里首先说明下H264的结构:

    00 00 00 01/00 00 01->nal(1bytes)->slice->宏块->运动估计向量。

    如果h264的body中出现了前缀则由00 00 00 01/00 00 01变为00 03 00 00 01/00 03 00 01.

    我们看到常用naltype 像sps= 0x07 pps= 0x08 sei = 0x06   I/P/B=  0x01/0x05 也就是说只判断naltype = 0x01/0x05是判断不出来I/P/B帧类型的,需要到slice层去判断用到“熵编码”具体的“熵编码”内容请看:“H.264官方中文版.pdf”.

    下面是扣的ffmpeg的源码判断I/P/B帧类型的实现:

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. int GetFrameType(NALU_t * nal)  
    2. {  
    3.     bs_t s;  
    4.     int frame_type = 0;   
    5.     unsigned char * OneFrameBuf_H264 = NULL ;  
    6.     if ((OneFrameBuf_H264 = (unsigned char *)calloc(nal->len + 4,sizeof(unsigned char))) == NULL)  
    7.     {  
    8.         printf("Error malloc OneFrameBuf_H264 ");  
    9.         return getchar();  
    10.     }  
    11.     if (nal->startcodeprefix_len == 3)  
    12.     {  
    13.         OneFrameBuf_H264[0] = 0x00;  
    14.         OneFrameBuf_H264[1] = 0x00;  
    15.         OneFrameBuf_H264[2] = 0x01;  
    16.         memcpy(OneFrameBuf_H264 + 3,nal->buf,nal->len);  
    17.     }  
    18.     else if (nal->startcodeprefix_len == 4)  
    19.     {  
    20.         OneFrameBuf_H264[0] = 0x00;  
    21.         OneFrameBuf_H264[1] = 0x00;  
    22.         OneFrameBuf_H264[2] = 0x00;  
    23.         OneFrameBuf_H264[3] = 0x01;  
    24.         memcpy(OneFrameBuf_H264 + 4,nal->buf,nal->len);  
    25.     }  
    26.     else  
    27.     {  
    28.         printf("H264读取错误! ");  
    29.     }  
    30.     bs_init( &s,OneFrameBuf_H264 + nal->startcodeprefix_len + 1  ,nal->len - 1 );  
    31.   
    32.   
    33.     if (nal->nal_unit_type == NAL_SLICE || nal->nal_unit_type ==  NAL_SLICE_IDR )  
    34.     {  
    35.         /* i_first_mb */  
    36.         bs_read_ue( &s );  
    37.         /* picture type */  
    38.         frame_type =  bs_read_ue( &s );  
    39.         switch(frame_type)  
    40.         {  
    41.         case 0: case 5: /* P */  
    42.             nal->Frametype = FRAME_P;  
    43.             break;  
    44.         case 1: case 6: /* B */  
    45.             nal->Frametype = FRAME_B;  
    46.             break;  
    47.         case 3: case 8: /* SP */  
    48.             nal->Frametype = FRAME_P;  
    49.             break;  
    50.         case 2: case 7: /* I */  
    51.             nal->Frametype = FRAME_I;  
    52.             I_Frame_Num ++;  
    53.             break;  
    54.         case 4: case 9: /* SI */  
    55.             nal->Frametype = FRAME_I;  
    56.             break;  
    57.         }  
    58.     }  
    59.     else if (nal->nal_unit_type == NAL_SEI)  
    60.     {  
    61.         nal->Frametype = NAL_SEI;  
    62.     }  
    63.     else if(nal->nal_unit_type == NAL_SPS)  
    64.     {  
    65.         nal->Frametype = NAL_SPS;  
    66.     }  
    67.     else if(nal->nal_unit_type == NAL_PPS)  
    68.     {  
    69.         nal->Frametype = NAL_PPS;  
    70.     }  
    71.     if (OneFrameBuf_H264)  
    72.     {  
    73.         free(OneFrameBuf_H264);  
    74.         OneFrameBuf_H264 = NULL;  
    75.     }  
    76.     return 1;  
    77. }  



    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. //H264一帧数据的结构体  
    2. typedef struct Tag_NALU_t  
    3. {  
    4.     unsigned char forbidden_bit;           //! Should always be FALSE  
    5.     unsigned char nal_reference_idc;       //! NALU_PRIORITY_xxxx  
    6.     unsigned char nal_unit_type;           //! NALU_TYPE_xxxx    
    7.     unsigned int  startcodeprefix_len;      //! 前缀字节数  
    8.     unsigned int  len;                     //! 包含nal 头的nal 长度,从第一个00000001到下一个000000001的长度  
    9.     unsigned int  max_size;                //! 最多一个nal 的长度  
    10.     unsigned char * buf;                   //! 包含nal 头的nal 数据  
    11.     unsigned char Frametype;               //! 帧类型  
    12.     unsigned int  lost_packets;            //! 预留  
    13. } NALU_t;  
    14.   
    15. //nal类型  
    16. enum nal_unit_type_e  
    17. {  
    18.     NAL_UNKNOWN     = 0,  
    19.     NAL_SLICE       = 1,  
    20.     NAL_SLICE_DPA   = 2,  
    21.     NAL_SLICE_DPB   = 3,  
    22.     NAL_SLICE_DPC   = 4,  
    23.     NAL_SLICE_IDR   = 5,    /* ref_idc != 0 */  
    24.     NAL_SEI         = 6,    /* ref_idc == 0 */  
    25.     NAL_SPS         = 7,  
    26.     NAL_PPS         = 8  
    27.     /* ref_idc == 0 for 6,9,10,11,12 */  
    28. };  
    29.   
    30. //帧类型  
    31. enum Frametype_e  
    32. {  
    33.     FRAME_I  = 15,  
    34.     FRAME_P  = 16,  
    35.     FRAME_B  = 17  
    36. };  



    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. //Mybs.h  
    2. #pragma once  
    3.   
    4. #include "Information.h"  
    5.   
    6. //读取字节结构体  
    7. typedef struct Tag_bs_t  
    8. {  
    9.     unsigned char *p_start;                //缓冲区首地址(这个开始是最低地址)  
    10.     unsigned char *p;                      //缓冲区当前的读写指针 当前字节的地址,这个会不断的++,每次++,进入一个新的字节  
    11.     unsigned char *p_end;                  //缓冲区尾地址     //typedef unsigned char   uint8_t;  
    12.     int     i_left;                        // p所指字节当前还有多少 “位” 可读写 count number of available(可用的)位   
    13. }bs_t;        
    14.   
    15.   
    16. /* 
    17. 函数名称: 
    18. 函数功能:初始化结构体 
    19. 参    数: 
    20. 返 回 值:无返回值,void类型 
    21. 思    路: 
    22. 资    料: 
    23.            
    24. */  
    25. void bs_init( bs_t *s, void *p_data, int i_data );  
    26.   
    27. /* 
    28. 该函数的作用是:从s中读出i_count位,并将其做为uint32_t类型返回 
    29. 思路: 
    30.     若i_count>0且s流并未结束,则开始或继续读取码流; 
    31.     若s当前字节中剩余位数大于等于要读取的位数i_count,则直接读取; 
    32.     若s当前字节中剩余位数小于要读取的位数i_count,则读取剩余位,进入s下一字节继续读取。 
    33. 补充: 
    34.     写入s时,i_left表示s当前字节还没被写入的位,若一个新的字节,则i_left=8; 
    35.     读取s时,i_left表示s当前字节还没被读取的位,若一个新的字节,则i_left=8。 
    36.     注意两者的区别和联系。 
    37.  
    38.     00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 0000000 
    39.     -------- -----000 00000000 ... 
    40.     写入s时:i_left = 3 
    41.     读取s时:i_left = 5 
    42.  
    43. 我思: 
    44.     字节流提前放在了结构体bs_s的对象bs_t里了,可能字节流不会一次性读取/分析完,而是根据需要,每次都读取几比特 
    45.     bs_s里,有专门的字段用来记录历史读取的结果,每次读取,都会在上次的读取位置上进行 
    46.     比如,100字节的流,经过若干次读取,当前位置处于中间一个字节处,前3个比特已经读取过了,此次要读取2比特 
    47.  
    48.     00001001 
    49.     000 01 001 (已读过的 本次要读的 以后要读的 ) 
    50.     i_count = 2 (计划去读2比特) 
    51.     i_left  = 5 (还有5比特未读,在本字节中) 
    52.     i_shr = s->i_left - i_count = 5 - 2 = 3 
    53.     *s->p >> i_shr,就把本次要读的比特移到了字节最右边(未读,但本次不需要的给移到了字节外,抛掉了) 
    54.     00000001 
    55.     i_mask[i_count] 即i_mask[2] 即0x03:00000011 
    56.     ( *s->p >> i_shr )&i_mask[i_count]; 即00000001 & 00000011 也就是00000001 按位与 00000011 
    57.     结果是:00000001 
    58.     i_result |= ( *s->p >> i_shr )&i_mask[i_count];即i_result |=00000001 也就是 i_result =i_result | 00000001 = 00000000 00000000 00000000 00000000 | 00000001 =00000000 00000000 00000000 00000001 
    59.     i_result = 
    60.     return( i_result ); 返回的i_result是4字节长度的,是unsigned类型 sizeof(unsigned)=4 
    61. */  
    62. int bs_read( bs_t *s, int i_count );  
    63.   
    64. /* 
    65. 函数名称: 
    66. 函数功能:从s中读出1位,并将其做为uint32_t类型返回。 
    67. 函数参数: 
    68. 返 回 值: 
    69. 思    路:若s流并未结束,则读取一位 
    70. 资    料: 
    71.         毕厚杰:第145页,u(n)/u(v),读进连续的若干比特,并将它们解释为“无符号整数” 
    72.         return i_result;    //unsigned int 
    73. */  
    74. int bs_read1( bs_t *s );  
    75.   
    76. /* 
    77. 函数名称: 
    78. 函数功能:从s中解码并读出一个语法元素值 
    79. 参    数: 
    80. 返 回 值: 
    81. 思    路: 
    82.         从s的当前位读取并计数,直至读取到1为止; 
    83.         while( bs_read1( s ) == 0 && s->p < s->p_end && i < 32 )这个循环用i记录了s当前位置到1为止的0的个数,并丢弃读到的第一个1; 
    84.         返回2^i-1+bs_read(s,i)。 
    85.         例:当s字节中存放的是0001010时,1前有3个0,所以i=3; 
    86.         返回的是:2^i-1+bs_read(s,i)即:8-1+010=9 
    87. 资    料: 
    88.         毕厚杰:第145页,ue(v);无符号指数Golomb熵编码 
    89.         x264中bs.h文件部分函数解读 http://wmnmtm.blog.163.com/blog/static/382457142011724101824726/ 
    90.         无符号整数指数哥伦布码编码 http://wmnmtm.blog.163.com/blog/static/38245714201172623027946/ 
    91. */  
    92. int bs_read_ue( bs_t *s );  
    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. //Mybs.cpp  
    2. #include "Mybs.h"  
    3.   
    4. void bs_init( bs_t *s, void *p_data, int i_data )  
    5. {  
    6.     s->p_start = (unsigned char *)p_data;        //用传入的p_data首地址初始化p_start,只记下有效数据的首地址  
    7.     s->p       = (unsigned char *)p_data;        //字节首地址,一开始用p_data初始化,每读完一个整字节,就移动到下一字节首地址  
    8.     s->p_end   = s->p + i_data;                   //尾地址,最后一个字节的首地址?  
    9.     s->i_left  = 8;                              //还没有开始读写,当前字节剩余未读取的位是8  
    10. }  
    11.   
    12.   
    13. int bs_read( bs_t *s, int i_count )  
    14. {  
    15.      static int i_mask[33] ={0x00,  
    16.                                   0x01,      0x03,      0x07,      0x0f,  
    17.                                   0x1f,      0x3f,      0x7f,      0xff,  
    18.                                   0x1ff,     0x3ff,     0x7ff,     0xfff,  
    19.                                   0x1fff,    0x3fff,    0x7fff,    0xffff,  
    20.                                   0x1ffff,   0x3ffff,   0x7ffff,   0xfffff,  
    21.                                   0x1fffff,  0x3fffff,  0x7fffff,  0xffffff,  
    22.                                   0x1ffffff, 0x3ffffff, 0x7ffffff, 0xfffffff,  
    23.                                   0x1fffffff,0x3fffffff,0x7fffffff,0xffffffff};  
    24.     /* 
    25.               数组中的元素用二进制表示如下: 
    26.  
    27.               假设:初始为0,已写入为+,已读取为- 
    28.                
    29.               字节:       1       2       3       4 
    30.                    00000000 00000000 00000000 00000000      下标 
    31.  
    32.               0x00:                           00000000      x[0] 
    33.  
    34.               0x01:                           00000001      x[1] 
    35.               0x03:                           00000011      x[2] 
    36.               0x07:                           00000111      x[3] 
    37.               0x0f:                           00001111      x[4] 
    38.  
    39.               0x1f:                           00011111      x[5] 
    40.               0x3f:                           00111111      x[6] 
    41.               0x7f:                           01111111      x[7] 
    42.               0xff:                           11111111      x[8]    1字节 
    43.  
    44.              0x1ff:                      0001 11111111      x[9] 
    45.              0x3ff:                      0011 11111111      x[10]   i_mask[s->i_left] 
    46.              0x7ff:                      0111 11111111      x[11] 
    47.              0xfff:                      1111 11111111      x[12]   1.5字节 
    48.  
    49.             0x1fff:                  00011111 11111111      x[13] 
    50.             0x3fff:                  00111111 11111111      x[14] 
    51.             0x7fff:                  01111111 11111111      x[15] 
    52.             0xffff:                  11111111 11111111      x[16]   2字节 
    53.  
    54.            0x1ffff:             0001 11111111 11111111      x[17] 
    55.            0x3ffff:             0011 11111111 11111111      x[18] 
    56.            0x7ffff:             0111 11111111 11111111      x[19] 
    57.            0xfffff:             1111 11111111 11111111      x[20]   2.5字节 
    58.  
    59.           0x1fffff:         00011111 11111111 11111111      x[21] 
    60.           0x3fffff:         00111111 11111111 11111111      x[22] 
    61.           0x7fffff:         01111111 11111111 11111111      x[23] 
    62.           0xffffff:         11111111 11111111 11111111      x[24]   3字节 
    63.  
    64.          0x1ffffff:    0001 11111111 11111111 11111111      x[25] 
    65.          0x3ffffff:    0011 11111111 11111111 11111111      x[26] 
    66.          0x7ffffff:    0111 11111111 11111111 11111111      x[27] 
    67.          0xfffffff:    1111 11111111 11111111 11111111      x[28]   3.5字节 
    68.  
    69.         0x1fffffff:00011111 11111111 11111111 11111111      x[29] 
    70.         0x3fffffff:00111111 11111111 11111111 11111111      x[30] 
    71.         0x7fffffff:01111111 11111111 11111111 11111111      x[31] 
    72.         0xffffffff:11111111 11111111 11111111 11111111      x[32]   4字节 
    73.  
    74.      */  
    75.     int      i_shr;             //  
    76.     int i_result = 0;           //用来存放读取到的的结果 typedef unsigned   uint32_t;  
    77.   
    78.     while( i_count > 0 )     //要读取的比特数  
    79.     {  
    80.         if( s->p >= s->p_end ) //字节流的当前位置>=流结尾,即代表此比特流s已经读完了。  
    81.         {                       //  
    82.             break;  
    83.         }  
    84.   
    85.         if( ( i_shr = s->i_left - i_count ) >= 0 )    //当前字节剩余的未读位数,比要读取的位数多,或者相等  
    86.         {                                           //i_left当前字节剩余的未读位数,本次要读i_count比特,i_shr=i_left-i_count的结果如果>=0,说明要读取的都在当前字节内  
    87.                                                     //i_shr>=0,说明要读取的比特都处于当前字节内  
    88.             //这个阶段,一次性就读完了,然后返回i_result(退出了函数)  
    89.             /* more in the buffer than requested */  
    90.             i_result |= ( *s->p >> i_shr )&i_mask[i_count];//“|=”:按位或赋值,A |= B 即 A = A|B  
    91.                                     //|=应该在最后执行,把结果放在i_result(按位与优先级高于复合操作符|=)  
    92.                                     //i_mask[i_count]最右侧各位都是1,与括号中的按位与,可以把括号中的结果复制过来  
    93.                                     //!=,左边的i_result在这儿全是0,右侧与它按位或,还是复制结果过来了,好象好几步都多余  
    94.             /*读取后,更新结构体里的字段值*/  
    95.             s->i_left -= i_count; //即i_left = i_left - i_count,当前字节剩余的未读位数,原来的减去这次读取的  
    96.             if( s->i_left == 0 ) //如果当前字节剩余的未读位数正好是0,说明当前字节读完了,就要开始下一个字节  
    97.             {  
    98.                 s->p++;              //移动指针,所以p好象是以字节为步长移动指针的  
    99.                 s->i_left = 8;       //新开始的这个字节来说,当前字节剩余的未读位数,就是8比特了  
    100.             }  
    101.             return( i_result );     //可能的返回值之一为:00000000 00000000 00000000 00000001 (4字节长)  
    102.         }  
    103.         else    /* i_shr < 0 ,跨字节的情况*/  
    104.         {  
    105.             //这个阶段,是while的一次循环,可能还会进入下一次循环,第一次和最后一次都可能读取的非整字节,比如第一次读了3比特,中间读取了2字节(即2x8比特),最后一次读取了1比特,然后退出while循环  
    106.             //当前字节剩余的未读位数,比要读取的位数少,比如当前字节有3位未读过,而本次要读7位  
    107.             //???对当前字节来说,要读的比特,都在最右边,所以不再移位了(移位的目的是把要读的比特放在当前字节最右)  
    108.             /* less(较少的) in the buffer than requested */  
    109.             i_result |= (*s->p&i_mask[s->i_left]) << -i_shr;    //"-i_shr"相当于取了绝对值  
    110.                                     //|= 和 << 都是位操作符,优先级相同,所以从左往右顺序执行  
    111.                                     //举例:int|char ,其中int是4字节,char是1字节,sizeof(int|char)是4字节  
    112.                                     //i_left最大是8,最小是0,取值范围是[0,8]  
    113.             i_count  -= s->i_left;   //待读取的比特数,等于原i_count减去i_left,i_left是当前字节未读过的比特数,而此else阶段,i_left代表的当前字节未读的比特全被读过了,所以减它  
    114.             s->p++;  //定位到下一个新的字节  
    115.             s->i_left = 8;   //对一个新字节来说,未读过的位数当然是8,即本字节所有位都没读取过  
    116.         }  
    117.     }  
    118.   
    119.     return( i_result );//可能的返回值之一为:00000000 00000000 00000000 00000001 (4字节长)  
    120. }  
    121.   
    122. int bs_read1( bs_t *s )  
    123. {  
    124.   
    125.     if( s->p < s->p_end )    
    126.     {  
    127.         unsigned int i_result;  
    128.   
    129.         s->i_left--;                           //当前字节未读取的位数少了1位  
    130.         i_result = ( *s->p >> s->i_left )&0x01;//把要读的比特移到当前字节最右,然后与0x01:00000001进行逻辑与操作,因为要读的只是一个比特,这个比特不是0就是1,与0000 0001按位与就可以得知此情况  
    131.         if( s->i_left == 0 )                   //如果当前字节剩余未读位数是0,即是说当前字节全读过了  
    132.         {  
    133.             s->p++;                             //指针s->p 移到下一字节  
    134.             s->i_left = 8;                     //新字节中,未读位数当然是8位  
    135.         }  
    136.         return i_result;                       //unsigned int  
    137.     }  
    138.   
    139.     return 0;                                  //返回0应该是没有读到东西  
    140. }  
    141.   
    142. int bs_read_ue( bs_t *s )  
    143. {  
    144.     int i = 0;  
    145.   
    146.     while( bs_read1( s ) == 0 && s->p < s->p_end && i < 32 )    //条件为:读到的当前比特=0,指针未越界,最多只能读32比特  
    147.     {  
    148.         i++;  
    149.     }  
    150.     return( ( 1 << i) - 1 + bs_read( s, i ) );      
    151. }  
    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1.   

    具体实现可参考这个:http://download.csdn.net/detail/zhuweigangzwg/5522123

    交流请加QQ群:62054820
    QQ:379969650

  • 相关阅读:
    join()方法的使用
    synchronized关键字
    voliatle关键字
    一.线程概述
    NIO demo
    同步阻塞I/O
    Ubuntu16.04.1 安装Nginx
    垃圾收集
    如何从头开始安装 wordpress
    centos 6 安装 gnu c++ 等开发工具
  • 原文地址:https://www.cnblogs.com/littleKing163/p/4823477.html
Copyright © 2020-2023  润新知