在HM的main函数中会执行cTAppEncTop.encode()语句进入TAppEncTop::encode()进行实际编码,此函数主要功能:
- 打开比特流文件
- 调用xInitLibCfg、xCreateLib、xInitLib等函数初始化各项参数
- 分配YUV缓冲区
- 读取YUV文件
- 对GOP中的每一帧进行编码
- 打印相关编码信息
代码如下:
Void TAppEncTop::encode()
{
fstream bitstreamFile(m_bitstreamFileName.c_str(), fstream::binary | fstream::out); // 打开比特流文件写入码流数据
if (!bitstreamFile) // 如果打开失败
{
fprintf(stderr, "\nfailed to open bitstream file `%s' for writing\n", m_bitstreamFileName.c_str());
exit(EXIT_FAILURE);
}
TComPicYuv* pcPicYuvOrg = new TComPicYuv;
TComPicYuv* pcPicYuvRec = NULL;
// initialize internal class & member variables
xInitLibCfg(); // 初始化编码器各项参数,如帧率、宽、高、编码结构GOP等等
xCreateLib(); // 创建编码后重构的视频文件
xInitLib(m_isField); // 初始化SPS、PPS相关参数
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 ) // m_isField表示是否为场编码,默认为否
{
pcPicYuvOrg->create ( m_iSourceWidth, m_iSourceHeightOrg, m_chromaFormatIDC, m_uiMaxCUWidth, m_uiMaxCUHeight, m_uiMaxTotalCUDepth, true );
cPicYuvTrueOrg.create(m_iSourceWidth, m_iSourceHeightOrg, m_chromaFormatIDC, m_uiMaxCUWidth, m_uiMaxCUHeight, m_uiMaxTotalCUDepth, true);
}
else
{
pcPicYuvOrg->create ( m_iSourceWidth, m_iSourceHeight, m_chromaFormatIDC, m_uiMaxCUWidth, m_uiMaxCUHeight, m_uiMaxTotalCUDepth, true );
cPicYuvTrueOrg.create(m_iSourceWidth, m_iSourceHeight, m_chromaFormatIDC, m_uiMaxCUWidth, m_uiMaxCUHeight, m_uiMaxTotalCUDepth, true );
}
#if EXTENSION_360_VIDEO
TExt360AppEncTop ext360(*this, m_cTEncTop.getGOPEncoder()->getExt360Data(), *(m_cTEncTop.getGOPEncoder()), *pcPicYuvOrg);
#endif
while ( !bEos ) // 此循环会将要编码的所有帧进行逐帧编码
{
// get buffers
xGetBuffer(pcPicYuvRec);
// read input YUV file
#if EXTENSION_360_VIDEO
if (ext360.isEnabled())
{
ext360.read(m_cTVideoIOYuvInputFile, *pcPicYuvOrg, cPicYuvTrueOrg, ipCSC);
}
else
{
m_cTVideoIOYuvInputFile.read( pcPicYuvOrg, &cPicYuvTrueOrg, ipCSC, m_aiPad, m_InputChromaFormatIDC, m_bClipInputVideoToRec709Range );
}
#else
// 读取输入的YUV文件
m_cTVideoIOYuvInputFile.read( pcPicYuvOrg, &cPicYuvTrueOrg, ipCSC, m_aiPad, m_InputChromaFormatIDC, m_bClipInputVideoToRec709Range );
#endif
// increase number of received frames
m_iFrameRcvd++; // 已接收到的帧数
// 当接收到的帧数(m_iFrameRcvd )等于要编码的帧数(m_framesToBeEncoded)时,bEos为true,跳出循环,结束编码
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
{ // 按照GOP结构对每一帧进行编码
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();
}
// temporally skip frames
if( m_temporalSubsampleRatio > 1 )
{
m_cTVideoIOYuvInputFile.skipFrames(m_temporalSubsampleRatio-1, m_inputFileWidth, m_inputFileHeight, m_InputChromaFormatIDC);
}
}
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;
}