H.264从SPS中提取视频宽高
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的倍数。
- width=(pic_width_in_mbs_minus1+)*;
- height=(pic_height_in_map_units_minus1+)*;
当宽高不是16整数倍时,frame_cropping_flag值为1,frame_mbs_only_flag为1以下为修正的代码:
- #include <stdio.h>
- #include <stdint.h>
- #include <string.h>
- #include <math.h>
- typedef unsigned int UINT;
- typedef unsigned char BYTE;
- typedef unsigned long DWORD;
- UINT Ue(BYTE *pBuff, UINT nLen, UINT &nStartBit)
- {
- //计算0bit的个数
- UINT nZeroNum = ;
- while (nStartBit < nLen * )
- {
- if (pBuff[nStartBit / ] & (0x80 >> (nStartBit % ))) //&:按位与,%取余
- {
- break;
- }
- nZeroNum++;
- nStartBit++;
- }
- nStartBit ++;
- //计算结果
- DWORD dwRet = ;
- for (UINT i=; i<nZeroNum; i++)
- {
- dwRet <<= ;
- if (pBuff[nStartBit / ] & (0x80 >> (nStartBit % )))
- {
- dwRet += ;
- }
- nStartBit++;
- }
- return ( << nZeroNum) - + dwRet;
- }
- int Se(BYTE *pBuff, UINT nLen, UINT &nStartBit)
- {
- int UeVal=Ue(pBuff,nLen,nStartBit);
- double k=UeVal;
- int nValue=ceil(k/);//ceil函数:ceil函数的作用是求不小于给定实数的最小整数。ceil(2)=ceil(1.2)=cei(1.5)=2.00
- if (UeVal % ==)
- nValue=-nValue;
- return nValue;
- }
- DWORD u(UINT BitCount,BYTE * buf,UINT &nStartBit)
- {
- DWORD dwRet = ;
- for (UINT i=; i<BitCount; i++)
- {
- dwRet <<= ;
- if (buf[nStartBit / ] & (0x80 >> (nStartBit % )))
- {
- dwRet += ;
- }
- nStartBit++;
- }
- return dwRet;
- }
- /**
- * H264的NAL起始码防竞争机制
- *
- * @param buf SPS数据内容
- *
- * @无返回值
- */
- void de_emulation_prevention(BYTE* buf,unsigned int* buf_size)
- {
- int i=,j=;
- BYTE* tmp_ptr=NULL;
- unsigned int tmp_buf_size=;
- int val=;
- tmp_ptr=buf;
- tmp_buf_size=*buf_size;
- for(i=;i<(tmp_buf_size-);i++)
- {
- //check for 0x000003
- val=(tmp_ptr[i]^0x00) +(tmp_ptr[i+]^0x00)+(tmp_ptr[i+]^0x03);
- if(val==)
- {
- //kick out 0x03
- for(j=i+;j<tmp_buf_size-;j++)
- tmp_ptr[j]=tmp_ptr[j+];
- //and so we should devrease bufsize
- (*buf_size)--;
- }
- }
- return;
- }
- /**
- * 解码SPS,获取视频图像宽、高信息
- *
- * @param buf SPS数据内容
- * @param nLen SPS数据的长度
- * @param width 图像宽度
- * @param height 图像高度
- * @成功则返回1 , 失败则返回0
- */
- int h264_decode_sps(BYTE * buf,unsigned int nLen,int &width,int &height,int &fps)
- {
- UINT StartBit=;
- fps=;
- de_emulation_prevention(buf,&nLen);
- int forbidden_zero_bit=u(,buf,StartBit);
- int nal_ref_idc=u(,buf,StartBit);
- int nal_unit_type=u(,buf,StartBit);
- if(nal_unit_type==)
- {
- int profile_idc=u(,buf,StartBit);
- int constraint_set0_flag=u(,buf,StartBit);//(buf[1] & 0x80)>>7;
- int constraint_set1_flag=u(,buf,StartBit);//(buf[1] & 0x40)>>6;
- int constraint_set2_flag=u(,buf,StartBit);//(buf[1] & 0x20)>>5;
- int constraint_set3_flag=u(,buf,StartBit);//(buf[1] & 0x10)>>4;
- int reserved_zero_4bits=u(,buf,StartBit);
- int level_idc=u(,buf,StartBit);
- int seq_parameter_set_id=Ue(buf,nLen,StartBit);
- if( profile_idc == || profile_idc == ||
- profile_idc == || profile_idc == )
- {
- int chroma_format_idc=Ue(buf,nLen,StartBit);
- if( chroma_format_idc == )
- int residual_colour_transform_flag=u(,buf,StartBit);
- int bit_depth_luma_minus8=Ue(buf,nLen,StartBit);
- int bit_depth_chroma_minus8=Ue(buf,nLen,StartBit);
- int qpprime_y_zero_transform_bypass_flag=u(,buf,StartBit);
- int seq_scaling_matrix_present_flag=u(,buf,StartBit);
- int seq_scaling_list_present_flag[];
- if( seq_scaling_matrix_present_flag )
- {
- for( int i = ; i < ; i++ ) {
- seq_scaling_list_present_flag[i]=u(,buf,StartBit);
- }
- }
- }
- int log2_max_frame_num_minus4=Ue(buf,nLen,StartBit);
- int pic_order_cnt_type=Ue(buf,nLen,StartBit);
- if( pic_order_cnt_type == )
- int log2_max_pic_order_cnt_lsb_minus4=Ue(buf,nLen,StartBit);
- else if( pic_order_cnt_type == )
- {
- int delta_pic_order_always_zero_flag=u(,buf,StartBit);
- int offset_for_non_ref_pic=Se(buf,nLen,StartBit);
- int offset_for_top_to_bottom_field=Se(buf,nLen,StartBit);
- int num_ref_frames_in_pic_order_cnt_cycle=Ue(buf,nLen,StartBit);
- int *offset_for_ref_frame=new int[num_ref_frames_in_pic_order_cnt_cycle];
- for( int i = ; i < num_ref_frames_in_pic_order_cnt_cycle; i++ )
- offset_for_ref_frame[i]=Se(buf,nLen,StartBit);
- delete [] offset_for_ref_frame;
- }
- int num_ref_frames=Ue(buf,nLen,StartBit);
- int gaps_in_frame_num_value_allowed_flag=u(,buf,StartBit);
- int pic_width_in_mbs_minus1=Ue(buf,nLen,StartBit);
- int pic_height_in_map_units_minus1=Ue(buf,nLen,StartBit);
- //width=(pic_width_in_mbs_minus1+1)*16;
- //height=(pic_height_in_map_units_minus1+1)*16;
- int frame_mbs_only_flag=u(,buf,StartBit);
- if(!frame_mbs_only_flag)
- int mb_adaptive_frame_field_flag=u(,buf,StartBit);
- int direct_8x8_inference_flag=u(,buf,StartBit);
- int frame_cropping_flag=u(,buf,StartBit);
- int frame_crop_left_offset=;
- int frame_crop_right_offset=;
- int frame_crop_top_offset=;
- int frame_crop_bottom_offset=;
- if(frame_cropping_flag)
- {
- frame_crop_left_offset=Ue(buf,nLen,StartBit);
- frame_crop_right_offset=Ue(buf,nLen,StartBit);
- frame_crop_top_offset=Ue(buf,nLen,StartBit);
- frame_crop_bottom_offset=Ue(buf,nLen,StartBit);
- }
- width = ((pic_width_in_mbs_minus1 +)*) - frame_crop_left_offset* - frame_crop_right_offset*;
- height= (( - frame_mbs_only_flag)* (pic_height_in_map_units_minus1 +) * ) - (frame_crop_top_offset * ) - (frame_crop_bottom_offset * );
- int vui_parameter_present_flag=u(,buf,StartBit);
- if(vui_parameter_present_flag)
- {
- int aspect_ratio_info_present_flag=u(,buf,StartBit);
- if(aspect_ratio_info_present_flag)
- {
- int aspect_ratio_idc=u(,buf,StartBit);
- if(aspect_ratio_idc==)
- {
- int sar_width=u(,buf,StartBit);
- int sar_height=u(,buf,StartBit);
- }
- }
- int overscan_info_present_flag=u(,buf,StartBit);
- if(overscan_info_present_flag)
- int overscan_appropriate_flagu=u(,buf,StartBit);
- int video_signal_type_present_flag=u(,buf,StartBit);
- if(video_signal_type_present_flag)
- {
- int video_format=u(,buf,StartBit);
- int video_full_range_flag=u(,buf,StartBit);
- int colour_description_present_flag=u(,buf,StartBit);
- if(colour_description_present_flag)
- {
- int colour_primaries=u(,buf,StartBit);
- int transfer_characteristics=u(,buf,StartBit);
- int matrix_coefficients=u(,buf,StartBit);
- }
- }
- int chroma_loc_info_present_flag=u(,buf,StartBit);
- if(chroma_loc_info_present_flag)
- {
- int chroma_sample_loc_type_top_field=Ue(buf,nLen,StartBit);
- int chroma_sample_loc_type_bottom_field=Ue(buf,nLen,StartBit);
- }
- int timing_info_present_flag=u(,buf,StartBit);
- if(timing_info_present_flag)
- {
- int num_units_in_tick=u(,buf,StartBit);
- int time_scale=u(,buf,StartBit);
- fps=time_scale/(*num_units_in_tick);
- }
- }
- return true;
- }
- else
- return false;
- }
参考资料:
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中提取视频宽高的更多相关文章
- 从H264码流中获取视频宽高 (SPS帧) 升级篇
之前写过 <从H264码流中获取视频宽高 (SPS帧)> . 但发现很多局限性,而且有时解出来是错误的. 所以重新去研究了. 用了 官方提供的代码库来解析. 花了点时间,从代码库里单独把解 ...
- 从H264码流中获取视频宽高 (SPS帧)
获取.h264视频宽高的方法 花了2个通宵终于搞定.(后面附上完整代码) http://write.blog.csdn.net/postedit/7852406 图像的高和宽在H264的SPS帧中.在 ...
- 多媒体开发之sps---解析sps得到图像的宽高
(1)通过块的宽高解析出真个h264的分辨率 如何解析SDP中包含的H.264的SPS和PPS串 http://www.pernet.tv.sixxs.org/thread-109-1-1.html ...
- JavaScript中的各种宽高以及位置总结
JavaScript中的各种宽高以及位置总结 在javascript中操作dom节点让其运动的时候,常常会涉及到各种宽高以及位置坐标等概念,如果不能很好地理解这些属性所代表的意义,就不能理解js的运动 ...
- LODOP中的各种宽高和位置简短问答
LODOP中的位置边距,可查看本博客另一篇相关博文:LODOOP中的各种边距 打印项.整体偏移.可打区域.内部边距关于LODOP中的各种宽高,可查看本博文简短问答下方的正文:.该文其实有两个以前的相关 ...
- AntDesign VUE:上传组件图片/视频宽高、文件大小、image/video/pdf文件类型等限制(Promise、Boolean)
文件大小限制 - Promise checkFileSize(file, rules) { return new Promise((resolve, reject) => { file.size ...
- 一步一步解析H.264码流的NALU(SPS,PSS,IDR)获取宽高和帧率
分析H.264码流的工具 CodecVisa,StreamEye以及VM Analyzer NALU是由NALU头和RBSP数据组成,而RBSP可能是SPS,PPS,Slice或SEI 而且SPS位于 ...
- 一种H.264高清视频的无参考视频质量评价算法(基于QP和跳过宏块数)
本文记录一种无参考视频质量评价算法.这是我们自己实验室前两年一个师姐做的,算法还是比较准确的,在此记录一下. 注意本算法前提是高清视频.而且是H.264编码方式. 该方法主要使用两个码流里面的参数进行 ...
- JavaScript及jQuery中的各种宽高属性图解
文/poetries(简书作者)原文链接:http://www.jianshu.com/p/60332df38393 著作权归作者所有,转载请联系作者获得授权, 并标注“简书作者”. 作者声明:本 ...
随机推荐
- Code Signal_练习题_differentSymbolsNaive
Given a string, find the number of different characters in it. Example For s = "cabca", th ...
- 获取url地址的参数值
使用频率很高,百度了记录一下 $.getUrlParam = function (name) { var reg = new RegExp("(^|&)" + name + ...
- Python os、sys、pickle、json等模块
1.os 所有和操作系统相关的内容都在os模块,一般用来操作文件系统 import os os.makedirs('dirname1/dirname2') # 可生成多层递归目录 os.removed ...
- HTTPS的安全性
一.Https介绍 1. 什么是Https HTTPS(全称:Hypertext Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP通道, ...
- 【代码笔记】iOS-4个可以单独点击的button
一,效果图. 二,工程图. 三,代码. ViewController.m #import "ViewController.h" @interface ViewController ...
- 标准工作流(AWE)邮件通知
今天遇到一个问题,UAT环境收不到流程待办,最终审批,最终拒绝等邮件. 检查了PT_WF_NOTIFICATION包的Notification类中的Send方法,发现如果app服务器在psappsrv ...
- 解读 --- 基于微软企业商务应用平台 (Microsoft Dynamics 365) 之上的人工智能 (AI) 解决方案
9月25日微软今年一年一度的Ignite 2017在佛罗里达州奥兰多市还是如期开幕了.为啥这么说?因为9月初五级飓风厄玛(Hurricane Irma) 在佛罗里达州登陆,在当地造成了挺大的麻烦.在这 ...
- Android应用程序启动过程(一)总结
一.App启动方式 1,冷启动 冷启动:当启动应用时,后台没有该应用的进程,这时系统会重新创建一个新的进程分配给该应用. 冷启动的特点:因为系统会重新创建一个新的进程分配给它,所以会创建和初始化App ...
- Android--用Valley框架去上传图片
1.除了用到了Volley,还用到了一个包httpmime(下载地址:http://download.csdn.net/detail/chequer_lkp/8102751) 2.需要一个工具类,该类 ...
- surging API
基于.NET CORE微服务框架 -谈谈surging API网关 基于.NET CORE微服务框架 -浅析如何使用surging surging 系列 NET Core 2.0 在WIN7系统 的H ...