最近学习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 晚