信源编码(六)h264 jm源码简析

注:此贴参考了众多文章,其中雷霄骅大神写的最为详细。这里就重点介绍下jm的lencode.c

参考博客:  H.264官方软件JM源代码简单分析-编码器lencod 

                     H.264官方软件JM源代码简单分析-解码器ldecod



1.int main(int argc,char **argv)

argc为参数的个数,*argv为命令行参数。

2. init_time();

 

3. alloc_encoder(&p_Enc);

p_Enc为全局变量,里面有4个变量:*p_Enc->p_Inp,*p_Enc->p_Vid,*p_Enc->p_trace,*p_Enc->bufferSize.

该函数就是为p_Enc这个全局变量中的p_Inpp_Vid,p_trace,bufferSize分配相应的内存空间。


4. Configure(p_Enc->p_Vid, p_Enc->p_Inp, argc, argv);

解析命令行参数,读取配置文件。根据配置文件初始化*p_Enc->p_Inp中的部分参数。p_Vid: pointer to the VideoParameters structure;

      p_Inp: pointer to theInputParameters structure.

  1> memset(&cfgparams, 0, sizeof (InputParameters));

   为已开辟内存空间的cfgparams的前sizeof(InputParameters)个字节的值设为0。

  2>InitParams(Map);

  设置编码参数的初始值。

  3>content = GetConfigFileContent(filename);

  分配内存buf,读取文件filename(第一路视频,主配置文件),将文件中的内容读到buf中,返回buf。

  4>ParseContent(p_Inp, Map, content, (int) strlen(content));

  解析符号序列buf,将全局输入变量写入buf里,全局输入变量在configfile.h里定义。

  5>content= GetConfigFileContent ( p_Inp->View1ConfigName );

  如果p_Inp->num_of_views>1(双目编码,为2),要读取第二路的文件内容(第二路视频的配置文件)p_Inp->View1ConfigName里就是第二路视频的配置文件

  函数功能同3>

  6>ParseContent (p_Inp,MapView1, content, (int) strlen(content));

  函数同4>

  7>PatchInp(p_Vid,p_Inp);

  检查输入参数,以保证一致性。

5. init_encoder(p_Enc->p_Vid,p_Enc->p_Inp);

  初始化编码器。读取输入视频文件,并初始化编码参数,如输入视频文件的格式、量化参数、预测模式、搜索范围、失真准则等等,完成序列级头信息的写入。调用之前,p_Enc->p_Inp的部分参数根据配置文件进行了相应的初始化(在configure函数中完成),但p_Enc->p_Vid中几乎是空白的。

  1>level_check(p_Vid,p_Inp);

  检查编码器的级别限制是否满足。

  2>OpenFiles(&p_Inp->input_file1);

  打开包含全部的视频序列的文件。Input_file1指的是第一路视频。

  3>OpenFiles(&p_Inp->input_file2);

  如果是双目视频编码,则还需打开包含第二路视频序列的文件。

  4> p_Vid->output_format[0] =p_Inp->output;

set_storage_format(p_Vid,&(p_Inp->source),&(p_Vid->p_Dpb_layer[0]->storage_format));

  第一路的输出格式,设置保存输出文件的格式。  第二路也类似。

  5>init_qmatrix(p_Vid, p_Inp);

  初始化Q矩阵的值(量化参数)。 分配内存,并且赋值。

  6>init_qoffset(p_Vid);

  初始化Q偏移矩阵的值。 分配内存。

       InitOffsetParam(p_Vid->p_Quant, p_Inp);

       初始化Q偏移矩阵的值。

  7>init_poc(p_Vid);

  初始化POC type0和1。(?)

  8>generate_parameter_sets(p_Vid);

  生成一个序列和图片参数集,并将它们保存在全局变量active_sps和active_pps中。sps--sequence parameter set;  pps--picture parameter set.

        (1)sps =AllocSPS();

        为一个序列参数集(sps)分配内存。

        (2)GenerateSequenceParameterSet(sps,p_Vid, 0);

        生成序列参数集:从全局变量中提取信息,生成序列参数集结构。保存在sps中。

        (3)AllocPPS();

        为图片参数集分配内存。

        (4)GeneratePictureParameterSet(p_Vid->PicParSet[0], sps, p_Vid, p_Inp, 0, 0, 0,p_Inp->cb_qp_index_offset, p_Inp->cr_qp_index_offset);

        生成图片参数集:生成图片参数集结构。

        (5)如果为双目视频,还有为p_Vid->sps[1]分配内存。

  9>generate_encode_parameters(p_Vid);

  生成编码参数。 生成编码参数的内存空间,分双路来分配,并且初始化。

  10>set_level_indices(p_Vid);

  根据level_idc来生成合适的指标来满足合适的level限制。

  11>init_img(p_Vid);

  用合适的参数来初始化图像结构。

        (1)RandomIntraInit(p_Vid, p_Vid->PicWidthInMbs, p_Vid->FrameHeightInMbs,p_Inp->RandomIntraMBRefresh);

          InitSEIMessages(p_Vid,p_Inp);

 

        (2)initInput(p_Vid, &p_Inp->source, &p_Inp->output);

 

        (3)AllocateFrameMemory(p_Vid,p_Inp, &p_Inp->source);

        创建帧内存缓冲buffer。

        p_Vid->bufp_Vid->ibuf。只是分配了内存。

        (4)create_context_memory(p_Vid, p_Inp);

        创建上下文记忆表。分配内存并且初始化。

  12>init_dpb(p_Vid,p_Vid->p_Dpb_layer[i]);

  对解码图像buffer分配内存,并赋给合理的值。P_Dpb(decoded picture buffer)

  13>init_out_buffer(p_Vid);

  初始化输出buffer用来直接输出。

  14>init_stats(p_Inp, p_Vid->p_Stats);

  15>init_dstats(p_Vid->p_Dist);

  16>init_global_buffers(p_Vid,p_Inp);

  动态分配帧大小的内存,和全局的buffer有关,这个buffer是在global.h中定义的。 

  对帧编码图像、场编码图像分配内存,并且对输入的待编码图像初始化内存数据。

       (1)Init_orig_buffers(p_Vid, &p_Vid->imgData);

        对输入的原始图像p_Vid->imgData。。。buffers分配内存,并初始化。

   memory_size +=get_mem2Dpel(&(imgData->frm_data[0]), p_Vid->height, p_Vid->width);    分配一个2维的内存序列array2D[dim0][dim1],dim0=p_Vid->height,dim1=p_Vid->width 保存在imgData->frm_data[0][][],

imgData->frm_data[1][][],imgData->frm_data[2][][]里。——YUV420格式的。

 

memset(&(imgData->frm_data[k][0][0]),128, p_Vid->height_cr * p_Vid->width_cr * sizeof(imgpel));  imgData->frm_data[0][][],imgData->

frm_data[1][][],imgData->frm_data[2][][]赋值,都为128。

       (2)Init_orig_buffers(p_Vid,&p_Vid->imgData0);

       对输入的原始图像p_Vid->imgData(是第二路??)buffers分配内存,并初始化。

       (3)p_Vid->PicPos[j].x    p_Vid->PicPos[j].y——宏块在当前帧中的位置

       (4)分配并设置运动估计的内存。  memory_size+= EPZSInit(p_Vid);

        chroma_mc_setup(p_Vid);设置色度补偿的变量

       (5)wpxInitWPXObject(p_Vid);

       初始化WPRobject的结构。分配内存p_Vid->pWPX->wp_ref_list[LIST_0]和p_Vid->pWPX->wp_ref_list[LIST_1]。??参考帧??

       (6)init_process_image(p_Vid, p_Inp );

       如果是双目视频,init_orig_buffers(p_Vid, &p_Vid->tempData3);

       (7)init_seq_structure(p_Vid, p_Inp, &memory_size );

       初始序列结构。分配序列内存,设置帧buffer的长度,初始化预测结构(p_seq_struct->p_prd的内存大小,预测结构的长度,初始化)——init_pred_struct

( p_Vid, p_Inp, p_seq_struct, memory_size);

init_gop_struct ( p_Inp, p_seq_struct, 1,memory_size );// IDR GOPs

init_gop_struct ( p_Inp, p_seq_struct, 0,memory_size );// Intra GOPs  这两个也类似

填充帧结构——populate_frm_struct()

              利用establish_random_access()来判断是否是IDR帧,

如果为IDR帧,则填充帧内编码/随机访问的预测结构元素,然后(帧内帧间设置,预测帧个数,最近的预测帧,选择预测结构,设置flag,对一个单独的帧单元设置参数,如果帧编码,对一幅单独的待编码图像设置参数付值完毕,更新帧指数)

否则,检查是否帧内,是否SP帧。如果都不是,填充常规的预测结构元素。

 

填充帧预测MV结构——populate_frm_struct_mvc(p_Vid, p_Inp, p_seq_struct, start, end );  双目时执行。  分别设置两路,每一路单独一个帧单元的参数。

 

  17>wpxInitWPXPasses(p_Vid,p_Inp);

  初始化WPR编码通道。  WP参考帧的数目,及算法。

  18>init_motion_search_module(p_Vid, p_Inp);

  初始化运动搜索。  创建保存搜索范围的序列p_Vid->spiral_searchp_Vid->

spiral_hpel_searchp_Vid->spiral_qpel_search,搜索矢量的字节数p_Vid->mvbits,参考帧的字节数p_Vid->refbits 初始化序列中的元素,初始化MVbits:p_Vid->

mvbits[-i]=p_Vid->mvbits[i] =bits初始化参考帧字节数p_Vid->refbits[i] =bits 初始化各搜索模式的MV,然后赋值。 选择失真准则。  初始化快速全像素搜索。

  19>information_init(p_Vid, p_Inp, p_Vid->p_Stats);

  打印协议头文件。

  20>start_sequence(p_Vid,p_Inp);

  打开输出文件,生成合适的序列头文件。

  21>setup_dpb_layer(p_Vid->p_Dpb_layer[i],p_Vid, p_Inp);

 

6.  encode_sequence(p_Enc->p_Vid,p_Enc->p_Inp);

编码视频序列。这个是编码视频的主要的函数。

  1>双目视频,设置待编码的视频帧数frames_to_code =p_Inp->no_frames<< 1;(帧数*2), p_frm帧预测结构, frm_struct_buffer帧结构缓冲。

  2>在所有编码帧中循环。

    1)for(curr_frame_to_code = 0; curr_frame_to_code < frames_to_code;curr_frame_to_code++)  //双目,在两路所有的帧中循环

    2)如果当前帧为第一路(第二路), 设置p_Vid->curr_frm_idx当前帧序号,p_Vid->p_curr_frm_struct当前帧的预测结构,p_Vid->number,p_Vid->view_id第几路。

3)set_dpb_layer_id(p_Vid, p_Vid->view_id);  设置当前帧是第几路

4)如果为第一路视频,则p_Vid->curr_frm_idx= p_Vid->number = curr_frame_to_code >> 1;

     如果为第二路视频,则p_Vid->curr_frm_idx= p_Vid->number = (curr_frame_to_code - 1) >> 1; 并且更新QP值。

5)更新帧数目统计;

6)prepare_frame_params(p_Vid, p_Inp,curr_frame_to_code);

为当前帧准备参数。

设置编码层,sps和cps,所有编码中要用到的参数。设置宏块,选择使用的失真准则。

设置slice类型。

计算当前块的编码参数(frame_no, slice_type, nal_ref_idc, poc, resetframe_num)。

    7)encode_one_frame(p_Vid, p_Inp);  ——下面有详细解析

编码一帧视频。

    8)一直在所有编码帧中循环,进行每一帧的编码。

7. free_encoder_memory(p_Enc->p_Vid, p_Enc->p_Inp);

释放编码器内存。

8. free_params(p_Enc->p_Inp); 

释放Input structures。

9. free_encoder(p_Enc);

释放Encoder Structure。

 

 

 

encode_one_frame(p_Vid, p_Inp)函数解析

1、gettime(&start_time);

设置开始编码的计时。

2put_buffer_frame(p_Vid);

设置指向帧结构的指针。

    p_Vid->pCurImg    = p_Vid->imgData.frm_data[0];

    p_Vid->pImgOrg[0] =p_Vid->imgData.frm_data[0];   //亮度分量 Y

    p_Vid->pImgOrg[1] =p_Vid->imgData.frm_data[1];   //色度分量 U

p_Vid->pImgOrg[2] =p_Vid->imgData.frm_data[2];   //色度分量 V

3init_frame (p_Vid, p_Inp);

初始化一幅新的视频帧的参数。

    p_Vid->current_mb_nr = 0;

    p_Vid->current_slice_nr = 0;

p_Vid->p_Stats->bit_slice = 0;

p_Vid->mb_data_JV[j][i].slice_nr =-1;   j=0-MAX_PLANE,i=0-p_Vid->

FrameSizeInMbs.为了保证在FMO下,正确编码。

init_fixed_qp(p_Vid, p_Inp); 对新的一帧视频,初始化量化参数。

