x264阅读记录-2 算法
7. x264_encoder_encode函数-1数组
查看该函数代码(Encoder.c文件)能够发现,该函数中注释很详细,对编码的整个步骤展现的也相对比较清晰。数据结构
在查看具体的代码以前,咱们须要了解牵扯到x264帧管理过程当中的三个数组:多线程
A. Setup new frame from picture 根据picture中的数据创建frame数据结构less
这儿要区分输入数据是否为NULL,若是为NULL就说明是要从缓冲中获取B帧来进行编码,不然直接根据输入数据创建当前要编码的帧结构:ide
步骤1:(这个步骤是在一个if判断语句以内进行操做的,判断条件就是传入的数据是否为NULL,联系前面对代码的阅读能够知道,若是是进行B帧的编码的话,参数就是NULL,这时是不须要取picture中的数据来创建frame数据结构的,由于在next列表中是存在有帧的,这时只须要从next列表中取就能够了) 函数
/* 1: Copy the picture to a frame and move it to a buffer */优化
首先是:ui
/* Get the picture(fenc) from the start of List(h->frames.unused) */编码
x264_frame_t *fenc = x264_frame_get ( h->frames .unused );
从x264_frame_get 函数的代码能够看到是直接从h ->frames .unused这个列表中直接把第一个元素去了出来,而后后面的元素依次前移。
其次是:
/* Cpy data from pic_in to fenc for further processing */
x264_frame_copy_picture( h , fenc , pic_in );
查看x264_frame_copy_picture 代码能够知道,根据pic_in中图像的颜色空间状况进行了数据的复制。
i_frame是用来标示图像的原始顺序(播放顺序)的:
fenc-> i_frame = h ->frames. i_input++; //frame count, i_frame is in presentation(raw yuv input) order.
接下来:
/* Put the picture(fenc) at the end of Next List(h->frames.next) */
x264_frame_put( h ->frames. next, fenc );
函数x264_frame_put的代码告诉咱们,咱们是将当帧fenc添加到了frames.next列表中。
当current数组中不存在帧的时候,就须要填充这个数组:
步骤2:
/* 2: Select frame types */
if( h-> frames.next [0] == NULL )
return 0;
/* Decide the Slice(Frame) type for Slices in next list, Return Once we found a Non-B frame*/
x264_slicetype_decide( h );
步骤3:
/* 3: move some B-frames and 1 non-B to encode queue(current list) from next queue */
首先是找出第一个非B帧,同时统计出B帧以前的非B帧的数目。
while( IS_X264_TYPE_B ( h->frames .next [bframes ]->i_type ) )
bframes++;
而后是:
/* Move 1 non-B frame into current List*/
x264_frame_put( h ->frames. current, x264_frame_get( &h ->frames. next[ bframes] ) );
.....
接下来是跟前面统计的B帧的数目bframes,从next的列表的开头(由于x264_frame_get函数就是从列表开头开始操做)依次取出相应的B帧放入到current列表最后(由于x264_frame_put函数就是不断的将参数中给出的帧添加到队尾的)
这主要是由于B帧必须等后面的非B帧编码结束后才能编码,因此把暂时不编的一系列B帧存入队列中,一直到非B帧才取出进行编码,以后再进行前面的B帧编码
/* Move the consequent B frames to current list from next list*/
while( bframes -- )
x264_frame_put( h ->frames. current, x264_frame_get( h ->frames. next ) );
下面对A步骤进行一下总结:
这个步骤其实就是一个准备工做,就是为了下一步进行帧编码的时候能够取到正确的帧,为此主要是在3个和帧管理相关的列表之间转移帧。
B. 选取帧进行编码
/* ------------------- Get frame to be encoded ------------------------- */
步骤4:从current列表中取出第一帧进行编码
/* 4: get picture to encode(The first frame in current list) */
h-> fenc = x264_frame_get( h ->frames. current );
if( h-> fenc == NULL )
{
/* Nothing yet to encode (ex: waiting for I/P with B frames) ??*/
/* waiting for filling bframe buffer */
pic_out->i_type = X264_TYPE_AUTO ; //??
return 0;
}
C.创建帧上下文
/* ------------------- Setup frame context ----------------------------- */
步骤5: /* 5: Init data dependant of frame type */
根据帧的类型来进行变量赋值,主要是这3个变量:i_nal_type, i_nal_ref_idc,i_slice_type
须要注意的是:对于IDR帧,须要调用函数x264_reference_reset来重置参考帧列表。(IDR帧是两个GOP之间的边界)
i_poc是帧在GOP中的顺序,i_frame是帧的播放顺序。
D. 初始化
/* ------------------- Init ----------------------------- */
首先,为当前帧创建参考帧列表
/* build ref list 0/1 for current encoding frame.*/
x264_reference_build_list( h, h-> fdec-> i_poc, i_slice_type );
其次,初始化码率控制相关
/* Init the rate control */
x264_ratecontrol_start( h, i_slice_type, h->fenc ->i_qpplus1 );
// Get the qp for macroblock.
i_global_qp = x264_ratecontrol_qp( h );
接下来,若是是B帧,须要为双向参考作好准备:
if( i_slice_type == SLICE_TYPE_B )
x264_macroblock_bipred_init( h );
E. 建立Slice头
/* ------------- Create(Initialize) slice header ----------------------- */
x264_slice_init( h, i_nal_type, i_slice_type , i_global_qp );
F. 写比特流
/* ---------------------- Write the bitstream -------------------------- */
首先是:初始化比特流上下文
/* Init bitstream context */
h-> out. i_nal = 0;
bs_init( & h-> out. bs, h-> out. p_bitstream, h->out .i_bitstream );
注意:access unit delimiters 是NALU的一种类型,用于分割不一样的访问单元。
而后是:写SPS和PPS
/* Write SPS and PPS */
因为SPS和PPS是一组图像共同的信息,只有当本帧图像是IDR是才须要写SPS和PPS信息。
/* generate sequence parameters */
x264_nal_start( h , NAL_SPS , NAL_PRIORITY_HIGHEST );
x264_sps_write( &h ->out. bs, h-> sps );
x264_nal_end( h );
/* generate picture parameters */
x264_nal_start( h , NAL_PPS , NAL_PRIORITY_HIGHEST );
x264_pps_write( &h ->out. bs, h-> pps );
x264_nal_end( h );
x264_nal_start 和x264_nal_end 两个函数完成对NALU的码流写入。x264_sps_write 和x264_pps_write 函数就是根据H.264规定的语法结构进行相应信息的写入。
接下来是:对帧的数据进行编码,并写码流
/* Write frame ,The Key function*/
i_frame_size = x264_slices_write( h );
x264_slices_write 是进行具体编码的核心函数。
注意:这个地方不明白为何要执行下列操做?
/* restore CPU state (before using float again ??) */
x264_cpu_restore( h-> param.cpu );
以后可能还要有一些操做:若是P帧编码出错,就从新编码成I帧。设置码流特征信息,设置码流输出。码流控制更新(x264_ratecontrol_end),更新参考帧(x264_reference_update),重置缓冲区(x264_frame_put),计算编码状态信息。
最后是:结束码流
/* End bitstream, set output */
*pi_nal = h-> out. i_nal;
*pp_nal = h-> out. nal;
G. 在编码完当前帧以后,更新编码器的相关状态
/* ---------------------- Update encoder state ------------------------- */
首先:更新码流控制相关
/* update rc */
x264_cpu_restore( h-> param.cpu );
x264_ratecontrol_end( h, i_frame_size * 8 );
而后:更新参考帧列表(同时回收了当前帧所使用的空间)
/* handle references , Update references after Encoing One frame.*/
if( i_nal_ref_idc != NAL_PRIORITY_DISPOSABLE )//
x264_reference_update( h );
// Put back.
x264_frame_put( h-> frames.unused , h ->fenc );
H. 计算相关的统计数据
/* ---------------------- Compute/Print statistics --------------------- */
包括各种型帧的数目,PSNR 等。
8. x264_encoder_encode函数-2
给出该函数的调用图:
对于其中的一些函数,已经进行了简单分析,就再也不分析了,主要分析一些重要的函数。
(1)x264_slicetype_decide
用于判断next list中的slice的类型。
这个函数中涉及到x264在何种状况下怎样决定帧的类型,同时对GOP的大小进行了限定。
(2)x264_ratecontrol_start和x264_ratecontrol_qp
和码率控制相关。
(3)x264_slice_init
主要包括头信息的初始化和宏块信息初始化。
(4)x264_sps_write和x264_pps_write
从调用函数能够看到,主要是调用了和写码流相关的函数:bs_write, bs_write_ue, bs_write_se, bs_write1,
这些函数都是将具体的信息采用熵编码的方式转换为码流。
(5) x264_slices_write
这个是核心函数。
这个函数是根据是否采用多线程而造成了两个分支
9. x264_slice_write函数
(1)首先是对存储帧统计数据的结构体进行初始化
/* init stats */
memset( & h-> stat. frame, 0, sizeof (h ->stat. frame) );
(2)因为本函数是对slice进行编码,因此先调用x264_nal_start来将slice相关的一些信息写入到NALU中。
(3)将slice的头信息写入到NALU中:x264_slice_header_write
主要是调用相关的码流写入函数。
(4)若是采用了CABAC编码的话,须要进行相关初始化。
主要调用x264_cabac_context_init和x264_cabac_encode_init
(5)for循环:这是对slice中的每个宏块进行编码。
bs_pos函数用于输出比特流的当前当前读写位置。
x264_macroblock_cache_load函数是将已编码数据参数和待编码数据装入到h->mb.cache中。
x264_macroblock_analyse函数完成模式选择:* Slice I: choose I_4x4 or I_16x16 mode
* Slice P: choose between using P mode or intra (4x4 or 16x16)
x264_macroblock_encode函数是根据选定的模式进行编码。
宏块编码完成以后,根据所采用的熵编码方式来进行码流的写入:x264_macroblock_write_cabac或x264_macroblock_write_cavlc
x264_macroblock_cache_save函数保存已编码的信息,为了下一次的编码作参考。
而后统计编码状态数据,若是使用了动态qp值,用 x264_ratecontrol_mb进行qp调整。
(6)bs_rbsp_trailing,写入编码结尾数据。
(7)x264_nal_end结束NALU
10. x264_macroblock_cache_load和x264_macroblock_cache_save函数
x264_macroblock_cache_load将参考帧中某位置的(重建后)数据保存进cache,供参考和反复使用。
x264_macroblock_cache_save在分析和编码后将当前块写进cache。
这两个函数都是在操做h->mb.cache,在而h->mb.cache的设计是很是巧妙的,这里还要牵扯到数组x264_scan8用来索引cache。
11. x264_macroblock_analyse函数-1
这个函数是进行H.264中主要的一个工做:模式选择,因此调用的函数也不少。这儿主要分析其流程。
(1)调用x264_macroblock_init函数
主要是初始化变量,如帧间、帧内的satd值、初始化MV的范围等一些参数。
(2)针对不一样类型的帧进行分析。
A. 对于I帧,调用x264_mb_analyse_intra进行帧内预测。在这个函数中,分别对16x16, 4x4 和8x8进行模式预测,计算SATD。而后进行比较16x16,8x8,4x4的cost,得到帧内编码的宏块的模式。
注意到最后的模式是使用h->mb .i_type来记录的,而且只是宏块的模式,对于具体的模式是记录在x264_mb_analysis_t结构体的元素i_predict16x16,i_predict8x8[2][2]和i_predict4x4[4][4]中。
B. 对于P帧,首先判断是不是skip模式,只有在左,上,左上,右上有一个是skip模式的时候,这时mb才多是skip模式。而后调用函数x264_macroblock_probe_pskip来判断当前宏块是否能够采用skip模式进行编码。
当不采用p_skip模式时,就须要从帧间模式中选择合适的模式来编码。
首先是调用函数x264_mb_analyse_load_costs来对全部可能的MV来提早计算cost:lambda*nbits.
这儿代码中的注释好像有些问题,对我有些误导。我仔细看了这儿的代码以后,具体理解是:
对于16x16,16x8,8x16,8x8,8x4,4x8,4x4,这些块的划分方式,代码中是先对16x16进行分析,调用函数x264_mb_analyse_inter_p16x16,而后能够计算获得相应的运动矢量和l0.me16x16 .cost(其中还要考虑到参考帧部分,运动估计)。
在开启对宏块进行子划分的状况下,先对8x8模式进行分析,调用的是函数x264_mb_analyse_inter_p8x8或x264_mb_analyse_inter_p8x8_mixed_ref(主要看是否容许子块采用不一样的参考帧),对4个8x8块进行分析,包括运动估计,参考帧选择等,最后获得运动矢量、参考帧和l0.i_cost8x8,l0.me8x8[i].cost。
接下来比较一下16x16和8x8下的cost,若是8x8的l0.i_cost8x8较小,就须要进行子划分8x4,4x8和4x4的分析。这样,对于4个8x8块,在for循环中进行依次分析。也是首先调用x264_mb_analyse_inter_p4x4函数对8x8块中的4个4x4块进行分析(运动估计,参考帧选择),最后计算获得i_cost4x4[i8x8],将这个i_cost4x4[i8x8]与l0.me8x8[i].cost比较,看是否要进行8x4,4x8的划分,对应的调用函数x264_mb_analyse_inter_p8x4和x264_mb_analyse_inter_p4x8,获得相应的l0.i_cost8x4 [i ]和l0.i_cost4x8 [i ],并通过和i_cost4x4[i]进行比较,选出最优的子划分保存在h->mb .i_sub_partition [i]中。通过这一系列操做,8x8模式就分析完了。
再接着分析16x8和8x16两种模式,分别调用函数x264_mb_analyse_inter_p16x8和x264_mb_analyse_inter_p8x16,计算获得l0.i_cost16x8和l0.i_cost8x16,并通过比较获得最优划分保存在mb.i_partition中。这样就完成了模式的选择。
完成模式选择以后,为了提升运动估计的精确度,对最优模式划分进行亚像素运动搜索,调用函数x264_me_refine_qpel。
因为在P帧中一样能够采用帧内预测,因此接着调用函数x264_mb_analyse_intra进行帧内模式选择,还有可能调用x264_mb_analyse_intra_chroma对色度进行模式选择。
最终通过一系列的比较,获得最优的模式保存在h->mb .i_type中。
C. 对于B帧,因为B帧能够双向参考,因此比较复杂。
首先调用x264_mb_predict_mv_direct16x16判断是否采用B_Direct模式,若是采用直接模式,则分别进行空间和时间的直接预测。这里B帧双向参考,前向和后向的参考和p帧的方向是相似的。
也调用x264_mb_analyse_load_costs函数来进行运动估计cost方面的初始化。
接着调用函数x264_mb_analyse_inter_b16x16来对16x16划分方式分析,其中对L0和L1两个参考列表中的帧都进行了运动搜索。
接下来对8x8方式进行分析,调用的是函数x264_mb_analyse_inter_b8x8,对4个8x8块进行了操做,获得i_cost8x8bi。并将i_cost8x8bi与16x16状况进行比较,若是i_cost8x8bi较小的话,须要根据4个8x8块在x264_mb_analyse_inter_b8x8函数中决定的子划分方式mb.i_sub_partition [i]来决定是采用8x16仍是16x8,调用相应的函数x264_mb_analyse_inter_b16x8和x264_mb_analyse_inter_b8x16,计算获得相应的cost以后,就能比较获得最优的划分方式,保存在 h-> mb. i_partition中。
从上面能够看到,在x264中B帧中宏块不支持8x8如下的划分的。
这样完成模式选择以后,一样对最优的划分模式进行亚像素的运动搜索,与P帧状况相似。仍是调用函数x264_me_refine_qpel,只是调用的次数更多。
一样采用了函数x264_mb_analyse_intra进行了帧内模式选择,最后从帧内和帧间中选出最佳的模式,保存在h->mb .i_type中。
(3)根据上面的分析更新宏块的信息,在函数x264_analyse_update_cache中完成。
12.x264_macroblock_analyse函数-2
上图就展现了x264_macroblock_analyse函数中调用函数。
(1)x264_mb_analyse_init函数
x264_mb_analyse_init()函数的功能包括:初始化码率控制的模型参数(码率控制依然基于Lagrangian率失真优化算法,因此初始化lambda系数),把各宏块分类的Cost设为COST_MAX,计算MV范围,快速决定Intra宏块。
(2)x264_mb_analyse_intra 函数
这个函数进行帧内的模式选择,主要包括对16x16,4x4和8x8三种大小遍历全部模式并比较其cost
首先,对于16x16模式,调用函数predict_16x16_mode_available来判断V,H,DC和Plane四种模式中有哪些是可用的(由于对于不一样位置的宏块,其左、上,左上和右上位置的宏块不必定都是存在的).结果保存在数组predict_mode中(记录了哪些模式可用),而后在for循环中对可行的模式进行预测计算,在循环中选出最好的一种模式。
A. h->predict_16x16 是函数指针数组,是经过x264_predict_16x16_init( h->param.cpu, h->predict_16x16 );来完成赋值的,具体代码在x264_encoder_open函数中。经过x264_predict_16x16_init的代码能够看到,h->predict_16x16 中就是Intra16x16所对应的4种预测方式(可是考虑到边界的状况,x264中是有7种方式),因此,代码:
h->predict_16x16 [i_mode ]( p_dst );
就完成了相应i_mode模式的预测计算,p_dst指向的就是相应的预测值。
B. h->pixf.mbcmp是函数指针数组,而这儿的h->pixf.mbcmp[PIXEL_16x16]就是该数组的第一个元素,能够发现这是一个函数,根据输入的参数能够计算获得一个值,通过查找,最终发现,mbcmp的值就是h->pixf中的的sad和satd中的一个,具体代码是在x264_encoder_open函数中:
memcpy( h-> pixf. mbcmp,( h-> mb. b_lossless || h->param .analyse .i_subpel_refine <= 1 ) ? h-> pixf. sad : h->pixf .satd , sizeof(h ->pixf. mbcmp) );
这样由p_src和p_dst就计算获得失真SAD或SATD。
C. 函数bs_size_ue用于对所采用的模式进行码率大小的估算,而后计算cost(仍然是基于Lagrange的):
i_sad = h ->pixf. mbcmp[PIXEL_16x16 ]( p_dst , FDEC_STRIDE , p_src,FENC_STRIDE ) + a-> i_lambda * bs_size_ue ( x264_mb_pred_mode16x16_fix[i_mode ] );
接下来,根据b_mbrd变量来采用不一样的方式(这儿对b_mbrd并不理解其含义)。调用x264_mb_analyse_intra_chroma对色度进行模式选择。和亮度16x16相似,采用了predict_8x8chroma_mode_available函数来选取合适的模式,而后利用for循环选出cost最小的模式,操做方式和上面的分析都是相似的。以后,有可能要调用函数x264_rd_cost_mb,如今尚未弄明白这是为何?!
对于Intra4x4,采用的是分红16个4x4块来处理的。因此整个过程式在for循环中的。
A. 为了减小编码时的比特数,x264先对所要采用的模式进行了一个预测,若是预测中的话就只须要一个比特,而预测不中的话就须要4个比特来保存所采用的模式。
从x264_mb_predict_intra4x4_mode函数代码能够看到,预测的方式就是根据左和上的宏块的预测模式中选择较小的那个模式。
B. 仍是老方法,调用函数predict_4x4_mode_available来找出哪些模式是可行的,而后针对这些模式利用predict_4x4进行预测,再计算cost,从中选择最优模式。
C. 为了下一个4x4块编码使用,须要对当前4x4块进行编码(这儿我还不是很理解):
/* Luma prediction */
h-> predict_4x4[a ->i_predict4x4[ x][ y]]( p_dst_by );
/* encoding the current 4x4 Block, Update the reconstructed data(fdec). */
x264_mb_encode_i4x4( h , idx , a ->i_qp );
/* Cache the intra 4x4 prediction mode of current Block */
h->mb .cache .intra4x4_pred_mode [x264_scan8 [idx ]] = a->i_predict4x4 [x ][y ]; //Update the prediction mode for later use??.
D. 须要注意的是为了和Intra16x16进行比较,这儿所采用的cost计算方式:
a-> i_sad_i4x4 += a ->i_lambda * 24; /* from JVT (SATD0) */ //??
//非RDO率失真优化模式下,宏块总代价cost_intra4*4 = 16个 4*4 小块的最佳 cost 求和 + 4 * 6 * lambda_mode.
//此处因为还未进行4X4代价计算,只是预先增长4 * 6 * lambda_mode.
//当采用4X4分块时,因为每一个4X4块的最优预测编码模式都须要进行编码传输,这样,相比较于16X16模式就多了传输比特数,
//为了合理公平比较,规定每一个8*8块加一个6*lambda_mode,所以就等因而加了一个 4 * 6 * lambda_mode.
一样出现b_mbrd,之后弄懂了再讨论。
对于Intra8x8,采用的是分红4个8x8块来处理,仍然是在for循环中进行的。
A. 为了减小比特数,一样进行了模式预测,和Intra4x4时采用的是一样的函数,只是参数有些改变。
B. 调用函数predict_4x4_mode_available来找到可行的模式。
C. 和Intra4x4相似,也是对当前8x8块进行编码:
/* Luma prediction for each 8x8 Block. */
h-> predict_8x8[a ->i_predict8x8[ x][ y]]( p_dst_by, h ->mb. i_neighbour8[idx ] );
/* Calculate the reconstructed data,Store the prediction data in p_dec */
x264_mb_encode_i8x8( h , idx , a ->i_qp );
/* Cache the intra 8x8 prediction mode for each 8x8 Block */
x264_macroblock_cache_intra8x8_pred( h , 2*x , 2*y, a ->i_predict8x8[ x][ y] );
这样Intra16x16,Intra4x4和Intra8x8模式都选择完了,获得了三种状况下的cost,而后进行比较获得最优的划分方式:
i_cost = analysis .i_sad_i16x16 ; // SAD cost of i_16x16
h-> mb. i_type = I_16x16 ;
if( analysis.i_sad_i4x4 < i_cost )
{
i_cost = analysis .i_sad_i4x4 ;
h-> mb. i_type = I_4x4 ;
}
if( analysis.i_sad_i8x8 < i_cost )
h-> mb. i_type = I_8x8 ; // We Only remember the Macro-block type.
(3)x264_macroblock_probe_pskip和x264_macroblock_probe_bskip
这两个函数是判断是否要采用skip模式。虽然是分别针对p帧和B帧,可是两个函数最终都是调用的同一个函数x264_macroblock_probe_skip,只是调用的时候参数是不同的。下面是x264_macroblock_probe_skip的调用方式:
这个函数主要原理就是对宏块进行预测,而后计算残差,而后对残差进行DCT变换,量化,而后利用x264_mb_decimate_score函数来为DCT系数打分,看是否能够将这些系数设置为0,这样就不用对这个宏块编码了,即skip模式,对于不一样的块,有不一样的阈值来限制。
(4)x264_mb_analyse_load_costs函数
关于这个函数,代码中给出的注释是:/* initialize an array of lambda*nbits for all possible mvs */
应该是对运动矢量所须要的cost进行提早计算,p_cost_mv[i]用于保存这些值。因为MVD的值可能为正也可为负,因此操做中对于正负MVD的运动矢量cost计算是同样的。操做的时候是先将p_cost_mv[i]的指针移动到数组中间的,而后从中间往两边赋值的。
网上有人指出,这个数组存在内存泄露的问题,确实我找了好屡次都没有找到关于这个对这个数组释放内存的操做。
(5)x264_mb_analyse_inter_p16x16函数
这个函数实现的是16x16的运动搜索。
首先利用宏LOAD_FENC加载须要编码的宏块数据,而后是一个for循环,该循环是参考帧的循环,从最近的一个参考帧开始搜索,一直到最远的一个参考帧。在循环中,先利用宏LOAD_HPELS载入参考帧数据,而后调用x264_mb_predict_mv_16x16函数寻找运动矢量的预测值MVP,主要以上、右上、左块运动矢量的中值做为预测值,并存储在m.mvp中。而后调用x264_mb_predict_mv_ref16x16函数,来寻找候选运动矢量。这些候选者包括:空间相邻的左、左上、上、右上块的MV;第0个参考帧中的当前块、右边块、下边快运动矢量乘以时间差权重。接下来调用x264_me_search_ref进行运动搜索。搜索时先从全部候选运动矢量中选出最佳的起点,而后使用小钻石法、六边形法、UMH或者全搜索搜索出最佳的整像素位置。在x264_me_search_ref函数的最后,同时调用了函数refine_subpel了进行1/2和1/4运动搜索,而且都是使用小钻石法。在搜索出最佳运动矢量后,若是当前是最近一个参考帧,并且最佳SA(T)D小与检测门限,则尝试对其进行P_SKIP编码。最后调用函数x264_macroblock_cache_ref保存搜索结果。
(6)x264_mb_analyse_inter_p8x8和x264_mb_analyse_inter_p8x8_mixed_ref函数
这两个函数实现对8x8模式的运动搜索。这两个函数区别在于,函数x264_mb_analyse_inter_p8x8只使用在16x16运动搜索中用到的帧。候选MV的数目也是有限制的。而x264_mb_analyse_inter_p8x8_mixed_ref则没有限制,这些8x8块的参考帧能够是不一样的帧。
(7)x264_mb_analyse_inter_p4x4函数
对8x8块内的4个4x4小块进行运动搜索的分析。和x264_mb_analyse_inter_p8x8是相似的。
(8)x264_mb_analyse_inter_p8x4和x264_mb_analyse_inter_p4x8函数
这两个函数是对8x8块的两个8x4或两个4x8进行运动搜索。和上面的都是相似的。
(9)x264_mb_cache_mv_p8x8函数
这个函数是为每一个8x8块保存MV。
(10)x264_mb_analyse_inter_p16x8和x264_mb_analyse_inter_p8x16函数
这两个函数完成对16x16块中的两个16x8块或两个8x16块的搜索。
为了加快搜索的速度,x264对这两个函数所采用的参考帧进行了限制:Only search in the reference frame found in the inter_8x8 motion estimation. The mv candidates is also limited. 也就是只采用在x264_mb_analyse_inter_p8x8_mixed_ref中使用到的参考帧。
(11)x264_me_refine_qpel函数
这个函数是完成亚像素的运动搜索,其实在该函数中是调用了refine_subpel来具体实施的。
(12)x264_mb_predict_mv_direct16x16函数
判断是否可使用Direct模式。
(13)x264_mb_mc 函数
这个函数是根据运动搜索完成以后的MV来进行宏块的运动补偿。
在这个函数中根据不一样的宏块类型进行运动补偿,下面是该函数调用的函数:
该函数在运动补偿时是和采用的参考序列式相关的。下面是一个流程图:
(14)x264_mb_analyse_inter_b16x16 函数
这对16x16进行运动搜索,因为是针对B帧的,因此涉及到了采用L0和L1参考帧列表的状况。
(15)x264_mb_analyse_inter_b8x8函数
这是B帧对应的8x8运动搜索,仍是宏块分为4个8x8块进行。
(16)x264_mb_analyse_inter_b16x8和x264_mb_analyse_inter_b8x16函数
B帧对应的16x8和8x16搜索,将宏块分红两个16x8或两个8x16.
13. x264_mb_analyse_inter_p16x16函数
(1)x264_mb_predict_mv_16x16
这个函数是对宏块的运动矢量进行预测,从而获得MVP。在进行MVP计算中采用的是左A,上B,右上C和左上D四个宏块的运动矢量。
在函数中要判断一下C是否存在的,若是C存在,则是优先使用C的MV,若是C不存在,那么就用D来代替C。
而后统计当前宏块所参考的参考帧序号与邻块ABC或ABD所参考的参考帧序号相同数 。若是与ABC或ABD中的两个或两个以上宏块的参考帧相同,那么直接调用x264_median_mv来取这三个邻块的运动矢量的中值做为预测运动矢量。若是只有一个相同时,预测运动矢量设置为该邻块的运动矢量。
(2)x264_mb_predict_mv_ref16x16
在代码中给出了注释,指出这个函数并非标准要求的,而是x264为了提升编码器系能而本身添加的。由于标准中并无要求在运动估计以前给出候选MV,而是只要求找到最优MV便可。x264为了加快MV的搜索,采用了候选MV的方式来预测搜索起点。
(3)x264_me_search_ref
这个函数就是具体进行运动搜索。搜索时从候选运动矢量中选取起点,而后根据设定进行运动搜索。最后调用了refine_subpel进行亚像素运动搜索。