【x264视频编码器应用与实现】七. x264结构句柄和编码器对象


摘要:

本文作为 “x264视频编码器应用与实现” 系列博文的第七篇,主要讨论x264编码器实例的参数初始化过程。


1. 打开编码器

在配置参数、分配图像结构的内存空间完成后,程序将执行一个至关重要的步骤,即打开编码器。打开编码器通过 x264_encoder_open 实现。该函数实现位于 api.c 中:

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
REALIGN_STACK x264_t *x264_encoder_open( x264_param_t *param )
{
x264_api_t *api = calloc( 1, sizeof( x264_api_t ) );
if( !api )
return NULL;

if( HAVE_BITDEPTH8 && param->i_bitdepth == 8 )
{
api->nal_encode = x264_8_nal_encode;
api->encoder_reconfig = x264_8_encoder_reconfig;
api->encoder_parameters = x264_8_encoder_parameters;
api->encoder_headers = x264_8_encoder_headers;
api->encoder_encode = x264_8_encoder_encode;
api->encoder_close = x264_8_encoder_close;
api->encoder_delayed_frames = x264_8_encoder_delayed_frames;
api->encoder_maximum_delayed_frames = x264_8_encoder_maximum_delayed_frames;
api->encoder_intra_refresh = x264_8_encoder_intra_refresh;
api->encoder_invalidate_reference = x264_8_encoder_invalidate_reference;

api->x264 = x264_8_encoder_open( param, api );
}
else if( HAVE_BITDEPTH10 && param->i_bitdepth == 10 )
{
api->nal_encode = x264_10_nal_encode;
api->encoder_reconfig = x264_10_encoder_reconfig;
api->encoder_parameters = x264_10_encoder_parameters;
api->encoder_headers = x264_10_encoder_headers;
api->encoder_encode = x264_10_encoder_encode;
api->encoder_close = x264_10_encoder_close;
api->encoder_delayed_frames = x264_10_encoder_delayed_frames;
api->encoder_maximum_delayed_frames = x264_10_encoder_maximum_delayed_frames;
api->encoder_intra_refresh = x264_10_encoder_intra_refresh;
api->encoder_invalidate_reference = x264_10_encoder_invalidate_reference;

api->x264 = x264_10_encoder_open( param, api );
}
else
x264_log_internal( X264_LOG_ERROR, "not compiled with %d bit depth support\n", param->i_bitdepth );

if( !api->x264 )
{
free( api );
return NULL;
}

/* x264_t is opaque */
return (x264_t *)api;
}

该函数实现的第一行创建了一个x264_api_t类型的结构实例,从名称可猜到该结构与x264的编码API有关,事实确实如此。该结构的定义如下,其每个成员的含义在注释中给出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
typedef struct x264_api_t
{
/* Internal reference to x264_t data */
x264_t *x264; // x264编码器实例句柄

/* API entry points */
void (*nal_encode)( x264_t *h, uint8_t *dst, x264_nal_t *nal ); // 将码流packet整理为nal规定格式;
int (*encoder_reconfig)( x264_t *, x264_param_t * ); // 根据输入参数重新配置编码器实例
void (*encoder_parameters)( x264_t *, x264_param_t * ); // 保存编码参数信息
int (*encoder_headers)( x264_t *, x264_nal_t **pp_nal, int *pi_nal ); // 保存视频头信息
int (*encoder_encode)( x264_t *, x264_nal_t **pp_nal, int *pi_nal, x264_picture_t *pic_in, x264_picture_t *pic_out ); // 编码图像帧
void (*encoder_close)( x264_t * ); // 关闭编码器
int (*encoder_delayed_frames)( x264_t * ); // 检测编码器中的缓存帧数量
int (*encoder_maximum_delayed_frames)( x264_t * ); // 当前设置下编码器最大可缓存的帧数
void (*encoder_intra_refresh)( x264_t * ); // 将下一帧编码为关键帧
int (*encoder_invalidate_reference)( x264_t *, int64_t pts ); // 废弃某一帧及其他参考该帧的数据
} x264_api_t;

从该函数的实现可知,其主要功能即为指定x264_api_t结构中的各个函数指针,并且通过调用x264_x_encoder_open来获取x264_t结构实例并赋予x264_api_t中的成员。其中当BIT_DEPTH的值为8时,x264_8_encoder_open实际指向的是encoder.c中的x264_encoder_open函数。该函数位于encoder.c的1446~1781行,其中包含了相当复杂的功能,具体的解析在下节详述。


2. 数据结构初始化并验证参数

在上一节讨论的函数encoder_open中调用以下函数实现实际的打开编码器操作:

1
api->x264 = x264_8_encoder_open( param, api );

而x264_8_encoder_open实际上对应的是源文件encoder.c中的函数x264_encoder_open,通过宏定义的方式与调用函数对应。在x264_encoder_open的开始,首先进行的是分配并初始化相应的数据结构,然后验证输出参数的合法性:

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
x264_t *x264_encoder_open( x264_param_t *param, void *api )
{
x264_t *h;
char buf[1000], *p;
int i_slicetype_length;

// 分配x264_t实例h的内存空间
CHECKED_MALLOCZERO( h, sizeof(x264_t) );

// 将输入参数拷贝到h结构中
memcpy( &h->param, param, sizeof(x264_param_t) );

if( param->param_free )
param->param_free( param );

#if HAVE_INTEL_DISPATCHER
x264_intel_dispatcher_override();
#endif

// 线程初始化
if( x264_threading_init() )
{
x264_log( h, X264_LOG_ERROR, "unable to initialize threading\n" );
goto fail;
}

// 验证参数
if( validate_parameters( h, 1 ) < 0 )
goto fail;

// ......
}

参数验证的函数validate_parameters同样定义与encoder.c中,位于429~1349行。具体功能如下:

2.1 验证宽高和颜色空间

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
static int validate_parameters( x264_t *h, int b_open )
{
// ......
// 如果参数中的宽高为负,则返回错误
if( h->param.i_width <= 0 || h->param.i_height <= 0 )
{
x264_log( h, X264_LOG_ERROR, "invalid width x height (%dx%d)\n",
h->param.i_width, h->param.i_height );
return -1;
}

// 验证颜色空间参数
int i_csp = h->param.i_csp & X264_CSP_MASK;
#if X264_CHROMA_FORMAT
if( CHROMA_FORMAT != CHROMA_400 && i_csp == X264_CSP_I400 )
{
x264_log( h, X264_LOG_ERROR, "not compiled with 4:0:0 support\n" );
return -1;
}
else if( CHROMA_FORMAT != CHROMA_420 && i_csp >= X264_CSP_I420 && i_csp < X264_CSP_I422 )
{
x264_log( h, X264_LOG_ERROR, "not compiled with 4:2:0 support\n" );
return -1;
}
else if( CHROMA_FORMAT != CHROMA_422 && i_csp >= X264_CSP_I422 && i_csp < X264_CSP_I444 )
{
x264_log( h, X264_LOG_ERROR, "not compiled with 4:2:2 support\n" );
return -1;
}
else if( CHROMA_FORMAT != CHROMA_444 && i_csp >= X264_CSP_I444 && i_csp <= X264_CSP_RGB )
{
x264_log( h, X264_LOG_ERROR, "not compiled with 4:4:4 support\n" );
return -1;
}
#endif
if( i_csp <= X264_CSP_NONE || i_csp >= X264_CSP_MAX )
{
x264_log( h, X264_LOG_ERROR, "invalid CSP (only I400/I420/YV12/NV12/NV21/I422/YV16/NV16/YUYV/UYVY/"
"I444/YV24/BGR/BGRA/RGB supported)\n" );
return -1;
}

// 验证宽高能否被 mod 整除
int w_mod = 1;
int h_mod = 1 << (PARAM_INTERLACED || h->param.b_fake_interlaced);
if( i_csp == X264_CSP_I400 )
{
h->param.analyse.i_chroma_qp_offset = 0;
h->param.analyse.b_chroma_me = 0;
h->param.vui.i_colmatrix = 2; /* undefined */
}
else if( i_csp < X264_CSP_I444 )
{
w_mod = 2;
if( i_csp < X264_CSP_I422 )
h_mod *= 2;
}

if( h->param.i_width % w_mod )
{
x264_log( h, X264_LOG_ERROR, "width not divisible by %d (%dx%d)\n",
w_mod, h->param.i_width, h->param.i_height );
return -1;
}
if( h->param.i_height % h_mod )
{
x264_log( h, X264_LOG_ERROR, "height not divisible by %d (%dx%d)\n",
h_mod, h->param.i_width, h->param.i_height );
return -1;
}
// ......
}

2.2 验证图像切割与纵横比参数

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
static int validate_parameters( x264_t *h, int b_open )
{
// ......
// 验证crop_rect参数
if( h->param.crop_rect.i_left >= h->param.i_width ||
h->param.crop_rect.i_right >= h->param.i_width ||
h->param.crop_rect.i_top >= h->param.i_height ||
h->param.crop_rect.i_bottom >= h->param.i_height ||
h->param.crop_rect.i_left + h->param.crop_rect.i_right >= h->param.i_width ||
h->param.crop_rect.i_top + h->param.crop_rect.i_bottom >= h->param.i_height )
{
x264_log( h, X264_LOG_ERROR, "invalid crop-rect %u,%u,%u,%u\n", h->param.crop_rect.i_left,
h->param.crop_rect.i_top, h->param.crop_rect.i_right, h->param.crop_rect.i_bottom );
return -1;
}
if( h->param.crop_rect.i_left % w_mod || h->param.crop_rect.i_right % w_mod ||
h->param.crop_rect.i_top % h_mod || h->param.crop_rect.i_bottom % h_mod )
{
x264_log( h, X264_LOG_ERROR, "crop-rect %u,%u,%u,%u not divisible by %dx%d\n", h->param.crop_rect.i_left,
h->param.crop_rect.i_top, h->param.crop_rect.i_right, h->param.crop_rect.i_bottom, w_mod, h_mod );
return -1;
}

//当sar_width和sar_height非正数时,均设为0
if( h->param.vui.i_sar_width <= 0 || h->param.vui.i_sar_height <= 0 )
{
h->param.vui.i_sar_width = 0;
h->param.vui.i_sar_height = 0;
}
// ......
}

2.3 验证线程与OpenCL参数

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
53
54
55
56
57
58
59
60
61
62
static int validate_parameters( x264_t *h, int b_open )
{
// ......
// 检查线程参数
if( h->param.i_threads == X264_THREADS_AUTO )
{
h->param.i_threads = x264_cpu_num_processors() * (h->param.b_sliced_threads?2:3)/2;
/* Avoid too many threads as they don't improve performance and
* complicate VBV. Capped at an arbitrary 2 rows per thread. */
// 至少两行宏块分配一个线程
int max_threads = X264_MAX( 1, (h->param.i_height+15)/16 / 2 );
h->param.i_threads = X264_MIN( h->param.i_threads, max_threads );
}

// 至少每4行宏块定义一个sliced_thread
int max_sliced_threads = X264_MAX( 1, (h->param.i_height+15)/16 / 4 );
if( h->param.i_threads > 1 )
{
#if !HAVE_THREAD
x264_log( h, X264_LOG_WARNING, "not compiled with thread support!\n");
h->param.i_threads = 1;
#endif
/* Avoid absurdly small thread slices as they can reduce performance
* and VBV compliance. Capped at an arbitrary 4 rows per thread. */
// 确保线程数不超过max_sliced_threads
if( h->param.b_sliced_threads )
h->param.i_threads = X264_MIN( h->param.i_threads, max_sliced_threads );
}
h->param.i_threads = x264_clip3( h->param.i_threads, 1, X264_THREAD_MAX );
if( h->param.i_threads == 1 )
{
h->param.b_sliced_threads = 0;
h->param.i_lookahead_threads = 1;
}
h->i_thread_frames = h->param.b_sliced_threads ? 1 : h->param.i_threads;
if( h->i_thread_frames > 1 )
h->param.nalu_process = NULL;

// 验证opencl参数
if( h->param.b_opencl )
{
#if !HAVE_OPENCL
x264_log( h, X264_LOG_WARNING, "OpenCL: not compiled with OpenCL support, disabling\n" );
h->param.b_opencl = 0;
#elif BIT_DEPTH > 8
x264_log( h, X264_LOG_WARNING, "OpenCL lookahead does not support high bit depth, disabling opencl\n" );
h->param.b_opencl = 0;
#else
if( h->param.i_width < 32 || h->param.i_height < 32 )
{
x264_log( h, X264_LOG_WARNING, "OpenCL: frame size is too small, disabling opencl\n" );
h->param.b_opencl = 0;
}
#endif
if( h->param.opencl_device_id && h->param.i_opencl_device )
{
x264_log( h, X264_LOG_WARNING, "OpenCL: device id and device skip count configured; dropping skip\n" );
h->param.i_opencl_device = 0;
}
}
// ......
}

