【HEVC学习与研究】31、HM编码器的基本结构

通过解码器代码的研究,已经对HEVC的编解码技术有了一个初步的认识。现在我们就对照着编码器的代码进一步理解HEVC视频编码算法的各个技术细节。

编码器在整个HM解决方案中的工程名为TAppEncoder,入口点函数位于encmain.cpp文件中:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. int main(int argc, char* argv[])  
  2. {  
  3.     TAppEncTop  cTAppEncTop;  
  4.   
  5.     // print information  
  6.     fprintf( stdout, "\n" );  
  7.     fprintf( stdout, "HM software: Encoder Version [%s]", NV_VERSION );  
  8.     fprintf( stdout, NVM_ONOS );  
  9.     fprintf( stdout, NVM_COMPILEDBY );  
  10.     fprintf( stdout, NVM_BITS );  
  11.     fprintf( stdout, "\n" );  
  12.   
  13.     // create application encoder class  
  14.     cTAppEncTop.create();  
  15.   
  16.     // parse configuration  
  17.     try  
  18.     {  
  19.         if(!cTAppEncTop.parseCfg( argc, argv ))  
  20.         {  
  21.             cTAppEncTop.destroy();  
  22.             return 1;  
  23.         }  
  24.     }  
  25.     catch (po::ParseFailure& e)  
  26.     {  
  27.         cerr << "Error parsing option \""<< e.arg <<"\" with argument \""<< e.val <<"\"." << endl;  
  28.         return 1;  
  29.     }  
  30.   
  31.     // starting time  
  32.     double dResult;  
  33.     long lBefore = clock();  
  34.   
  35.     // call encoding function  
  36.     cTAppEncTop.encode();  
  37.   
  38.     // ending time  
  39.     dResult = (double)(clock()-lBefore) / CLOCKS_PER_SEC;  
  40.     printf("\n Total Time: %12.3f sec.\n", dResult);  
  41.   
  42.     // destroy application encoder class  
  43.     cTAppEncTop.destroy();  
  44.   
  45.     return 0;  
  46. }  

可以很清楚地看到,整个main函数非常简洁清晰,主要可以分为几大部分,分别是输入软件信息、创建编码器类的实例、解析配置文件、获取开始时间、编码数据、计算耗费时间和销毁编码器类的实例几大部分。我们主要关心的编码过程仅通过调用编码器实例的一个方法实现:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. // call encoding function  
  2. cTAppEncTop.encode();  
该函数的实现如下:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. Void TAppEncTop::encode()  
  2. {  
  3.     fstream bitstreamFile(m_pchBitstreamFile, fstream::binary | fstream::out);  
  4.     if (!bitstreamFile)  
  5.     {  
  6.         fprintf(stderr, "\nfailed to open bitstream file `%s' for writing\n", m_pchBitstreamFile);  
  7.         exit(EXIT_FAILURE);  
  8.     }  
  9.   
  10.     TComPicYuv*       pcPicYuvOrg = new TComPicYuv;  
  11.     TComPicYuv*       pcPicYuvRec = NULL;  
  12.   
  13.     // initialize internal class & member variables  
  14.     xInitLibCfg();  
  15.     xCreateLib();  
  16.     xInitLib();  
  17.   
  18.     // main encoder loop  
  19.     Int   iNumEncoded = 0;  
  20.     Bool  bEos = false;  
  21.   
  22.     list<AccessUnit> outputAccessUnits; ///< list of access units to write out.  is populated by the encoding process  
  23.   
  24.     // allocate original YUV buffer  
  25.     pcPicYuvOrg->create( m_iSourceWidth, m_iSourceHeight, m_uiMaxCUWidth, m_uiMaxCUHeight, m_uiMaxCUDepth );  
  26.   
  27.     while ( !bEos )  
  28.     {  
  29.         // get buffers  
  30.         xGetBuffer(pcPicYuvRec);  
  31.   
  32.         // read input YUV file  
  33.         m_cTVideoIOYuvInputFile.read( pcPicYuvOrg, m_aiPad );  
  34.   
  35.         // increase number of received frames  
  36.         m_iFrameRcvd++;  
  37.   
  38.         bEos = (m_iFrameRcvd == m_framesToBeEncoded);  
  39.   
  40.         Bool flush = 0;  
  41.         // if end of file (which is only detected on a read failure) flush the encoder of any queued pictures  
  42.         if (m_cTVideoIOYuvInputFile.isEof())  
  43.         {  
  44.             flush = true;  
  45.             bEos = true;  
  46.             m_iFrameRcvd--;  
  47.             m_cTEncTop.setFramesToBeEncoded(m_iFrameRcvd);  
  48.         }  
  49.   
  50.         // call encoding function for one frame  
  51.         m_cTEncTop.encode( bEos, flush ? 0 : pcPicYuvOrg, m_cListPicYuvRec, outputAccessUnits, iNumEncoded );  
  52.   
  53.         // write bistream to file if necessary  
  54.         if ( iNumEncoded > 0 )  
  55.         {  
  56.             xWriteOutput(bitstreamFile, iNumEncoded, outputAccessUnits);  
  57.             outputAccessUnits.clear();  
  58.         }  
  59.     }  
  60.   
  61.     m_cTEncTop.printSummary();  
  62.   
  63.     // delete original YUV buffer  
  64.     pcPicYuvOrg->destroy();  
  65.     delete pcPicYuvOrg;  
  66.     pcPicYuvOrg = NULL;  
  67.   
  68.     // delete used buffers in encoder class  
  69.     m_cTEncTop.deletePicBuffer();  
  70.   
  71.     // delete buffers & classes  
  72.     xDeleteBuffer();  
  73.     xDestroyLib();  
  74.   
  75.     printRateSummary();  
  76.   
  77.     return;  
  78. }  

该函数中首先调用pcPicYuvOrg->create( m_iSourceWidth, m_iSourceHeight, m_uiMaxCUWidth, m_uiMaxCUHeight, m_uiMaxCUDepth )分配YUV数据缓存,然后再while循环中逐帧读取YUV数据、设置当前以编码的帧数、编码当前帧、写出码流,随后做其他清理工作。核心功能实现在m_cTEncTop.encode( bEos, flush ? 0 : pcPicYuvOrg, m_cListPicYuvRec, outputAccessUnits, iNumEncoded )函数中。在该函数中调用m_cGOPEncoder.compressGOP(m_iPOCLast, m_iNumPicRcvd, m_cListPic, rcListPicYuvRecOut, accessUnitsOut)进行编码一个GOP的操作。这个函数奇长无比,用了接近1500行代码,看来实现了很多很多很多的功能。这个碉堡了的函数究竟做了些啥事儿呢?这个函数中大部分内容就是在为了编码当前slice做准备,以及编码完成之后一些辅助操作。实际编码过程的操作由以下函数m_pcSliceEncoder->compressSlice( pcPic )实现。

这又是一个碉堡了的函数,占了将近400行……代码就不贴了,会死人的……简单看下好了。

首先还是各种编码的配置,包括配置熵编码器、初始化CU编码器等。在完成了一长串的设置之后,在compressCU函数中实现对一个CU的编码:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. m_pcCuEncoder->compressCU( pcCU );  
具体的细节,且待下文。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值