关于h264bitstream的bug修正及完善

孙风畔
2023-12-01

最近学习HEVC,参考h264bitstream开源库重新写代码解析码流。在观察H264码流的分析结果时,发现该库分析的结果与商业工具有些不同。以前也遇到过,还写了篇文章《解决h264bitstream的一个bug》,经调试发现h264bitstream库实现上有些小问题,于是就修改修改,形成此文。

一、头文件

1、改名及新加

将sps_t结构体的residual_colour_transform_flag改名为separate_colour_plane_flag。根据最新文档,sps_t结构体新加ChromaArrayType。slice_header_t结构体添加colour_plane_id成员。

2、新加vector

去掉extern "C"的限制,添加std的vector。

#include <vector>
using std::vector;

3、分离部分结构体

将slice_header_t结构体的pwt、rplr和drpm独立出来。因为这些字段的数量不固定,使用了vector存储,代码如下:

// predictive weight table
typedef struct
{
    int luma_log2_weight_denom;
    int chroma_log2_weight_denom;
    int luma_weight_l0_flag[64];
    int luma_weight_l0[64];
    int luma_offset_l0[64];
    int chroma_weight_l0_flag[64];
    int chroma_weight_l0[64][2];
    int chroma_offset_l0[64][2];
    int luma_weight_l1_flag[64];
    int luma_weight_l1[64];
    int luma_offset_l1[64];
    int chroma_weight_l1_flag[64];
    int chroma_weight_l1[64][2];
    int chroma_offset_l1[64][2];
} pwt_t;

// ref pic list modification
typedef struct
{
    int modification_of_pic_nums_idc;
    int abs_diff_pic_num_minus1;
    int long_term_pic_num;
} rplm_tt;

typedef struct
{
    int ref_pic_list_modification_flag_l0;
    int ref_pic_list_modification_flag_l1;

    vector<rplm_tt> rplm;
} rplm_t;

// decoded ref pic marking
typedef struct
{
    int memory_management_control_operation;
    int difference_of_pic_nums_minus1;
    int long_term_pic_num;
    int long_term_frame_idx;
    int max_long_term_frame_idx_plus1;
} drpm_tt;
typedef struct
{
    int no_output_of_prior_pics_flag;
    int long_term_reference_flag;
    int adaptive_ref_pic_marking_mode_flag;

    vector<drpm_tt> drpm;
} drpm_t;


slice_header_t对应的变更如下:

pwt_t pwt;
rplm_t rplm;
drpm_t drpm;


二、实现文件

1、将read_ref_pic_list_reordering函数改名为read_ref_pic_list_modification。根据不同的数值添加到vector中。实现变更如下:

//7.3.3.1 Reference picture list modification syntax
void read_ref_pic_list_modification(h264_stream_t* h, bs_t* b)
{
    slice_header_t* sh = h->sh;
    rplm_tt rplmtt;

    if( ! is_slice_type( sh->slice_type, SH_SLICE_TYPE_I ) && ! is_slice_type( sh->slice_type, SH_SLICE_TYPE_SI ) )
    {
        sh->rplm.ref_pic_list_modification_flag_l0 = bs_read_u1(b);
        if( sh->rplm.ref_pic_list_modification_flag_l0 )
        {
            do
            {
                rplmtt.modification_of_pic_nums_idc = bs_read_ue(b);
                if( rplmtt.modification_of_pic_nums_idc == 0 ||
                    rplmtt.modification_of_pic_nums_idc == 1 )
                {
                    rplmtt.abs_diff_pic_num_minus1 = bs_read_ue(b);
                }
                else if( rplmtt.modification_of_pic_nums_idc == 2 )
                {
                    rplmtt.long_term_pic_num = bs_read_ue(b);
                }
                sh->rplm.rplm.push_back(rplmtt);
            } while( rplmtt.modification_of_pic_nums_idc != 3 && ! bs_eof(b) );
        }
    }
    if( is_slice_type( sh->slice_type, SH_SLICE_TYPE_B ) )
    {
        sh->rplm.ref_pic_list_modification_flag_l1 = bs_read_u1(b);
        if( sh->rplm.ref_pic_list_modification_flag_l1 )
        {
            do
            {
                rplmtt.modification_of_pic_nums_idc = bs_read_ue(b);
                if( rplmtt.modification_of_pic_nums_idc == 0 ||
                    rplmtt.modification_of_pic_nums_idc == 1 )
                {
                    rplmtt.abs_diff_pic_num_minus1 = bs_read_ue(b);
                }
                else if( rplmtt.modification_of_pic_nums_idc == 2 )
                {
                    rplmtt.long_term_pic_num = bs_read_ue(b);
                }
                sh->rplm.rplm.push_back(rplmtt);
            } while( rplmtt.modification_of_pic_nums_idc != 3 && ! bs_eof(b) );
        }
    }
}

2、与上类似,read_dec_ref_pic_marking函数变更如下:

//7.3.3.3 Decoded reference picture marking syntax
void read_dec_ref_pic_marking(h264_stream_t* h, bs_t* b)
{
    slice_header_t* sh = h->sh;
    drpm_tt drpmtt;

    if( h->nal->nal_unit_type == 5 )
    {
        sh->drpm.no_output_of_prior_pics_flag = bs_read_u1(b);
        sh->drpm.long_term_reference_flag = bs_read_u1(b);
    }
    else
    {
        sh->drpm.adaptive_ref_pic_marking_mode_flag = bs_read_u1(b);
        if( sh->drpm.adaptive_ref_pic_marking_mode_flag )
        {
            do
            {
                drpmtt.memory_management_control_operation = bs_read_ue(b);
                if( drpmtt.memory_management_control_operation == 1 ||
                    drpmtt.memory_management_control_operation == 3 )
                {
                    drpmtt.difference_of_pic_nums_minus1 = bs_read_ue(b);
                }
                if(drpmtt.memory_management_control_operation == 2 )
                {
                    drpmtt.long_term_pic_num = bs_read_ue(b);
                }
                if( drpmtt.memory_management_control_operation == 3 ||
                    drpmtt.memory_management_control_operation == 6 )
                {
                    drpmtt.long_term_frame_idx = bs_read_ue(b);
                }
                if( drpmtt.memory_management_control_operation == 4 )
                {
                    drpmtt.max_long_term_frame_idx_plus1 = bs_read_ue(b);
                }
                sh->drpm.drpm.push_back(drpmtt);
            } while( drpmtt.memory_management_control_operation != 0 && ! bs_eof(b) );
        }
    }
}


3、read_pred_weight_table函数中使用的num_ref_idx_l0_active_minus1为pps_t结构体的,这是错误的。正确的是使用slice_header_t结构体的num_ref_idx_l0_active_minus1。

for( i = 0; i <= pps->num_ref_idx_l0_active_minus1; i++ )

要更改为

for( i = 0; i <= sh->num_ref_idx_l0_active_minus1; i++ )


其它一些细小的修改不在此文列出。源代码见github仓库:https://github.com/latelee/H264BSAnalyzer的Branch_dev分支。具体如下:
https://github.com/latelee/H264BSAnalyzer/blob/Branch_dev/H264BSAnalyzer/h264_stream.h

https://github.com/latelee/H264BSAnalyzer/blob/Branch_dev/H264BSAnalyzer/h264_stream.cpp


李迟 2015.9.28 晚

 类似资料: