PPS structure analysis
is similar to the previous analysis of SPS
First, define the PPS class:
In the 3.NAL Unit
directory, create a new file PicParamSet.cpp
and PicParamSet.h
write the class definition and function implementation in these two files.
The class definition is written in the PicParamSet.h
file, defines the syntax element variable to be parsed, and defines the corresponding setter function. The code is as follows:
#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
The specific implementation of the setter function is written in PicParamSet.cpp
, which are all simple set assignment methods. All the flag bits are still stored in a flag bit by bit and parsed from it. The code is as follows:
#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);
}
2. Parse the PPS data in NALUnit:
1. Add a function for parsing signed exponential Golomb encoding:
Since the PPS syntax element contains signed exponential Golomb encoded data, a new function is created here. Get_sev_code_num
Unsigned exponential Golomb encoding (k) is converted to a signed (n) formula: \(n = (-1)^{(k+1 )} \times 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. Get the value of each member variable in PPS:
Add functions in NALUnit.h and NALUnit.cpp, Parse_as_seq_param_set() is used to parse syntax elements, the code is as follows. (All can be parsed in the order of the official documents in Study Notes 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;
}
3. Add the calling part:
Go back Stream.cpp
to , find the switch (nalType) conditional branch in the Parse_h264_bitstream() function, and add the part that parses the sequence parameter set pps later:
case 8:
// 解析PPS NAL 数据
if (m_pps)
{
delete m_pps;
}
m_pps = new CPicParamSet;
nalUint.Parse_as_pic_param_set(m_pps);
break;