init_mb_line_intra_up(p_Vid, p_Inp);  对新的一帧视频,初始化量化参数。

init_dec_ref_pic_marking_buffer(p_Vid);  初始化memorymanagement control operation。

p_Vid->direct_spatial_mv_pred_flag   设置直接MV空间预测的标志。

如果为第一路视频,设置一下三个参数:

 p_Vid->DFDisableIdc ,p_Vid->DFAlphaC0Offset ,p_Vid->DFBetaOffset

如果为第二路视频,

 p_Vid->DFDisableIdc ,p_Vid->DFAlphaC0Offset ,p_Vid->DFBetaOffset

另外设置p_Vid->AdaptiveRounding

4read_input_data(p_Vid)

1>如果为第一路视频,

file_read =read_one_frame (p_Vid, &p_Inp->input_file1,p_Vid->frm_no_in_file, p_Inp->infile_header, &p_Inp->source,&p_Inp->output, p_Vid->imgData0.frm_data);    ——————  ### 从文件中读取一帧视频——ReadFrameConcatenated(在级联的原始文件中读取一帧视频,ReadData(vfile里读取read_size个字节,并保存在缓冲区cur_buf里,并且文件的当前读写位置向后移,,读Y.U.V三个通道,最终读取的数据保存在缓冲区p_Vid->buf))p_Vid->buf2img(pImage[0],p_Vid->buf, source->width[0], source->height[0], output->width[0],output->height[0], symbol_size_in_bytes, bit_scale)(将从文件读到缓冲区p_Vid->buf的内容转存到图像源结构pImage[0]中,利用memcpy函数将buf中的内容保存到pImage[0]起始的地址中,,pImage[0]Y通道,另外pImage[1]pImage[2]UV两个通道 

 p_Vid->imgData0.frm_data)  ###

如果为第二路视频

file_read = read_one_frame(p_Vid, &p_Inp->input_file2, p_Vid->frm_no_in_file,p_Inp->infile_header, &p_Inp->source, &p_Inp->output,p_Vid->imgData0.frm_data);  ——————    ###执行过程同第一路###

 

2>pad_borders(....);  如果帧大小不是宏块大小的整数倍,就扩充帧使之为宏块大小的整数倍。

 

5process_image(p_Vid, p_Inp);

   利用memcpy函数将imgIn->frm_data[0][0]起始的数据保存到imgOut->

frm_data[0][0]起始的内存里,其实就是利用输入的数据对输出数据进行初始化。imgOut->frm_data[0][0],imgOut->frm_data[1][0],imgOut->frm_data[2][0]YUV三个通道的数据

6pad_borders(....);

7、如果为第一路视频,p_Vid->field_pic_ptr =p_Vid->field_pic1;

   如果为第二路视频,p_Vid->field_pic_ptr =p_Vid->field_pic2;

8wpxInitWPXPasses(p_Vid, p_Inp);

   初始化WPR编码通道。   初始化参考帧,算法。

9、根据是帧编码还是场编码来确定是执行下面两个函数的哪一个?(这里为帧编码)

perform_encode_field(p_Vid);  or   perform_encode_frame(p_Vid);

上面两个函数也很重要,编码的主要函数。以帧编码为例(下面有详细解析)

10write_frame_picture(p_Vid);

写编码帧。

11、如果为双目,则第二路视频会使用第一路视频的anchor_pic_lag的值。

12free_slice_data(p_Vid);

释放所有片的内存和数据结构。

13compute_distortion(p_Vid,&p_Vid->imgData);

计算失真。

14store_coded_picture(p_Vid->p_Dpb_layer[p_Vid->view_id]);

保存编码图像。

15gettime(&end_time);

结束编码统计时间。

16、第一帧编码完毕,开始输出编码第一帧的各种信息。

17、刷新输出数据。

18update_bitcounter_stats(p_Vid); 更新码字统计器。

19update_idr_order_stats(p_Vid);

至此,encode_one_frame结束。

 

帧编码——perform_encode_frame(p_Vid):

    如果if((p_Vid->type == B_SLICE || p_Vid->type == P_SLICE ||p_Vid->type==I_SLICE) && p_Inp->RDPictureDecision),

I帧、P帧并且满足RDPictureDecision则执行

frame_picture_mp (p_Vid, p_Inp); //同样很重要的函数。这里执行这一个

否则,执行

frame_picture (p_Vid, p_Vid->frame_pic[0], &p_Vid->imgData,0);

      p_Vid->p_frame_pic =p_Vid->frame_pic[0];//编码中重要的函数

 

frame_picture_mp (p_Vid, p_Inp); 

   p_Vid->type == I_SLICE,则执行frame_picture_mp_i_slice(p_Vid, p_Inp)

