P-frame 分析 -libx264

本文深入探讨了在视频编码过程中,如何根据宏块的特性选择最合适的帧间预测模式,以实现高效的数据压缩。通过分析宏块与参考帧之间的相似性,提出了一种基于门限值的帧间预测模式筛选机制,有效减少了不必要的计算,提高了编码效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

//在对P帧或B帧的宏块进行预测之前,先判断当前帧是否适宜用帧内模式,

  //如果宏块的临近已编码宏块均不采用帧内模式,并且若宏块所在的slice为p的话,

  //参考帧相应位置的宏块也不采用帧内模式的话,则该宏块采用帧内预测的可能性就很小。

  //那么在该宏块用帧间模式得到的最小的SAD后,只要计算帧内16*16预测模式的SAD,

  //将二者相比,当比值超过门限值时,即用帧间预测模式,而不用计算帧内4*4的模式了。


else if( h->sh.i_type == SLICE_TYPE_P )// P帧预测模式的选择

    {
        //当是P帧时,首先判断是否是skip模式,只有在左,上,左上,右上有一个是skip模式的时候,

        //这时mb才 可能是skip模式:b_skip = x264_macroblock_probe_pskip( h ); 

        //当不采用p_skip模式时,则调用x264_mb_analyse_inter_p16x16( x264_t *h, x264_mb_analysis_t *a )

        //进行帧间16*16块的分析:计算p16x16模式的运动适量及cost(其中还要考虑到参考帧部分,运动估计)。

        //然后在允许16*16块分割时,进一步分析8*8,4*4,16*8,8*16块的运动矢量,并比较cost的大小。

        
//获得最佳的运动估计模式。得到子宏块的分割方式存储在 i_partion。随后根据i_partion进行像素细化。


        int b_skip = 0;
        int i_intra_cost, i_intra_type;

        h->mc.prefetch_ref( h->mb.pic.p_fref[0][0][h->mb.i_mb_x&3], h->mb.pic.i_stride[0], 0 );//空函数?


        /* Fast P_SKIP detection */
        analysis.b_try_pskip = 0;
        if( h->param.analyse.b_fast_pskip )
        {
            if( h->param.i_threads > 1 && h->mb.cache.pskip_mv[1] > h->mb.mv_max_spel[1] )
                // FIXME don't need to check this if the reference frame is done

                {}
            else if( h->param.analyse.i_subpel_refine >= 3 )
                analysis.b_try_pskip = 1;
            else if( h->mb.i_mb_type_left == P_SKIP ||
                     h->mb.i_mb_type_top == P_SKIP ||
                     h->mb.i_mb_type_topleft == P_SKIP ||
                     h->mb.i_mb_type_topright == P_SKIP )
                b_skip = x264_macroblock_probe_pskip( h );//判断是否可以进行P_skip编码

        }

        h->mc.prefetch_ref( h->mb.pic.p_fref[0][0][h->mb.i_mb_x&3], h->mb.pic.i_stride[0], 1 );

        if( b_skip )
        {
            h->mb.i_type = P_SKIP;
            h->mb.i_partition = D_16x16;
            assert( h->mb.cache.pskip_mv[1] <= h->mb.mv_max_spel[1] || h->param.i_threads == 1 );
        }
        else
        {
            const unsigned int flags = h->param.analyse.inter;
            int i_type;
            int i_partition;
            int i_thresh16x8;
            int i_satd_inter, i_satd_intra;

            x264_mb_analyse_load_costs( h, &analysis );// 载入当前MB和临近MB块的信息



            x264_mb_analyse_inter_p16x16( h, &analysis );//P16x16 宏块预测模式的分析;下面是这个函数的解析

         static void x264_mb_analyse_inter_p16x16( x264_t *h, x264_mb_analysis_t *)
         {
             x264_me_t m;
              int i_ref, i_mvc;
              DECLARE_ALIGNED_4( int16_t mvc[8][2] ); 
              int i_halfpel_thresh = INT_MAX;
              int *p_halfpel_thresh = h->mb.pic.i_fref[0]>? &i_halfpel_thresh : NULL;
                                            
                                             /* 16x16 Search on all ref frame */
               m.i_pixel = PIXEL_16x16;
               m.p_cost_mv = a->p_cost_mv; //初始化MV的cost

              LOAD_FENC( &m, h->mb.pic.p_fenc, 0, 0 );//加载将要编码的宏块

                                            
               a->l0.me16x16.cost = INT_MAX;//初始化ME的cost

               for( i_ref = 0; i_ref < h->mb.pic.i_fref[0]; i_ref++ )//遍历所有的参考帧寻找最佳的参考块

               {
                   const int i_ref_cost = REF_COST( 0, i_ref );//参考帧的cost

                    i_halfpel_thresh -= i_ref_cost;
                    m.i_ref_cost = i_ref_cost;
                    m.i_ref = i_ref;
                                            
                   /* search with ref */
                  LOAD_HPELS( &m, h->mb.pic.p_fref[0][i_ref], 0, i_ref, 0, 0 );//加载参考帧

                  x264_mb_predict_mv_16x16( h, 0, i_ref, m.mvp );//确定搜索的初始位置(中值法)

                  x264_mb_predict_mv_ref16x16( h, 0, i_ref, mvc, &i_mvc );//寻找其它候选运动矢量。这些候选者包括:空间相邻的左、左上、上、右上块的MV;第0个参考帧中的当前块、右边块、下边快运动矢量乘以时间差权重。(时空域和空间域)

                  x264_me_search_ref( h, &m, mvc, i_mvc, p_halfpel_thresh );//搜索该参考帧中最佳的匹配块(亚像素搜索在里面)

                                            
                  /* early termination
                                             * SSD threshold would probably be better than SATD */

                  if( i_ref == 0&& a->b_try_pskip && m.cost-m.cost_mv < 300*a->i_lambda
                   && abs(m.mv[0]-h->mb.cache.pskip_mv[0])+ abs(m.mv[1]-h->mb.cache.pskip_mv[1]) <= 1
                                             && x264_macroblock_probe_pskip( h ) )
                   {
                        h->mb.i_type = P_SKIP;
                        x264_analyse_update_cache( h, a );
                        assert( h->mb.cache.pskip_mv[1] <= h->mb.mv_max_spel[1] || h->param.i_threads == 1 );
                        return;
                   }
                                            
                   m.cost += i_ref_cost;
                   i_halfpel_thresh += i_ref_cost;
                                            
                    if( m.cost < a->l0.me16x16.cost )
                    h->mc.memcpy_aligned( &a->l0.me16x16, &m, sizeof(x264_me_t) );
                                            
            /* save mv for predicting neighbors */
            *(uint32_t*)a->l0.mvc[i_ref][0] = *(uint32_t*)h->mb.mvr[0][i_ref][h->mb.i_mb_xy] = *(uint32_t*)m.mv;
            }
                                            
        x264_macroblock_cache_ref( h, 0, 0, 4, 4, 0, a->l0.me16x16.i_ref );//这里存储的P_L0的ME

         assert( a->l0.me16x16.mv[1] <= h->mb.mv_max_spel[1] || h->param.i_threads == 1 );
                                            
          h->mb.i_type = P_L0;
          if( a->i_mbrd )
         {
        x264_mb_cache_fenc_satd( h );//

          if( a->l0.me16x16.i_ref == 0 && *(uint32_t*)a->l0.me16x16.mv == *(uint32_t*)h->mb.cache.pskip_mv )
         {
            h->mb.i_partition = D_16x16;
          x264_macroblock_cache_mv_ptr( h, 0, 0, 4, 4, 0, a->l0.me16x16.mv );//这里存储的P_L0的MV

             a->l0.i_rd16x16 = x264_rd_cost_mb( h, a->i_lambda2 );//保留cost

            }
              }
              } 

            if( h->mb.i_type == P_SKIP )
                return;

            if( flags & X264_ANALYSE_PSUB16x16 )//P8x8预测模式的选择

            {
                if( h->param.analyse.b_mixed_references )
                    x264_mb_analyse_inter_p8x8_mixed_ref( h, &analysis );
                else
                    x264_mb_analyse_inter_p8x8( h, &analysis ); 
            }

            /* Select best inter mode */
            i_type = P_L0;
            i_partition = D_16x16;
            i_cost = analysis.l0.me16x16.cost;

            if( ( flags & X264_ANALYSE_PSUB16x16 ) &&
                analysis.l0.i_cost8x8 < analysis.l0.me16x16.cost )
            {
                i_type = P_8x8;
                i_partition = D_8x8;
                i_cost = analysis.l0.i_cost8x8;

                /* Do sub 8x8 */
                if( flags & X264_ANALYSE_PSUB8x8 )//4x4,8x4,4x8,帧间预测模式的选择

                {
                    for( i = 0; i < 4; i++ )
                    {
                        x264_mb_analyse_inter_p4x4( h, &analysis, i );
                        if( analysis.l0.i_cost4x4[i] < analysis.l0.me8x8[i].cost )
                        {
                            int i_cost8x8 = analysis.l0.i_cost4x4[i];
                            h->mb.i_sub_partition[i] = D_L0_4x4;

                            x264_mb_analyse_inter_p8x4( h, &analysis, i );
                            COPY2_IF_LT( i_cost8x8, analysis.l0.i_cost8x4[i],
                                         h->mb.i_sub_partition[i], D_L0_8x4 );//比较8*4和 i_cost8*8哪个代价低

                            x264_mb_analyse_inter_p4x8( h, &analysis, i );
                            COPY2_IF_LT( i_cost8x8, analysis.l0.i_cost4x8[i],
                                         h->mb.i_sub_partition[i], D_L0_4x8 );//比较4*8和 i_cost8*8哪个代价低

                            i_cost += i_cost8x8 - analysis.l0.me8x8[i].cost;
                        }
                        x264_mb_cache_mv_p8x8( h, &analysis, i );//更新8*8的MV信息
                    }
                    analysis.l0.i_cost8x8 = i_cost;
                }
            }

            /* Now do 16x8/8x16 */
            i_thresh16x8 = analysis.l0.me8x8[1].cost_mv + analysis.l0.me8x8[2].cost_mv;
            if( ( flags & X264_ANALYSE_PSUB16x16 ) &&
                analysis.l0.i_cost8x8 < analysis.l0.me16x16.cost + i_thresh16x8 )
            {
                x264_mb_analyse_inter_p16x8( h, &analysis );
                COPY3_IF_LT( i_cost, analysis.l0.i_cost16x8, i_type, P_L0, i_partition, D_16x8 );//比较16*8和16*16哪个代价低

                x264_mb_analyse_inter_p8x16( h, &analysis );
                COPY3_IF_LT( i_cost, analysis.l0.i_cost8x16, i_type, P_L0, i_partition, D_8x16 );//比较8*16和16*16哪个代价低
            }

            h->mb.i_partition = i_partition;

            /* refine qpel */
            //FIXME mb_type costs?

            if( analysis.i_mbrd )//重新划分模式

            {
                /* refine later *///intra省略
            }
            else if( i_partition == D_16x16 )
            {
                x264_me_refine_qpel( h, &analysis.l0.me16x16 );
                i_cost = analysis.l0.me16x16.cost;
            }
            else if( i_partition == D_16x8 )
            {
                x264_me_refine_qpel( h, &analysis.l0.me16x8[0] );
                x264_me_refine_qpel( h, &analysis.l0.me16x8[1] );//半像素和1/4像素搜索
                i_cost = analysis.l0.me16x8[0].cost + analysis.l0.me16x8[1].cost;//保存代价函数值
            }
            else if( i_partition == D_8x16 )
            {
                x264_me_refine_qpel( h, &analysis.l0.me8x16[0] );
                x264_me_refine_qpel( h, &analysis.l0.me8x16[1] );
                i_cost = analysis.l0.me8x16[0].cost + analysis.l0.me8x16[1].cost;
            }
            else if( i_partition == D_8x8 )
            {
                int i8x8;
                i_cost = 0;
                for( i8x8 = 0; i8x8 < 4; i8x8++ )
                {
                    switch( h->mb.i_sub_partition[i8x8] )
                    {
                        case D_L0_8x8:
                            x264_me_refine_qpel( h, &analysis.l0.me8x8[i8x8] );
                            i_cost += analysis.l0.me8x8[i8x8].cost;
                            break;
                        case D_L0_8x4:
                            x264_me_refine_qpel( h, &analysis.l0.me8x4[i8x8][0] );
                            x264_me_refine_qpel( h, &analysis.l0.me8x4[i8x8][1] );
                            i_cost += analysis.l0.me8x4[i8x8][0].cost +
                                      analysis.l0.me8x4[i8x8][1].cost;
                            break;
                        case D_L0_4x8:
                            x264_me_refine_qpel( h, &analysis.l0.me4x8[i8x8][0] );
                            x264_me_refine_qpel( h, &analysis.l0.me4x8[i8x8][1] );
                            i_cost += analysis.l0.me4x8[i8x8][0].cost +
                                      analysis.l0.me4x8[i8x8][1].cost;
                            break;

                        case D_L0_4x4:
                            x264_me_refine_qpel( h, &analysis.l0.me4x4[i8x8][0] );
                            x264_me_refine_qpel( h, &analysis.l0.me4x4[i8x8][1] );
                            x264_me_refine_qpel( h, &analysis.l0.me4x4[i8x8][2] );
                            x264_me_refine_qpel( h, &analysis.l0.me4x4[i8x8][3] );
                            i_cost += analysis.l0.me4x4[i8x8][0].cost +
                                      analysis.l0.me4x4[i8x8][1].cost +
                                      analysis.l0.me4x4[i8x8][2].cost +
                                      analysis.l0.me4x4[i8x8][3].cost;
                            break;
                        default:
                            x264_log( h, X264_LOG_ERROR, "internal error (!8x8 && !4x4)\n" );
                            break;
                    }
                }
            }

            if( h->mb.b_chroma_me )//对色度进行解析

            {
                x264_mb_analyse_intra_chroma( h, &analysis );
                x264_mb_analyse_intra( h, &analysis, i_cost - analysis.i_satd_i8x8chroma );
                analysis.i_satd_i16x16 += analysis.i_satd_i8x8chroma;
                analysis.i_satd_i8x8 += analysis.i_satd_i8x8chroma;
                analysis.i_satd_i4x4 += analysis.i_satd_i8x8chroma;
            }
            else
                x264_mb_analyse_intra( h, &analysis, i_cost ); //intra分析


            i_satd_inter = i_cost;
            i_satd_intra = X264_MIN3( analysis.i_satd_i16x16,
                                      analysis.i_satd_i8x8,
                                      analysis.i_satd_i4x4 );//比较I16*16,I8*8,i4*4哪个模式代价低,选择最小的
                      //重新划分模式
            if( analysis.i_mbrd )
            {
                x264_mb_analyse_p_rd( h, &analysis, X264_MIN(i_satd_inter, i_satd_intra) );
                i_type = P_L0;
                i_partition = D_16x16;
                i_cost = analysis.l0.me16x16.cost;
                COPY2_IF_LT( i_cost, analysis.l0.i_cost16x8, i_partition, D_16x8 );
                COPY2_IF_LT( i_cost, analysis.l0.i_cost8x16, i_partition, D_8x16 );
                COPY3_IF_LT( i_cost, analysis.l0.i_cost8x8, i_partition, D_8x8, i_type, P_8x8 );
                h->mb.i_type = i_type;
                h->mb.i_partition = i_partition;
                if( i_cost < COST_MAX )
                    x264_mb_analyse_transform_rd( h, &analysis, &i_satd_inter, &i_cost );
                x264_intra_rd( h, &analysis, i_satd_inter * 5/);
            }

            i_intra_type = I_16x16;
            i_intra_cost = analysis.i_satd_i16x16;
            COPY2_IF_LT( i_intra_cost, analysis.i_satd_i8x8, i_intra_type, I_8x8 );
            COPY2_IF_LT( i_intra_cost, analysis.i_satd_i4x4, i_intra_type, I_4x4 );
            COPY2_IF_LT( i_intra_cost, analysis.i_satd_pcm, i_intra_type, I_PCM );
            COPY2_IF_LT( i_cost, i_intra_cost, i_type, i_intra_type );//比较帧内帧间的cost


            if( i_intra_cost == COST_MAX )
                i_intra_cost = i_cost * i_satd_intra / i_satd_inter + 1;

            h->mb.i_type = i_type;
            h->stat.frame.i_intra_cost += i_intra_cost;
            h->stat.frame.i_inter_cost += i_cost;

            h->stat.frame.i_mbs_analysed++;


          //下面这段在实际中可以省略,因为编码时用不到

            if( analysis.i_mbrd >= 2 && h->mb.i_type != I_PCM )

            {
                if( IS_INTRA( h->mb.i_type ) )
                {
                    x264_intra_rd_refine( h, &analysis );
                }
                else if( i_partition == D_16x16 )
                {
                    x264_macroblock_cache_ref( h, 0, 0, 4, 4, 0, analysis.l0.me16x16.i_ref );
                    x264_me_refine_qpel_rd( h, &analysis.l0.me16x16, analysis.i_lambda2, 0, 0 ); //率失真函数

                }
                else if( i_partition == D_16x8 )
                {
                    h->mb.i_sub_partition[0] = h->mb.i_sub_partition[1] =
                    h->mb.i_sub_partition[2] = h->mb.i_sub_partition[3] = D_L0_8x8;
                    x264_macroblock_cache_ref( h, 0, 0, 4, 2, 0, analysis.l0.me16x8[0].i_ref );
                    x264_macroblock_cache_ref( h, 0, 2, 4, 2, 0, analysis.l0.me16x8[1].i_ref );
                    x264_me_refine_qpel_rd( h, &analysis.l0.me16x8[0], analysis.i_lambda2, 0, 0 );
                    x264_me_refine_qpel_rd( h, &analysis.l0.me16x8[1], analysis.i_lambda2, 8, 0 );
                }
                else if( i_partition == D_8x16 )
                {
                    h->mb.i_sub_partition[0] = h->mb.i_sub_partition[1] =
                    h->mb.i_sub_partition[2] = h->mb.i_sub_partition[3] = D_L0_8x8;
                    x264_macroblock_cache_ref( h, 0, 0, 2, 4, 0, analysis.l0.me8x16[0].i_ref );
                    x264_macroblock_cache_ref( h, 2, 0, 2, 4, 0, analysis.l0.me8x16[1].i_ref );
                    x264_me_refine_qpel_rd( h, &analysis.l0.me8x16[0], analysis.i_lambda2, 0, 0 );
                    x264_me_refine_qpel_rd( h, &analysis.l0.me8x16[1], analysis.i_lambda2, 4, 0 );
                }
                else if( i_partition == D_8x8 )
                {
                    int i8x8;
                    x264_analyse_update_cache( h, &analysis );
                    for( i8x8 = 0; i8x8 < 4; i8x8++ )
                    {
                        if( h->mb.i_sub_partition[i8x8] == D_L0_8x8 )
                        {
                            x264_me_refine_qpel_rd( h, &analysis.l0.me8x8[i8x8], analysis.i_lambda2, i8x8*4, 0 );
                        }
                        else if( h->mb.i_sub_partition[i8x8] == D_L0_8x4 )
                        {
                           x264_me_refine_qpel_rd( h, &analysis.l0.me8x4[i8x8[0], analysis.i_lambda2, i8x8*4+0, 0 ;
                           x264_me_refine_qpel_rd( h, &analysis.l0.me8x4[i8x8[1], analysis.i_lambda2, i8x8*4+2, 0 ;
                        }
                        else if( h->mb.i_sub_partition[i8x8] == D_L0_4x8 )
                        {
                           x264_me_refine_qpel_rd( h, &analysis.l0.me4x8[i8x[0], analysis.i_lambda2, i8x8*4+0, 0 );
                           x264_me_refine_qpel_rd( h, &analysis.l0.me4x8[i8x[1], analysis.i_lambda2, i8x8*4+1, 0 );
                        }
                        else if( h->mb.i_sub_partition[i8x8] == D_L0_4x4 )
                        {
                           x264_me_refine_qpel_rd( h, &analysis.l0.me4x4[i8x[0], analysis.i_lambda2, i8x8*4+0, 0 );
                           x264_me_refine_qpel_rd( h, &analysis.l0.me4x4[i8x[1], analysis.i_lambda2, i8x8*4+1, 0 );
                           x264_me_refine_qpel_rd( h, &analysis.l0.me4x4[i8x[2], analysis.i_lambda2, i8x8*4+2, 0 );
                           x264_me_refine_qpel_rd( h, &analysis.l0.me4x4[i8x[3], analysis.i_lambda2, i8x8*4+3, 0 );
                        }
                    }
                }
            }
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值