HEVC函数入门(1)——HM编码器的基本结构
来源:互联网 发布:tcp监听端口检测失败 编辑:程序博客网 时间:2024/06/05 18:24
首先说明这篇主要是记录用(意思是里面很多是我自己的理解,我也是刚入门的新手,肯定有许多不正确的地方,还请轻喷),使用的是HM 16.3 VS2015 当然如果有新手不太懂的话也可以看看。
本文主要参照http://blog.csdn.net/shaqoneal/article/details/36398707
int main(int argc, char* argv[]){ TAppEncTop cTAppEncTop; // print information fprintf( stdout, "\n" ); fprintf( stdout, "HM software: Encoder Version [%s] (including RExt)", NV_VERSION ); fprintf( stdout, NVM_ONOS ); fprintf( stdout, NVM_COMPILEDBY ); fprintf( stdout, NVM_BITS ); fprintf( stdout, "\n\n" ); // create application encoder class cTAppEncTop.create(); // parse configuration try { if(!cTAppEncTop.parseCfg( argc, argv )) { cTAppEncTop.destroy();#if ENVIRONMENT_VARIABLE_DEBUG_AND_TEST EnvVar::printEnvVar();#endif return 1; } } catch (df::program_options_lite::ParseFailure &e) { std::cerr << "Error parsing option \""<< e.arg <<"\" with argument \""<< e.val <<"\"." << std::endl; return 1; }#if PRINT_MACRO_VALUES printMacroSettings();#endif#if ENVIRONMENT_VARIABLE_DEBUG_AND_TEST EnvVar::printEnvVarInUse();#endif // starting time Double dResult; clock_t lBefore = clock(); // call encoding function cTAppEncTop.encode(); // ending time dResult = (Double)(clock()-lBefore) / CLOCKS_PER_SEC; printf("\n Total Time: %12.3f sec.\n", dResult); // destroy application encoder class cTAppEncTop.destroy(); return 0;}//! \}
这里是主函数,在TAppEncoder中的Source Files中,我们现在不关注TAppEncCfg.cpp,先打开encmain.cpp,可以很清楚地看到,整个main函数非常简洁清晰,主要可以分为几大部分,分别是输入软件信息、创建编码器类的实例、解析配置文件、获取开始时间、编码数据、计算耗费时间和销毁编码器类的实例几大部分。
如上所示,从英文中找到call encoding function也就是这里是实际进行编码的地方,右键查看所有引用,下方TAppEncTop::encode XXX406这个是encode函数所在的地方,其内容为
Void TAppEncTop::encode(){ fstream bitstreamFile(m_pchBitstreamFile, fstream::binary | fstream::out); if (!bitstreamFile) { fprintf(stderr, "\nfailed to open bitstream file `%s' for writing\n", m_pchBitstreamFile); exit(EXIT_FAILURE); } TComPicYuv* pcPicYuvOrg = new TComPicYuv; TComPicYuv* pcPicYuvRec = NULL; // initialize internal class & member variables xInitLibCfg(); xCreateLib(); xInitLib(m_isField); printChromaFormat(); // main encoder loop Int iNumEncoded = 0; Bool bEos = false; const InputColourSpaceConversion ipCSC = m_inputColourSpaceConvert; const InputColourSpaceConversion snrCSC = (!m_snrInternalColourSpace) ? m_inputColourSpaceConvert : IPCOLOURSPACE_UNCHANGED; list<AccessUnit> outputAccessUnits; ///< list of access units to write out. is populated by the encoding process TComPicYuv cPicYuvTrueOrg; // allocate original YUV buffer if( m_isField ) { pcPicYuvOrg->create( m_iSourceWidth, m_iSourceHeightOrg, m_chromaFormatIDC, m_uiMaxCUWidth, m_uiMaxCUHeight, m_uiMaxCUDepth ); cPicYuvTrueOrg.create(m_iSourceWidth, m_iSourceHeightOrg, m_chromaFormatIDC, m_uiMaxCUWidth, m_uiMaxCUHeight, m_uiMaxCUDepth); } else { pcPicYuvOrg->create( m_iSourceWidth, m_iSourceHeight, m_chromaFormatIDC, m_uiMaxCUWidth, m_uiMaxCUHeight, m_uiMaxCUDepth ); cPicYuvTrueOrg.create(m_iSourceWidth, m_iSourceHeight, m_chromaFormatIDC, m_uiMaxCUWidth, m_uiMaxCUHeight, m_uiMaxCUDepth); } while ( !bEos ) { // get buffers xGetBuffer(pcPicYuvRec); // read input YUV file m_cTVideoIOYuvInputFile.read( pcPicYuvOrg, &cPicYuvTrueOrg, ipCSC, m_aiPad, m_InputChromaFormatIDC ); // increase number of received frames m_iFrameRcvd++; bEos = (m_isField && (m_iFrameRcvd == (m_framesToBeEncoded >> 1) )) || ( !m_isField && (m_iFrameRcvd == m_framesToBeEncoded) ); Bool flush = 0; // if end of file (which is only detected on a read failure) flush the encoder of any queued pictures if (m_cTVideoIOYuvInputFile.isEof()) { flush = true; bEos = true; m_iFrameRcvd--; m_cTEncTop.setFramesToBeEncoded(m_iFrameRcvd); } // call encoding function for one frame if ( m_isField ) { m_cTEncTop.encode( bEos, flush ? 0 : pcPicYuvOrg, flush ? 0 : &cPicYuvTrueOrg, snrCSC, m_cListPicYuvRec, outputAccessUnits, iNumEncoded, m_isTopFieldFirst ); } else { m_cTEncTop.encode( bEos, flush ? 0 : pcPicYuvOrg, flush ? 0 : &cPicYuvTrueOrg, snrCSC, m_cListPicYuvRec, outputAccessUnits, iNumEncoded ); } // write bistream to file if necessary if ( iNumEncoded > 0 ) { xWriteOutput(bitstreamFile, iNumEncoded, outputAccessUnits); outputAccessUnits.clear(); } } m_cTEncTop.printSummary(m_isField); // delete original YUV buffer pcPicYuvOrg->destroy(); delete pcPicYuvOrg; pcPicYuvOrg = NULL; // delete used buffers in encoder class m_cTEncTop.deletePicBuffer(); cPicYuvTrueOrg.destroy(); // delete buffers & classes xDeleteBuffer(); xDestroyLib(); printRateSummary(); return;}
这么长看的我都慌了,先参考上面博客链接里面讲的
该函数中首先调用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的编码:
我们直接看核心功能实现,这里博主使用的HM10,和我使用的16.3略有不同,我这里关于这个函数是这个样子的
// call encoding function for one frame if ( m_isField ) { m_cTEncTop.encode( bEos, flush ? 0 : pcPicYuvOrg, flush ? 0 : &cPicYuvTrueOrg, snrCSC, m_cListPicYuvRec, outputAccessUnits, iNumEncoded, m_isTopFieldFirst ); } else { m_cTEncTop.encode( bEos, flush ? 0 : pcPicYuvOrg, flush ? 0 : &cPicYuvTrueOrg, snrCSC, m_cListPicYuvRec, outputAccessUnits, iNumEncoded ); }
这里分成两个了,个人理解第一个是对第一帧编码,第二个是对后续帧编码,那么来看看第一个的内容吧
右键查看它的引用,跳转到了一个.h文件里面(这个不是很懂了就),暂时不管那么多,接着跳转,啊啊啊啊不对,不是这么跳转的。我是
先全拖住再F12跳转的,所以错了应该这样
这样就跳转到了encode里面
找到这个
// compress GOP
m_cGOPEncoder.compressGOP(m_iPOCLast, m_iNumPicRcvd, m_cListPic, rcListPicYuvRecOut, accessUnitsOut, false, false, snrCSC, m_printFrameMSE);
再跳转 一步步跳转之后,最后找CU要跳转compressCtu,我一直搜索compressCU找不到来着。今天的内容就到这儿吧
- HEVC函数入门(1)——HM编码器的基本结构
- 【HEVC学习与研究】31、HM编码器的基本结构
- 【HEVC学习与研究】31、HM编码器的基本结构
- [HEVC] HEVC学习(二) —— HM的整体结构及一些基本概念
- 【HEVC学习与研究】37、HM编码器的基本结构2:帧内编码部分的代码骨架
- HM编码器代码阅读(21)——熵编码的概念以及在HEVC中应用
- HEVC函数入门(10)——HM软件手册software-manual(GOP,RPS,POC)
- HEVC函数入门(13)——HEVC中容易混淆的类和结构
- HEVC学习(二) —— HM的整体结构及一些基本概念
- HEVC学习(二) —— HM的整体结构及一些基本概念
- HEVC学习(二) —— HM的整体结构及一些基本概念
- HEVC学习(二) —— HM的整体结构及一些基本概念
- HEVC学习(二) —— HM的整体结构及一些基本概念
- HEVC之路(1)——HM的运行
- HEVC学习(一) —— HM的使用
- HEVC学习(一) —— HM的使用
- HEVC学习(一) —— HM的使用
- HEVC学习(一) —— HM的使用
- java面试宝典及答案
- pandas修改DataFrame的列名
- XML生成和解析
- Java与算法(9)
- 625. Minimum Factorization & 621. Task Scheduler
- HEVC函数入门(1)——HM编码器的基本结构
- 我的网络编程学习之路——TCP(流)套接字的读和写
- 守护进程
- java炒冷饭系列03 接口修饰符
- java开发交流
- 欢迎使用CSDN-markdown编辑器
- Java实现-删除数字
- 深入理解Spring--动手实现一个简单的SpringIOC容器
- 重新理解一下对象class这个东西