(

 frame_picture——编码一帧图像(更新MV限制);( 初始化权重参数功能;  重设WP准备并且分配内存给编码帧图像结构;code_a_picture(p_Vid, frame);编码一幅图像,主要编码函数calc_picture_bits(frame)——计算当前picture的字节数; find_distortion (p_Vid, imgData);寻找YUV三个分量的distortion

store_coding_and_rc_info(p_Vid,&coding_info);  (保存编码信息)

frame_picture_mp_exit(p_Vid,&coding_info);(重设编码状态信息)

Frame_picture_mp_i_slice()结束。

 )

p_Vid->type == P_SLICE,则执行frame_picture_mp_p_slice(p_Vid, p_Inp)

(

 frame_picture——编码一帧图像(更新MV限制);( 初始化权重参数功能;  重设WP准备并且分配内存给编码帧图像结构;code_a_picture(p_Vid, frame);编码一幅图像,主要编码函数 calc_picture_bits(frame)——计算当前picture的字节数; find_distortion (p_Vid, imgData);寻找YUV三个分量的distortion

store_coding_and_rc_info(p_Vid,&coding_info);  (保存编码信息)

frame_picture_mp_exit(p_Vid,&coding_info);(重设编码状态信息)

Frame_picture_mp_i_slice()结束。

 )——frame_picture_mp()结束。

——frame_picture_mp()结束。

 

——perform_encode_frame(p_Vid)结束。

 

 

 

code_a_picture(.....)——code_a_plane(p_Vid, p_Inp)(

1.FmoInit();//FMO initialization:Generatesp_Vid->MapUnitToSliceGroupMap and p_Vid->MBAmap.

2.FmoStartPicture (p_Vid);  //在每一幅图像开始初始化FMO  3.CalculateQuant4x4Param (p_Vid);CalculateOffset4x4Param(p_Vid);  //在帧这一级上计算4*4的量化参数和量化偏移参数   如果8*8模式允许执行,则4.CalculateQuant8x8Param(p_Vid); CalculateOffset8x8Param(p_Vid);  //和上一步含义类似

5.reset_pic_bin_count(p_Vid)  

6.在所有宏块中循环,一帧中所有宏块未编码完,就一直循环

while (NumberOfCodedMBs < p_Vid->PicSizeInMbs)

  encode_one_slice 编码一片

7.FmoSetLastMacroblockInSlice (p_Vid, p_Vid->current_mb_nr); 

8.接着对下一片的处理 p_Vid->current_slice_nr++; p_Vid->p_Stats->bit_slice =0; 9.6.步当前片组的所有片的循环完成 SliceGroup++——处理下一个片组10.FmoEndPicture ();  结束片模块。

11.DeblockFrame(p_Vid,p_Vid->enc_picture->imgY,p_Vid->enc_picture->imgUV);//去块滤波

12.code_a_plane()结束。

 )——code_a_picture结束。

 

 

encode_one_slice()

[1].CurrentMbAddr = FmoGetFirstMacroblockInSlice(p_Vid, SliceGroupId);

得到当前片中的第一个宏块。

[2].init_slice (p_Vid, &currSlice, CurrentMbAddr);

初始化当前片的参数,为当前图像结构中的待编码片分配内存。

[3].init_bipred_enabled(p_Vid);

初始化双向预测时的参数。

[4].init_quant_4x4(currSlice);

初始化4*4量化矩阵。

[5].init_quant_8x8(currSlice);

初始化8*8量化矩阵。

[6].init_quant_Chroma(currSlice);

初始化色度值量化矩阵。

[7].currSlice->set_lagrangian_multipliers(currSlice);

设置拉格朗日乘子。

[8].SetCtxModelNumber (currSlice);

设置语义模型(??)。

[9].len = start_slice(currSlice, cur_stats);

生成片头文件,返回头文件需要的字节数。为当前的slice分配内存,并且自增slice(Picture->no_slices)。

[10].while (end_of_slice == FALSE)    //在宏块之间循环

[11].start_macroblock(currSlice,  &currMB, CurrentMbAddr, FALSE);

初始化当前的宏块。设置当前编码宏块的下一宏块的坐标。根据亮度的QP和位深度更新色度QP。去块滤波的参数。

[12].currSlice->encode_one_macroblock(currMB);  //编码宏块主要的函数

[13].end_encode_one_macroblock(currMB);

编码完一个宏块后,更新宏块参数。

   1>.update_qp_cbp(currMB);

    更新QP参数值(在SKIP MBsor MBAFF情况下)。

   2>.更新min_rdcost  min_dcost   min_rate

[14].write_macroblock (currMB, 1);

将所选的句法元素传输到NAL(网络自适应层)。

   1>.初始并更新帧内宏块的个数。

   2>.写一个宏块。currSlice->write_MB_layer(currMB, 0, &i);

         写宏块类型se.value1 = MBType2Value(currMB);——将宏块类型转换为编码值。

         currSlice->writeMB_typeInfo (currMB,&se, dataPart);——算术编码一个给定宏块的宏块类型信息。

         currSlice->writeMB_transform_size(currMB,&se, dataPart);算术编码宏块的帧内预测大小的标志信息。

         no_bits += writeIntraModes(currMB);编码帧内预测模式所用的字节数。

         no_bits += write_chroma_intra_pred_mode(currMB);编码色度帧内预测模式所用的字节数。

         *coeff_rate = write_CBP_and_Dquant(currMB);编码当前宏块的CBP、量化值。

         *coeff_rate += currSlice->writeCoeff16x16(currMB, PLANE_Y);写16x16宏块的系数。

   3>.设置一共的字节统计器。

   4>.记录所有MB的总数目。

[15].end_macroblock (currMB, &end_of_slice,&recode_macroblock);

根据所用的片模式停止对当前宏块的处理。

[16].如果对当前宏块的最后处理已经完成,则

将去找当前宏块的下一个宏块的地址FmoGetNextMBNr (p_Vid, CurrentMbAddr);如果当前片已经处理完毕,则设置end_of_slice=TRUE;如果当前片还未完,则NumberOfCodedMBs++,next_macroblock (currMB)(更新下一个宏块的坐标和统计参数)。

如果对当前宏块的最后处理还未完成,则

找到并记录该宏块。

[17].第[10]步在当前片的所有宏块之间循环结束。

[18].终止当前片,返回已编码的宏块数。


 

 

encode_one_macroblock (currMB)函数解析

void encode_one_macroblock_high(Macroblock *currMB)。该函数针对帧内还是帧间宏块进行所有模式下的编码,得到率失真代价最小的一种模式,来作为该块的编码模式。

1、init_md_best(&md_best);

初始化最佳的模式信息(最佳块编码模式,最小代价,最小率失真代价,最小码率,最优模式等等)。

最优参考帧。。

2、intra |= RandomIntra (p_Vid, currMB->mbAddrX);

判断是帧内还是帧间?

3、init_enc_mb_params(currMB, &enc_mb, intra);

初始化当前宏块的编码参数(设置帧间搜索范围,各编码模式是否有效,设置并保存拉格朗日参数,设置默认的帧内色度预测模式)。

4、currSlice->store_coding_state (currMB,currSlice->p_RDO->cs_cm);

保存CABAC编码状态。

如果为帧间,执行下列步骤(5-8)// if (!intra)--帧间  else--帧内

5、如果SKIP模式有效,则

    FindSkipModeMotionVector(currMB);   寻找SKIP模式下的运动矢量

6、计算16x16、16x8、8x16三种帧间预测模式下的运动估计。

for (mode = 1; mode < 4; mode++)

  后面帧间运动估计(帧间预测)部分详细介绍。

7、如果P8x8模式有效,则执行P8x8模式的帧内预测。

      ResetRD8x8Data(p_Vid,p_RDO->tr8x8);  初始化8x8块的rd_data数据块。

      for (block = 0;block < 4; block++)  将一个宏块分为4个8x8的子宏块。

        submacroblock_mode_decision(...);选择8x8下的最优模式决定。

        在P8x8模式预测的几种预测方法中循环,得最优的预测,和该预测下的运动矢量等信息。

    后面帧间运动估计(帧间预测)部分详细介绍。

8、执行8x8、8x4、4x8、4x4下的帧间预测模式下的运动估计。

    后面帧间运动估计(帧间预测)部分详细介绍。

如果为帧内,执行下列步骤(9)// if (!intra)--帧间  else--帧内

    9、min_cost =DISTBLK_MAX;  初始化min_cost。

 

 

帧内、帧间都要执行

10、set_chroma_pred_mode(currMB, enc_mb, mb_available,chroma_pred_mode_range);

设置色度预测模式。(1.计算当前宏块所有的帧内色度预测模式,得预测值[DC预测、垂直预测、水平预测、平面预测],UV两个通道;2.对当前宏块的色度预测使用RDcost方法来决定最优预测模式,当前宏块的UV两个通道的色度值分别保存在p_Vid->pImgOrg[1]和p_Vid->pImgOrg[2]里面;3、保存色度预测模式的范围chroma_pred_mode_range[0]和[1],应该是UV两个通道各自最佳的预测模式(??)。)

 

11、choosebest macroblock mode

 for (currMB->c_ipred_mode =chroma_pred_mode_range[0];currMB->c_ipred_mode<=chroma_pred_mode_range[1];currMB->c_ipred_mode++)

根据色度预测决定的范围,在所有的亮度预测模式下循环,得亮度块编码的最优的编码模式和相应的RDcost等等信息。

12、for(index=0; index < max_index; index++)

在所有的宏块编码模式中循环,得最优的宏块编码模式。

Index=0-8,9中编码模式。这9种模式与index的对应关系为:

(1)index=0  mode=0PSKIP模式——Skip mode

(2)index=1 mode=1   P16*16  ——帧间16*16

(3)index=2 mode=2   P16*8   ——帧间16*8

(4)index=3 mode=3   P8*16   ——帧间8*16

(5)index=4 mode=8   P8*8    ——帧间8*8

(6)index=5 mode=10  I16MB  ——帧内16*16

(7)index=6 mode=9   I4MB   ——帧内4*4

(8)index=7 mode=13  I8MB   ——帧内8*8

(9)index=8 mode=14  IPCM   ——IPCM mode

对于帧间编码模式,index=0-8都这几种都执行。(详细介绍见下面帧间编码部分)

对于帧内编码模式,index=0-4时都不执行,

<1>.index=5时(帧内16x16),执行compute_mode_RD_cost(currMB,&enc_mb, (short) mode, &inter_skip);

compute_mode_RD_cost()——执行16*16帧内编码,并计算RDcost,与当前最小的RDcost比较,更新最小代价和最优编码模式等等信息。

(1).currSlice->set_modes_and_refs_for_blocks(currMB, (short) mode);

设置当前块的编码模式为该循环下的模式(即帧内16*16模式),并且设置相应模式下的宏块的参考块。

(2).bslice_16x16_termination_control(p_Inp,p_Vid->b8x8info, &ctr16x16, mode, bslice);

对B帧的P16x16模式,更新预测方向以便能够检查所有的预测方向。

(3).CheckPredictionParams(currMB,p_Vid->b8x8info, mode)

检查预测参数是否在有效的范围内,如果有效,则执行预测(下一步),无效就跳出。

(4).RDCost_for_macroblocks(currMB, enc_mb->lambda_mdfp, mode)

计算该循环下对应模式的RDcost。

  [1].set_modes_and_refs_for_blocks();

  设置该宏块的预测模式和该模式下宏块的参考帧。

 [2].set_motion_vectors_mb();

  设置当前块的运动矢量,如果为I帧,则执行空函数。

 [3].mode_decision_for_I16x16_MB();

  进行量化、编码等步骤得到编码系数,重建值,CBP等等。

 [4].currSlice->chroma_residual_coding (currMB);

  色度残差编码。UV两通道。(设置模式和参考帧,进行预测,计算残差,对残差进行整数DCT变换、DC系数Hadamard变换、DC系数量化、反Hadamard变换、AC系数量化、反DCT变换、重建)

 [5].计算失真Distortion;

 distortion = currSlice->getDistortion(currMB);  //分别计算亮度和色度分量的失真。

 [6].保存编码状态;

 currSlice->store_coding_state(currMB, currSlice->p_RDO->cs_cm);

 [7].计算码率rate;

 rate = currSlice->write_MB_layer(currMB, 1, &coeff_rate);

 [8].重设编码状态;

 currSlice->reset_coding_state(currMB, currSlice->p_RDO->cs_cm);

 [9].计算rdcost;

  rdcost = distortion + (rate>0?(weight_cost(lambda, rate)): (weight_cost(lambda, 1)/2));

 [10].更新最小Cost;

 currMB->min_rdcost = rdcost;

 currMB->min_dcost = distortion;

 currMB->min_rate =weight_cost(lambda, coeff_rate);

 currMB->min_bits = rate;

(5).store_macroblock_parameters(currMB, mode);

保存宏块参数。保存最优模式,预测值,重建值等等信息。

(6).terminate_trans = transform_termination_control(currMB, mode);

<2>.index=6时(帧内4x4),执行compute_mode_RD_cost(currMB,&enc_mb, (short) mode, &inter_skip);

compute_mode_RD_cost()——执行4x4帧内编码,并计算RDcost,与当前最小的RDcost比较,更新最小代价和最优编码模式等等信息。

(1).currSlice->set_modes_and_refs_for_blocks(currMB, (short) mode);

设置当前块的编码模式为该循环下的模式(即帧内4x4模式),并且设置相应模式下的宏块的参考块。

(2).bslice_16x16_termination_control(p_Inp,p_Vid->b8x8info, &ctr16x16, mode, bslice);

对B帧的P16x16模式,更新预测方向以便能够检查所有的预测方向。

(3).CheckPredictionParams(currMB,p_Vid->b8x8info, mode)

检查预测参数是否在有效的范围内,如果有效,则执行预测(下一步),无效就跳出。

(4).RDCost_for_macroblocks(currMB, enc_mb->lambda_mdfp, mode)

计算该循环下对应模式的RDcost。

  [1].set_modes_and_refs_for_blocks();

  设置该宏块的预测模式和该模式下宏块的参考帧。

 [2].set_motion_vectors_mb();

  设置当前块的运动矢量,如果为I帧,则执行空函数。

 [3].mode_decision_for_I4x4_MB();

  进行量化、编码等步骤得到编码系数,重建值,CBP等等。

 [4].currSlice->chroma_residual_coding (currMB);

  色度残差编码。UV两通道。(设置模式和参考帧,进行预测,计算残差,对残差进行整数DCT变换、DC系数Hadamard变换、DC系数量化、反Hadamard变换、AC系数量化、反DCT变换、重建)

 [5].计算失真Distortion;

 distortion = currSlice->getDistortion(currMB);  //分别计算亮度和色度分量的失真。

 [6].保存编码状态;

 currSlice->store_coding_state(currMB, currSlice->p_RDO->cs_cm);

 [7].计算码率rate;

 rate = currSlice->write_MB_layer(currMB, 1, &coeff_rate);

 [8].重设编码状态;

 currSlice->reset_coding_state(currMB, currSlice->p_RDO->cs_cm);

 [9].计算rdcost;

  rdcost = distortion + (rate>0?(weight_cost(lambda, rate)): (weight_cost(lambda, 1)/2));

 [10].更新最小Cost;

 currMB->min_rdcost = rdcost;

 currMB->min_dcost = distortion;

 currMB->min_rate = weight_cost(lambda,coeff_rate);

 currMB->min_bits = rate;

(6).store_macroblock_parameters(currMB, mode);

保存宏块参数。保存最优模式,预测值,重建值等等信息。

(7).terminate_trans = transform_termination_control(currMB, mode);

 

    9restore_nz_coeff(currMB);

保存非零系数的值。

   10、设置最后的宏块参数。

       (1).update_qp_cbp_tmp(currMB,p_RDO->cbp);

       (2).currSlice->set_stored_mb_parameters (currMB);

       设置已保存宏块的参数。 保存重建值,系数和CBP,宏块类型,变换大小的标志,参考帧,帧内预测模式,MV等等。

     至此,encode_one_macroblock()函数执行完毕。

 

 

 

帧间运动估计(帧内预测)的具体执行过程

116x1616x88x16三种模式——在一个循环里面

for (mode = 1; mode < 4; mode++)

 mode=1——16x16

 mode=2——16x8

 mode=3——8x16

以下函数,这三种模式执行过程都一样,只是16x16的循环一遍(分为一个16x16块),另外两种模式循环执行两遍(分为两个8x8块)。

[1].update_lambda_costs(currMB, &enc_mb, lambda_mf); 更新lambda_mf;

[2].PartitionMotionSearch(currMB, mode, block, lambda_mf);  运动搜索函数

  1).gettime( &me_time_start );   开始运动估计计时。

  2).设置搜索块的大小和搜索步长,参考帧数目,mv_block,m_cost。

  3).init_mv_block(currMB, &mv_block, (short)blocktype, list, (char) ref, bx, by);  初始化MV块。  (更新mv块的位置信息,初始化参考帧,运动矢量WP参数)

  4).设置全像素搜索使用的算法。

  5).get_original_block(p_Vid, &mv_block);   得原始块数据。

  6).在参考帧中循环,进行运动搜索。

  for (list = 0; list < numlists; list++)  //参考列表个数,如果为B帧,有两个参考列表;其余情况,一个参考帧列表 numlists=1 or 2

  for (ref=0; ref < currSlice->listXsize[list+list_offset]; ref++)//在每一个参考列表里循环时,参考帧个数由currSlice->listXsize[list+list_offset]决定,由此参数决定每一帧进行运动搜索时的参考帧的个数。

   若当前配置文件里NumberReferenceFrames = 2,则双目之间参考帧的关系为下图。每一路参考帧为其前面编码的前两帧,另外第二路视频还有多加一路来自第一路相应位置的参考帧。

  m_cost = &p_Vid->motion_cost[blocktype][list][ref][block8x8];

  7).get_search_range(&mv_block, p_Inp, ref,blocktype);

  设置搜索范围。

  8).在所有宏块间循环,得到与当前块m_cost最小的宏块。

 *m_cost = BlockMotionSearch (currMB,&mv_block, bx<<2, by<<2, lambda_factor);    (得当前块的领域,准备ME参数,得到初始宏块值,得MV预测器,由此得预测值,整像素搜索,计算全像素搜索下的min_mcost,亚像素搜索,计算亚像素搜索下的min_mcost,设置MV,返回最小cost)

  9).设置运动矢量和参考帧。

  set_me_parameters(motion,&currSlice->all_mv[list][ref][blocktype][by][bx], list, (char) ref,step_h, step_v, pic_block_y, pic_block_x);

 10).释放存储MV的缓存。

 11).gettime(&me_time_end);  结束ME的统计时间。由timediff()计算用于ME的时间。

