下面将分析x264编码的核心算法部分:
首先先了解一下x264中比特流的层次结构:每个比特都隶属于某个句法元素,句法元素被组织成有层次的结构,分别描述各个层次的信息。如下图所示:
x264分层结构由五层组成,分别是序列参数集、图像参数集、片(Slice)、和宏块和子块。参数集是一个独立的数据单位,不依赖于参数集外的其它句法元素。下图描述了参数集与参数集外的句法元素之间的关系。
而本文所要分析的就是对应的片编码。下面看源代码:
static void *x264_slices_write( x264_t *h )
{
int i_slice_num = 0;
int last_thread_mb = h->sh.i_last_mb;
/* init stats */
//初始化一些状态
memset( &h->stat.frame, 0, sizeof(h->stat.frame) );
h->mb.b_reencode_mb = 0;
//循环每一个slice
while( h->sh.i_first_mb + SLICE_MBAFF*h->mb.i_mb_stride <= last_thread_mb )
{
h->sh.i_last_mb = last_thread_mb;
if( !i_slice_num || !x264_frame_new_slice( h, h->fdec ) )
{
if( h->param.i_slice_max_mbs )
{
if( SLICE_MBAFF )
{
// convert first to mbaff form, add slice-max-mbs, then convert back to normal form
int last_mbaff = 2*(h->sh.i_first_mb % h->mb.i_mb_width)
+ h->mb.i_mb_width*(h->sh.i_first_mb / h->mb.i_mb_width)
+ h->param.i_slice_max_mbs - 1;
int last_x = (last_mbaff % (2*h->mb.i_mb_width))/2;
int last_y = (last_mbaff / (2*h->mb.i_mb_width))*2 + 1;
h->sh.i_last_mb = last_x + h->mb.i_mb_stride*last_y;
}
else
{
h->sh.i_last_mb = h->sh.i_first_mb + h->param.i_slice_max_mbs - 1;
if( h->sh.i_last_mb < last_thread_mb && last_thread_mb - h->sh.i_last_mb < h->param.i_slice_min_mbs )
h->sh.i_last_mb = last_thread_mb - h->param.i_slice_min_mbs;
}
i_slice_num++;
}
else if( h->param.i_slice_count && !h->param.b_sliced_threads )
{
int height = h->mb.i_mb_height >> PARAM_INTERLACED;
int width = h->mb.i_mb_width << PARAM_INTERLACED;
i_slice_num++;
h->sh.i_last_mb = (height * i_slice_num + h->param.i_slice_count/2) / h->param.i_slice_count * width - 1;
}
}
h->sh.i_last_mb = X264_MIN( h->sh.i_last_mb, last_thread_mb );
//利用函数指针进行具体的块编码操作
if( x264_stack_align( x264_slice_write, h ) )
goto fail;
h->sh.i_first_mb = h->sh.i_last_mb + 1;
// if i_first_mb is not the last mb in a row then go to the next mb in MBAFF order
if( SLICE_MBAFF && h->sh.i_first_mb % h->mb.i_mb_width )
h->sh.i_first_mb -= h->mb.i_mb_stride;
}
return (void *)0;
fail:
/* Tell other threads we're done, so they wouldn't wait for it */
if( h->param.b_sliced_threads )
x264_threadslice_cond_broadcast( h, 2 );
return (void *)-1;
}
下面分析具体的块编码函数源代码:
static intptr_t x264_slice_write( x264_t *h )
{
int i_skip;
int mb_xy, i_mb_x, i_mb_y;
/* NALUs other than the first use a 3-byte startcode.
* Add one extra byte for the rbsp, and one more for the final CABAC putbyt