JM86中encode_one_macroblock函数注释

转载至:http://blog.csdn.net/hu1fang2zhen3/article/details/6301648


http://blog.csdn.net/maopig/article/details/6532589


encode_one_macroblock函数中的注释有一小部分是别人写的,大部分是我在看代码的过程中根据自己的理解所写的,如有什么不对的地方,还望大家指教~~~~

  1. /*! 
  2.  ************************************************************************************* 
  3.  * /brief 
  4.  *    Mode Decision for a macroblock 
  5.  *    //+++该函数的作用是编码一个宏块(包括帧间、帧内、帧内预测的方式)。 
  6.  *    NOTE:从上面程序段中可以看出JM8.5中对7种宏块模式是采用全部遍历的方式,所以导致的计算复杂度很高。 
  7.  ************************************************************************************* 
  8.  */  
  9.  void encode_one_macroblock ()  
  10.  {  
  11.    static const int  b8_mode_table[6]  = {0, 4, 5, 6, 7};         // DO NOT CHANGE ORDER !!!     
  12.    // //++ B片子宏块类型。0:Direct_8*8,4:8*8,5:8*4,6:4*8,7:4*4  
  13.    static const int  mb_mode_table[7]  = {0, 1, 2, 3, P8x8, I16MB, I4MB}; // DO NOT CHANGE ORDER !!!  
  14.     //+++//    0:16X16 Direct模式,在B帧中有效                                                                                                                                  
  15.         //        1:Inter16X16,在帧间有效                                 
  16.         //        2:Inter16X8,在帧间有效                                     
  17.         //        3:Inter8X16,在帧间有效                                             
  18.         // P8X8:帧间有效:包括Inter8x8,Inter8x4,Inter4x8,Inter4x4                                                 
  19.         // I16MB:Intra16X16帧内有效                                                         
  20.         // I4MB:Intra有效                                                     
  21.         // I8MB:Intra有效                                                         
  22.         // IPCM:Intra有效,不要预测,直接对RAW数据编码.                                                                 
  23.     
  24.    int         valid[MAXMODE];  
  25.    int         rerun, block, index, mode, i0, i1, j0, j1, pdir, ref, i, j, k, ctr16x16, dummy;  
  26.    double      qp, lambda_mode, lambda_motion, min_rdcost, rdcost = 0, max_rdcost=1e30;  
  27.    int         lambda_motion_factor;  
  28.    int         fw_mcost, bw_mcost, bid_mcost, mcost, max_mcost=(1<<30);  
  29.    int         curr_cbp_blk/*+++当前宏块每个 4*4 块是否有非零系数,跟CBP功能类似,CBP 是表示的每个8*8 块*/,  
  30.                cnt_nonz = 0, best_cnt_nonz = 0, best_fw_ref = 0, best_pdir;  
  31.    int         cost=0;  
  32.    int         min_cost = max_mcost, min_cost8x8, cost8x8, cost_direct=0, have_direct=0, i16mode;  
  33.    //+++根据对下面代码的阅读,这儿有比较要说明一下上面声明的一些变量,这对于理解代码很重要.  
  34.    //+++首先,要提到best_mode,我本来以为这个量是在本函数中声明的一个变量,然后仔细在上面寻找,都没有找到,后来才发现,  
  35.    //+++wow,原来best_mode在这个文件(rdopt.c)的开头声明过,是本文件的一个全局变量,yes,终于明白这个了  
  36.    //+++其次,对于上面的一些变量,介绍一下:  
  37.    //+++min_rdcost是用来保存RDO模式下的最小代价;min_cost则是非RDO模式下的代价  
  38.    //+++min_cost8x8是亚宏块级(P8x8)非RDO模式下最小模式代价;cost8x8是宏块(16x16)中4个8x8块的代价和(每一个8x8都是最佳代价min_cost8x8)  
  39.    //+++fw_mcost,bw_mcost和bid_mcost是中间变量用来记录(前向,后向和双向)最佳参考帧对应的代价  
  40.    //+++cost是一个中间变量,主要是在非RDO模式下,(帧间预测)记录宏块级或亚宏块级中某一个模式中所有块(16x16是1块,16x8是2块...)的所有代价之和(参考帧代价,运动估计)  
  41.    //+++mcost也是一个中间变量,也是非RDO模式下,在进行最优参考帧时,用于累加代价之和(参考帧代价,运动估计)  
  42.     
  43.    int         intra1 = 0;  
  44.     
  45.    int         intra       = (((img->type==P_SLICE||img->type==SP_SLICE) && img->mb_y==img->mb_y_upd && img->mb_y_upd!=img->mb_y_intra) || img->type==I_SLICE);  
  46.    int         spframe     = (img->type==SP_SLICE);  
  47.    int         siframe     = (img->type==SI_SLICE);  
  48.    int         bframe      = (img->type==B_SLICE);  
  49.    int         runs        = (input->RestrictRef==1 && input->rdopt==2 && (img->type==P_SLICE || img->type==SP_SLICE || (img->type==B_SLICE && img->nal_reference_idc>0)) ? 2 : 1);  
  50.     
  51.    int         checkref    = (input->rdopt && input->RestrictRef && (img->type==P_SLICE || img->type==SP_SLICE));  
  52.    Macroblock* currMB      = &img->mb_data[img->current_mb_nr];  
  53.    Macroblock* prevMB      = img->current_mb_nr ? &img->mb_data[img->current_mb_nr-1]:NULL ;  
  54.     
  55.    int     **ipredmodes = img->ipredmode;//+++ipredmode帧内预测模式  
  56.    int     best_bw_ref = -1;  
  57.    int     ******allmvs = img->all_mv;  
  58.     
  59.    int  l,list_offset;  
  60.    int curr_mb_field = ((img->MbaffFrameFlag)&&(currMB->mb_field));  
  61.    // find out the correct list offsets  
  62.    if (curr_mb_field)  
  63.    {  
  64.      if(img->current_mb_nr%2)  
  65.        list_offset = 4; // bottom field mb  
  66.      else  
  67.        list_offset = 2; // top field mb  
  68.    }  
  69.    else  
  70.    {  
  71.      list_offset = 0;  // no mb aff or frame mb  
  72.    }  
  73.    if(input->FMEnable)  
  74.      decide_intrabk_SAD();  
  75.     
  76.    intra |= RandomIntra (img->current_mb_nr);    // Forced Pseudo-Random Intra  
  77.    //===== SET VALID MODES =====  
  78.    valid[I4MB]   = 1;    //++ 宏块可以采用intra4*4模式编码  
  79.    valid[I16MB]  = 1;    //++ 宏块可以采用intra16*16模式编码  
  80.     
  81.    valid[0]      = (!intra );    //++ 宏块是否采用inter模式编码  
  82.    valid[1]      = (!intra && input->InterSearch16x16);    //++ 宏块是否采用inter16*16模式编码  
  83.    valid[2]      = (!intra && input->InterSearch16x8);    //++ 宏块是否采用inter16*8模式编码  
  84.    valid[3]      = (!intra && input->InterSearch8x16);    //++ 宏块是否采用inter8*16模式编码  
  85.    valid[4]      = (!intra && input->InterSearch8x8);    //++ 宏块是否采用inter8*8模式编码  
  86.    valid[5]      = (!intra && input->InterSearch8x4);    //++ 宏块是否采用inter8*4模式编码  
  87.    valid[6]      = (!intra && input->InterSearch4x8);    //++ 宏块是否采用inter4*8模式编码  
  88.    valid[7]      = (!intra && input->InterSearch4x4);    //++ 宏块是否采用inter4*4模式编码  
  89.    valid[P8x8]   = (valid[4] || valid[5] || valid[6] || valid[7]);    //++ 宏块是否采用亚宏块模式编码   +++P8x8=8  
  90.    valid[12]     = (siframe);    //++ 宏块是否采用SI模式编码  
  91.    if (!img->MbaffFrameFlag)//+++//在非宏块级帧场自适应且是场模式下 进行色度矢量校正  
  92.    {  
  93.      for (l=0+list_offset;l<(2+list_offset);l++)  
  94.      {  
  95.        for(k = 0; k < listXsize[l]; k++)  
  96.        {  
  97.          listX[l][k]->chroma_vector_adjustment= 0;  
  98.          if(img->structure == TOP_FIELD && img->structure != listX[l][k]->structure)  
  99.            listX[l][k]->chroma_vector_adjustment = -2;  
  100.          if(img->structure == BOTTOM_FIELD && img->structure != listX[l][k]->structure)  
  101.            listX[l][k]->chroma_vector_adjustment = 2;  
  102.        }  
  103.      }  
  104.    }  
  105.    else  
  106.    {  
  107.      if (curr_mb_field)  
  108.      {  
  109.        for (l=0+list_offset;l<(2+list_offset);l++)  
  110.        {  
  111.          for(k = 0; k < listXsize[l]; k++)  
  112.          {  
  113.            listX[l][k]->chroma_vector_adjustment= 0;  
  114.            if(img->current_mb_nr % 2 == 0 && listX[l][k]->structure == BOTTOM_FIELD)  
  115.              listX[l][k]->chroma_vector_adjustment = -2;  
  116.            if(img->current_mb_nr % 2 == 1 && listX[l][k]->structure == TOP_FIELD)  
  117.              listX[l][k]->chroma_vector_adjustment = 2;  
  118.          }  
  119.        }  
  120.      }  
  121.      else  
  122.      {  
  123.        for (l=0+list_offset;l<(2+list_offset);l++)  
  124.        {  
  125.          for(k = 0; k < listXsize[l]; k++)  
  126.          {  
  127.            listX[l][k]->chroma_vector_adjustment= 0;  
  128.          }  
  129.        }  
  130.      }  
  131.       
  132.    }  
  133. //+++ 设置RDO与非RDO下的lambda系数  
  134.    //===== SET LAGRANGE PARAMETERS =====  
  135.    if (input->rdopt)  
  136.    {  
  137.      qp = (double)img->qp - SHIFT_QP;    //++ ???  
  138.      if (input->successive_Bframe>0)  
  139.        lambda_mode   = 0.68 * pow (2, qp/3.0) * (img->type==B_SLICE? max(2.00,min(4.00,(qp / 6.0))):spframe?max(1.4,min(3.0,(qp / 12.0))):1.0);   
  140.      else  
  141.        lambda_mode   = 0.85 * pow (2, qp/3.0) * (img->type==B_SLICE? 4.0:spframe?max(1.4,min(3.0,(qp / 12.0))):1.0);   
  142.      lambda_motion = sqrt (lambda_mode);  
  143.    }  
  144.    else  
  145.    {  
  146.      lambda_mode = lambda_motion = QP2QUANT[max(0,img->qp-SHIFT_QP)];  
  147.    }  
  148.    lambda_motion_factor = LAMBDA_FACTOR (lambda_motion);    //++ 使用该变量的原因主要是为了在运算过程中使用定点运算但又能提高精度  
  149.     
  150.     
  151.    for (rerun=0; rerun<runs; rerun++)    //++ runs=2是针对loss rdo模式,其余的情况runs=1  
  152.    {  
  153.      if (runs==2)  
  154.      {  
  155.        if (rerun==0)   input->rdopt=1;  
  156.        else            input->rdopt=2;  
  157.      }  
  158.       
  159.      // reset chroma intra predictor to default  
  160.      currMB->c_ipred_mode = DC_PRED_8;  
  161. //+++++++++++++++++++++++++++  
  162. //+++帧间预测,计算其选择合适模式后的开销  
  163.      //+++(根据拉格朗日率失真准则)  
  164.      if (!intra)  
  165.      {  
  166.          //+++B帧的直接模式预测  
  167.        //===== set direct motion vectors =====  
  168.        if (bframe)//+++P帧无direct模式 但有skip模式  
  169.        {  
  170.          Get_Direct_Motion_Vectors ();//+++不返回direct模式代价  
  171.        }  
  172.        //+++//宏块级运动估计  
  173.        //===== MOTION ESTIMATION FOR 16x16, 16x8, 8x16 BLOCKS =====  
  174.        for (min_cost=1<<20, best_mode=1, mode=1; mode<4; mode++)  
  175.        {  
  176.          if (valid[mode])//+++//对应于程序外部(即CFG文件中)的设置  
  177.          {  
  178.          //+++//对于16x16,MB只分一个块;对于16x8和8x16,MB被分成两个块  
  179.            for (cost=0, block=0; block<(mode==1?1:2); block++)    //++ 16*16 分割方式只需要计算一次,16*8 和 8*16 分割方式需要计算两次  
  180.            {  
  181.            //+++/块匹配!!!lambda_motion用来求运动矢量消耗的码率  
  182.              PartitionMotionSearch (mode, block, lambda_motion);    //++ 对 16*16、16*8、8*16 分割方式进行 ME  
  183.              //--- set 4x4 block indizes (for getting MV) ---  
  184.              j = (block==1 && mode==2 ? 2 : 0);    //++ 如果现在处理的是 16*8 分割方式的第 2 个分割,则 j=2  
  185.              i = (block==1 && mode==3 ? 2 : 0);    //++ 如果现在处理的是 8*16 分割方式的第 2 个分割,则 i=2  
  186.              //+++通过循环在list0中寻找前向预测的最佳参考帧(best_fw_ref),代价为fw_mcost,  
  187.              //--- get cost and reference frame for forward prediction ---  
  188.              for (fw_mcost=max_mcost, ref=0; ref<listXsize[LIST_0+list_offset]; ref++)  
  189.              {  
  190.                if (!checkref || ref==0 || CheckReliabilityOfRef (block, LIST_0, ref, mode))  
  191.                {  
  192.                     //++ 参考帧索引号编码所需要使用的比特数作为编码代价的一部分  
  193.                    //+++这个地方就是利用拉格朗日进行计算开销的函数,事实上,这个地方要考虑很多东西的开销,可以发现,下面的代码中还要加上其他的  
  194.                    //+++mcost->(1)REF_COST参考帧代价  
  195.                  mcost  = (input->rdopt ? REF_COST (lambda_motion_factor, ref, LIST_0 + list_offset) : (int)(2*lambda_motion*min(ref,1)));  
  196.                  //+++motion_cost(运动搜索代价)是一个全局变量,其值在PartitionMotionSearch函数内进行过改变  
  197.                  //+++so 此时motion_cost就是保存的相应的运动搜索代价  
  198.                  //+++那么,mcost->(2)motion_cost运动搜索代价  
  199.                  mcost += motion_cost[mode][LIST_0][ref][block];//+++可以发现motion_cost数组的参数mode,block和  
  200.                                                                //+++PartitionMotionSearch(PMS)函数的参数是一致的,并且在PMS函数中  
  201.                                                               //+++计算motion_cost时也是对ref(参考帧)进行循环的,所以将两者联系起来  
  202.                                                              //+++我们就可以理解这儿motion_cost的数组中参数的含义了.  
  203.                                                             //+++另外在[JM86核心函数研究]中有对motion_cost的详细介绍  
  204.                                                            //+++该数组存储了一个宏块的分块在不同帧间模式和参考帧下的运动估计的开销值。  
  205.                                                           //+++函数PartitionMotionSearch的主要任务就是更新该数组。同时,该数组也在决定最佳参考帧时被引用。  
  206.                  if (mcost < fw_mcost)//+++根据参考代价选出该模式下的最佳参考帧  
  207.                  {  
  208.                      //+++记录最佳参考帧及预测代价  
  209.                    fw_mcost    = mcost;  
  210.                    best_fw_ref = ref;  
  211.                  }  
  212.                }  
  213.              }  
  214.              //+++B帧还需记录向后及双向预测的各个参考帧与参考代价  
  215.              if (bframe)  
  216.              {  
  217.                //--- get cost for bidirectional prediction ---  
  218.                for (bw_mcost=max_mcost, ref=0; ref<listXsize[LIST_1 + list_offset]; ref++)  
  219.                {//+++B帧的计算方法与I,P帧不一样  
  220.                  mcost  = (input->rdopt ? REF_COST (lambda_motion_factor, ref, LIST_1 + list_offset) : (int)(2*lambda_motion*min(ref,1)));  
  221.                  mcost += motion_cost[mode][LIST_1][ref][block];  
  222.                  if (mcost < bw_mcost)  
  223.                  {  
  224.                    bw_mcost    = mcost;  
  225.                    best_bw_ref = ref;  
  226.                  }  
  227.                }  
  228.                // search bidirectional between best forward and ref_idx=0 backward  
  229.                bid_mcost  = (input->rdopt ? (REF_COST (lambda_motion_factor, best_fw_ref,LIST_0+list_offset)+REF_COST (lambda_motion_factor, 0,LIST_1+list_offset)) : (int)(2*lambda_motion*min(best_fw_ref,1)));  
  230.                bid_mcost += BIDPartitionCost (mode, block, best_fw_ref, 0, lambda_motion_factor);  
  231.                //--- get prediction direction ----  
  232.                if (fw_mcost<=bw_mcost && fw_mcost<=bid_mcost)  
  233.                {  
  234.                  best_pdir = 0;  
  235.                  best_bw_ref = 0;  
  236.                  cost += fw_mcost;  
  237.                }  
  238.                else if (bw_mcost<=fw_mcost && bw_mcost<=bid_mcost)  
  239.                {  
  240.                  best_pdir = 1;  
  241.                  cost += bw_mcost;  
  242.                  best_fw_ref = 0;  
  243.                }  
  244.                else  
  245.                {  
  246.                  best_pdir = 2;  
  247.                  cost += bid_mcost;  
  248.                  best_bw_ref = 0;  
  249.                }  
  250.              }  
  251.              else // if (bframe)  
  252.              {  
  253.                best_pdir  = 0;//+++best_pdir是最佳预测方向,对于非B帧(P帧)预测方向就一个直接设置为0,而对于  
  254.                               //+++B帧要从三种方式(前向,后向,双向)中选择一个,从上面if中的代码中我们就可以看出来  
  255.                cost      += fw_mcost;  
  256.              }  
  257.              //+++下面要记录参考帧和MV信息,说明一下:这些全局变量是作为BlockMotionSearch()中运动搜索时运动向量预测的依据  
  258.              //+++问:下一宏块的运动向量等信息也用当前宏块的运动向量预测吗?答:ME 算法是开放部分,你想怎么做就怎么做。JM 在 ME 时会用到相邻块的 MV;  
  259.              //+++对于MB是以4*4的块为单位来记录mv和ref的  
  260.              if (mode==1)//+++16x16  
  261.              {  
  262.                if (best_pdir==1)//+++后向参考  
  263.                {  
  264.                  for (j=0; j<4; j++)  
  265.                  {  
  266.                    for (i=0; i<4; i++)  
  267.                    {  
  268.                        //+++保存参考信息至重建单元,B帧多做后项参考  
  269.                    //+++enc_picture 中好像是最终数据,img 中存的是中间数据  
  270.                      enc_picture->ref_idx[LIST_0][img->block_x+(block&1)*2+i][img->block_y+(block&2)+j] = -1;  
  271.                      enc_picture->ref_pic_id [LIST_0][img->block_x+(block&1)*2+i][img->block_y+(block&2)+j] = -1;  
  272.                      enc_picture->mv[LIST_0][img->block_x+(block&1)*2+i][img->block_y+(block&2)+j][0] = 0;  
  273.                      enc_picture->mv[LIST_0][img->block_x+(block&1)*2+i][img->block_y+(block&2)+j][1] = 0;  
  274.                    }  
  275.                  }  
  276.                }  
  277.                else  
  278.                {  
  279.                  for (j=0; j<4; j++)  
  280.                  {  
  281.                    for (i=0; i<4; i++)  
  282.                    {  
  283.                      enc_picture->ref_idx[LIST_0][img->block_x+(block&1)*2+i][img->block_y+(block&2)+j] = best_fw_ref;  
  284.                      enc_picture->ref_pic_id [LIST_0][img->block_x+(block&1)*2+i][img->block_y+(block&2)+j] = enc_picture->ref_pic_num[LIST_0 + list_offset][enc_picture->ref_idx[LIST_0][img->block_x+(block&1)*2+i][img->block_y+(block&2)+j]];   
  285.                      enc_picture->mv[LIST_0][img->block_x+(block&1)*2+i][img->block_y+(block&2)+j][0] = img->all_mv[i][j][LIST_0][best_fw_ref][mode][0];  
  286.                      enc_picture->mv[LIST_0][img->block_x+(block&1)*2+i][img->block_y+(block&2)+j][1] = img->all_mv[i][j][LIST_0][best_fw_ref][mode][1];  
  287.                    }  
  288.                  }  
  289.                }  
  290.                if (bframe)  
  291.                {  
  292.                  if (best_pdir==0)  
  293.                  {  
  294.                    for (j=0; j<4; j++)  
  295.                    {  
  296.                      for (i=0; i<4; i++)  
  297.                      {  
  298.                        enc_picture->ref_idx[LIST_1][img->block_x+(block&1)*2+i][img->block_y+(block&2)+j] = -1;  
  299.                        enc_picture->ref_pic_id [LIST_1][img->block_x+(block&1)*2+i][img->block_y+(block&2)+j] = -1;    
  300.                        enc_picture->mv[LIST_1][img->block_x+(block&1)*2+i][img->block_y+(block&2)+j][0] = 0;  
  301.                        enc_picture->mv[LIST_1][img->block_x+(block&1)*2+i][img->block_y+(block&2)+j][1] = 0;  
  302.                      }  
  303.                    }  
  304.                  }  
  305.                  else  
  306.                  {  
  307.                    for (j=0; j<4; j++)  
  308.                    {  
  309.                      for (i=0; i<4; i++)  
  310.                      {  
  311.                        enc_picture->ref_idx[LIST_1][img->block_x+(block&1)*2+i][img->block_y+(block&2)+j] = best_bw_ref;  
  312.                        enc_picture->ref_pic_id [LIST_1][img->block_x+(block&1)*2+i][img->block_y+(block&2)+j] = enc_picture->ref_pic_num[LIST_1 + list_offset][enc_picture->ref_idx[LIST_1][img->block_x+(block&1)*2+i][img->block_y+(block&2)+j]];  
  313.                        if(best_bw_ref>=0)  
  314.                        {  
  315.                          enc_picture->mv[LIST_1][img->block_x+(block&1)*2+i][img->block_y+(block&2)+j][0] = img->all_mv[i][j][LIST_1][best_bw_ref][mode][0];  
  316.                          enc_picture->mv[LIST_1][img->block_x+(block&1)*2+i][img->block_y+(block&2)+j][1] = img->all_mv[i][j][LIST_1][best_bw_ref][mode][1];  
  317.                        }  
  318.                      }  
  319.                    }  
  320.                  }  
  321.                }  
  322.              }  
  323.              else if (mode==2)//+++16x8  
  324.              {  
  325.                for (j=0; j<2; j++)  
  326.                {  
  327.                  for (i=0; i<4; i++)  
  328.                  {  
  329.                    if (best_pdir==1)  
  330.                    {  
  331.                      enc_picture->ref_idx[LIST_0][img->block_x+i][img->block_y+block*2+j] = -1;  
  332.                      enc_picture->ref_pic_id [LIST_0][img->block_x+i][img->block_y+block*2+j] = -1;  
  333.                      enc_picture->mv[LIST_0][img->block_x+i][img->block_y+block*2+j][0] = 0;  
  334.                      enc_picture->mv[LIST_0][img->block_x+i][img->block_y+block*2+j][1] = 0;  
  335.                    }  
  336.                    else  
  337.                    {  
  338.                      enc_picture->ref_idx[LIST_0][img->block_x+i][img->block_y+block*2+j] = best_fw_ref;  
  339.                      enc_picture->ref_pic_id [LIST_0][img->block_x+i][img->block_y+block*2+j] = enc_picture->ref_pic_num[LIST_0 + list_offset][enc_picture->ref_idx[LIST_0][img->block_x+i][img->block_y+block*2+j]];  
  340.                      enc_picture->mv[LIST_0][img->block_x+i][img->block_y+block*2+j][0] = img->all_mv[i][j+block*2][LIST_0][best_fw_ref][mode][0];  
  341.                      enc_picture->mv[LIST_0][img->block_x+i][img->block_y+block*2+j][1] = img->all_mv[i][j+block*2][LIST_0][best_fw_ref][mode][1];  
  342.                    }  
  343.                    if (bframe)  
  344.                    {  
  345.                      if (best_pdir==0)  
  346.                      {  
  347.                        enc_picture->ref_idx[LIST_1][img->block_x+i][img->block_y+block*2+j] = -1;  
  348.                        enc_picture->ref_pic_id [LIST_1][img->block_x+i][img->block_y+block*2+j] = -1;  
  349.                        enc_picture->mv[LIST_1][img->block_x+i][img->block_y+block*2+j][0] = 0;  
  350.                        enc_picture->mv[LIST_1][img->block_x+i][img->block_y+block*2+j][1] = 0;  
  351.                      }  
  352.                      else  
  353.                      {  
  354.                        enc_picture->ref_idx[LIST_1][img->block_x+i][img->block_y+block*2+j] = best_bw_ref;  
  355.                        enc_picture->ref_pic_id [LIST_1][img->block_x+i][img->block_y+block*2+j] = enc_picture->ref_pic_num[LIST_1 + list_offset][enc_picture->ref_idx[LIST_1][img->block_x+i][img->block_y+block*2+j]];  
  356.                        if(best_bw_ref>=0)  
  357.                        {  
  358.                          enc_picture->mv[LIST_1][img->block_x+i][img->block_y+block*2+j][0] = img->all_mv[i][j][LIST_1][best_bw_ref][mode][0];  
  359.                          enc_picture->mv[LIST_1][img->block_x+i][img->block_y+block*2+j][1] = img->all_mv[i][j][LIST_1][best_bw_ref][mode][1];  
  360.                        }  
  361.                      }  
  362.                    }  
  363.                  }  
  364.                }  
  365.              }  
  366.              else//+++8x16  
  367.              {  
  368.                for (j=0; j<4; j++)  
  369.                {  
  370.                  for (i=0; i<2; i++)  
  371.                  {  
  372.                    if (best_pdir==1)  
  373.                    {  
  374.                      enc_picture->ref_idx[LIST_0][img->block_x+block*2+i][img->block_y+j] = -1;  
  375.                      enc_picture->ref_pic_id [LIST_0][img->block_x+block*2+i][img->block_y+j] = -1;  
  376.                      enc_picture->mv[LIST_0][img->block_x+block*2+i][img->block_y+j][0] = 0;  
  377.                      enc_picture->mv[LIST_0][img->block_x+block*2+i][img->block_y+j][1] = 0;  
  378.                    }  
  379.                    else  
  380.                    {  
  381.                      enc_picture->ref_idx[LIST_0][img->block_x+block*2+i][img->block_y+j] = best_fw_ref;  
  382.                      enc_picture->ref_pic_id [LIST_0][img->block_x+block*2+i][img->block_y+j] = enc_picture->ref_pic_num[LIST_0 + list_offset][enc_picture->ref_idx[LIST_0][img->block_x+block*2+i][img->block_y+j]];  
  383.                      enc_picture->mv[LIST_0][img->block_x+block*2+i][img->block_y+j][0] = img->all_mv[i][j][LIST_0][best_fw_ref][mode][0];  
  384.                      enc_picture->mv[LIST_0][img->block_x+block*2+i][img->block_y+j][1] = img->all_mv[i][j][LIST_0][best_fw_ref][mode][1];  
  385.                    }  
  386.                    if (bframe)  
  387.                    {  
  388.                      if (best_pdir==0)  
  389.                      {  
  390.                        enc_picture->ref_idx[LIST_1][img->block_x+block*2+i][img->block_y+j] = -1;  
  391.                        enc_picture->ref_pic_id [LIST_1][img->block_x+block*2+i][img->block_y+j] = -1;  
  392.                        enc_picture->mv[LIST_1][img->block_x+block*2+i][img->block_y+j][0] = 0;  
  393.                        enc_picture->mv[LIST_1][img->block_x+block*2+i][img->block_y+j][1] = 0;  
  394.                      }  
  395.                      else  
  396.                      {  
  397.                        enc_picture->ref_idx[LIST_1][img->block_x+block*2+i][img->block_y+j] = best_bw_ref;  
  398.                        enc_picture->ref_pic_id [LIST_1][img->block_x+block*2+i][img->block_y+j] = enc_picture->ref_pic_num[LIST_1 + list_offset][enc_picture->ref_idx[LIST_1][img->block_x+block*2+i][img->block_y+j]];  
  399.                        if(best_bw_ref>=0)  
  400.                        {  
  401.                          enc_picture->mv[LIST_1][img->block_x+block*2+i][img->block_y+j][0] = img->all_mv[i][j][LIST_1][best_bw_ref][mode][0];  
  402.                          enc_picture->mv[LIST_1][img->block_x+block*2+i][img->block_y+j][1] = img->all_mv[i][j][LIST_1][best_bw_ref][mode][1];  
  403.                        }  
  404.                      }  
  405.                    }  
  406.                  }  
  407.                }  
  408.              }  
  409.              //+++以8x8块记录各个模式的预测信息 每个分割的8x8相同  
  410.              //+++此处解释一下best8x8fwref:: 该2维数组存储了一个宏块的4个8x8block在各个帧间预测模式下的最佳前向参考帧。  
  411.              //+++上面的代码完成了最佳参考帧的选择,所以这儿将最佳参考帧的信息存储在这个数组中。在计算预测值时,程序会引用该数组作为参考。  
  412.              //----- set reference frame and direction parameters -----  
  413.              if (mode==3)//+++两个16x8:如果以8x8为单位,分别拥有(0,2)和(1,3)  
  414.              {  
  415.                best8x8fwref   [3][block] = best8x8fwref   [3][block+2] = best_fw_ref;  
  416.                best8x8pdir    [3][block] = best8x8pdir    [3][block+2] = best_pdir;  
  417.                best8x8bwref   [3][block] = best8x8bwref   [3][block+2] = best_bw_ref;  
  418.              }  
  419.              else if (mode==2)//+++两个8x16:如果以8x8为单位,分别拥有(0,1)和(2,3)  
  420.              {  
  421.                best8x8fwref   [2][2*block] = best8x8fwref   [2][2*block+1] = best_fw_ref;  
  422.                best8x8pdir    [2][2*block] = best8x8pdir    [2][2*block+1] = best_pdir;  
  423.                best8x8bwref   [2][2*block] = best8x8bwref   [2][2*block+1] = best_bw_ref;  
  424.              }  
  425.              else//+++一个16x16:如果以8x8为单位,(0,1,2,3)的设置是一样的  
  426.              {  
  427.                best8x8fwref   [1][0] = best8x8fwref   [1][1] = best8x8fwref   [1][2] = best8x8fwref   [1][3] = best_fw_ref;  
  428.                best8x8pdir    [1][0] = best8x8pdir    [1][1] = best8x8pdir    [1][2] = best8x8pdir    [1][3] = best_pdir;  
  429.                best8x8bwref   [1][0] = best8x8bwref   [1][1] = best8x8bwref   [1][2] = best8x8bwref   [1][3] = best_bw_ref;  
  430.              }  
  431.                
  432.              //--- set reference frames and motion vectors ---  
  433.              if (mode>1 && block==0)  //+++mode>1,说明mode=2和3的时候执行,block==0,说明是对第一个分块.  
  434.                                      //+++这里说明一下:对于16x8,8x16来说,只有两个宏块分割,这里保存第一块分割的运动信息,目的是给下一个分割做运动矢量预测使用  
  435.                                     //+++由于前一个分割不需要下一个分割的运动矢量信息,因此只需要保存前一个分割即可.  
  436.                                    //+++另外,在这之前,事实上已经保存过,所以这里是重复保存了  
  437.                SetRefAndMotionVectors (block, mode, best_pdir, best_fw_ref, best_bw_ref);  
  438.           } // for (block=0; block<(mode==1?1:2); block++)  
  439.           //+++我在这个地方解释一下cost与min_cost:min_cost是为了寻找三种宏块模式(16x16,16x8,8x16)中代价最小的模式  
  440.           //+++由于宏块模式在计算的时候是进一步分成小块计算的,所以cost就是用来保存一种宏块模式下对应所有小块的代价之和  
  441.           //+++模式16x16下:cost对应一个16x16大小的块;模式16x8模式下:cost对应两个16x8大小的块......  
  442.           //+++所以下面这一个if语句就是在寻找代价最小的宏块模式(best_mode)  
  443.           if (cost < min_cost)  
  444.           {  
  445.             best_mode = mode;  
  446.             min_cost  = cost;  
  447.           }  
  448.         } // if (valid[mode])  
  449.       } // for (mode=1; mode<4; mode++)//+++宏块级模式结束  
  450.       //+++现在宏块级运动估计结束,best_mode保存了最佳的宏块模式,min_cost记录其对应的代价  
  451.       //+++亚宏块级运动估计  
  452.       if (valid[P8x8])  
  453.       {  
  454.         cost8x8 = 0;  
  455.          
  456.         //===== store coding state of macroblock =====  
  457.         store_coding_state (cs_mb);  
  458.         //+++遍历16x16宏块的4个8x8块  
  459.         //=====  LOOP OVER 8x8 SUB-PARTITIONS  (Motion Estimation & Mode Decision) =====  
  460.         for (cbp8x8=cbp_blk8x8=cnt_nonz_8x8=0, block=0; block<4; block++)  
  461.          
  462.         {  
  463.           //--- set coordinates ---  
  464.           j0 = ((block/2)<<3);    j1 = (j0>>2);  
  465.           i0 = ((block%2)<<3);    i1 = (i0>>2);  
  466.           //+++对宏块中的每个8x8块的模式循环  
  467.           //+++这个地方解释一下:min_cost8x8用于记录非RDO模式下的最小代价,而min_rdcost则是RDO模式下的最小代价  
  468.           //=====  LOOP OVER POSSIBLE CODING MODES FOR 8x8 SUB-PARTITION  =====  
  469.           for (min_cost8x8=(1<<20), min_rdcost=1e30, index=(bframe?0:1); index<5; index++)  
  470.           {  
  471.             if (valid[mode=b8_mode_table[index]])//+++b8_mode_table[6] = {0, 4, 5, 6, 7};  
  472.             {  
  473.                 curr_cbp_blk = 0;//+++初始化当前cbp_blk  
  474.                
  475.               if (mode==0)  
  476.               {  
  477.                 //--- Direct Mode ---  
  478.                 if (!input->rdopt)  
  479.                 {  
  480.                   cost_direct += (cost = Get_Direct_Cost8x8 ( block, lambda_mode ));  
  481.                   if (cost==1<<30)  
  482.                     cost_direct = (1<<30);  
  483.                   have_direct ++;//+++记录是否做过direct模式的标记  
  484.                 }  
  485.                 best_fw_ref = direct_ref_idx[LIST_0][img->block_x+(block&1)*2][img->block_y+(block&2)];  
  486.                 best_bw_ref = direct_ref_idx[LIST_1][img->block_x+(block&1)*2][img->block_y+(block&2)];  
  487.                 best_pdir   = direct_pdir[img->block_x+(block&1)*2][img->block_y+(block&2)];  
  488.               } // if (mode==0)  
  489.               else//+++非direct模式下  
  490.               {  
  491.                 //--- motion estimation for all reference frames ---  
  492.                 PartitionMotionSearch (mode, block, lambda_motion);  
  493.                 //+++这一部分与宏块级的类似  
  494.                 //--- get cost and reference frame for forward prediction ---  
  495.                 for (fw_mcost=max_mcost, ref=0; ref<listXsize[LIST_0+list_offset]; ref++)  
  496.                 {  
  497.                   if (!checkref || ref==0 || CheckReliabilityOfRef (block, LIST_0, ref, mode))  
  498.                   {  
  499.                   //+++(1)参考帧代价  
  500.                     mcost  = (input->rdopt ? REF_COST(lambda_motion_factor,ref,LIST_0+list_offset) : (int)(2*lambda_motion*min(ref,1)));  
  501.                     //+++(2)MV代价  
  502.                     mcost += motion_cost[mode][LIST_0][ref][block];  
  503.                     if (mcost < fw_mcost)  
  504.                     {  
  505.                       fw_mcost    = mcost;  
  506.                       best_fw_ref = ref;  
  507.                     }  
  508.                   }  
  509.                 }  
  510.                  
  511.                 //store forward reference index for every block  
  512.                 for (j=0; j<2; j++)  
  513.                   for (i=0; i<2; i++)  
  514.                   {  
  515.                     enc_picture->ref_idx[LIST_0][img->block_x+(block&1)*2+i][img->block_y+(block&2)+j] = best_fw_ref;  
  516.                     enc_picture->ref_pic_id [LIST_0][img->block_x+(block&1)*2+i][img->block_y+(block&2)+j] = enc_picture->ref_pic_num[LIST_0 + list_offset][enc_picture->ref_idx[LIST_0][img->block_x+(block&1)*2+i][img->block_y+(block&2)+j]];  
  517.                   }  
  518.                 //+++B帧有所不同,还要考虑后向和双向模式,其实与上面的代码是相似的  
  519.                 if (bframe)  
  520.                 {  
  521.                  
  522.                   for (bw_mcost=max_mcost, ref=0; ref<listXsize[LIST_1+list_offset]; ref++)  
  523.                   {  
  524.                     mcost  = (input->rdopt ? REF_COST(lambda_motion_factor,ref,LIST_1+list_offset) : (int)(2*lambda_motion*min(ref,1)));  
  525.                      
  526.                     mcost += motion_cost[mode][LIST_1][ref][block];  
  527.                     if (mcost < bw_mcost)  
  528.                     {  
  529.                       bw_mcost    = mcost;  
  530.                       best_bw_ref = ref;  
  531.                     }  
  532.                   }  
  533.                    
  534.                   // bidirectional uses best forward and zero backward reference  
  535.                   bid_mcost  = (input->rdopt ? (REF_COST (lambda_motion_factor, best_fw_ref, LIST_0 + list_offset)+REF_COST (lambda_motion_factor, 0, LIST_1 + list_offset)) : (int)(2*lambda_motion*min(best_fw_ref,1)));  
  536.                   bid_mcost += BIDPartitionCost (mode, block, best_fw_ref, 0, lambda_motion_factor );  
  537.                    
  538.                   //--- get prediction direction ----  
  539.                   if      (fw_mcost<=bw_mcost && fw_mcost<=bid_mcost)  
  540.                   {  
  541.                     best_pdir = 0;  
  542.                     cost = fw_mcost;  
  543.                     best_bw_ref = -1;  
  544.                   }  
  545.                   else if (bw_mcost<=fw_mcost && bw_mcost<=bid_mcost)  
  546.                   {  
  547.                     best_pdir = 1;  
  548.                     cost = bw_mcost;  
  549.                     best_fw_ref = -1;  
  550.                   }  
  551.                   else  
  552.                   {  
  553.                     best_pdir = 2;  
  554.                     cost = bid_mcost;  
  555.                     best_bw_ref = 0;  
  556.                   }  
  557.                     //store backward reference index for every block  
  558.                   for (j=0; j<2; j++)  
  559.                     for (i=0; i<2; i++)  
  560.                     {  
  561.                       enc_picture->ref_idx[LIST_0][img->block_x+(block&1)*2+i][img->block_y+(block&2)+j] = best_fw_ref;  
  562.                       enc_picture->ref_idx[LIST_1][img->block_x+(block&1)*2+i][img->block_y+(block&2)+j] = best_bw_ref;  
  563.                       //enc_picture->ref_pic_id [LIST_1][img->block_x+(block&1)*2+i][img->block_y+(block&2)+j] = enc_picture->ref_pic_num[LIST_1 + list_offset][enc_picture->ref_idx[LIST_1][img->block_x+(block&1)*2+i][img->block_y+(block&2)+j]];   
  564.                     }  
  565.                 } // if (bframe)  
  566.                 else  
  567.                 {  
  568.                   best_pdir = 0;  
  569.                   cost      = fw_mcost;  
  570.                 }  
  571.               } // if (mode!=0)  
  572.                
  573.               //--- store coding state before coding with current mode ---  
  574.               store_coding_state (cs_cm);  
  575.                
  576.               //+++计算RDO模式代价rdcost或非RDO模式代价cost  
  577.               if (input->rdopt)  
  578.               {  
  579.                  //+++RDCost_for_8x8blocks是在4种亚宏块模式中选择最佳的模式(代价)  
  580.                 //+++准确来说RDCost_for_8x8blocks主要是得到编号为block的8x8块,在mode模式下的代价  
  581.                 //--- get and check rate-distortion cost ---  
  582.                 rdcost = RDCost_for_8x8blocks (&cnt_nonz, &curr_cbp_blk, lambda_mode,  
  583.                                                block, mode, best_pdir, best_fw_ref, best_bw_ref);  
  584.                                               //+++block:8x8块在16x16大小的宏块中的位置  
  585.                                               //+++mode:亚宏块模式  
  586.               }  
  587.               else  
  588.               {  
  589.                 cost += (REF_COST (lambda_motion_factor, B8Mode2Value (mode, best_pdir), list_offset + (best_pdir<1?0:1)) - 1);  
  590.               }  
  591.               //+++根据cost(非RDO模式)或rdcost(RDO模式)选择最佳模式  
  592.               //--- set variables if best mode has changed ---  
  593.               if (( input->rdopt && rdcost < min_rdcost) ||  
  594.                   (!input->rdopt && cost   < min_cost8x8  )   )  
  595.               {  
  596.                 min_cost8x8                  = cost;//+++非RDO代价  
  597.                 min_rdcost                   = rdcost;//+++RDO代价  
  598.                 best8x8mode          [block] = mode;//最佳的亚宏块模式  
  599.                 best8x8pdir    [P8x8][block] = best_pdir;  
  600.                 best8x8fwref   [P8x8][block] = best_fw_ref;  
  601.                 best8x8bwref   [P8x8][block] = best_bw_ref;  
  602.                  
  603.                 //--- store number of nonzero coefficients ---  
  604.                 best_cnt_nonz  = cnt_nonz;  
  605.                 //+++存储cbp、残差AC系数和重建帧中的预测值  
  606.                 if (input->rdopt)  
  607.                 {                     
  608.                   //--- store block cbp ---  
  609.                   cbp_blk8x8    &= (~(0x33 << (((block>>1)<<3)+((block%2)<<1)))); // delete bits for block  
  610.                   cbp_blk8x8    |= curr_cbp_blk;  
  611.                    
  612.                   //--- store coefficients ---  
  613.                   for (k=0; k< 4; k++)  
  614.                     for (j=0; j< 2; j++)  
  615.                       for (i=0; i<65; i++)  cofAC8x8[block][k][j][i] = img->cofAC[block][k][j][i]; // 18->65 for ABT  
  616.                        
  617.                       //--- store reconstruction and prediction ---  
  618.                   for (j=j0; j<j0+8; j++)  
  619.                     for (i=i0; i<i0+8; i++)  
  620.                     {  
  621.                       rec_mbY8x8[j][i] = enc_picture->imgY[img->pix_y+j][img->pix_x+i];  
  622.                       mpr8x8    [j][i] = img->mpr[i][j];  
  623.                     }  
  624.                 }  
  625.                  
  626.                 //--- store coding state ---  
  627.                 store_coding_state (cs_b8);  
  628.               } // if (rdcost <= min_rdcost)  
  629.                
  630.               //--- re-set coding state as it was before coding with current mode was performed ---  
  631.               reset_coding_state (cs_cm);  
  632.             } // if (valid[mode=b8_mode_table[index]])  
  633.           } // for (min_rdcost=1e30, index=(bframe?0:1); index<6; index++)//+++5种8x8模式循环结束  
  634.           //+++cost8x8:非RDO模式下的代价,其实cost8x8是一个宏块中的4个8x8块的代价和,  
  635.           //+++换句话说,其实cost8x8就是一个宏块(16x16)在亚宏块模式下的总的代价和.  
  636.           //+++这个量主要用来和上面计算的最佳宏块级模式(非RDO情况下)对应的最小代价min_cost来进行比较,  
  637.           //+++看宏块级模式或亚宏块级模式哪个更好,从下面的代码中我们也可以看出来.  
  638.           cost8x8 += min_cost8x8;  
  639. //          if (!input->rdopt) cost8x8+= min_cost8x8;  
  640. //          else cost8x8 += min_rdcost;           
  641.            
  642.           if (!input->rdopt)  
  643.           {  
  644.             mode = best8x8mode[block];  
  645.             pdir = best8x8pdir[P8x8][block];  
  646.             //+++编码残差系数  
  647.             curr_cbp_blk  = 0;  
  648.             //+++cnt_nonz表示非零系数的编码代价;  
  649.             best_cnt_nonz = LumaResidualCoding8x8 (&dummy, &curr_cbp_blk, block, pdir,  
  650.                                                    (pdir==0||pdir==2?mode:0),  
  651.                                                    (pdir==1||pdir==2?mode:0),  
  652.                                                    (best8x8fwref[P8x8][block]),  
  653.                                                    (best8x8bwref[P8x8][block]));  
  654.             cbp_blk8x8   &= (~(0x33 << (((block>>1)<<3)+((block%2)<<1)))); // delete bits for block  
  655.             cbp_blk8x8   |= curr_cbp_blk;  
  656.              
  657.             //--- store coefficients ---  
  658.             for (k=0; k< 4; k++)  
  659.               for (j=0; j< 2; j++)  
  660.                 for (i=0; i<65; i++)  cofAC8x8[block][k][j][i] = img->cofAC[block][k][j][i]; // 18->65 for ABT  
  661.                  
  662.                 //--- store reconstruction and prediction ---  
  663.             for (j=j0; j<j0+8; j++)  
  664.               for (i=i0; i<i0+8; i++)  
  665.               {  
  666.                 rec_mbY8x8[j][i] = enc_picture->imgY[img->pix_y+j][img->pix_x+i];  
  667.                 mpr8x8    [j][i] = img->mpr[i][j];  
  668.               }  
  669.           }  
  670.            
  671.           //----- set cbp and count of nonzero coefficients ---  
  672.           if (best_cnt_nonz)  
  673.           {  
  674.             cbp8x8        |= (1<<block);  
  675.             cnt_nonz_8x8  += best_cnt_nonz;  
  676.           }  
  677.            
  678.           mode=best8x8mode[block];//第2396行会用到mode,best8x8mode[block]应该是第block号8x8块所选取的最佳亚宏块级模式  
  679.           //===== reset intra prediction modes (needed for prediction, must be stored after 8x8 mode dec.) =====  
  680.           j0 = img->block_y+2*(block/2);  
  681.           i0 = img->block_x+2*(block%2);  
  682.           for (j=j0; j<j0+2; j++)  
  683.             for (i=i0; i<i0+2; i++)  
  684.               ipredmodes[i][j]         = DC_PRED;  
  685.           i0 = 4*block;  
  686.           for (i=i0; i<i0+4; i++)    currMB->intra_pred_modes[i]  = DC_PRED;  
  687.              
  688.           if (block<3)  
  689.           {  
  690.             //===== re-set reconstructed block =====  
  691.             j0   = 8*(block/2);  
  692.             i0   = 8*(block%2);  
  693.             for (j=j0; j<j0+8; j++)  
  694.               for (i=i0; i<i0+8; i++)   
  695.                 enc_picture->imgY[img->pix_y+j][img->pix_x+i] = rec_mbY8x8[j][i];  
  696.           } // if (block<3)  
  697.             //+++这儿与宏块级类似,要设置MV和参考帧  
  698.           //===== set motion vectors and reference frames (prediction) =====  
  699.           SetRefAndMotionVectors (block, mode, best8x8pdir[P8x8][block], best8x8fwref[P8x8][block], best8x8bwref[P8x8][block]);  
  700.           //===== set the coding state after current block =====  
  701.           reset_coding_state (cs_b8);  
  702.         } // for (cbp8x8=cbp_blk8x8=cnt_nonz_8x8=0, block=0; block<4; block++)  
  703.         //+++此刻完成了对一个宏块(16x16)在亚宏块模式下的计算,或者说对宏块中每个8x8块完成亚宏块模式的选择  
  704.         //+++并且在亚宏块模式下的代价(4个8x8块代价之和)也计算完毕,在cost8x8中,下面会用到,和宏块级模式(min_cost)进行比较  
  705.         //===== store intra prediction modes for 8x8+ macroblock mode =====  
  706.         for (k=0, j=img->block_y; j<img->block_y+4; j++)  
  707.         {  
  708.           for (     i=img->block_x; i<img->block_x+4; i++, k++)  
  709.           {  
  710.             b8_ipredmode       [k] = ipredmodes    [i][j];  
  711.             b8_intra_pred_modes[k] = currMB->intra_pred_modes[k];  
  712.           }  
  713.         }  
  714.          
  715.         //--- re-set coding state (as it was before 8x8 block coding) ---  
  716.         reset_coding_state (cs_mb);  
  717.         for (i=0; i<16; i++)  
  718.           for(j=0; j<16; j++)  
  719.               //++ 原代码中这里 img->mpr 的下标写反了  
  720. //          diffy[j][i] = imgY_org[img->opix_y+j][img->opix_x+i]-img->mpr[j][i];  
  721.             diffy[j][i] = imgY_org[img->opix_y+j][img->opix_x+i]-img->mpr[i][j];  
  722.              
  723.              
  724.     //+++到这儿应该是一个结点了啊  
  725.         //+++(在非RDO模式下)对一个宏块(当前宏块),此时宏块级(16x16,16x8,8x16)的最小开销已经计算出来,即min_cost  
  726.         //+++子宏块级(8x8,8x4,4x8和4x4)的开销也计算出来(4个8x8块代价之和),即cost8x8  
  727.         //+++这样可以比较这两种方式,选择开销更小的那种宏块分割方式  
  728.         if(cost8x8 < min_cost)//+++// 帧间8x8模式与帧间16x16代价相比  
  729.         {  
  730.            best_mode = P8x8;  
  731.            min_cost = cost8x8;  
  732.         }  
  733.         //+++到这儿我们也可以看到,在非RDO模式下,best_mode保存的是一个宏块(当前宏块)帧间预测的最优模式,是(16x16,16x8,8x16和P8x8)之一.  
  734.         //+++(16x16,16x8,8x16)这三种宏块级的模式是选择最优模式后再与P8x8模式比较.min_cost现在保存的是一个宏块(当前宏块)帧间预测的最小代价  
  735.         //+++从下面帧内预测的代码我们也可以看到,min_cost作为帧间最小代价是要和帧内两种模式I4MB和I16MB进行比较的.  
  736.       }  
  737.       else // if (valid[P8x8])  
  738.       {  
  739.         //+++这个地方是不用子宏块划分的方法时,将其开销设为很大  
  740.         cost8x8 = (1<<20);  
  741.       }  
  742.     
  743.       // Find a motion vector for the Skip mode  
  744.       if((img->type == P_SLICE)||(img->type == SP_SLICE))  
  745.         FindSkipModeMotionVector ();//+++用于计算MV预测值  
  746.     }  
  747.     else // if (img->type!=I_SLICE)//+++不进行帧间预测  
  748.     {  
  749.       min_cost = (1<<20);  
  750.     }  
  751. //+++帧间预测的相关开销计算完毕  
  752. //++++++++++++++++++++++++++++  
  753. //===================================  
  754.  //+++下面是对帧内预测进行求其开销  
  755.     if (input->rdopt)  
  756.     {//+++使用RDO准则的情况  
  757.       int mb_available_up;  
  758.       int mb_available_left;  
  759.       int mb_available_up_left;  
  760.        
  761.       min_rdcost = max_rdcost;  
  762.        
  763.       // precompute all new chroma intra prediction modes  
  764.       //++ 对色度进行帧内预测,并求出最优模式,  
  765.       //+++RDO模式下,需要色度预测,保存在currMB->c_ipred_mode中  
  766.       IntraChromaPrediction8x8(&mb_available_up, &mb_available_left, &mb_available_up_left);  
  767.       //+++阅读完IntraChromaPrediction8x8函数的代码,并且结合下面的代码,我们可以看到:  
  768.       //+++这个函数不做任何运动估计和补偿,而仅仅是求出要进行帧内编码的色度宏块需要的参考块  
  769.       //+++本来在currMB->c_ipred_mode保存了最佳的模式,但是下面却被覆盖掉了  
  770.        
  771.       //++ 分别在四种色度模式下进行 RDO 计算,如果是 inter 模式,因为色度预测模式与 SSD 计算  
  772.       //++ 无关,因此只需要计算一次(利用 currMB->c_ipred_mode == DC_PRED_8 条件限制来实现)  
  773.       for (currMB->c_ipred_mode=DC_PRED_8; currMB->c_ipred_mode<=PLANE_8; currMB->c_ipred_mode++)  
  774.       {  
  775.          
  776.         // bypass if c_ipred_mode is not allowed  
  777.           //+++若当前 c_ipred_mode 模式不被允许,则考虑下一个 c_ipred_mode 模式;  
  778.         if ((currMB->c_ipred_mode==VERT_PRED_8 && !mb_available_up) ||  
  779.           (currMB->c_ipred_mode==HOR_PRED_8 && !mb_available_left) ||  
  780.           (currMB->c_ipred_mode==PLANE_8 && (!mb_available_left || !mb_available_up || !mb_available_up_left)))  
  781.           continue;  
  782.          
  783.          
  784.         //===== GET BEST MACROBLOCK MODE =====  
  785.         for (ctr16x16=0, index=0; index<7; index++)  
  786.         {  
  787.             //+++mb_mode_table[7]  = {0, 1, 2, 3, P8x8, I16MB, I4MB};  
  788.           mode = mb_mode_table[index];  
  789.            
  790.           //--- for INTER16x16 check all prediction directions ---  
  791.           if (mode==1 && img->type==B_SLICE)  
  792.           {  
  793.             best8x8pdir[1][0] = best8x8pdir[1][1] = best8x8pdir[1][2] = best8x8pdir[1][3] = ctr16x16;  
  794.             if (ctr16x16 < 2) index--;  
  795.             ctr16x16++;  
  796.           }  
  797.            
  798.           img->NoResidueDirect = 0;  
  799.            
  800.           if (valid[mode])  
  801.           {  
  802.             // bypass if c_ipred_mode not used  
  803.             //++ 设置当前宏块类型以及其中每个8*8块的分割方式和预测方向,每个4*4块的参考帧索引  
  804.             //++ 该函数在下面的 RDCost_for_macroblocks 函数内再次调用,进行了重复操作  
  805.             SetModesAndRefframeForBlocks (mode);  
  806.             if (currMB->c_ipred_mode == DC_PRED_8 ||    //++ 利用这个条件限制来实现 inter 模式时只计算一次 RDO  
  807.               (IS_INTRA(currMB) ))  
  808.             {  
  809.             //+++此刻我的理解是:RDCost_for_macroblocks是在模式0-3,P8x8,Intra4x4和Intra16x16之间选择一个最佳模式  
  810.             //+++这是利用RDO的方法.上面再帧间预测时其实已经将在1-3中选择出了最佳模式,但那是在非RDO模式下的  
  811.                 //+++关于这个地方的RDCost_for_macroblocks我要说明一下:该函数是求出对应mode模式下的RDO代价,然后将这个代价与min_rdcost进行比较  
  812.                 //+++如果此时RDO的代价比较小,返回1,执行if,保存相关参数,并且修改min_rdcost,否则返回0,这样下面的if语句不执行,接着再比较下一个模式.  
  813.                 //+++所以我们可以看到(0-3,P8x8,Intra4x4和Intra16x16)这些模式的比较是随着外面的这个for循环,依次在RDCost_fo...函数中完成的,  
  814.               if (RDCost_for_macroblocks (lambda_mode, mode, &min_rdcost))    //++ 帧内模式时亮度存在重复计算情况:因为色度预测模式与亮度预测模式无关,所以在该色度模  
  815.               {                                                                //++ 式循环中每次计算得到的 intra16*16 和 intra4*4 宏块类型的最佳亮度模式都是完全相同的  
  816.                 //Rate control  
  817.                 if(mode == P8x8)  
  818.                 {  
  819.                   for (i=0; i<16; i++)  
  820.                     for(j=0; j<16; j++)  
  821.                       diffy[j][i] = imgY_org[img->opix_y+j][img->opix_x+i] - mpr8x8[j][i];  
  822.                 }else  
  823.                 {  
  824.                   for (i=0; i<16; i++)  
  825.                     for(j=0; j<16; j++)  
  826.                       diffy[j][i] = imgY_org[img->opix_y+j][img->opix_x+i] - pred[j][i];  
  827.                 }  
  828.                 //+++存储该模式下的一些参数:因为RDCost_for_macroblocks的计算结果是mode模式的预测开销小于min_rdcost,  
  829.                 //+++所以mode模式其实更好,所以现在要保存该模式的一些参数.  
  830.                 store_macroblock_parameters (mode);  
  831.               }  
  832.             }  
  833.           }  
  834.           if (valid[0] && bframe && mode == 0 && currMB->cbp && (currMB->cbp&15) != 15) //g050  
  835.           {  
  836.             img->NoResidueDirect = 1;  
  837.             if (RDCost_for_macroblocks (lambda_mode, mode, &min_rdcost))  
  838.             {  
  839.               //Rate control  
  840.               for (i=0; i<16; i++)  
  841.                 for(j=0; j<16; j++)  
  842.                   diffy[j][i] = imgY_org[img->opix_y+j][img->opix_x+i] - pred[j][i];  
  843.                 store_macroblock_parameters (mode);  
  844.             }  
  845.           }  
  846.         }  
  847.       }  
  848.     }  
  849.     else//+++不使用RDO准则的情况  
  850.     {  
  851.     //+++从下面的代码可以看到,在不同模式下计算出的cost都要和min_cost(帧间预测代价)进行比较  
  852.     //+++然后决定是否更新min_cost和best_mode,所以我们也可以看到,最终的代价也是保存在min_cost中,  
  853.     //+++best_mode对应保存最佳模式,从而用于最后的熵编码  
  854.      
  855.     //+++下面对Direct,I4MB和I16MB三种模式进行依次比较,代码是类似的.  
  856.       if (valid[0] && bframe) // check DIRECT MODE  
  857.       {  
  858.         cost  = (have_direct?cost_direct:Get_Direct_CostMB (lambda_mode));  
  859.         cost -= (int)floor(16*lambda_motion+0.4999);  
  860.         if (cost <= min_cost)  
  861.         {  
  862.           //Rate control  
  863.           for (i=0; i<16; i++)  
  864.             for(j=0; j<16; j++)  
  865.               diffy[j][i] = imgY_org[img->pix_y+j][img->pix_x+i]-img->mpr[i][j];  
  866.             //+++更新min_cost和best_mode  
  867.             min_cost  = cost;  
  868.             best_mode = 0;  
  869.         }  
  870.       }  
  871.       if (valid[I4MB]) // check INTRA4x4  
  872.       {  
  873.       //+++从Intra4x4的9中模式中选择最佳的模式,函数Mode_Dec..中的cost就是当前宏块的代价  
  874.       //+++追踪各个函数,我们可以看到函数调用顺序如下:在非RDO模式下调用  
  875.       //+++Mode_Decision_for_Intra4x4Macroblock(得到一个宏块的代价)  
  876.       //+++->Mode_Decision_for_8x8IntraBlocks(得到一个8x8块代价)  
  877.       //+++->Mode_Decision_for_Intra4x4Macroblock(得到一个4x4块代价),  
  878.       //+++其实最后一个函数才是真正的Intra4x4模式选择的核心,在这个函数中包含9中模式的比较(RDO和非RDO)  
  879.         currMB->cbp = Mode_Decision_for_Intra4x4Macroblock (lambda_mode, &cost);//++ 计算当前宏块采用intra4*4编码时的代价和CBP  
  880.         if (cost <= min_cost)  
  881.         {  
  882.           //Rate control  
  883.           for (i=0; i<16; i++)  
  884.             for(j=0; j<16; j++)  
  885.               diffy[j][i] = imgY_org[img->pix_y+j][img->pix_x+i]-img->mpr[i][j];//++ 保存采用intra4*4编码时最佳模式的残差块  
  886.              //+++更新min_cost和best_mode  
  887.             min_cost  = cost;  
  888.             best_mode = I4MB;  
  889.         }  
  890.       }  
  891.       if (valid[I16MB]) // check INTRA16x16  
  892.       {  
  893.       //+++比较与RDO模式下代码可以发现,其实在RDO模式下进行Intra16x16预测时,调用的函数Intra16x16_Mode_Decision的代码  
  894.       //+++与这儿类似:也是先intrapred_luma_16x16,再find_sad_16x16,最后dct_luma_16x16  
  895.       //+++同时这也说明:intra16*16 在进行4种模式选择时候,无论是否在RDO情况下,其选择过程是相同的。  
  896.         intrapred_luma_16x16 ();    //++ 分别计算当前宏块在4种intra16*16帧内预测模式下的预测块  
  897.                                     //+++intrapred_luma_16x16执行完后,预测的结果保存在数组img->mprr_2中  
  898.         cost = find_sad_16x16 (&i16mode);    //++ 计算intra16*16类型的代价(以SATD作为判断标准,因为该段代码是不采用RDO的处理过程)  
  899.                                             //+++此时i16mode保存的是Intra16x16的最佳模式,cost是对应的代价  
  900.          
  901.         if (cost < min_cost)    //++ 如果采用intra16*16类型编码的代价小于采用intra4*4类型编码的代价,则对当前宏块采用intra16*16类型编码  
  902.         {  
  903.           //Rate control  
  904.           for (i=0; i<16; i++)  
  905.             for(j=0; j<16; j++)  
  906.               diffy[j][i] = imgY_org[img->pix_y+j][img->pix_x+i]-img->mprr_2[i16mode][j][i];  
  907.              //+++更新min_cost和best_mode  
  908.             best_mode   = I16MB;  
  909.             //+++这里说明一下:find_sad_16x16函数中求sad时已经做过DCT变换,但在dct_luma_16x16函数中又要重新做一遍呢?  
  910.             //+++答:前者是对每种预测模式都要做,后者是对最佳模式才做。每种模式做了之后并没有把结果都保存起来,所以要对最佳模式重新再做一次。  
  911.             currMB->cbp = dct_luma_16x16 (i16mode);    //++ 对当前宏块采用intra16*16类型进行编码,并计算CBP  
  912.         }  
  913.       }  
  914.     }  
  915.     //+++通过比较RDO模式与非RDO模式我们可以发现,  
  916.     //+++在intra4*4与intra16*16之间做选择时,RDO情况下色度要参与计算,而非RDO情况下色度不参与计算  
  917.     //+++并且我们可以看到在非RDO模式下,具体的帧内预测还是帧间预测的选择在这儿完成(可以在这儿看到具体的代码)  
  918.     //+++但是对于RDO模式,具体的帧内还是帧间的选择却是在函数RDCost_for_macroblocks中完成的.  
  919. //+++帧内预测结束  
  920. //===================================  
  921.     if (rerun==0)  
  922.     {  
  923.       intra1 = (currMB->mb_type==I16MB || currMB->mb_type==I4MB ? 1 : 0);  
  924.     }  
  925.   } // for (rerun=0; rerun<runs; rerun++)  
  926.   //+++到现在,帧内和帧间模式的选择终于结束  
  927.    
  928.   //+++下面的要进行存储编码参数,分RDO和非RDO  
  929.   //+++印证了[h264率失真RDO研究]一文中的一句:  
  930.   //+++"如果用RDO代价函数,因为计算代价时已经编码,择优后仅需对编码进行存储;  
  931.   //+++如果用非RDO代价函数,则择优后还要进行编码过程,编码后存储参数."  
  932.   //+++从下面的代码我们也可清楚的看到  
  933.   if (input->rdopt)  
  934.   {  
  935.      
  936.     if ((cbp!=0 || best_mode==I16MB ))  
  937.       currMB->prev_cbp = 1;  
  938.     else if (cbp==0 && !input->RCEnable)  
  939.     {  
  940.       currMB->delta_qp = 0;  
  941.       currMB->qp = currMB->prev_qp;  
  942.       img->qp = currMB->qp;  
  943.       currMB->prev_cbp = 0;  
  944.     }  
  945. //+++存储编码参数  
  946.     set_stored_macroblock_parameters ();  
  947. //+++    (1)保存重建图像  
  948. //+++    rdopt->rec_mbY[j][i] = rec_mbY[j][i];  
  949. //+++    rdopt->rec_mbU[j][i] = rec_mbU[j][i];    
  950. //+++    rdopt->rec_mbV[j][i] = rec_mbV[j][i];   
  951. //+++    (2)变换系数和cbp  
  952. //+++    img->cofAC=i4p(cofAC);  
  953. //+++    img->cofDC=i3p(cofDC);  
  954. //+++    currMB->cbp      = cbp;  
  955. //+++    currMB->cbp_blk = cbp_blk;  
  956. //+++    (3)宏块模式  
  957. //+++    currMB->mb_type = mode(best_mode);  
  958. //+++    (4)参考帧保存  
  959. //+++    rdopt.c 1511行,对前向参考,后向参考帧分别进行了保存  
  960. //+++    (5)帧内预测模式  
  961. //+++    currMB->c_ipred_mode = best_c_imode;  
  962. //+++    (6)为当前块保存运动向量  
  963. //+++    SetMotionVectorsMB (currMB, bframe);  
  964. //+++    从上面其实可以看到"="右边的量在rdopt.c中都是全局变量,在完成encode_one_macroblock之前,对这些参数进行一下保存.  
  965.   }  
  966.   else  
  967.   {  
  968.     //===== set parameters for chosen mode =====  
  969.     SetModesAndRefframeForBlocks (best_mode);    //++ 设置当前宏块的参数,包括:宏块类型(mb_type)、4个8*8块的分割模式和预测方向(b8mode,b8pdir)、16个4*4块的参考帧索引(ref_idx,ref_pic_id)  
  970.     if (best_mode==P8x8)  
  971.     {  
  972.       SetCoeffAndReconstruction8x8 (currMB);  
  973.     }  
  974.     else  
  975.     {  
  976.       if (best_mode!=I4MB)  
  977.       {  
  978.         for (k=0, j=img->block_y; j<img->block_y+4; j++)  
  979.           for (     i=img->block_x; i<img->block_x+4; i++, k++)  
  980.           {  
  981.             ipredmodes    [i][j] = DC_PRED;  
  982.             currMB->intra_pred_modes[k] = DC_PRED;  
  983.           }  
  984.           if (best_mode!=I16MB)  
  985.           {  
  986.             LumaResidualCoding ();  
  987.             //Rate control  
  988.             for (i=0; i<16; i++)  
  989.               for(j=0; j<16; j++)  
  990.                 diffy[j][i] = imgY_org[img->pix_y+j][img->pix_x+i]-img->mpr[i][j];  
  991.           }  
  992.       }  
  993.     }  
  994.     //+++  
  995.     // precompute all chroma intra prediction modes  
  996.     IntraChromaPrediction8x8(NULL, NULL, NULL);    //++ 对色度块进行处理,包括:分别计算4种色度帧内预测模式下的预测块、代价(该分支未采用RDO,因此以SATD作为判断标准)、最佳预测模式  
  997.     img->i16offset = 0;  
  998.     dummy = 0;  
  999.     ChromaResidualCoding (&dummy);  
  1000.     if (best_mode==I16MB)  
  1001.     {  
  1002.       img->i16offset = I16Offset  (currMB->cbp, i16mode);  
  1003.     }  
  1004.     SetMotionVectorsMB (currMB, bframe);  
  1005.      
  1006.     //===== check for SKIP mode =====  
  1007.     if ((img->type==P_SLICE || img->type==SP_SLICE) && best_mode==1 && currMB->cbp==0 &&  
  1008.       enc_picture->ref_idx[LIST_0][img->block_x][img->block_y]==0 &&  
  1009.       enc_picture->mv[LIST_0][img->block_x][img->block_y][0]==allmvs[0][0][0][0][0][0] &&  
  1010.       enc_picture->mv[LIST_0][img->block_x][img->block_y][1]==allmvs[0][0][0][0][0][1]               )  
  1011.     {  
  1012.       currMB->mb_type=currMB->b8mode[0]=currMB->b8mode[1]=currMB->b8mode[2]=currMB->b8mode[3]=0;  
  1013.     }  
  1014.      
  1015.     if(img->MbaffFrameFlag)  
  1016.       set_mbaff_parameters();  
  1017.   }  
  1018.    
  1019.   // Rate control  
  1020.   if(input->RCEnable)  
  1021.   {    
  1022.     if(img->type==P_SLICE)  
  1023.     {  
  1024.       img->MADofMB[img->current_mb_nr] = calc_MAD();  
  1025.        
  1026.       if(input->basicunit<img->Frame_Total_Number_MB)  
  1027.       {  
  1028.         img->TotalMADBasicUnit +=img->MADofMB[img->current_mb_nr];  
  1029.          
  1030.         /* delta_qp is present only for non-skipped macroblocks*/  
  1031.         if ((cbp!=0 || best_mode==I16MB))  
  1032.           currMB->prev_cbp = 1;  
  1033.         else  
  1034.         {  
  1035.           img->qp -= currMB->delta_qp;  
  1036.           currMB->delta_qp = 0;  
  1037.           currMB->qp = img->qp;  
  1038.           currMB->prev_cbp = 0;  
  1039.         }  
  1040.         /* When MBAFF is used, delta_qp is only present for the first non-skipped macroblock of each 
  1041.         macroblock pair*/  
  1042.         if (input->MbInterlace)  
  1043.         {  
  1044.           if(!currMB->mb_field)  
  1045.           {  
  1046.             DELTA_QP = currMB->delta_qp;  
  1047.             QP      = currMB->qp;  
  1048.           }  
  1049.           else  
  1050.           {  
  1051.             DELTA_QP2 = currMB->delta_qp;  
  1052.             QP2      = currMB->qp;  
  1053.           }  
  1054.         }        
  1055.       }  
  1056.     }  
  1057.   }  
  1058.    
  1059.    //+++保存最佳代价在rdopt->min_rdcost中  
  1060.   if(input->rdopt)  
  1061.     rdopt->min_rdcost = min_rdcost;  
  1062.   else  
  1063.     rdopt->min_rdcost = min_cost;  
  1064.   if(img->MbaffFrameFlag)  
  1065.   {  
  1066.     if (img->current_mb_nr%2) //bottom  
  1067.     {  
  1068.       if ((currMB->mb_type ? 0:((img->type == B_SLICE) ? !currMB->cbp:1))  // bottom is skip  
  1069.         &&(prevMB->mb_type ? 0:((img->type == B_SLICE) ? !prevMB->cbp:1))) // top is skip  
  1070.       {  
  1071.         if (!(field_flag_inference() == curr_mb_field))  
  1072.         {  
  1073.           rdopt->min_rdcost = 1e30;  // don't allow coding of an MB pair as skip if wrong inference  
  1074.         }  
  1075.       }  
  1076.     }  
  1077.   }  
  1078.    
  1079.   //+++RestrictRefFrames应该是和容错有关系的。我的理解是,如果某个区域在后面帧里被intra方式update(从而可以避免错误传播),  
  1080.   //+++这个区域很可能是出现错误的区域,因此,不使用这个区域作参考以避免错误传播。请大家指正  
  1081.   //===== Decide if this MB will restrict the reference frames =====  
  1082.   if (input->RestrictRef==1)  
  1083.   {  
  1084.     if (input->rdopt==1)  
  1085.     {  
  1086.       refresh_map[2*img->mb_y  ][2*img->mb_x  ] = (intra ? 1 : 0);  
  1087.       refresh_map[2*img->mb_y  ][2*img->mb_x+1] = (intra ? 1 : 0);  
  1088.       refresh_map[2*img->mb_y+1][2*img->mb_x  ] = (intra ? 1 : 0);  
  1089.       refresh_map[2*img->mb_y+1][2*img->mb_x+1] = (intra ? 1 : 0);  
  1090.     }  
  1091.     else if (input->rdopt==2)  
  1092.     {  
  1093.       refresh_map[2*img->mb_y  ][2*img->mb_x  ] = (intra1==0 && (currMB->mb_type==I16MB || currMB->mb_type==I4MB) ? 1 : 0);  
  1094.       refresh_map[2*img->mb_y  ][2*img->mb_x+1] = (intra1==0 && (currMB->mb_type==I16MB || currMB->mb_type==I4MB) ? 1 : 0);  
  1095.       refresh_map[2*img->mb_y+1][2*img->mb_x  ] = (intra1==0 && (currMB->mb_type==I16MB || currMB->mb_type==I4MB) ? 1 : 0);  
  1096.       refresh_map[2*img->mb_y+1][2*img->mb_x+1] = (intra1==0 && (currMB->mb_type==I16MB || currMB->mb_type==I4MB) ? 1 : 0);  
  1097.     }  
  1098.   }  
  1099.   else if (input->RestrictRef==2)  
  1100.   {  
  1101.     refresh_map[2*img->mb_y  ][2*img->mb_x  ] = (currMB->mb_type==I16MB || currMB->mb_type==I4MB ? 1 : 0);  
  1102.     refresh_map[2*img->mb_y  ][2*img->mb_x+1] = (currMB->mb_type==I16MB || currMB->mb_type==I4MB ? 1 : 0);  
  1103.     refresh_map[2*img->mb_y+1][2*img->mb_x  ] = (currMB->mb_type==I16MB || currMB->mb_type==I4MB ? 1 : 0);  
  1104.     refresh_map[2*img->mb_y+1][2*img->mb_x+1] = (currMB->mb_type==I16MB || currMB->mb_type==I4MB ? 1 : 0);  
  1105.   }  
  1106.    
  1107.   if(input->FMEnable)  
  1108.     skip_intrabk_SAD(best_mode, listXsize[LIST_0+list_offset]);  
  1109. }//+++void encode_one_macroblock(),该函数终于完成了  


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值