2.4 关键帧间隔与frame_packing

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
// ......
// 修正关键帧最大间隔
h->param.i_keyint_max = x264_clip3( h->param.i_keyint_max, 1, X264_KEYINT_MAX_INFINITE );
if( h->param.i_keyint_max == 1 )
{
// 全I帧编码
h->param.b_intra_refresh = 0;
h->param.analyse.i_weighted_pred = 0;
h->param.i_frame_reference = 1;
h->param.i_dpb_size = 1;
}

// 立体视频中帧打包格式
if( h->param.i_frame_packing < -1 || h->param.i_frame_packing > 7 )
{
x264_log( h, X264_LOG_WARNING, "ignoring unknown frame packing value\n" );
h->param.i_frame_packing = -1;
}
if( h->param.i_frame_packing == 7 &&
((h->param.i_width - h->param.crop_rect.i_left - h->param.crop_rect.i_right) % 3 ||
(h->param.i_height - h->param.crop_rect.i_top - h->param.crop_rect.i_bottom) % 3) )
{
x264_log( h, X264_LOG_ERROR, "cropped resolution %dx%d not compatible with tile format frame packing\n",
h->param.i_width - h->param.crop_rect.i_left - h->param.crop_rect.i_right,
h->param.i_height - h->param.crop_rect.i_top - h->param.crop_rect.i_bottom );
return -1;
}
// ......

2.5 码率控制相关参数

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
  // ......
//
h->param.rc.f_rf_constant = x264_clip3f( h->param.rc.f_rf_constant, -QP_BD_OFFSET, 51 );
h->param.rc.f_rf_constant_max = x264_clip3f( h->param.rc.f_rf_constant_max, -QP_BD_OFFSET, 51 );
h->param.rc.i_qp_constant = x264_clip3( h->param.rc.i_qp_constant, -1, QP_MAX );
h->param.analyse.i_subpel_refine = x264_clip3( h->param.analyse.i_subpel_refine, 0, 11 );
h->param.rc.f_ip_factor = X264_MAX( h->param.rc.f_ip_factor, 0.01f );
h->param.rc.f_pb_factor = X264_MAX( h->param.rc.f_pb_factor, 0.01f );
if( h->param.rc.i_rc_method == X264_RC_CRF )
{
h->param.rc.i_qp_constant = h->param.rc.f_rf_constant + QP_BD_OFFSET;
h->param.rc.i_bitrate = 0;
}
if( b_open && (h->param.rc.i_rc_method == X264_RC_CQP || h->param.rc.i_rc_method == X264_RC_CRF)
&& h->param.rc.i_qp_constant == 0 )
{
h->mb.b_lossless = 1;
h->param.i_cqm_preset = X264_CQM_FLAT;
h->param.psz_cqm_file = NULL;
h->param.rc.i_rc_method = X264_RC_CQP;
h->param.rc.f_ip_factor = 1;
h->param.rc.f_pb_factor = 1;
h->param.analyse.b_psnr = 0;
h->param.analyse.b_ssim = 0;
h->param.analyse.i_chroma_qp_offset = 0;
h->param.analyse.i_trellis = 0;
h->param.analyse.b_fast_pskip = 0;
h->param.analyse.i_noise_reduction = 0;
h->param.analyse.b_psy = 0;
h->param.i_bframe = 0;
/* 8x8dct is not useful without RD in CAVLC lossless */
if( !h->param.b_cabac && h->param.analyse.i_subpel_refine < 6 )
h->param.analyse.b_transform_8x8 = 0;
}
if( h->param.rc.i_rc_method == X264_RC_CQP )
{
float qp_p = h->param.rc.i_qp_constant;
float qp_i = qp_p - 6*log2f( h->param.rc.f_ip_factor );
float qp_b = qp_p + 6*log2f( h->param.rc.f_pb_factor );
if( qp_p < 0 )
{
x264_log( h, X264_LOG_ERROR, "qp not specified\n" );
return -1;
}

h->param.rc.i_qp_min = x264_clip3( (int)(X264_MIN3( qp_p, qp_i, qp_b )), 0, QP_MAX );
h->param.rc.i_qp_max = x264_clip3( (int)(X264_MAX3( qp_p, qp_i, qp_b ) + .999), 0, QP_MAX );
h->param.rc.i_aq_mode = 0;
h->param.rc.b_mb_tree = 0;
h->param.rc.i_bitrate = 0;
}
h->param.rc.i_qp_max = x264_clip3( h->param.rc.i_qp_max, 0, QP_MAX );
h->param.rc.i_qp_min = x264_clip3( h->param.rc.i_qp_min, 0, h->param.rc.i_qp_max );
h->param.rc.i_qp_step = x264_clip3( h->param.rc.i_qp_step, 2, QP_MAX );
h->param.rc.i_bitrate = x264_clip3( h->param.rc.i_bitrate, 0, 2000000 );
if( h->param.rc.i_rc_method == X264_RC_ABR && !h->param.rc.i_bitrate )
{
x264_log( h, X264_LOG_ERROR, "bitrate not specified\n" );
return -1;
}
h->param.rc.i_vbv_buffer_size = x264_clip3( h->param.rc.i_vbv_buffer_size, 0, 2000000 );
h->param.rc.i_vbv_max_bitrate = x264_clip3( h->param.rc.i_vbv_max_bitrate, 0, 2000000 );
h->param.rc.f_vbv_buffer_init = x264_clip3f( h->param.rc.f_vbv_buffer_init, 0, 2000000 );
if( h->param.rc.i_vbv_buffer_size )
{
if( h->param.rc.i_rc_method == X264_RC_CQP )
{
x264_log( h, X264_LOG_WARNING, "VBV is incompatible with constant QP, ignored.\n" );
h->param.rc.i_vbv_max_bitrate = 0;
h->param.rc.i_vbv_buffer_size = 0;
}
else if( h->param.rc.i_vbv_max_bitrate == 0 )
{
if( h->param.rc.i_rc_method == X264_RC_ABR )
{
x264_log( h, X264_LOG_WARNING, "VBV maxrate unspecified, assuming CBR\n" );
h->param.rc.i_vbv_max_bitrate = h->param.rc.i_bitrate;
}
else
{
x264_log( h, X264_LOG_WARNING, "VBV bufsize set but maxrate unspecified, ignored\n" );
h->param.rc.i_vbv_buffer_size = 0;
}
}
else if( h->param.rc.i_vbv_max_bitrate < h->param.rc.i_bitrate &&
h->param.rc.i_rc_method == X264_RC_ABR )
{
x264_log( h, X264_LOG_WARNING, "max bitrate less than average bitrate, assuming CBR\n" );
h->param.rc.i_bitrate = h->param.rc.i_vbv_max_bitrate;
}
}
else if( h->param.rc.i_vbv_max_bitrate )
{
x264_log( h, X264_LOG_WARNING, "VBV maxrate specified, but no bufsize, ignored\n" );
h->param.rc.i_vbv_max_bitrate = 0;
}
// ......

