H.264有两种封装模式:

(1)annexb模式:传统模式,使用start code来分隔NAL, SPS和PPS是在ES流的头部;

(2)mp4模式:没有start code,使用NALU长度(固定字节,通常为4个字节)来分隔NAL。AVCodecContext的extradata内部保存着分隔的字节数,SPS和PPS;

1. 找到SPS

视频的宽高保存在SPS中。那么提取宽高首先要找到SPS。annexb模式直接读取视频数据,根据NAL type找到SPS即可。mp4模式应该从extradata中找到SPS。

mp4格式的extradata格式如下图:

FFMpeg中解析extradata的函数是ff_h264_decode_extradata()。注意第5字节的最后2位,表示的就是NAL size的字节数-1。在AVCC格式中,每个NAL前面都会有NAL size字段。NAL size可能是1字节、2字节或4字节(4字节较常见),解析extradata重要目的就是确认这个值,之后2个字节是SPS长度,长度之后就是SPS。

2. 从SPS中提取宽高

以下代码是网上常见的,但是很明显写的有问题,宽高不一定非要是16的倍数。

  1. width=(pic_width_in_mbs_minus1+)*;
  2. height=(pic_height_in_map_units_minus1+)*;

当宽高不是16整数倍时,frame_cropping_flag值为1,frame_mbs_only_flag为1以下为修正的代码:

  1. #include <stdio.h>
  2. #include <stdint.h>
  3. #include <string.h>
  4. #include <math.h>
  5.  
  6. typedef unsigned int UINT;
  7. typedef unsigned char BYTE;
  8. typedef unsigned long DWORD;
  9.  
  10. UINT Ue(BYTE *pBuff, UINT nLen, UINT &nStartBit)
  11. {
  12. //计算0bit的个数
  13. UINT nZeroNum = ;
  14. while (nStartBit < nLen * )
  15. {
  16. if (pBuff[nStartBit / ] & (0x80 >> (nStartBit % ))) //&:按位与,%取余
  17. {
  18. break;
  19. }
  20. nZeroNum++;
  21. nStartBit++;
  22. }
  23. nStartBit ++;
  24.  
  25. //计算结果
  26. DWORD dwRet = ;
  27. for (UINT i=; i<nZeroNum; i++)
  28. {
  29. dwRet <<= ;
  30. if (pBuff[nStartBit / ] & (0x80 >> (nStartBit % )))
  31. {
  32. dwRet += ;
  33. }
  34. nStartBit++;
  35. }
  36. return ( << nZeroNum) - + dwRet;
  37. }
  38.  
  39. int Se(BYTE *pBuff, UINT nLen, UINT &nStartBit)
  40. {
  41. int UeVal=Ue(pBuff,nLen,nStartBit);
  42. double k=UeVal;
  43. int nValue=ceil(k/);//ceil函数:ceil函数的作用是求不小于给定实数的最小整数。ceil(2)=ceil(1.2)=cei(1.5)=2.00
  44. if (UeVal % ==)
  45. nValue=-nValue;
  46. return nValue;
  47. }
  48.  
  49. DWORD u(UINT BitCount,BYTE * buf,UINT &nStartBit)
  50. {
  51. DWORD dwRet = ;
  52. for (UINT i=; i<BitCount; i++)
  53. {
  54. dwRet <<= ;
  55. if (buf[nStartBit / ] & (0x80 >> (nStartBit % )))
  56. {
  57. dwRet += ;
  58. }
  59. nStartBit++;
  60. }
  61. return dwRet;
  62. }
  63.  
  64. /**
  65. * H264的NAL起始码防竞争机制
  66. *
  67. * @param buf SPS数据内容
  68. *
  69. * @无返回值
  70. */
  71. void de_emulation_prevention(BYTE* buf,unsigned int* buf_size)
  72. {
  73. int i=,j=;
  74. BYTE* tmp_ptr=NULL;
  75. unsigned int tmp_buf_size=;
  76. int val=;
  77.  
  78. tmp_ptr=buf;
  79. tmp_buf_size=*buf_size;
  80. for(i=;i<(tmp_buf_size-);i++)
  81. {
  82. //check for 0x000003
  83. val=(tmp_ptr[i]^0x00) +(tmp_ptr[i+]^0x00)+(tmp_ptr[i+]^0x03);
  84. if(val==)
  85. {
  86. //kick out 0x03
  87. for(j=i+;j<tmp_buf_size-;j++)
  88. tmp_ptr[j]=tmp_ptr[j+];
  89.  
  90. //and so we should devrease bufsize
  91. (*buf_size)--;
  92. }
  93. }
  94.  
  95. return;
  96. }
  97.  
  98. /**
  99. * 解码SPS,获取视频图像宽、高信息
  100. *
  101. * @param buf SPS数据内容
  102. * @param nLen SPS数据的长度
  103. * @param width 图像宽度
  104. * @param height 图像高度
  105.  
  106. * @成功则返回1 , 失败则返回0
  107. */
  108. int h264_decode_sps(BYTE * buf,unsigned int nLen,int &width,int &height,int &fps)
  109. {
  110. UINT StartBit=;
  111. fps=;
  112. de_emulation_prevention(buf,&nLen);
  113.  
  114. int forbidden_zero_bit=u(,buf,StartBit);
  115. int nal_ref_idc=u(,buf,StartBit);
  116. int nal_unit_type=u(,buf,StartBit);
  117. if(nal_unit_type==)
  118. {
  119. int profile_idc=u(,buf,StartBit);
  120. int constraint_set0_flag=u(,buf,StartBit);//(buf[1] & 0x80)>>7;
  121. int constraint_set1_flag=u(,buf,StartBit);//(buf[1] & 0x40)>>6;
  122. int constraint_set2_flag=u(,buf,StartBit);//(buf[1] & 0x20)>>5;
  123. int constraint_set3_flag=u(,buf,StartBit);//(buf[1] & 0x10)>>4;
  124. int reserved_zero_4bits=u(,buf,StartBit);
  125. int level_idc=u(,buf,StartBit);
  126.  
  127. int seq_parameter_set_id=Ue(buf,nLen,StartBit);
  128.  
  129. if( profile_idc == || profile_idc == ||
  130. profile_idc == || profile_idc == )
  131. {
  132. int chroma_format_idc=Ue(buf,nLen,StartBit);
  133. if( chroma_format_idc == )
  134. int residual_colour_transform_flag=u(,buf,StartBit);
  135. int bit_depth_luma_minus8=Ue(buf,nLen,StartBit);
  136. int bit_depth_chroma_minus8=Ue(buf,nLen,StartBit);
  137. int qpprime_y_zero_transform_bypass_flag=u(,buf,StartBit);
  138. int seq_scaling_matrix_present_flag=u(,buf,StartBit);
  139.  
  140. int seq_scaling_list_present_flag[];
  141. if( seq_scaling_matrix_present_flag )
  142. {
  143. for( int i = ; i < ; i++ ) {
  144. seq_scaling_list_present_flag[i]=u(,buf,StartBit);
  145. }
  146. }
  147. }
  148. int log2_max_frame_num_minus4=Ue(buf,nLen,StartBit);
  149. int pic_order_cnt_type=Ue(buf,nLen,StartBit);
  150. if( pic_order_cnt_type == )
  151. int log2_max_pic_order_cnt_lsb_minus4=Ue(buf,nLen,StartBit);
  152. else if( pic_order_cnt_type == )
  153. {
  154. int delta_pic_order_always_zero_flag=u(,buf,StartBit);
  155. int offset_for_non_ref_pic=Se(buf,nLen,StartBit);
  156. int offset_for_top_to_bottom_field=Se(buf,nLen,StartBit);
  157. int num_ref_frames_in_pic_order_cnt_cycle=Ue(buf,nLen,StartBit);
  158.  
  159. int *offset_for_ref_frame=new int[num_ref_frames_in_pic_order_cnt_cycle];
  160. for( int i = ; i < num_ref_frames_in_pic_order_cnt_cycle; i++ )
  161. offset_for_ref_frame[i]=Se(buf,nLen,StartBit);
  162. delete [] offset_for_ref_frame;
  163. }
  164. int num_ref_frames=Ue(buf,nLen,StartBit);
  165. int gaps_in_frame_num_value_allowed_flag=u(,buf,StartBit);
  166. int pic_width_in_mbs_minus1=Ue(buf,nLen,StartBit);
  167. int pic_height_in_map_units_minus1=Ue(buf,nLen,StartBit);
  168.  
  169. //width=(pic_width_in_mbs_minus1+1)*16;
  170. //height=(pic_height_in_map_units_minus1+1)*16;
  171.  
  172. int frame_mbs_only_flag=u(,buf,StartBit);
  173. if(!frame_mbs_only_flag)
  174. int mb_adaptive_frame_field_flag=u(,buf,StartBit);
  175.  
  176. int direct_8x8_inference_flag=u(,buf,StartBit);
  177. int frame_cropping_flag=u(,buf,StartBit);
  178. int frame_crop_left_offset=;
  179. int frame_crop_right_offset=;
  180. int frame_crop_top_offset=;
  181. int frame_crop_bottom_offset=;
  182. if(frame_cropping_flag)
  183. {
  184. frame_crop_left_offset=Ue(buf,nLen,StartBit);
  185. frame_crop_right_offset=Ue(buf,nLen,StartBit);
  186. frame_crop_top_offset=Ue(buf,nLen,StartBit);
  187. frame_crop_bottom_offset=Ue(buf,nLen,StartBit);
  188. }
  189.  
  190. width = ((pic_width_in_mbs_minus1 +)*) - frame_crop_left_offset* - frame_crop_right_offset*;
  191. height= (( - frame_mbs_only_flag)* (pic_height_in_map_units_minus1 +) * ) - (frame_crop_top_offset * ) - (frame_crop_bottom_offset * );
  192.  
  193. int vui_parameter_present_flag=u(,buf,StartBit);
  194. if(vui_parameter_present_flag)
  195. {
  196. int aspect_ratio_info_present_flag=u(,buf,StartBit);
  197. if(aspect_ratio_info_present_flag)
  198. {
  199. int aspect_ratio_idc=u(,buf,StartBit);
  200. if(aspect_ratio_idc==)
  201. {
  202. int sar_width=u(,buf,StartBit);
  203. int sar_height=u(,buf,StartBit);
  204. }
  205. }
  206. int overscan_info_present_flag=u(,buf,StartBit);
  207. if(overscan_info_present_flag)
  208. int overscan_appropriate_flagu=u(,buf,StartBit);
  209. int video_signal_type_present_flag=u(,buf,StartBit);
  210. if(video_signal_type_present_flag)
  211. {
  212. int video_format=u(,buf,StartBit);
  213. int video_full_range_flag=u(,buf,StartBit);
  214. int colour_description_present_flag=u(,buf,StartBit);
  215. if(colour_description_present_flag)
  216. {
  217. int colour_primaries=u(,buf,StartBit);
  218. int transfer_characteristics=u(,buf,StartBit);
  219. int matrix_coefficients=u(,buf,StartBit);
  220. }
  221. }
  222. int chroma_loc_info_present_flag=u(,buf,StartBit);
  223. if(chroma_loc_info_present_flag)
  224. {
  225. int chroma_sample_loc_type_top_field=Ue(buf,nLen,StartBit);
  226. int chroma_sample_loc_type_bottom_field=Ue(buf,nLen,StartBit);
  227. }
  228. int timing_info_present_flag=u(,buf,StartBit);
  229. if(timing_info_present_flag)
  230. {
  231. int num_units_in_tick=u(,buf,StartBit);
  232. int time_scale=u(,buf,StartBit);
  233. fps=time_scale/(*num_units_in_tick);
  234. }
  235. }
  236. return true;
  237. }
  238. else
  239. return false;
  240. }