[3].得cost和参考帧。

list_prediction_cost(currMB, LIST_0, block, mode, &enc_mb,bmcost, best.ref);

[4].assign_enc_picture_params(currMB, mode, &best, 2 * block);

保存最优mv预测的信息。

[5].set_block8x8_info(b8x8info, mode, block, &best);

设置参考帧和方向参数。

[6].设置参考帧和运动矢量(if (mode>0&&block==0))

currSlice->set_ref_and_motion_vectors(currMB, motion, &best, block);

[7].保存这三种模式下的最优模式,最小cost。

  md_best.mode = (byte) mode;

  md_best.cost = cost;

  currMB->best_mode = (short) mode;

  min_cost  = cost;

[8].至此这三种帧间预测模式搜索完毕。

2、P8x8模式

      [1].ResetRD8x8Data(p_Vid, p_RDO->tr8x8);  初始化8x8块的rd_data数据块。

      [2].for (block =0; block < 4; block++)  将一个宏块分为4个8x8的子宏块。

       submacroblock_mode_decision(...);选择8x8下的最优模式决定。

        在P8x8模式预测的几种预测方法中循环,得最优的预测,和该预测下的运动矢量等信息。submacroblock_mode_decision_p_slice(...);

      1).设置坐标,初始化8x8预测下该块的预测模式和信息。

      2).保存编码状态。currSlice->store_coding_state(currMB,currSlice->p_RDO->cs_tmp);

      3).在一个宏块的4个8x8块中循环,分别计算每一块的最优预测模式。

      4).SubPartitionMotionSearch(currMB, mode, block, lambda_mf); 对子宏块的运动搜索。

          <1>.gettime(&me_time_start );  开始运动估计计时;

          <2>.设置搜索块的大小和搜索步长,参考帧数目,mv_block,m_cost。

          <3>.全像素搜索。

              currMB->IntPelME =EPZS_motion_estimation;

              初始化mv_block.   init_mv_block(...);

              得原始块。   get_original_block(p_Vid,&mv_block);

              在参考帧中循环,寻找最佳的预测和匹配块。  执行过程大概同16x16、16x8、8x16的过程。

      5).得到cost和参考帧,并保存。

      6).计算rdcost。

      rdcost =rdcost_for_8x8blocks (currMB, dataTr, &cnt_nonz, &curr_cbp_blk,enc_mb->lambda_mdfp, block, (short) mode, &best, min_rdcost);

      7).保存最优模式和最小rdcost等值。

        min_cost8x8              = *cost;

        min_rdcost               = rdcost;       

        *partition               = best;

        partition->mode          = (char) mode;

        currMB->b8x8[block].mode = (char)mode;

      8).保存MV值,参考帧,重设编码状态。

      9).至此一个8x8块的mv搜索完成。

      set_subblock8x8_info(b8x8info,P8x8, block, p_RDO->tr8x8); 设置P8x8模式下的8x8块的模式信息。