2.6 Slice参数

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
  // ......
h->param.i_slice_max_size = X264_MAX( h->param.i_slice_max_size, 0 );
h->param.i_slice_max_mbs = X264_MAX( h->param.i_slice_max_mbs, 0 );
h->param.i_slice_min_mbs = X264_MAX( h->param.i_slice_min_mbs, 0 );
if( h->param.i_slice_max_mbs )
h->param.i_slice_min_mbs = X264_MIN( h->param.i_slice_min_mbs, h->param.i_slice_max_mbs/2 );
else if( !h->param.i_slice_max_size )
h->param.i_slice_min_mbs = 0;
if( PARAM_INTERLACED && h->param.i_slice_min_mbs )
{
x264_log( h, X264_LOG_WARNING, "interlace + slice-min-mbs is not implemented\n" );
h->param.i_slice_min_mbs = 0;
}
int mb_width = (h->param.i_width+15)/16;
if( h->param.i_slice_min_mbs > mb_width )
{
x264_log( h, X264_LOG_WARNING, "slice-min-mbs > row mb size (%d) not implemented\n", mb_width );
h->param.i_slice_min_mbs = mb_width;
}

int max_slices = (h->param.i_height+((16<<PARAM_INTERLACED)-1))/(16<<PARAM_INTERLACED);
if( h->param.b_sliced_threads )
h->param.i_slice_count = x264_clip3( h->param.i_threads, 0, max_slices );
else
{
h->param.i_slice_count = x264_clip3( h->param.i_slice_count, 0, max_slices );
if( h->param.i_slice_max_mbs || h->param.i_slice_max_size )
h->param.i_slice_count = 0;
}
if( h->param.i_slice_count_max > 0 )
h->param.i_slice_count_max = X264_MAX( h->param.i_slice_count, h->param.i_slice_count_max );
// ......

2.7 码流结构参数

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
h->param.i_frame_reference = x264_clip3( h->param.i_frame_reference, 1, X264_REF_MAX );
h->param.i_dpb_size = x264_clip3( h->param.i_dpb_size, 1, X264_REF_MAX );
if( h->param.i_scenecut_threshold < 0 )
h->param.i_scenecut_threshold = 0;
h->param.analyse.i_direct_mv_pred = x264_clip3( h->param.analyse.i_direct_mv_pred, X264_DIRECT_PRED_NONE, X264_DIRECT_PRED_AUTO );
if( !h->param.analyse.i_subpel_refine && h->param.analyse.i_direct_mv_pred > X264_DIRECT_PRED_SPATIAL )
{
x264_log( h, X264_LOG_WARNING, "subme=0 + direct=temporal is not supported\n" );
h->param.analyse.i_direct_mv_pred = X264_DIRECT_PRED_SPATIAL;
}
h->param.i_bframe = x264_clip3( h->param.i_bframe, 0, X264_MIN( X264_BFRAME_MAX, h->param.i_keyint_max-1 ) );
h->param.i_bframe_bias = x264_clip3( h->param.i_bframe_bias, -90, 100 );
if( h->param.i_bframe <= 1 )
h->param.i_bframe_pyramid = X264_B_PYRAMID_NONE;
h->param.i_bframe_pyramid = x264_clip3( h->param.i_bframe_pyramid, X264_B_PYRAMID_NONE, X264_B_PYRAMID_NORMAL );
h->param.i_bframe_adaptive = x264_clip3( h->param.i_bframe_adaptive, X264_B_ADAPT_NONE, X264_B_ADAPT_TRELLIS );
if( !h->param.i_bframe )
{
h->param.i_bframe_adaptive = X264_B_ADAPT_NONE;
h->param.analyse.i_direct_mv_pred = 0;
h->param.analyse.b_weighted_bipred = 0;
h->param.b_open_gop = 0;
}
if( h->param.b_intra_refresh && h->param.i_bframe_pyramid == X264_B_PYRAMID_NORMAL )
{
x264_log( h, X264_LOG_WARNING, "b-pyramid normal + intra-refresh is not supported\n" );
h->param.i_bframe_pyramid = X264_B_PYRAMID_STRICT;
}
if( h->param.b_intra_refresh && (h->param.i_frame_reference > 1 || h->param.i_dpb_size > 1) )
{
x264_log( h, X264_LOG_WARNING, "ref > 1 + intra-refresh is not supported\n" );
h->param.i_frame_reference = 1;
h->param.i_dpb_size = 1;
}
if( h->param.b_intra_refresh && h->param.b_open_gop )
{
x264_log( h, X264_LOG_WARNING, "intra-refresh is not compatible with open-gop\n" );
h->param.b_open_gop = 0;
}

2.8 帧率和时间戳参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
float fps = (float)h->param.i_fps_num / h->param.i_fps_den;
if( h->param.i_keyint_min == X264_KEYINT_MIN_AUTO )
h->param.i_keyint_min = X264_MIN( h->param.i_keyint_max / 10, (int)fps );
h->param.i_keyint_min = x264_clip3( h->param.i_keyint_min, 1, h->param.i_keyint_max/2+1 );
h->param.rc.i_lookahead = x264_clip3( h->param.rc.i_lookahead, 0, X264_LOOKAHEAD_MAX );
{
int maxrate = X264_MAX( h->param.rc.i_vbv_max_bitrate, h->param.rc.i_bitrate );
float bufsize = maxrate ? (float)h->param.rc.i_vbv_buffer_size / maxrate : 0;
h->param.rc.i_lookahead = X264_MIN( h->param.rc.i_lookahead, X264_MAX( h->param.i_keyint_max, bufsize*fps ) );
}

if( !h->param.i_timebase_num || !h->param.i_timebase_den || !(h->param.b_vfr_input || h->param.b_pulldown) )
{
h->param.i_timebase_num = h->param.i_fps_den;
h->param.i_timebase_den = h->param.i_fps_num;
}

