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帧类型的实现:

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

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

交流请加QQ群:62054820
QQ:379969650

【转载】 H264的I/P/B帧类型判断的更多相关文章

  1. 对H.264帧类型判断方法

    背景描述 我们经常在网络直播推流或者客户端拉流的时候,需要对获取到的H.264视频帧进行判断后处理,我们经常获取到各种不同的视频数据0x67 0x68 0x65 0x61,0x27 0x28 0x25 ...

  2. 从TS流定位H264的每一个视频帧开始,判断出帧类型

    从TS流定位H264的每一个视频帧开始,判断出帧类型(待续)

  3. H264(NAL简介与I帧判断)

    1.NAL全称Network Abstract Layer, 即网络抽象层.         在H.264/AVC视频编码标准中,整个系统框架被分为了两个层面:视频编码层面(VCL)和网络抽象层面(N ...

  4. (转)H264(NAL简介与I帧判断)

    1.NAL全称Network Abstract Layer, 即网络抽象层.         在H.264/AVC视频编码标准中,整个系统框架被分为了两个层面:视频编码层面(VCL)和网络抽象层面(N ...

  5. [译] QUIC Wire Layout Specification - Frame Types and Formats | QUIC协议标准中文翻译(4) 帧类型和格式

    欢迎访问我的个人网站获取更好的阅读排版体验: [译] QUIC Wire Layout Specification - Frame Types and Formats | QUIC协议标准中文翻译(4 ...

  6. H264编码原理以及I帧、B和P帧详解, H264码流结构分析

    H264码流结构分析 http://blog.csdn.net/chenchong_219/article/details/37990541 1.码流总体结构: h264的功能分为两层,视频编码层(V ...

  7. x264中的帧类型、条带类型、数据分区(data partition)

    1 条带类型(slice type) x264的条带有三种基本类型分别为:I(主要用于帧内图像编码).P(用于帧间前向参考预测图像编码).B(用于帧间双向参考预测图像编码).SI与SP(切换码流时用) ...

  8. JSFL元件类型判断 转载于 https://blog.csdn.net/linking530/article/details/8364600

    //获取舞台上第一层第一帧上的全部元件 var els = fl.getDocumentDOM().getTimeline().layers[0].frames[0].elements; //遍历元件 ...

  9. 【JavaScript】Object.prototype.toString.call()进行类型判断

    权声明:本文为博主原创文章,未经博主允许不得转载. op = Object.prototype, ostring = op.toString, ... function isFunction(it)  ...

随机推荐

  1. IDEA中配置JUnit单元测试

    参考安装教程:https://www.jianshu.com/p/c37753b6dbd6 如果想用junit4的话,需要在pom.xml中配置. 需要安装JUnitGenerator V2.0插件, ...

  2. phpstorm添加站点

    1.在界面上快捷键 ctrl+alt+s 或打开File->settings 2.找到Deployment 设置完后确定即可使用.

  3. windows2008 r2 不能启用网络发现解决方法

    1.出现的问题: 在“网络和共享中心”-“网络发现”不论如何,“启用”网络发现功能,系统都会自动重置为关闭状态. 2.解决方法: 运行中输入 services.msc-->在里边找到下边上个服务 ...

  4. Django之crm

    crm注册 crm注册Form from django import forms from crm import models from django.core.exceptions import V ...

  5. privilege instruction error

    检查新建的回调函数是否用了__stdcall修饰

  6. Django的rest_framework的序列化组件之序列化多表字段的方法

    首先,因为我们安装了restframework,所以我们需要在django的settings中引入restframework INSTALLED_APPS = [ 'django.contrib.ad ...

  7. C++ 中 int 与string相互转换

    int -->  string 1.使用itoa()函数 将任意类型的数字变量转换为字串子变量. #include<stdio.h> #include<iostream> ...

  8. 使用python语言计算n的阶乘

    计算“1x2x3x4” def factorial(n): result = n ,n): result *= i return resultdef main(): print factorial(4 ...

  9. java爬取网页Unicode转UTF-8中文

    unicode编码简而言之就是将每一个字符用16位2进制数标识.但是通常都用4位的16进制数标识. 例如: 1)中文字符串"你好"的unicode码为:\u60\u597d; 2) ...

  10. httpclient的简单使用

    1.通过get请求后台,注意tomcat的编码设置成utf-8;    <Connector connectionTimeout="20000" port="808 ...