zoukankan      html  css  js  c++  java
  • 【视频编解码·学习笔记】13. 提取PPS信息程序

    PPS结构解析
    与之前解析SPS方式类似

    一、定义PPS类:

    3.NAL Unit目录下,新建PicParamSet.cppPicParamSet.h,在这两个文件中写入类的定义和函数实现。

    类定义写在PicParamSet.h文件中,定义待解析语法元素变量,并定义相应的setter函数,代码如下:

    #ifndef _PICPARAM_SET_H
    #define _PICPARAM_SET_H
    
    class CPicParamSet
    {
    public:
    	CPicParamSet();
    	~CPicParamSet();
    
    	void Set_pps_id(UINT8 ppsID);
    	void Set_sps_id(UINT8 spsID);
    	void Set_num_slice_groups(UINT8 num_slice_grops);
    	void Set_num_ref_idx(UINT8 l0, UINT8 l1);
    	void Set_weighted_bipred_idc(UINT8 weighted_bipred_idc);
    	void Set_pic_init_qp(int pic_init_qp);
    	void Set_pic_init_qs(int pic_init_qs);
    	void Set_chroma_qp_index_offset(int chroma_qp_index_offset);
    	void Set_multiple_flags(UINT16 flags);
    
    private:
    	UINT8  m_pps_id;
    	UINT8  m_sps_id;
    	bool   m_entropy_coding_flag;
    	bool   m_bottom_field_pic_order_in_frame_present_flag;
    	UINT8  m_num_slice_groups;
    	UINT8  m_num_ref_idx_l0_default_active;
    	UINT8  m_num_ref_idx_l1_default_active;
    	bool   m_weighted_pred_flag;
    	UINT8  m_weighted_bipred_idc;
    	int    m_pic_init_qp;
    	int    m_pic_init_qs;
    	int    m_chroma_qp_index_offset;
    	bool   m_deblocking_filter_control_present_flag;
    	bool   m_constrained_intra_pred_flag;
    	bool   m_redundant_pic_cnt_present_flag;
    	bool   m_transform_8x8_mode_flag;
    };
    
    #endif // !_PICPARAM_SET_H
    

    setter函数具体实现写在PicParamSet.cpp中,均为简单的set赋值方法,所有的标志位仍按位存在一个flag中,并从中解析,代码如下:

    #include "stdafx.h"
    #include "PicParamSet.h"
    
    CPicParamSet::CPicParamSet()
    {
    }
    
    CPicParamSet::~CPicParamSet()
    {
    }
    
    void CPicParamSet::Set_pps_id(UINT8 ppsID)
    {
    	m_pps_id = ppsID;
    }
    
    void CPicParamSet::Set_sps_id(UINT8 spsID)
    {
    	m_sps_id = spsID;
    }
    
    void CPicParamSet::Set_num_slice_groups(UINT8 num_slice_grops)
    {
    	m_num_slice_groups = num_slice_grops;
    }
    
    void CPicParamSet::Set_num_ref_idx(UINT8 l0, UINT8 l1)
    {
    	m_num_ref_idx_l0_default_active = l0;
    	m_num_ref_idx_l1_default_active = l1;
    }
    
    void CPicParamSet::Set_weighted_bipred_idc(UINT8 weighted_bipred_idc)
    {
    	m_weighted_bipred_idc = weighted_bipred_idc;
    }
    
    void CPicParamSet::Set_pic_init_qp(int pic_init_qp)
    {
    	m_pic_init_qp = pic_init_qp;
    }
    
    void CPicParamSet::Set_pic_init_qs(int  pic_init_qs)
    {
    	m_pic_init_qs = pic_init_qs;
    }
    
    void CPicParamSet::Set_chroma_qp_index_offset(int chroma_qp_index_offset)
    {
    	m_chroma_qp_index_offset = chroma_qp_index_offset;
    }
    
    void CPicParamSet::Set_multiple_flags(UINT16 flags)
    {
    	m_entropy_coding_flag = flags & 1;
    	m_bottom_field_pic_order_in_frame_present_flag = flags & (1 << 1);
    	m_weighted_pred_flag = flags & (1 << 2);
    	m_deblocking_filter_control_present_flag = flags & (1 << 3);
    	m_constrained_intra_pred_flag = flags & (1 << 4);
    	m_redundant_pic_cnt_present_flag = flags & (1 << 5);
    }
    

    二、解析NALUnit中PPS数据:

    1. 添加解析有符号指数哥伦布编码函数:

    由于PPS语法元素中包含有符号指数哥伦布编码的数据,这里新建一个函数Get_sev_code_num
    无符号指数哥伦布编码(k)转为有符号的(n)公式:(n = (-1)^{(k+1)} imes Ceil(k / 2))

    int Get_sev_code_num(UINT8 * buf, UINT8 & bytePosition, UINT8 & bitPosition)
    {
    	int uev = Get_uev_code_num(buf, bytePosition, bitPosition);
    	int sign = (uev % 2) ? 1 : -1;
    	int sev = sign * ((uev + 1) >> 1);
    	return sev;
    }
    

    2. 获取PPS中各个成员变量的值:

    在NALUnit.h和NALUnit.cpp中添加函数,Parse_as_seq_param_set() 用于解析语法元素,代码如下。(均按照学习笔记12中官方文档顺序解析即可)

    int CNalUnit::Parse_as_pic_param_set(CPicParamSet * pps)
    {
    	UINT8  pps_id = 0;
    	UINT8  sps_id = 0;
    	bool   entropy_coding_flag = 0;
    	bool   bottom_field_pic_order_in_frame_present_flag = 0;
    	UINT8  num_slice_groups = 0;
    	UINT8  num_ref_idx_l0_default_active = 0;
    	UINT8  num_ref_idx_l1_default_active = 0;
    	bool   weighted_pred_flag = 0;
    	UINT8  weighted_bipred_idc = 0;
    	int    pic_init_qp = 0;
    	int    pic_init_qs = 0;
    	int    chroma_qp_index_offset = 0;
    	bool   deblocking_filter_control_present_flag = 0;
    	bool   constrained_intra_pred_flag = 0;
    	bool   redundant_pic_cnt_present_flag = 0;
    
    
    	UINT8 bitPosition = 0;
    	UINT8 bytePosition = 0;
    	UINT16 flags = 0;
    
    	pps_id = Get_uev_code_num(m_pSODB, bytePosition, bitPosition);
    	sps_id = Get_uev_code_num(m_pSODB, bytePosition, bitPosition);
    
    	entropy_coding_flag = Get_bit_at_position(m_pSODB, bytePosition, bitPosition);
    	flags |= entropy_coding_flag;
    	bottom_field_pic_order_in_frame_present_flag = Get_bit_at_position(m_pSODB, bytePosition, bitPosition);
    	flags |= bottom_field_pic_order_in_frame_present_flag << 1;
    
    	num_slice_groups = Get_uev_code_num(m_pSODB, bytePosition, bitPosition) + 1;
    
    	if (1 != num_slice_groups)
    	{
    		return -1;
    	}
    
    	num_ref_idx_l0_default_active = Get_uev_code_num(m_pSODB, bytePosition, bitPosition) + 1;
    	num_ref_idx_l1_default_active = Get_uev_code_num(m_pSODB, bytePosition, bitPosition) + 1;
    
    	weighted_pred_flag = Get_bit_at_position(m_pSODB, bytePosition, bitPosition);
    	flags |= weighted_pred_flag << 2;
    
    	// 这里是获取连续两个比特位数据——把第一次获取到的左移1位,再加上第二次获取到的
    	weighted_bipred_idc = Get_bit_at_position(m_pSODB, bytePosition, bitPosition) << 1 + Get_bit_at_position(m_pSODB, bytePosition, bitPosition);
    
    	pic_init_qp = Get_sev_code_num(m_pSODB, bytePosition, bitPosition) + 26;
    	pic_init_qs = Get_sev_code_num(m_pSODB, bytePosition, bitPosition) + 26;
    	chroma_qp_index_offset = Get_sev_code_num(m_pSODB, bytePosition, bitPosition);
    
    	deblocking_filter_control_present_flag = Get_bit_at_position(m_pSODB, bytePosition, bitPosition);
    	flags |= deblocking_filter_control_present_flag << 3;
    	constrained_intra_pred_flag = Get_bit_at_position(m_pSODB, bytePosition, bitPosition);
    	flags |= constrained_intra_pred_flag << 4;
    	redundant_pic_cnt_present_flag = Get_bit_at_position(m_pSODB, bytePosition, bitPosition);
    	flags |= redundant_pic_cnt_present_flag << 5;
    
    	pps->Set_pps_id(pps_id);
    	pps->Set_sps_id(sps_id);
    	pps->Set_num_slice_groups(num_slice_groups);
    	pps->Set_num_ref_idx(num_ref_idx_l0_default_active, num_ref_idx_l1_default_active);
    	pps->Set_weighted_bipred_idc(weighted_bipred_idc);
    	pps->Set_pic_init_qp(pic_init_qp);
    	pps->Set_pic_init_qs(pic_init_qs);
    	pps->Set_chroma_qp_index_offset(chroma_qp_index_offset);
    	pps->Set_multiple_flags(flags);
    
    	return 0;
    }
    

    三、添加调用部分:

    回到Stream.cpp中,找到Parse_h264_bitstream() 函数中switch (nalType)条件分支,在后面添加解析序列参数集pps的部分:

    case 8:
    	// 解析PPS NAL 数据
    	if (m_pps)
    	{
    		delete m_pps;
    	}
    	m_pps = new CPicParamSet;
    	nalUint.Parse_as_pic_param_set(m_pps);
    	break;
    
  • 相关阅读:
    JQuery:自动触发事件
    SQL Server 取日期时间部分
    使用IIS 7.0 / 7.5 时配置HttpModules需要注意
    Winform:中直接打开指定文件
    jQuery 时间获取扩展
    喵星史话(一)——猫的起源
    2013年的环法
    ie8下奇怪的问题:float:left之后,右侧的div会影响左侧
    虚假IP和DNS污染
    android中setBackgroundResource和setBackgroundDrawable和用法
  • 原文地址:https://www.cnblogs.com/shuofxz/p/8551519.html
Copyright © 2011-2022 走看看