3、8x88x44x84x4四种模式

     [1].for (block =0; block < 4; block++)  将一个宏块分为4个8x8的子宏块。

  submacroblock_mode_decision()选择8x8下的最优模式决定。

   在8x8块里,根据这四种预测模式来预测,得最优的预测模式,和该预测下的运动矢量等信息。submacroblock_mode_decision_p_slice(...);

      1).设置坐标,初始化8x8预测下该块的预测模式和信息。

      2).保存编码状态。currSlice->store_coding_state(currMB,currSlice->p_RDO->cs_tmp);

      3).对8x8块中的这4种预测模式,计算该块中的最优的预测模式。

      4).SubPartitionMotionSearch(currMB, mode, block, lambda_mf); 对子宏块的运动搜索。

          <1>.gettime(&me_time_start );  开始运动估计计时;

          <2>.设置搜索块的大小和搜索步长,参考帧数目,mv_block,m_cost。

          <3>.全像素搜索。

              currMB->IntPelME =EPZS_motion_estimation;

              初始化mv_block.   init_mv_block(...);

              得原始块。   get_original_block(p_Vid,&mv_block);

              在参考帧中循环,寻找最佳的预测和匹配块。  执行过程大概同16x16、16x8、8x16的过程。

      5).得到cost和参考帧,运动矢量,并保存。

      6).计算rdcost。

      rdcost =rdcost_for_8x8blocks (currMB, dataTr, &cnt_nonz, &curr_cbp_blk,enc_mb->lambda_mdfp, block, (short) mode, &best, min_rdcost);

      7).保存最优模式和最小rdcost等值。

        min_cost8x8              = *cost;

        min_rdcost               = rdcost;       

        *partition               = best;

        partition->mode          = (char) mode;

        currMB->b8x8[block].mode = (char)mode;

      8).保存MV值,参考帧,重设编码状态。

      9).至此一个8x8块的mv搜索完成。

      set_subblock8x8_info(b8x8info,P8x8, block, p_RDO->tr8x8); 设置P8x8模式下的8x8块的模式信息。


 

 

