关于h264bitstream的bug修正及完善

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

1
2
#include <vector>
using std::vector;

3、分离部分结构体

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// 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;
} 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;
} drpm_t;

slice_header_t对应的变更如下:

1
2
3
pwt_t pwt;
rplm_t rplm;
drpm_t drpm;

二、实现文件

1、

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
//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函数变更如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//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。

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

要更改为

1
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 晚