2.9 去块滤波与运动搜索等

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
h->param.i_deblocking_filter_alphac0 = x264_clip3( h->param.i_deblocking_filter_alphac0, -6, 6 );
h->param.i_deblocking_filter_beta = x264_clip3( h->param.i_deblocking_filter_beta, -6, 6 );
h->param.analyse.i_luma_deadzone[0] = x264_clip3( h->param.analyse.i_luma_deadzone[0], 0, 32 );
h->param.analyse.i_luma_deadzone[1] = x264_clip3( h->param.analyse.i_luma_deadzone[1], 0, 32 );

h->param.i_cabac_init_idc = x264_clip3( h->param.i_cabac_init_idc, 0, 2 );

if( h->param.i_cqm_preset < X264_CQM_FLAT || h->param.i_cqm_preset > X264_CQM_CUSTOM )
h->param.i_cqm_preset = X264_CQM_FLAT;

if( h->param.analyse.i_me_method < X264_ME_DIA ||
h->param.analyse.i_me_method > X264_ME_TESA )
h->param.analyse.i_me_method = X264_ME_HEX;
h->param.analyse.i_me_range = x264_clip3( h->param.analyse.i_me_range, 4, 1024 );
if( h->param.analyse.i_me_range > 16 && h->param.analyse.i_me_method <= X264_ME_HEX )
h->param.analyse.i_me_range = 16;
if( h->param.analyse.i_me_method == X264_ME_TESA &&
(h->mb.b_lossless || h->param.analyse.i_subpel_refine <= 1) )
h->param.analyse.i_me_method = X264_ME_ESA;
h->param.analyse.b_mixed_references = h->param.analyse.b_mixed_references && h->param.i_frame_reference > 1;
h->param.analyse.inter &= X264_ANALYSE_PSUB16x16|X264_ANALYSE_PSUB8x8|X264_ANALYSE_BSUB16x16|
X264_ANALYSE_I4x4|X264_ANALYSE_I8x8;
h->param.analyse.intra &= X264_ANALYSE_I4x4|X264_ANALYSE_I8x8;
if( !(h->param.analyse.inter & X264_ANALYSE_PSUB16x16) )
h->param.analyse.inter &= ~X264_ANALYSE_PSUB8x8;
if( !h->param.analyse.b_transform_8x8 )
{
h->param.analyse.inter &= ~X264_ANALYSE_I8x8;
h->param.analyse.intra &= ~X264_ANALYSE_I8x8;
}
h->param.analyse.i_trellis = x264_clip3( h->param.analyse.i_trellis, 0, 2 );
h->param.rc.i_aq_mode = x264_clip3( h->param.rc.i_aq_mode, 0, 3 );
h->param.rc.f_aq_strength = x264_clip3f( h->param.rc.f_aq_strength, 0, 3 );
if( h->param.rc.f_aq_strength == 0 )
h->param.rc.i_aq_mode = 0;

3. 设置纵横比与初始化SPS和PPS

在验证参数完成后,打开编码器函数将会执行设置纵横比和初始化SPS和PPS的操作:

1
2
3
4
5
6
7
8
9
10
x264_t *x264_encoder_open( x264_param_t *param )
{
// ......
set_aspect_ratio( h, &h->param, 1 );

x264_sps_init( h->sps, h->param.i_sps_id, &h->param );
x264_sps_init_scaling_list( h->sps, &h->param );
x264_pps_init( h->pps, h->param.i_sps_id, &h->param, h->sps );
// ......
}

3.1 设置纵横比

VUI是H.264码流中SPS包含的可选结构,其中主要包含了图像纵横比、图像颜色格式等信息。设置图像纵横比通过以下函数实现:

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
static void set_aspect_ratio( x264_t *h, x264_param_t *param, int initial )
{
/* VUI */
if( param->vui.i_sar_width > 0 && param->vui.i_sar_height > 0 )
{
uint32_t i_w = param->vui.i_sar_width;
uint32_t i_h = param->vui.i_sar_height;
uint32_t old_w = h->param.vui.i_sar_width;
uint32_t old_h = h->param.vui.i_sar_height;

x264_reduce_fraction( &i_w, &i_h );

while( i_w > 65535 || i_h > 65535 )
{
i_w /= 2;
i_h /= 2;
}

x264_reduce_fraction( &i_w, &i_h );

if( i_w != old_w || i_h != old_h || initial )
{
h->param.vui.i_sar_width = 0;
h->param.vui.i_sar_height = 0;
if( i_w == 0 || i_h == 0 )
x264_log( h, X264_LOG_WARNING, "cannot create valid sample aspect ratio\n" );
else
{
x264_log( h, initial?X264_LOG_INFO:X264_LOG_DEBUG, "using SAR=%d/%d\n", i_w, i_h );
h->param.vui.i_sar_width = i_w;
h->param.vui.i_sar_height = i_h;
}
}
}
}

3.2 SPS和PPS的初始化

SPS和PPS组成H.264码流的头信息。在调用x264_encoder_open打开编码器的过程中,调用set.c中实现的x264_sps_init和x264_pps_init初始化SPS和PPS数据。初始化SPS的实现如下:

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
void x264_sps_init( x264_sps_t *sps, int i_id, x264_param_t *param )
{
int csp = param->i_csp & X264_CSP_MASK;

sps->i_id = i_id;
sps->i_mb_width = ( param->i_width + 15 ) / 16;
sps->i_mb_height= ( param->i_height + 15 ) / 16;
sps->b_frame_mbs_only = !(param->b_interlaced || param->b_fake_interlaced);
if( !sps->b_frame_mbs_only )
sps->i_mb_height = ( sps->i_mb_height + 1 ) & ~1;
sps->i_chroma_format_idc = csp >= X264_CSP_I444 ? CHROMA_444 :
csp >= X264_CSP_I422 ? CHROMA_422 :
csp >= X264_CSP_I420 ? CHROMA_420 : CHROMA_400;

sps->b_qpprime_y_zero_transform_bypass = param->rc.i_rc_method == X264_RC_CQP && param->rc.i_qp_constant == 0;
if( sps->b_qpprime_y_zero_transform_bypass || sps->i_chroma_format_idc == CHROMA_444 )
sps->i_profile_idc = PROFILE_HIGH444_PREDICTIVE;
else if( sps->i_chroma_format_idc == CHROMA_422 )
sps->i_profile_idc = PROFILE_HIGH422;
else if( BIT_DEPTH > 8 )
sps->i_profile_idc = PROFILE_HIGH10;
else if( param->analyse.b_transform_8x8 || param->i_cqm_preset != X264_CQM_FLAT || sps->i_chroma_format_idc == CHROMA_400 )
sps->i_profile_idc = PROFILE_HIGH;
else if( param->b_cabac || param->i_bframe > 0 || param->b_interlaced || param->b_fake_interlaced || param->analyse.i_weighted_pred > 0 )
sps->i_profile_idc = PROFILE_MAIN;
else
sps->i_profile_idc = PROFILE_BASELINE;

sps->b_constraint_set0 = sps->i_profile_idc == PROFILE_BASELINE;
/* x264 doesn't support the features that are in Baseline and not in Main,
* namely arbitrary_slice_order and slice_groups. */
sps->b_constraint_set1 = sps->i_profile_idc <= PROFILE_MAIN;
/* Never set constraint_set2, it is not necessary and not used in real world. */
sps->b_constraint_set2 = 0;
sps->b_constraint_set3 = 0;
sps->b_constraint_set4 = sps->i_profile_idc >= PROFILE_MAIN && sps->i_profile_idc <= PROFILE_HIGH10 && sps->b_frame_mbs_only;
sps->b_constraint_set5 = (sps->i_profile_idc == PROFILE_MAIN || sps->i_profile_idc == PROFILE_HIGH) && param->i_bframe == 0;

sps->i_level_idc = param->i_level_idc;
if( param->i_level_idc == 9 && ( sps->i_profile_idc == PROFILE_BASELINE || sps->i_profile_idc == PROFILE_MAIN ) )
{
sps->b_constraint_set3 = 1; /* level 1b with Baseline or Main profile is signalled via constraint_set3 */
sps->i_level_idc = 11;
}
/* Intra profiles */
if( param->i_keyint_max == 1 && sps->i_profile_idc >= PROFILE_HIGH )
sps->b_constraint_set3 = 1;

sps->vui.i_num_reorder_frames = param->i_bframe_pyramid ? 2 : param->i_bframe ? 1 : 0;
/* extra slot with pyramid so that we don't have to override the
* order of forgetting old pictures */
sps->vui.i_max_dec_frame_buffering =
sps->i_num_ref_frames = X264_MIN(X264_REF_MAX, X264_MAX4(param->i_frame_reference, 1 + sps->vui.i_num_reorder_frames,
param->i_bframe_pyramid ? 4 : 1, param->i_dpb_size));
sps->i_num_ref_frames -= param->i_bframe_pyramid == X264_B_PYRAMID_STRICT;
if( param->i_keyint_max == 1 )
{
sps->i_num_ref_frames = 0;
sps->vui.i_max_dec_frame_buffering = 0;
}

/* number of refs + current frame */
int max_frame_num = sps->vui.i_max_dec_frame_buffering * (!!param->i_bframe_pyramid+1) + 1;
/* Intra refresh cannot write a recovery time greater than max frame num-1 */
if( param->b_intra_refresh )
{
int time_to_recovery = X264_MIN( sps->i_mb_width - 1, param->i_keyint_max ) + param->i_bframe - 1;
max_frame_num = X264_MAX( max_frame_num, time_to_recovery+1 );
}

sps->i_log2_max_frame_num = 4;
while( (1 << sps->i_log2_max_frame_num) <= max_frame_num )
sps->i_log2_max_frame_num++;

sps->i_poc_type = param->i_bframe || param->b_interlaced || param->i_avcintra_class ? 0 : 2;
if( sps->i_poc_type == 0 )
{
int max_delta_poc = (param->i_bframe + 2) * (!!param->i_bframe_pyramid + 1) * 2;
sps->i_log2_max_poc_lsb = 4;
while( (1 << sps->i_log2_max_poc_lsb) <= max_delta_poc * 2 )
sps->i_log2_max_poc_lsb++;
}

sps->b_vui = 1;

sps->b_gaps_in_frame_num_value_allowed = 0;
sps->b_mb_adaptive_frame_field = param->b_interlaced;
sps->b_direct8x8_inference = 1;

x264_sps_init_reconfigurable( sps, param );

sps->vui.b_overscan_info_present = param->vui.i_overscan > 0 && param->vui.i_overscan <= 2;
if( sps->vui.b_overscan_info_present )
sps->vui.b_overscan_info = ( param->vui.i_overscan == 2 ? 1 : 0 );

sps->vui.b_signal_type_present = 0;
sps->vui.i_vidformat = ( param->vui.i_vidformat >= 0 && param->vui.i_vidformat <= 5 ? param->vui.i_vidformat : 5 );
sps->vui.b_fullrange = ( param->vui.b_fullrange >= 0 && param->vui.b_fullrange <= 1 ? param->vui.b_fullrange :
( csp >= X264_CSP_BGR ? 1 : 0 ) );
sps->vui.b_color_description_present = 0;

sps->vui.i_colorprim = ( param->vui.i_colorprim >= 0 && param->vui.i_colorprim <= 12 ? param->vui.i_colorprim : 2 );
sps->vui.i_transfer = ( param->vui.i_transfer >= 0 && param->vui.i_transfer <= 18 ? param->vui.i_transfer : 2 );
sps->vui.i_colmatrix = ( param->vui.i_colmatrix >= 0 && param->vui.i_colmatrix <= 14 ? param->vui.i_colmatrix :
( csp >= X264_CSP_BGR ? 0 : 2 ) );
if( sps->vui.i_colorprim != 2 || sps->vui.i_transfer != 2 || sps->vui.i_colmatrix != 2 )
sps->vui.b_color_description_present = 1;

if( sps->vui.i_vidformat != 5 || sps->vui.b_fullrange || sps->vui.b_color_description_present )
sps->vui.b_signal_type_present = 1;

/* FIXME: not sufficient for interlaced video */
sps->vui.b_chroma_loc_info_present = param->vui.i_chroma_loc > 0 && param->vui.i_chroma_loc <= 5 &&
sps->i_chroma_format_idc == CHROMA_420;
if( sps->vui.b_chroma_loc_info_present )
{
sps->vui.i_chroma_loc_top = param->vui.i_chroma_loc;
sps->vui.i_chroma_loc_bottom = param->vui.i_chroma_loc;
}

sps->vui.b_timing_info_present = param->i_timebase_num > 0 && param->i_timebase_den > 0;

if( sps->vui.b_timing_info_present )
{
sps->vui.i_num_units_in_tick = param->i_timebase_num;
sps->vui.i_time_scale = param->i_timebase_den * 2;
sps->vui.b_fixed_frame_rate = !param->b_vfr_input;
}

sps->vui.b_vcl_hrd_parameters_present = 0; // we don't support VCL HRD
sps->vui.b_nal_hrd_parameters_present = !!param->i_nal_hrd;
sps->vui.b_pic_struct_present = param->b_pic_struct;

// NOTE: HRD related parts of the SPS are initialised in x264_ratecontrol_init_reconfigurable

sps->vui.b_bitstream_restriction = !(sps->b_constraint_set3 && sps->i_profile_idc >= PROFILE_HIGH);
if( sps->vui.b_bitstream_restriction )
{
sps->vui.b_motion_vectors_over_pic_boundaries = 1;
sps->vui.i_max_bytes_per_pic_denom = 0;
sps->vui.i_max_bits_per_mb_denom = 0;
sps->vui.i_log2_max_mv_length_horizontal =
sps->vui.i_log2_max_mv_length_vertical = (int)log2f( X264_MAX( 1, param->analyse.i_mv_range*4-1 ) ) + 1;
}

sps->b_avcintra = !!param->i_avcintra_class;
sps->i_cqm_preset = param->i_cqm_preset;
}

初始化PPS的实现如下:

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
void x264_pps_init( x264_pps_t *pps, int i_id, x264_param_t *param, x264_sps_t *sps )
{
pps->i_id = i_id;
pps->i_sps_id = sps->i_id;
pps->b_cabac = param->b_cabac;

pps->b_pic_order = !param->i_avcintra_class && param->b_interlaced;
pps->i_num_slice_groups = 1;

pps->i_num_ref_idx_l0_default_active = param->i_frame_reference;
pps->i_num_ref_idx_l1_default_active = 1;

pps->b_weighted_pred = param->analyse.i_weighted_pred > 0;
pps->b_weighted_bipred = param->analyse.b_weighted_bipred ? 2 : 0;

pps->i_pic_init_qp = param->rc.i_rc_method == X264_RC_ABR || param->b_stitchable ? 26 + QP_BD_OFFSET : SPEC_QP( param->rc.i_qp_constant );
pps->i_pic_init_qs = 26 + QP_BD_OFFSET;

pps->i_chroma_qp_index_offset = param->analyse.i_chroma_qp_offset;
pps->b_deblocking_filter_control = 1;
pps->b_constrained_intra_pred = param->b_constrained_intra;
pps->b_redundant_pic_cnt = 0;

pps->b_transform_8x8_mode = param->analyse.b_transform_8x8 ? 1 : 0;
}

4. 初始化编码相关信息

这一部分主要实现初始化编码过程中的细节信息,如宏块、帧、条带信息,各种编码工具设置,码流结构和码率控制等相关信息。

4.1 初始化mb和frame信息

这一部分主要实现初始化宏块和帧的相关信息,具体见代码注释:

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
  h->mb.i_mb_width = h->sps->i_mb_width;	// 以宏块为单位的帧宽度
h->mb.i_mb_height = h->sps->i_mb_height; // 以宏块为单位的帧高度
h->mb.i_mb_count = h->mb.i_mb_width * h->mb.i_mb_height; // 一帧中宏块的数量

// 色度分量的缓存偏移量
h->mb.chroma_h_shift = CHROMA_FORMAT == CHROMA_420 || CHROMA_FORMAT == CHROMA_422;
h->mb.chroma_v_shift = CHROMA_FORMAT == CHROMA_420;

/* Adaptive MBAFF and subme 0 are not supported as we require halving motion
* vectors during prediction, resulting in hpel mvs.
* The chosen solution is to make MBAFF non-adaptive in this case. */
// 宏块帧场自适应开关
h->mb.b_adaptive_mbaff = PARAM_INTERLACED && h->param.analyse.i_subpel_refine;

/* Init frames. */
// 设置帧相关初始化参数
if( h->param.i_bframe_adaptive == X264_B_ADAPT_TRELLIS && !h->param.rc.b_stat_read )
h->frames.i_delay = X264_MAX(h->param.i_bframe,3)*4;
else
h->frames.i_delay = h->param.i_bframe;
if( h->param.rc.b_mb_tree || h->param.rc.i_vbv_buffer_size )
h->frames.i_delay = X264_MAX( h->frames.i_delay, h->param.rc.i_lookahead );
i_slicetype_length = h->frames.i_delay;
h->frames.i_delay += h->i_thread_frames - 1;
h->frames.i_delay += h->param.i_sync_lookahead;
h->frames.i_delay += h->param.b_vfr_input;
h->frames.i_bframe_delay = h->param.i_bframe ? (h->param.i_bframe_pyramid ? 2 : 1) : 0;

// 初始化参考帧、解码缓存等参数
h->frames.i_max_ref0 = h->param.i_frame_reference;
h->frames.i_max_ref1 = X264_MIN( h->sps->vui.i_num_reorder_frames, h->param.i_frame_reference );
h->frames.i_max_dpb = h->sps->vui.i_max_dec_frame_buffering;
h->frames.b_have_lowres = !h->param.rc.b_stat_read
&& ( h->param.rc.i_rc_method == X264_RC_ABR
|| h->param.rc.i_rc_method == X264_RC_CRF
|| h->param.i_bframe_adaptive
|| h->param.i_scenecut_threshold
|| h->param.rc.b_mb_tree
|| h->param.analyse.i_weighted_pred );
h->frames.b_have_lowres |= h->param.rc.b_stat_read && h->param.rc.i_vbv_buffer_size > 0;
h->frames.b_have_sub8x8_esa = !!(h->param.analyse.inter & X264_ANALYSE_PSUB8x8);

h->frames.i_last_idr =
h->frames.i_last_keyframe = - h->param.i_keyint_max;
h->frames.i_input = 0;
h->frames.i_largest_pts = h->frames.i_second_largest_pts = -1;
h->frames.i_poc_last_open_gop = -1;

4.2 初始化预测、变换、量化、熵编码和去块滤波等信息

这一部分主要用于设置各种预测、差值、变换编码、量化、熵编码和去块滤波的函数指针与其他参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
x264_predict_16x16_init( h->param.cpu, h->predict_16x16 );
x264_predict_8x8c_init( h->param.cpu, h->predict_8x8c );
x264_predict_8x16c_init( h->param.cpu, h->predict_8x16c );
x264_predict_8x8_init( h->param.cpu, h->predict_8x8, &h->predict_8x8_filter );
x264_predict_4x4_init( h->param.cpu, h->predict_4x4 );
x264_pixel_init( h->param.cpu, &h->pixf );
x264_dct_init( h->param.cpu, &h->dctf );
x264_zigzag_init( h->param.cpu, &h->zigzagf_progressive, &h->zigzagf_interlaced );
memcpy( &h->zigzagf, PARAM_INTERLACED ? &h->zigzagf_interlaced : &h->zigzagf_progressive, sizeof(h->zigzagf) );
x264_mc_init( h->param.cpu, &h->mc, h->param.b_cpu_independent );
x264_quant_init( h, h->param.cpu, &h->quantf );
x264_deblock_init( h->param.cpu, &h->loopf, PARAM_INTERLACED );
x264_bitstream_init( h->param.cpu, &h->bsf );
if( h->param.b_cabac )
x264_cabac_init( h );
else
x264_cavlc_init( h );

mbcmp_init( h );
chroma_dsp_init( h );

例如:x264_predict_16x16_init函数的实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
void x264_predict_16x16_init( uint32_t cpu, x264_predict_t pf[7] )
{
pf[I_PRED_16x16_V ] = x264_predict_16x16_v_c;
pf[I_PRED_16x16_H ] = x264_predict_16x16_h_c;
pf[I_PRED_16x16_DC] = x264_predict_16x16_dc_c;
pf[I_PRED_16x16_P ] = x264_predict_16x16_p_c;
pf[I_PRED_16x16_DC_LEFT]= predict_16x16_dc_left_c;
pf[I_PRED_16x16_DC_TOP ]= predict_16x16_dc_top_c;
pf[I_PRED_16x16_DC_128 ]= predict_16x16_dc_128_c;

// ......
}

该函数的作用是指定各个帧内预测模式的实现函数指针。其他函数如x264_pixel_init表示各种模式下不同残差统计值的计算方法,x264_dct_init表示各种不同块的dct变换实现,x264_zigzag_init表示像素块中系数扫描方法等等。

4.3 初始化码流与线程相关信息

该部分主要用于配置输出码流以及编码线程相关的参数。

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
    h->out.i_nal = 0;
h->out.i_bitstream = X264_MAX( 1000000, h->param.i_width * h->param.i_height * 4
* ( h->param.rc.i_rc_method == X264_RC_ABR ? pow( 0.95, h->param.rc.i_qp_min )
: pow( 0.95, h->param.rc.i_qp_constant ) * X264_MAX( 1, h->param.rc.f_ip_factor )));

h->nal_buffer_size = h->out.i_bitstream * 3/2 + 4 + 64; /* +4 for startcode, +64 for nal_escape assembly padding */
CHECKED_MALLOC( h->nal_buffer, h->nal_buffer_size );

CHECKED_MALLOC( h->reconfig_h, sizeof(x264_t) );

if( h->param.i_threads > 1 &&
x264_threadpool_init( &h->threadpool, h->param.i_threads, (void*)encoder_thread_init, h ) )
goto fail;
if( h->param.i_lookahead_threads > 1 &&
x264_threadpool_init( &h->lookaheadpool, h->param.i_lookahead_threads, NULL, NULL ) )
goto fail;

#if HAVE_OPENCL
if( h->param.b_opencl )
{
h->opencl.ocl = x264_opencl_load_library();
if( !h->opencl.ocl )
{
x264_log( h, X264_LOG_WARNING, "failed to load OpenCL\n" );
h->param.b_opencl = 0;
}
}
#endif

h->thread[0] = h;
for( int i = 1; i < h->param.i_threads + !!h->param.i_sync_lookahead; i++ )
CHECKED_MALLOC( h->thread[i], sizeof(x264_t) );
if( h->param.i_lookahead_threads > 1 )
for( int i = 0; i < h->param.i_lookahead_threads; i++ )
{
CHECKED_MALLOC( h->lookahead_thread[i], sizeof(x264_t) );
*h->lookahead_thread[i] = *h;
}
*h->reconfig_h = *h;

for( int i = 0; i < h->param.i_threads; i++ )
{
int init_nal_count = h->param.i_slice_count + 3;
int allocate_threadlocal_data = !h->param.b_sliced_threads || !i;
if( i > 0 )
*h->thread[i] = *h;

if( x264_pthread_mutex_init( &h->thread[i]->mutex, NULL ) )
goto fail;
if( x264_pthread_cond_init( &h->thread[i]->cv, NULL ) )
goto fail;

if( allocate_threadlocal_data )
{
h->thread[i]->fdec = x264_frame_pop_unused( h, 1 );
if( !h->thread[i]->fdec )
goto fail;
}
else
h->thread[i]->fdec = h->thread[0]->fdec;

CHECKED_MALLOC( h->thread[i]->out.p_bitstream, h->out.i_bitstream );
/* Start each thread with room for init_nal_count NAL units; it'll realloc later if needed. */
CHECKED_MALLOC( h->thread[i]->out.nal, init_nal_count*sizeof(x264_nal_t) );
h->thread[i]->out.i_nals_allocated = init_nal_count;

if( allocate_threadlocal_data && x264_macroblock_cache_allocate( h->thread[i] ) < 0 )
goto fail;
}

if( x264_lookahead_init( h, i_slicetype_length ) )
goto fail;

for( int i = 0; i < h->param.i_threads; i++ )
if( x264_macroblock_thread_allocate( h->thread[i], 0 ) < 0 )
goto fail;

4.4 初始化码率控制相关信息

函数x264_ratecontrol_new进行码率控制功能的相关初始化操作:

1
2
if( x264_ratecontrol_new( h ) < 0 )
goto fail;

在其内部,通过一个x264_ratecontrol_t结构rc执行编码的码率控制,配置完成后绑定到每一个编码线程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int x264_ratecontrol_new( x264_t *h )
{
x264_ratecontrol_t *rc;
// ......

for( int i = 0; i<h->param.i_threads; i++ )
{
h->thread[i]->rc = rc+i;
if( i )
{
rc[i] = rc[0];
h->thread[i]->param = h->param;
h->thread[i]->mb.b_variable_qp = h->mb.b_variable_qp;
h->thread[i]->mb.ip_offset = h->mb.ip_offset;
}
}

return 0;
}

其中具体的配置细节在“码率控制”一章中详述。


Author: Yin Wenjie
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source Yin Wenjie !
 Previous
【x264视频编码器应用与实现】八. x264编码一帧图像API:x264_encoder_encode(一) 【x264视频编码器应用与实现】八. x264编码一帧图像API:x264_encoder_encode(一)
摘要:本文作为 “x264视频编码器应用与实现” 系列博文的第八篇,主要讨论x264编码器实例的图像编码API的实现。
2022-06-01 Yin Wenjie
Next 
【x264视频编码器应用与实现】六. x264的图像与码流结构 【x264视频编码器应用与实现】六. x264的图像与码流结构
摘要:本文作为 “x264视频编码器应用与实现” 系列博文的第六篇,主要讨论x264中图像与码流结构体的定义和使用。
2022-02-24 Yin Wenjie
  TOC