帧内预测模式的具体执行过程

1、I16MB——mode_decision_for_I16x16_MB()

   [1].find_best_mode_I16x16_MB(currMB, lambda, DISTBLK_MAX)

      return (int) currSlice->find_sad_16x16 (currMB);

         找到16x16帧内预测模式中最优的预测模式。

         1>.set_intrapred_16x16(currMB, PLANE_Y, &left_avail,&up_avail, &left_up_avail);

         设置16x16预测模式,得预测值:

         PredPel[i] = (imgpel)p_Vid->dc_pred_value;

         2>.在16x16所有的预测模式中循环;

           如果该宏块周围有相邻的宏块可以来预测,则生成16x16的帧内预测块: get_intrapred_16x16(currMB, PLANE_Y, k, left_avail, up_avail);

           然后计算当前块与预测块的残差,保存在i16blk4x4[][][][]里,对残差进行Hadamard变换(?变换了两次??),计算变换后所有系数的绝对值之和——distI16x16(currMB,p_Vid->pCurImg, curr_mpr_16x16[k], best_intra_sad2);根据每种预测模式的current_intra_sad_2,选择最小的最为16x16预测模式下的最优预测模式。

[2].return currMB->residual_transform_quant_luma_16x16 (currMB,PLANE_Y);

选择出16x16下的最优预测模式后,对该模式下残差值进行整数 DCT变换、直流系数Hadamard变换、直流系数量化、直流系数反Hadamard变换、直流系数反量化、AC系数量化,AC系数IDCT变换。然后得到重建值sample_reconstruct(&img_enc[currMB->pix_y],curr_mpr_16x16[new_intra_mode], currSlice->tblk16x

16, 0,currMB->pix_x, 16, 16, max_imgpel_value, DQ_BITS)。

 

 

2、I4MB——mode_decision_for_I4x4_MB()

[1].for (*cost=0, b8=0; b8<4;b8++)

将16x16宏块分为4个8x8的子宏块,分别计算这4个8x8块的cost,然后相加,作为该16x16宏块的cost。

Mode_Decision_for_IntraSubMBlocks (currMB, b8, lambda, &cost8x8,non_zero);计算一个8x8块的最优预测模式。

[2].for (b4=0; b4<4; b4++)

将8x8块分为4个4x4的子宏块,分别计算这4个4x4块的cost,然后相加,作为该8x8块的cost。

mode_decision_for_I4x4_blocks (currMB, b8, b4, lambda, &cost4x4);

[3].mode_decision_for_I4x4_blocks_JM_High()

4x4块的模式决定。

求领域,得4x4块的预测值(9种预测方式下的预测值),在所有4x4的9种预测模式下循环,检查这9种模式中各个模式是否为有效模式,如果为有效模式,生成4x4的预测块,计算预测块与原始块的残差,然后计算该块的rdcost,使用的函数为:rdcost_for_4x4_intra_blocks()

      整数DCT变换、量化、反量化、反DCT变换,重建,并且计算该4x4块的失真(distortion):

distortion += compute_SSE4x4(&p_Vid->pCurImg[pic_opix_y],&p_Vid->

enc_picture->imgY[pic_pix_y],pic_pix_x, pic_pix_x);(如果当前失真已经大于最小失真,则跳出,不再计算后面的部分),计算该4x4块相应预测模式下编码的码率currSlice->writeIntraPredMode(&se, dataPart);rate = se.len;加上编码亮度系数分量的码率rate  += writeCoeff4x4_CABAC (currMB,PLANE_Y, b8, b4, 1)。计算rdcostrdcost = distortion + weighted_cost(lambda,rate)。 重设编码状态,返回rdcost。

如果rdcost比min_cost小,则保存系数,保存重建值,保存该预测模式为最佳模式。

在9种循环模式下执行完后,设置帧内最佳模式预测,保存编码系数,保存重建值和预测值。

[4].*cost += cost4x4;

计算完4个4x4块的预测编码、模式决定后,计算总的cost。

[5].*cost += cost8x8;

计算完4个8x8块的预测编码、模式决定后,计算总的cost。

 

 

编码最优模式选择的具体执行过程

