X264码率控制三(Mb-Tree介绍)

X264码率控制一(abr码率控制)

X264码率控制二(vbv码率控制)

X264码率控制三(Mb-Tree介绍)

一、MBTree原理介绍

        MbTree主要是计算参考帧(P帧/Bref)中每一个块的遗传信息。

        把帧间的参考关系看作是遗传/继承关系;b帧块参考了P帧块可以看作是b帧块从P帧块哪里继承了信息或者说是P帧块将信息遗传给了b帧块。 P帧块遗传的信息越多也就是意味着越重要应该更清晰(QP更低),反之则应该更模糊(QP更高)。注:Mb-tree是宏块级的算法,本文中的文字P帧、B帧等的遗传/继承都是指宏块。

        slicetype_frame_cost() 会计算出B参考P0\P1的MB 的intra_cost和inter_cost, inter_cost越小表示从P0\P1 继承的信息越多,也即P帧 继承给B帧的信息越多。

        P0和P1遗传给B的信息如下式,inter_cost越小则propagate_cost越大,遗传信息越多。

        这里P0和P1都遗传给了B,所以单独的P0遗传给B的信息需要一个系数,根据P0到B的距离以及P1到B的距离可以得出一个系数 ,如下:

        可以得到P0遗传给B的信息为:

        B参考P0来编码,即B从 P0继承了信息,也就是P0给了B 遗传信息,需要将继承信息累加到对应的宏块中。

        由于运动搜索的结果不一定让mv刚好处于某一个Mb上,可能处于多个Mb宏块上,则将当前的遗传代价根据所占块的面积来分配给各个宏块。

mb0的遗传代价=s_indx0/s_all∗propagate_cost;

mb1的遗传代价=s_indx1/s_all∗propagate_cost;

mb2的遗传代价=s_indx2/s_all∗propagate_cost;

mb3的遗传代价=s_indx3/s_all∗propagate_cost;

        上面的s_all是一个16x16的宏块面积,即s_all=256;s_indx0~s_indx3是所处宏块实际占用的面积。

        由于x264编码器编码的时候有各种参数可以调整qp,所以这些qp会影响遗传信息,需要对遗传信息进行修正。

1、aq mode每一个对宏块有一个qp_offset会对每一个MB的qp进行调整,所以propagate_cost需要乘上qp的系数:

 2、可变帧率下每一个帧的显示时长(duration)不同,duration更大的帧则更加重要也需要更加清晰,duration长的帧在屏幕上停留的时间更长对主观影响更大,所以也需要根据每一帧的duration来修正propagate_cost,对应的系数为fps_factor=cur_duration/average_duration;

3、若B为bref会被其他的b参考,也就是说B会遗传信息给其他帧,如果P0不够清晰会将P0帧的模糊传递给B,B又通过遗传给其他帧会导致模糊的扩散,所以需要加上B帧的遗传(P帧也是类似需要加上多级遗传),假设B遗传给其他帧的信息为propagate_cost_b则P0的遗传信息为: 

4、P0不仅仅会遗传给B也会遗传给其他帧,最后要累加上遗传给其他帧的propagate_cost。 

二、MBTree调整Mb qp的过程

        mb在计算qp的时候会加上qp_offset ,mb_tree对qp_offset的调整如下:

        参数说明: qcompress:表示qp变换的程度,1qp固定不变,0qp可以变化; propagate_cost:表示当前块的遗传代价。 intra_cost:当前块的帧内代价; fps_factor:平均每帧时长和当前帧时长的比值; weigℎtdelta:weighted_pre关闭,mbtree和psy开启才会生效的参数。

        上面公式(1)等价于公式(2) ,在代码中使用第公式(2)来计算。

        代码实现具体在macroblock_tree_finish()函数中,如下:

nt fps_factor = round( average_duration / frame->f_duration) * 256 / 0.5;
if( ref0_distance && frame->f_weighted_cost_delta[ref0_distance-1] > 0 )
    weightdelta = (1.0 - frame->f_weighted_cost_delta[ref0_distance-1]);