参考资料:

1. http://www.latelee.org/my-study/get-width-height-framerate-from-bitstream.html

2. https://blog.csdn.net/yue_huang/article/details/75126155

H.264从SPS中提取视频宽高的更多相关文章

  1. 从H264码流中获取视频宽高 (SPS帧) 升级篇

    之前写过 <从H264码流中获取视频宽高 (SPS帧)> . 但发现很多局限性,而且有时解出来是错误的. 所以重新去研究了. 用了 官方提供的代码库来解析. 花了点时间,从代码库里单独把解 ...

  2. 从H264码流中获取视频宽高 (SPS帧)

    获取.h264视频宽高的方法 花了2个通宵终于搞定.(后面附上完整代码) http://write.blog.csdn.net/postedit/7852406 图像的高和宽在H264的SPS帧中.在 ...

  3. 多媒体开发之sps---解析sps得到图像的宽高

    (1)通过块的宽高解析出真个h264的分辨率 如何解析SDP中包含的H.264的SPS和PPS串 http://www.pernet.tv.sixxs.org/thread-109-1-1.html ...

  4. JavaScript中的各种宽高以及位置总结

    JavaScript中的各种宽高以及位置总结 在javascript中操作dom节点让其运动的时候,常常会涉及到各种宽高以及位置坐标等概念,如果不能很好地理解这些属性所代表的意义,就不能理解js的运动 ...

  5. LODOP中的各种宽高和位置简短问答

    LODOP中的位置边距,可查看本博客另一篇相关博文:LODOOP中的各种边距 打印项.整体偏移.可打区域.内部边距关于LODOP中的各种宽高,可查看本博文简短问答下方的正文:.该文其实有两个以前的相关 ...

  6. AntDesign VUE:上传组件图片/视频宽高、文件大小、image/video/pdf文件类型等限制(Promise、Boolean)

    文件大小限制 - Promise checkFileSize(file, rules) { return new Promise((resolve, reject) => { file.size ...

  7. 一步一步解析H.264码流的NALU(SPS,PSS,IDR)获取宽高和帧率

    分析H.264码流的工具 CodecVisa,StreamEye以及VM Analyzer NALU是由NALU头和RBSP数据组成,而RBSP可能是SPS,PPS,Slice或SEI 而且SPS位于 ...

  8. 一种H.264高清视频的无参考视频质量评价算法(基于QP和跳过宏块数)

    本文记录一种无参考视频质量评价算法.这是我们自己实验室前两年一个师姐做的,算法还是比较准确的,在此记录一下. 注意本算法前提是高清视频.而且是H.264编码方式. 该方法主要使用两个码流里面的参数进行 ...

  9. JavaScript及jQuery中的各种宽高属性图解

    文/poetries(简书作者)原文链接:http://www.jianshu.com/p/60332df38393 著作权归作者所有,转载请联系作者获得授权, 并标注“简书作者”.   作者声明:本 ...

随机推荐

  1. Code Signal_练习题_differentSymbolsNaive

    Given a string, find the number of different characters in it. Example For s = "cabca", th ...

  2. 获取url地址的参数值

    使用频率很高,百度了记录一下 $.getUrlParam = function (name) { var reg = new RegExp("(^|&)" + name + ...

  3. Python os、sys、pickle、json等模块

    1.os 所有和操作系统相关的内容都在os模块,一般用来操作文件系统 import os os.makedirs('dirname1/dirname2') # 可生成多层递归目录 os.removed ...

  4. HTTPS的安全性

    一.Https介绍 1. 什么是Https HTTPS(全称:Hypertext Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP通道, ...

  5. 【代码笔记】iOS-4个可以单独点击的button

    一,效果图. 二,工程图. 三,代码. ViewController.m #import "ViewController.h" @interface ViewController ...

  6. 标准工作流(AWE)邮件通知

    今天遇到一个问题,UAT环境收不到流程待办,最终审批,最终拒绝等邮件. 检查了PT_WF_NOTIFICATION包的Notification类中的Send方法,发现如果app服务器在psappsrv ...

  7. 解读 --- 基于微软企业商务应用平台 (Microsoft Dynamics 365) 之上的人工智能 (AI) 解决方案

    9月25日微软今年一年一度的Ignite 2017在佛罗里达州奥兰多市还是如期开幕了.为啥这么说?因为9月初五级飓风厄玛(Hurricane Irma) 在佛罗里达州登陆,在当地造成了挺大的麻烦.在这 ...

  8. Android应用程序启动过程(一)总结

    一.App启动方式 1,冷启动 冷启动:当启动应用时,后台没有该应用的进程,这时系统会重新创建一个新的进程分配给该应用. 冷启动的特点:因为系统会重新创建一个新的进程分配给它,所以会创建和初始化App ...

  9. Android--用Valley框架去上传图片

    1.除了用到了Volley,还用到了一个包httpmime(下载地址:http://download.csdn.net/detail/chequer_lkp/8102751) 2.需要一个工具类,该类 ...

  10. surging API

    基于.NET CORE微服务框架 -谈谈surging API网关 基于.NET CORE微服务框架 -浅析如何使用surging surging 系列 NET Core 2.0 在WIN7系统 的H ...