在上一周的x264关于MB模式选择学习后,就JM15.1中模式选择,作为一个比较。
在JM里面,编码的最小的单元还是MB,核心函数还是encode_one_macroblock,不过在JM里面,已经将encode_one_macroblock分成了4个等级,由configfile里面的RDOptimization的设置值来选择不同等级的encode_one_macroblock,函数表现形式如下:
switch (params->rdopt)
{
case 0:
encode_one_macroblock = encode_one_macroblock_low;
break;
case 1:
default:
encode_one_macroblock = encode_one_macroblock_high;
break;
case 2:
encode_one_macroblock = encode_one_macroblock_highfast;
break;
case 3:
encode_one_macroblock = encode_one_macroblock_highloss;
break;
}
现在就encode_one_macroblock_low (Slice *currSlice, Macroblock *currMB)进行讨论:
前面这个部分主要是进行参数的设置,ex,
//这个Intra主要是对帧间帧内编码的初始的一个选择初始化:
intra = (short) (islice || (pslice && img->mb_y==img->mb_y_upd && img->mb_y_upd!=img->mb_y_intra));
//获得SearchMode的flag.
UMHEX_decide_intrabk_SAD(); smpUMHEX_decide_intrabk_SAD();
//===== MOTION ESTIMATION FOR 16x16, 16x8, 8x16 BLOCKS =====
//这在个for循环里面对以上3个模式进行比较MV的cost,
for (min_cost=INT_MAX, mode=1; mode<4; mode++)
{
//针对上面的不同的MB的分块,进行块的循环
for (cost=0, block=0; block<(mode==1?1:2); block++)
{
//进行MB的划分:在下面有详解
PartitionMotionSearch (currMB, mode, block, lambda_mf);
//其他函数省略
}
}
在完成了16*16,8*16,16*8之后,进行判断是否进行再次的分割,
//下面的和上面的类似,进行的是8x8, 8x4, 4x8 and 4x4的分割,
if ((!inter_skip) && enc_mb.valid[P8x8])
{
}
//========= C H O O S E B E S T M A C R O B L O C K M O D E =========
if (enc_mb.valid[0] && bslice) // check DIRECT MODE
{
if(have_direct)
{
switch(params->Transform8x8Mode)
{
case 1: // Mixture of 8x8 & 4x4 transform
cost = ((cost8x8_direct < cost_direct) || !(enc_mb.valid[5] && enc_mb.valid[6] && enc_mb.valid[7]))
? cost8x8_direct : cost_direct;
break;
case 2: // 8x8 Transform only
cost = cost8x8_direct;
break;
default: // 4x4 Transform only
cost = cost_direct;
break;
}
}
else
{ //!have_direct
cost = GetDirectCostMB (currMB, bslice);
}
if (cost!=INT_MAX)
{
cost -= (int)floor(16*enc_mb.lambda_md+0.4999);
}
if (cost <= min_cost)
{
if(active_sps->direct_8x8_inference_flag && params->Transform8x8Mode)
{
if(params->Transform8x8Mode==2)
currMB->luma_transform_size_8x8_flag=1;
else
{
if(cost8x8_direct < cost_direct)
currMB->luma_transform_size_8x8_flag=1;
else
currMB->luma_transform_size_8x8_flag=0;
}
}
else
currMB->luma_transform_size_8x8_flag=0;
//进行PartitionMotionSearch解析,对里面的重要的参数说明
PartitionMotionSearch (Macroblock *currMB,
int blocktype,
int block8x8,
int *lambda_factor)
{
//
这里的bx0, by0两个数组分别对应了SKIP模式,16×16,16×8,8×16,P8×8这四种模式的横坐标和纵坐标。
//如图所示的16×16宏块,首先划分为4个8×8子块(因为PartitionMotionSearch()函数处理的最小块的尺寸为//8×8),以 4×4block为单位设定坐标,图上已标出4个8×8子块左上角的块坐标。SKIP模式实际上并不牵涉到这个//函数,因此坐标全部置零;16×16模式只 有第一个坐标起作用,后三个置零;16×8只有前两个有意义,标出两个//partition的左上角坐标,如图标出了(0,0),(0,2),对照bx0, by0可以看到相应坐标值;最多子块情况为4个8×8,即最//后一组坐标。
static int bx0[5][4] = {{0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,2,0,0}, {0,2,0,2}};
static int by0[5][4] = {{0,0,0,0}, {0,0,0,0}, {0,2,0,0}, {0,0,0,0}, {0,0,2,2}};
。。。。
int step_h0 = (params->part_size[ parttype][0]);//这个是对分割的类型的水平的x大小
int step_v0 = (params->part_size[ parttype][1]);//y
int step_h = (params->part_size[blocktype][0]);//块的水平x大小,如:16*16,8*16,16*8
int step_v = (params->part_size[blocktype][1]);//y
。。。。。
int by = by0[parttype][block8x8];
int bx = bx0[parttype][block8x8];
//进行帧间编码时,对前后向参考帧
for (list=0; list<numlists;list++)
{
//对前向或后向的参考帧进行比较
for (ref=0; ref < listXsize[list+list_offset]; ref++)
{
//===== LOOP OVER SUB MACRO BLOCK partitions
for (v=by; v<by + step_v0; v += step_v)
{
pic_block_y = img->block_y + v;
for (h=bx; h<bx+step_h0; h+=step_h)
{
all_mv = img->all_mv[list][ref][blocktype][v][h];
pic_block_x = img->block_x + h;
//--- motion search for block ---
if (Motion_Selected != 1)
{
//
BlockMotionSearch 主要是为当前MB找到在参考帧中的最小cost,
mcost =
BlockMotionSearch (currMB, ref, list, h<<2, v<<2, blocktype, search_range, lambda_factor);
*m_cost += mcost;
if ( (params->Transform8x8Mode == 1) && params->RDOQ_CP_MV && (blocktype == 4) && currMB->luma_transform_size_8x8_flag)
{
tmp_mv8[list][ref][v][h][0] = all_mv[0];
tmp_mv8[list][ref][v][h][1] = all_mv[1];
motion_cost8[list][ref][block8x8] = mcost;
}
}
else
updateMV_mp(m_cost, ref, list, h, v, blocktype, lambda_factor, block8x8);
//--- set motion vectors and reference frame (for motion vector prediction) ---
for (j=pic_block_y; j<pic_block_y + step_v; j++)
{
memset(&ref_array [j][pic_block_x], ref, step_h * sizeof(char));
for (i=pic_block_x; i<pic_block_x + step_h; i++)
{
memcpy(mv_array [j][i], all_mv, 2* sizeof(short));
}
}
}
}
}
}
}
以上是对JM里面的encode_one_macroblock_low;进行的分析,其他的encode_one_macroblock流程类似。
针对前面的X264的宏块模式的选择:JM在做这一部分的时候,过程比较详细,考虑的也很前面,针对不同的RDoption会有不同的宏块的编码方式,这样得到的信号在信噪比,图像质量方面会有提高,但是计算的复杂度,编码时间等方面就不如x264的直接。