float strength = 5.0f * (1.0f - h->param.rc.f_qcompress);
for( int mb_index = 0; mb_index < h->mb.i_mb_count; mb_index++ )
{
        int intra_cost = (frame->i_intra_cost[mb_index] * frame->i_inv_qscale_factor[mb_index] + 128) >> 8;
        if( intra_cost )
        {
            int propagate_cost = (frame->i_propagate_cost[mb_index] * fps_factor + 128) >> 8;
            float log2_ratio = x264_log2(intra_cost + propagate_cost) - x264_log2(intra_cost) + weightdelta;
            frame->f_qp_offset[mb_index] = frame->f_qp_offset_aq[mb_index] - strength * log2_ratio;
        }
    }

 三、MBTree的主要代码

1、主要函数调用流程

macroblock_tree()  

      -->slicetype_frame_cost()计算帧内和帧间代价  

      -->macroblock_tree_propagate()根据代价计算遗传代价  

      -->macroblock_tree_finish()根据遗传代价计算每一个宏块的偏移

macroblock_tree_propagate()  

      -->mbtree_propagate_cost()计算当前帧从参考帧中继承来的信息  

       -->mbtree_propagate_list()将遗传的信息保存到对应宏块中

2、主要函数介绍 

// 代码有删减
//计算当前块从参考帧块继承来的信息
void mbtree_propagate_cost()
{
    float fps = *fps_factor;
    for( int i = 0; i < len; i++ )
    {
        int intra_cost = intra_costs[i];//帧内代价
        int inter_cost = X264_MIN(intra_costs[i], inter_costs[i] & LOWRES_COST_MASK);
        float propagate_intra  = intra_cost * inv_qscales[i];//帧内代价乘aq的系数
       //propagate_in为当前帧遗传给其他帧的遗传信息,
        float propagate_amount = propagate_in[i] + propagate_intra*fps;
        float propagate_num    = intra_cost - inter_cost;
        float propagate_denom  = intra_cost;
        dst[i] = X264_MIN((int)(propagate_amount * propagate_num / propagate_denom + 0.5f), 32767);
    }
}
//代码有删减
//将当前块遗传出去的代价保存到对应的块
mbtree_propagate_list()
{
    for( int i = 0; i < len; i++ )
    {
        int listamount = propagate_amount[i];//当前的遗传代价
        if( !M32( mvs[i] ) )//mv为0则当前遗传代价只属于一个Mb
        {
            MC_CLIP_ADD( ref_costs[mb_y*stride + i], listamount );
            continue;
        }   
        unsigned idx0 = mbx + mby * stride;//当前宏块的位置
        unsigned idx2 = idx0 + stride;//下放宏块的位置
        
       //这里的xy相对的是一个Mb块形成的正方形大块的的范围(但是实际上宽高和乘上了2),
        //一个MB的块是16x16,所以x和y的范围都是在0-32,所以这里的计算面积被放大了4倍
        int idx0weight = (32-y)*(32-x);//当前宏块的参考面积
        int idx1weight = (32-y)*x;     //右侧宏块的参考面积
        int idx2weight = y*(32-x);   //下方宏块的参考面积
        int idx3weight = y*x;//右下宏块的参考面积
       //下面的512等于256*2
       // >> 10等于‘/256 / 4’,‘/256’就是除总面积来计算每一个宏块的参考面积的比例,
       //除以4因为上面的xy计算的面积被放大了4倍
        idx0weight = (idx0weight * listamount + 512) >> 10;
        idx1weight = (idx1weight * listamount + 512) >> 10;
        idx2weight = (idx2weight * listamount + 512) >> 10;
        idx3weight = (idx3weight * listamount + 512) >> 10;
        //更新当前宏块遗传出去的代价
        MC_CLIP_ADD( ref_costs[idx0+0], idx0weight );
        MC_CLIP_ADD( ref_costs[idx0+1], idx1weight );
        MC_CLIP_ADD( ref_costs[idx2+0], idx2weight );
        MC_CLIP_ADD( ref_costs[idx2+1], idx3weight );
    }
}

  • 20
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值