一、准备工作:
回到之前SimpleH264Analyzer
程序,找到SPS信息,并对其做解析
调整项目目录结构:
修改Global.h
文件中代码,添加新数据类型UINT16,之前编写的工程中,UINT8和UINT32都为小写表示,为了更符合编程规范,将其改为全大写(可使用ctrl+H在整个解决方案内进行替换)。
typedef unsigned char UINT8;
typedef unsigned short UINT16;
typedef unsigned int UINT32;
之后编写的程序会有越来越多的输出,如果全部输入到控制台中,会非常杂乱。因此输出变成两种方式,一种在控制台输出,另一种输出到日志文件中。步骤如下:
1 新建Configuration.h
文件,放到1.Application
目录下,添加代码:
#ifdef _CONFIGURATION_H_
#define _CONFIGURATION_H_
#include <fstream>
#define TRACE_CONFIG_CONSOLE 1
#define TRACE_CONFIG_LOGOUT 1
extern std::ofstream g_traceFile;
#endif
2 新建Configuration.cpp
,放到1.Application
目录下,添加代码:
#include "stdafx.h"
#include "Configuration.h"
#if TRACE_CONFIG_LOGOUT
std::ofstream g_traceFile;
#endif
3 在stdafx.h
中添加引用库:
#include <string>
#include "Configuration.h"
4 是否写入日志文件定义在Stream.cpp
中的构造函数中:
在 CStreamFile::CStreamFile(TCHAR * fileName) 中添加:
#if TRACE_CONFIG_LOGOUT
g_traceFile.open(L"trace.txt");
if (!g_traceFile.is_open())
{
file_error(1);
}
g_traceFile << "Trace file:" << endl;
#endif
析构函数CStreamFile::~CStreamFile()
中添加:
#ifdef TRACE_CONFIG_LOGOUT
if (g_traceFile.is_open())
{
g_traceFile.close();
}
#endif
当日志文件打开失败时,调用函数file_error(1),因此修改void CStreamFile::file_error(int idx) 函数,在其中添加错误代码1的方案:
case 1:
wcout << L"Error: opening trace file failed." << endl;
break;
完成以上配置后编译运行程序,在 \bin\Debug 目录下会生成一个trace.txt
文件,写入了这个字符串“Trace file:”
为了替换之前在控制台直接输出,在CStreamFile类中新建一个函数,首先在Stream.h文件中声明函数(private)
void dump_NAL_type(UINT8 nalType);
在Stream.cpp
中添加这个函数的实现
void CStreamFile::dump_NAL_type(UINT8 nalType)
{
#if TRACE_CONFIG_CONSOLE
wcout << L"NAL Unit Type: " << nalType << endl;
#endif
#if TRACE_CONFIG_LOGOUT
g_traceFile << "NAL Unit Type: " << to_string(nalType) << endl;
#endif
}
将 Parse_h264_bitstream() 函数中 wcout输出改为调用新函数:
dump_NAL_type(nalType);
重新编译运行,由于此时控制台和日志文件输出开关均打开,因此可在控制台和trace.txt中看到NAL Unit Type的输出
二、定义SPS类:
新建类CSeqParamSet,将生成的CSeqParamSet.h
、CSeqParamSet.cpp
放到 “3.NAL Unit” 目录下
按照上一个笔记中官方文档中提到的编码结构,将所有语法元素一一定义出来,并设置setter函数:
修改SeqParamSet.h
:
#ifndef _SEQ_PARAM_SET_H_
#define _SEQ_PARAM_SET_H_
class CSeqParamSet
{
public:
CSeqParamSet();
~CSeqParamSet();
void Set_profile_level_idc(UINT8 profile, UINT8 level);
void Set_sps_id(UINT8 spsID);
void Set_chroma_format_idc(UINT8 chromaFormatIdc);
void Set_bit_depth(UINT8 bit_depth_luma, UINT8 bit_depth_chroma);
void Set_max_frame_num(UINT32 maxFrameNum);
void Set_poc_type(UINT8 pocType);
void Set_max_poc_cnt(UINT32 maxPocCnt);
void Set_max_num_ref_frames(UINT32 maxRefFrames);
void Set_sps_multiple_flags(UINT32 flags);
void Set_pic_reslution_in_mbs(UINT16 widthInMBs, UINT16 heightInMapUnits);
void Set_frame_crop_offset(UINT32 offsets[4]);
private:
UINT8 m_profile_idc;
UINT8 m_level_idc;
UINT8 m_sps_id;
// for uncommon profile...
UINT8 m_chroma_format_idc;
bool m_separate_colour_plane_flag;
UINT8 m_bit_depth_luma;
UINT8 m_bit_depth_chroma;
bool m_qpprime_y_zero_transform_bypass_flag;
bool m_seq_scaling_matrix_present_flag;
// ...for uncommon profile
UINT32 m_max_frame_num;
UINT8 m_poc_type;
UINT32 m_max_poc_cnt;
UINT32 m_max_num_ref_frames;
bool m_gaps_in_frame_num_value_allowed_flag;
UINT16 m_pic_width_in_mbs;
UINT16 m_pic_height_in_map_units;
UINT16 m_pic_height_in_mbs; // 图像实际高度 not defined in spec, derived...
bool m_frame_mbs_only_flag;
bool m_mb_adaptive_frame_field_flag;
bool m_direct_8x8_inference_flag;
bool m_frame_cropping_flag;
UINT32 m_frame_crop_offset[4];
bool m_vui_parameters_present_flag;
// UINT32 m_reserved;
};
#endif
在SeqParamSet.cpp
文件中实现所有的setter函数,就是一个简单的赋值过程:
#include "stdafx.h"
#include "SeqParamSet.h"
CSeqParamSet::CSeqParamSet()
{
}
CSeqParamSet::~CSeqParamSet()
{
}
void CSeqParamSet::Set_profile_level_idc(UINT8 profile, UINT8 level)
{
m_profile_idc = profile;
m_level_idc = level;
}
void CSeqParamSet::Set_sps_id(UINT8 sps_id)
{
m_sps_id = sps_id;
}
void CSeqParamSet::Set_chroma_format_idc(UINT8 chromaFormatIdc)
{
m_chroma_format_idc = chromaFormatIdc;
}
void CSeqParamSet::Set_bit_depth(UINT8 bit_depth_luma, UINT8 bit_depth_chroma)
{
m_bit_depth_luma = bit_depth_luma;
m_bit_depth_chroma = bit_depth_chroma;
}
void CSeqParamSet::Set_max_frame_num(UINT32 maxFrameNum)
{
m_max_frame_num = maxFrameNum;
}
void CSeqParamSet::Set_poc_type(UINT8 pocType)
{
m_poc_type = pocType;
}
void CSeqParamSet::Set_max_poc_cnt(UINT32 maxPocCnt)
{
m_max_poc_cnt = maxPocCnt;
}
void CSeqParamSet::Set_max_num_ref_frames(UINT32 maxRefFrames)
{
m_max_num_ref_frames = maxRefFrames;
}
void CSeqParamSet::Set_sps_multiple_flags(UINT32 flags)
{
m_separate_colour_plane_flag = flags & (1 << 21);
m_qpprime_y_zero_transform_bypass_flag = flags & (1 << 20);
m_seq_scaling_matrix_present_flag = flags & (1 << 19);
m_gaps_in_frame_num_value_allowed_flag = flags & (1 << 5);
m_frame_mbs_only_flag = flags & (1 << 4);
m_mb_adaptive_frame_field_flag = flags & (1 << 3);
m_direct_8x8_inference_flag = flags & (1 << 2);
m_frame_cropping_flag = flags & (1 << 1);
m_vui_parameters_present_flag = flags & 1;
}
void CSeqParamSet::Set_pic_reslution_in_mbs(UINT16 widthInMBs, UINT16 heightInMapUnits)
{
m_pic_width_in_mbs = widthInMBs;
m_pic_height_in_map_units = heightInMapUnits;
m_pic_height_in_mbs = m_frame_mbs_only_flag ? m_pic_height_in_map_units : 2 * m_pic_height_in_map_units;
}
void CSeqParamSet::Set_frame_crop_offset(UINT32 offsets[4])
{
for (int idx = 0; idx < 4; idx++)
{
m_frame_crop_offset[idx] = offsets[idx];
}
}
三、无符号指数哥伦布数据解码:
与学习笔记9中实现的无符号指数哥伦布解码部分完全相同,仅将代码放在下面(笔记9中有详细解释):
在0.Global目录下,新建Utils.h
,定义指数哥伦布编码中两个必要的函数:
#ifndef _UTILS_H_
#define _UTILS_H_
#include "Global.h"
int Get_bit_at_position(UINT8 *buf, UINT8 &bytePosition, UINT8 &bitPosition);
int Get_uev_code_num(UINT8 *buf, UINT8 &bytePosition, UINT8 &bitPosition);
#endif
在0.Global目录下,新建Utils.cpp
,实现上面两个函数:
#include "stdafx.h"
#include "Utils.h"
// 根据bytePosition和bitPosition 获取当前比特位二进制数值 返回0/1
int Get_bit_at_position(UINT8 * buf, UINT8 & bytePosition, UINT8 & bitPosition)
{
UINT8 mask = 0, val = 0;
mask = 1 << (7 - bitPosition);
val = ((buf[bytePosition] & mask) != 0);
if (++bitPosition > 7)
{
bytePosition++;
bitPosition = 0;
}
return val;
}
// 将接下来一个指数哥伦布编码 转换成十进制数值
int Get_uev_code_num(UINT8 * buf, UINT8 & bytePosition, UINT8 & bitPosition)
{
assert(bitPosition < 8);
UINT8 val = 0, prefixZeroCount = 0;
int prefix = 0, surfix = 0;
while (true)
{
val = Get_bit_at_position(buf, bytePosition, bitPosition);
if (val == 0)
{
prefixZeroCount++;
}
else
{
break;
}
}
prefix = (1 << prefixZeroCount) - 1;
for (size_t i = 0; i < prefixZeroCount; i++)
{
val = Get_bit_at_position(buf, bytePosition, bitPosition);
surfix += val * (1 << (prefixZeroCount - i - 1));
}
prefix += surfix;
return prefix;
}
可将学习笔记9主函数中的代码复制过来进行测试,能正确输出解码结果即可。
四、解析NALUnit中SPS数据:
将UALUnit中的语法元素,按照协议规定解析为SPS中各个成员变量的值
在NALUnit.h
和NALUnit.cpp
中添加函数,**Parse_as_seq_param_set() **用于解析语法元素,代码如下。(均按照学习笔记10中官方文档顺序解析即可)
int CNalUnit::Parse_as_seq_param_set(CSeqParamSet * sps)
{
UINT8 profile_idc = 0;
UINT8 level_idc = 0;
UINT8 sps_id = 0;
UINT8 chroma_format_idc = 0;
bool separate_colour_plane_flag = 0;
UINT8 bit_depth_luma = 0;
UINT8 bit_depth_chroma = 0;
bool qpprime_y_zero_transform_bypass_flag = 0;
bool seq_scaling_matrix_present_flag = 0;
UINT32 max_frame_num = 0;
UINT8 poc_type = 0;
UINT32 max_poc_cnt = 0;
UINT32 max_num_ref_frames = 0;
bool gaps_in_frame_num_value_allowed_flag = 0;
UINT16 pic_width_in_mbs = 0;
UINT16 pic_height_in_map_units = 0;
UINT16 pic_height_in_mbs = 0; // 图像实际高度 not defined in spec, derived...
bool frame_mbs_only_flag = 0;
bool mb_adaptive_frame_field_flag = 0;
bool direct_8x8_inference_flag = 0;
bool frame_cropping_flag = 0;
UINT32 frame_crop_offset[4] = { 0 };
bool vui_parameters_present_flag = 0;
UINT8 bytePosition = 3, bitPosition = 0;
UINT32 flags = 0; //会检索到各种flag元素,每个元素占一个比特,最终按先后顺序放到flags中
profile_idc = m_pSODB[0];
// 第二个字节是constraint_set_flag 暂时用不到,空过去m_pSODB[1]
level_idc = m_pSODB[2];
sps_id = Get_uev_code_num(m_pSODB, bytePosition, bitPosition); //这里是一个无符号指数哥伦布编码,用前面写好的函数提取
if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 244 || profile_idc == 44 ||
profile_idc == 83 || profile_idc == 86 || profile_idc == 118 || profile_idc == 128)
{
chroma_format_idc = Get_uev_code_num(m_pSODB, bytePosition, bitPosition);
if (chroma_format_idc == 3)
{
separate_colour_plane_flag = Get_bit_at_position(m_pSODB, bytePosition, bitPosition);
// 提取到的单个flag,放到flag集合中的(可用的最高位上)
flags |= (separate_colour_plane_flag << 21);
}
bit_depth_luma = Get_uev_code_num(m_pSODB, bytePosition, bitPosition) + 8;
bit_depth_chroma = Get_uev_code_num(m_pSODB, bytePosition, bitPosition) + 8;
qpprime_y_zero_transform_bypass_flag = Get_bit_at_position(m_pSODB, bytePosition, bitPosition);
flags |= (qpprime_y_zero_transform_bypass_flag << 20);
seq_scaling_matrix_present_flag = Get_bit_at_position(m_pSODB, bytePosition, bitPosition);
flags |= (seq_scaling_matrix_present_flag << 19);
if (seq_scaling_matrix_present_flag)
{
// 这个部分暂时用不到,先返回一个错误码代替
return -1;
}
}
// 下面不求log2_max_frame_num,而是直接将原来的数字求出来
max_frame_num = 1 << (Get_uev_code_num(m_pSODB, bytePosition, bitPosition) + 4);
poc_type = Get_uev_code_num(m_pSODB, bytePosition, bitPosition);
if (0 == poc_type)
{
max_poc_cnt = 1 << (Get_uev_code_num(m_pSODB, bytePosition, bitPosition) + 4);
}
else
{
// 暂时不考虑这种情况
return -1;
}
max_num_ref_frames = Get_uev_code_num(m_pSODB, bytePosition, bitPosition);
gaps_in_frame_num_value_allowed_flag = Get_bit_at_position(m_pSODB, bytePosition, bitPosition);
flags |= (gaps_in_frame_num_value_allowed_flag << 5); //中间跳过了好多位,为本该有却没实现的flag留出位置
pic_width_in_mbs = Get_uev_code_num(m_pSODB, bytePosition, bitPosition) + 1;
pic_height_in_map_units = Get_uev_code_num(m_pSODB, bytePosition, bitPosition) + 1;
frame_mbs_only_flag = Get_bit_at_position(m_pSODB, bytePosition, bitPosition);
flags |= (frame_mbs_only_flag << 4);
if (!frame_mbs_only_flag)
{
mb_adaptive_frame_field_flag = Get_bit_at_position(m_pSODB, bytePosition, bitPosition);
flags |= (mb_adaptive_frame_field_flag << 3);
}
direct_8x8_inference_flag = Get_bit_at_position(m_pSODB, bytePosition, bitPosition);
flags |= (direct_8x8_inference_flag << 2);
frame_cropping_flag = Get_bit_at_position(m_pSODB, bytePosition, bitPosition);
flags |= (direct_8x8_inference_flag << 1);
if (frame_cropping_flag)
{
for (int idx = 0; idx < 4; idx++)
{
frame_crop_offset[idx] = Get_uev_code_num(m_pSODB, bytePosition, bitPosition);
}
}
vui_parameters_present_flag = Get_bit_at_position(m_pSODB, bytePosition, bitPosition);
flags |= vui_parameters_present_flag;
// 解析码流完成
sps->Set_profile_level_idc(profile_idc, level_idc);
sps->Set_sps_id(sps_id);
sps->Set_chroma_format_idc(chroma_format_idc);
sps->Set_bit_depth(bit_depth_luma, bit_depth_chroma);
sps->Set_max_frame_num(max_frame_num);
sps->Set_poc_type(poc_type);
sps->Set_max_poc_cnt(max_poc_cnt);
sps->Set_max_num_ref_frames(max_num_ref_frames);
sps->Set_sps_multiple_flags(flags);
sps->Set_pic_reslution_in_mbs(pic_width_in_mbs, pic_height_in_map_units);
if (frame_cropping_flag)
{
sps->Set_frame_crop_offset(frame_crop_offset);
}
return 0;
}
五、添加调用部分:
回到Stream.cpp
中,找到Parse_h264_bitstream() 函数,在学习笔记6中,已经完成了nalType的提取,并得到了SODB数据,在后面添加解析序列参数集sps的部分。
CNalUnit nalUint(&m_nalVec[1], m_nalVec.size() - 1);
switch (nalType)
{
case 7:
// 解析SPS NAL 数据
if (m_sps)
{
delete m_sps;
}
m_sps = new CSeqParamSet;
nalUint.Parse_as_seq_param_set(m_sps);
break;
default:
break;
}
可对其进行单步调试,重点看这两个参数 pic_width_in_mbs,pic_height_in_map_units,分别是以宏块为单位的宽、高分辨率。本次调试使用的视频仍是学习笔记3使用的视频,之前设置的参数为:
SourceWidth = 176 # Image width in Pels, must be multiple of 16
SourceHeight = 144 # Image height in Pels, must be multiple of 16
宏块分辨率要在原来基础上除16,即宽11、高9。这两个参数吻合,基本表明程序没有问题。
【视频编解码·学习笔记】11. 提取SPS信息程序的更多相关文章
-
【视频编解码&#183;学习笔记】13. 提取PPS信息程序
PPS结构解析 与之前解析SPS方式类似 一.定义PPS类: 在3.NAL Unit目录下,新建PicParamSet.cpp和PicParamSet.h,在这两个文件中写入类的定义和函数实现. 类定 ...
-
【视频编解码&#183;学习笔记】8. 熵编码算法:基本算法列举 &; 指数哥伦布编码
一.H.264中的熵编码基本方法: 熵编码具有消除数据之间统计冗余的功能,在编码端作为最后一道工序,将语法元素写入输出码流 熵解码作为解码过程的第一步,将码流解析出语法元素供后续步骤重建图像使用 在H ...
-
【视频编解码&#183;学习笔记】5. NAL Unit 结构分析
在上篇笔记中通过一个小程序,可以提取NAL Unit所包含的的字节数据.H.264码流中的每一个NAL Unit的作用并不是相同的,而是根据不同的类型起不同的作用.下面将对NAL Unit中的数据进行 ...
-
【视频编解码&#183;学习笔记】6. H.264码流分析工程创建
一.准备工作: 新建一个VS工程SimpleH264Analyzer, 修改工程属性参数-> 输出目录:$(SolutionDir)bin\$(Configuration)\,工作目录:$(So ...
-
【视频编解码&#183;学习笔记】3. H.264视频编解码工程JM的下载与编解码
一.下载JM工程: JM是H.264标准制定团队所认可的官方参考软件.网址如下 http://iphome.hhi.de/suehring/tml/ 从页面中可找到相应的工程源码,本次选择JM 8.6 ...
-
【视频编解码&#183;学习笔记】10. 序列参数集(SPS)介绍
一.SPS 相关概念: SPS即 "Sequence Paramater Set",又称作序列参数集. SPS中保存了一组编码视频序列(Coded video sequence)的 ...
-
【视频编解码&#183;学习笔记】4. H.264的码流封装格式 &; 提取NAL有效数据
一.码流封装格式简单介绍: H.264的语法元素进行编码后,生成的输出数据都封装为NAL Unit进行传递,多个NAL Unit的数据组合在一起形成总的输出码流.对于不同的应用场景,NAL规定了一种通 ...
-
【视频编解码&#183;学习笔记】12. 图像参数集(PPS)介绍
一.PPS相关概念: 除了序列参数集SPS之外,H.264中另一重要的参数集合为图像参数集Picture Paramater Set(PPS). 通常情况下,PPS类似于SPS,在H.264的裸码流中 ...
-
【视频编解码&#183;学习笔记】4. H.264的码流封装格式
一.码流封装格式简单介绍: H.264的语法元素进行编码后,生成的输出数据都封装为NAL Unit进行传递,多个NAL Unit的数据组合在一起形成总的输出码流.对于不同的应用场景,NAL规定了一种通 ...
随机推荐
-
ligerui多选动态下拉框
今天下午要求做一个支持多选的,并且插件用ligerui的,当时有点小懵了,因为没用过ligerui啊!而且按照API的介绍,我做得也很好啊,可是为什么就是显示不出来?据说有位小神比较厉害,请教来之,两 ...
-
【linux】暂时解决sis m672(神舟F4000 D9) linux驱动 宽屏分辨率的问题?
1. 首先安装包 sudo apt-get install gcc make binutils git xorg-dev mesa-common-dev libdrm-dev libtool buil ...
-
MVC客户端验证配置
<appSettings> <add key="ClientValidationEnabled" value="true"/> < ...
-
Hao123这个流氓
Author:KillerLegend Date:2014.2.27 From:http://www.cnblogs.com/killerlegend/p/3572591.html Hao123真让人 ...
-
Dear Project Manager, I Hate You
项目经理,我恨你,而且我知道你也恨我.我真的不理解,你究竟是做什么的. 你是一个多么独特的角色呀,几乎每个公司都要雇用你这样的人.可在不管大大小小的项目中,你与其说是帮忙,不如说是添乱.我坚信,大部分 ...
-
javascript中的function对象
function对象都是Function的实例: > Object.getOwnPropertyNames(Function) [ 'length', 'name', 'arguments', ...
-
hdu-1010 dfs+剪枝
思路: 剪枝的思路参考博客:http://www.cnblogs.com/zibuyu/archive/2012/08/17/2644396.html 在其基础之上有所改进 题意可以给抽象成给出一个 ...
-
Struts支持的contentType
'ez' => 'application/andrew-inset', 'hqx' => 'application/mac-binhex40', 'cpt' => 'applicat ...
-
python 多进程 logging:ConcurrentLogHandler
python 多进程 logging:ConcurrentLogHandler python的logging模块RotatingFileHandler仅仅是线程安全的,如果多进程多线程使用,推荐 Co ...
-
Dubbo+Nacos做注册中心和配置中心
项目结构 相关代码 EchoService public interface EchoService { String echo(String msg); } DefaultEchoService @ ...