首先说明这篇主要是记录用(意思是里面很多是我自己的理解,我也是刚入门的新手,肯定有许多不正确的地方,还请轻喷),使用的是HM 16.3 VS2015 当然如果有新手不太懂的话也可以看看。
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; listoutputAccessUnits; ///< 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找不到来着。今天的内容就到这儿吧