[Video codec · study notes] 13. Extract PPS information program

PPS structure analysis
is similar to the previous analysis of SPS

First, define the PPS class:

In the 3.NAL Unitdirectory, create a new file PicParamSet.cppand PicParamSet.hwrite the class definition and function implementation in these two files.

The class definition is written in the PicParamSet.hfile, 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.cppto , 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;

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324376929&siteId=291194637