for (index=0; index <max_index; index++)

在所有的宏块编码模式中循环,得最优的宏块编码模式。

Index=0-8,9中编码模式。这9种模式与index的对应关系为:

(10)index=0 mode=0   PSKIP模式——Skip mode

(11)index=1 mode=1   P16*16  ——帧间16*16

(12)index=2 mode=2   P16*8   ——帧间16*8

(13)index=3 mode=3   P8*16   ——帧间8*16

(14)index=4 mode=8   P8*8    ——帧间8*8

(15)index=5 mode=10  I16MB  ——帧内16*16

(16)index=6 mode=9   I4MB   ——帧内4*4

(17)index=7 mode=13  I8MB   ——帧内8*8

(18)index=8 mode=14  IPCM   ——IPCM mode

一、帧间

帧间预测,上述所有编码模式都执行,在所有编码模式中寻找最优的模式。

<1>.index=0时(PSKIP),执行compute_mode_RD_cost(currMB,&enc_mb, (short) mode, &inter_skip);

compute_mode_RD_cost()——执行Skip mode模式下的编码,并计算RDcost,与当前最小的RDcost比较,更新最小代价和最优编码模式等等信息。

(1).currSlice->set_modes_and_refs_for_blocks(currMB, (short) mode);

设置当前块的编码模式为该循环下的模式(即Skip mode模式),并且设置相应模式下的宏块的参考块。

(2).bslice_16x16_termination_control(p_Inp,p_Vid->b8x8info, &ctr16x16, mode, bslice);

对B帧的P16x16模式,更新预测方向以便能够检查所有的预测方向。

(3).CheckPredictionParams(currMB,p_Vid->b8x8info, mode)

检查预测参数是否在有效的范围内,如果有效,则执行预测(下一步),无效就跳出。

(4).RDCost_for_macroblocks(currMB, enc_mb->lambda_mdfp, mode)

计算该循环下对应模式的RDcost。

  [1].set_modes_and_refs_for_blocks();

  设置该宏块的预测模式和该模式下宏块的参考帧。

 [2].set_motion_vectors_mb();

  设置当前块的运动矢量。

 [3].currSlice->luma_residual_coding(currMB);

  设置参考帧,亮度预测,由预测值得残差,保存预测值。

 [4].currSlice->chroma_residual_coding (currMB);

  色度残差编码。UV两通道。(设置模式和参考帧,进行预测,计算残差,对残差进行整数DCT变换、DC系数Hadamard变换、DC系数量化、反Hadamard变换、AC系数量化、反DCT变换、重建)

 [5].计算失真Distortion;

 distortion = currSlice->getDistortion(currMB); //分别计算亮度和色度分量的失真。

 [6].保存编码状态;

 currSlice->store_coding_state(currMB, currSlice->p_RDO->cs_cm);

 [7].计算码率rate;

 rate = currSlice->write_MB_layer(currMB, 1, &coeff_rate);

 [8].重设编码状态;

 currSlice->reset_coding_state(currMB, currSlice->p_RDO->cs_cm);

 [9].计算rdcost;

  rdcost = distortion + (rate>0?(weight_cost(lambda, rate)): (weight_cost(lambda, 1)/2));

 [10].更新最小Cost;

 currMB->min_rdcost = rdcost;

 currMB->min_dcost = distortion;

 currMB->min_rate =weight_cost(lambda, coeff_rate);

 currMB->min_bits = rate;

(7).store_macroblock_parameters(currMB, mode);

保存宏块参数。保存最优模式,预测值,重建值等等信息。

(6).terminate_trans = transform_termination_control(currMB, mode);

<2>.index=1,,2,3时(P16x16,P16x8,P8x16),执行compute_mode_RD_cost(currMB,&enc_mb, (short) mode, &inter_skip);

compute_mode_RD_cost()——执行P16x16(P16x8,P8x16)模式下的编码,并计算RDcost,与当前最小的RDcost比较,更新最小代价和最优编码模式等等信息。

(1).currSlice->set_modes_and_refs_for_blocks(currMB, (short) mode);

设置当前块的编码模式为该循环下的模式(即P16x16(P16x8,P8x16)模式),并且设置相应模式下的宏块的参考块。

(2).bslice_16x16_termination_control(p_Inp,p_Vid->b8x8info, &ctr16x16, mode, bslice);

对B帧的P16x16模式,更新预测方向以便能够检查所有的预测方向。

(3).CheckPredictionParams(currMB,p_Vid->b8x8info, mode)

检查预测参数是否在有效的范围内,如果有效,则执行预测(下一步),无效就跳出。

(4).RDCost_for_macroblocks(currMB, enc_mb->lambda_mdfp, mode)

计算该循环下对应模式的RDcost。

  [1].set_modes_and_refs_for_blocks();

  设置该宏块的预测模式和该模式下宏块的参考帧。

 [2].set_motion_vectors_mb();

  设置当前块的运动矢量。

 [3].currSlice->luma_residual_coding(currMB);

  设置参考帧,亮度预测,由预测值得残差,对残差进行变换、量化、反量化、反变换、重建,保存重建值。

 [4].currSlice->chroma_residual_coding (currMB);

  色度残差编码。UV两通道。(设置模式和参考帧,进行预测,计算残差,对残差进行整数DCT变换、DC系数Hadamard变换、DC系数量化、反Hadamard变换、AC系数量化、反DCT变换、重建)

 [5].计算失真Distortion;

 distortion = currSlice->getDistortion(currMB);  //分别计算亮度和色度分量的失真。

 [6].保存编码状态;

 currSlice->store_coding_state (currMB,currSlice->p_RDO->cs_cm);

 [7].计算码率rate;

 rate = currSlice->write_MB_layer(currMB, 1, &coeff_rate);

 [8].重设编码状态;

 currSlice->reset_coding_state(currMB, currSlice->p_RDO->cs_cm);

 [9].计算rdcost;

  rdcost = distortion + (rate>0? (weight_cost(lambda,rate)): (weight_cost(lambda, 1)/2));

 [10].更新最小Cost;

 currMB->min_rdcost = rdcost;

 currMB->min_dcost = distortion;

 currMB->min_rate =weight_cost(lambda, coeff_rate);

 currMB->min_bits = rate;

(8).store_macroblock_parameters(currMB, mode);

保存宏块参数。保存最优模式,预测值,重建值等等信息。

(6).terminate_trans = transform_termination_control(currMB, mode);

<3>.index=1,2,3时(P16x16,P16x8,P8x16),执行compute_mode_RD_cost(currMB,&enc_mb, (short) mode, &inter_skip);

 


 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值