在TAppDecTop.cpp ,最重要的是decode 函數,下面將對其進行分析,是解碼上層的一個重要函數。
代碼如下,代碼後將進行分析。
Void TAppDecTop::decode(){ Int poc; TComList<TComPic*>* pcListPic = NULL; ifstream bitstreamFile(m_pchBitstreamFile, ifstream::in | ifstream::binary); if (!bitstreamFile) { fprintf(stderr, "\nfailed to open bitstream file `%s' for reading\n", m_pchBitstreamFile); exit(EXIT_FAILURE); } InputByteStream bytestream(bitstreamFile); // create & initialize internal classes xCreateDecLib(); xInitDecLib (); m_iPOCLastDisplay += m_iSkipFrame; // set the last displayed POC correctly for skip forward. // main decoder loop Bool recon_opened = false; // reconstruction file not yet opened. (must be performed after SPS is seen) while (!!bitstreamFile) { /* location serves to work around a design fault in the decoder, whereby * the process of reading a new slice that is the first slice of a new frame * requires the TDecTop::decode() method to be called again with the same * nal unit. */ streampos location = bitstreamFile.tellg(); AnnexBStats stats = AnnexBStats(); Bool bPreviousPictureDecoded = false; vector<uint8_t> nalUnit; InputNALUnit nalu; byteStreamNALUnit(bytestream, nalUnit, stats); // call actual decoding function Bool bNewPicture = false; if (nalUnit.empty()) { /* this can happen if the following occur: * - empty input file * - two back-to-back start_code_prefixes * - start_code_prefix immediately followed by EOF */ fprintf(stderr, "Warning: Attempt to decode an empty NAL unit\n"); } else { read(nalu, nalUnit); if( (m_iMaxTemporalLayer >= 0 && nalu.m_temporalId > m_iMaxTemporalLayer) || !isNaluWithinTargetDecLayerIdSet(&nalu) ) { if(bPreviousPictureDecoded) { bNewPicture = true; bPreviousPictureDecoded = false; } else { bNewPicture = false; } } else { bNewPicture = m_cTDecTop.decode(nalu, m_iSkipFrame, m_iPOCLastDisplay); if (bNewPicture) { bitstreamFile.clear(); /* location points to the current nalunit payload[1] due to the * need for the annexB parser to read three extra bytes. * [1] except for the first NAL unit in the file * (but bNewPicture doesn't happen then) */ bitstreamFile.seekg(location-streamoff(3)); bytestream.reset(); } bPreviousPictureDecoded = true; } } if (bNewPicture || !bitstreamFile) { m_cTDecTop.executeLoopFilters(poc, pcListPic); printf("\npoc =%d\n",poc); } if( pcListPic ) {printf("\nnaluType =%d\n",nalu.m_nalUnitType); if ( m_pchReconFile && !recon_opened ) { if (!m_outputBitDepthY) { m_outputBitDepthY = g_bitDepthY; } if (!m_outputBitDepthC) { m_outputBitDepthC = g_bitDepthC; } m_cTVideoIOYuvReconFile.open( m_pchReconFile, true, m_outputBitDepthY, m_outputBitDepthC, g_bitDepthY, g_bitDepthC ); // write mode recon_opened = true; } if ( bNewPicture && ( nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_IDR || nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_IDR_N_LP || nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_BLA_N_LP || nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_BLANT || nalu.m_nalUnitType == NAL_UNIT_CODED_SLICE_BLA ) ) { xFlushOutput( pcListPic ); } // write reconstruction to file if(bNewPicture) { xWriteOutput( pcListPic, nalu.m_temporalId ); } } } xFlushOutput( pcListPic ); // delete buffers m_cTDecTop.deletePicBuffer(); // destroy internal classes xDestroyDecLib();}
代碼xCreateDecLib 和 xInitDecLib 重要是初始化四叉樹和解碼需要的全域變數和申請記憶體。
當提供一個碼流檔案的檔案名稱後,進行ifstream bitstreamFile(m_pchBitstreamFile, ifstream::in | ifstream::binary); 以二進位方式開啟檔案名稱,碼流以位元組方式InputByteStream bytestream(bitstreamFile);進行讀操作。
我們都知道,HEVC/H265 是以NAL方式組織資料的,解析VPS,SPS,PPS,SEI,SEI_SUFFIX 後,其他的是一個個slice的NAL資料,而Deblocking & SAO Filters 等濾波是對整個picuture進行濾波操作,出現從第二幀開始,每幀的第一個slice兩次進行解析,這是參考軟體的一個bug或不好的地方,其實完全可以知道是否是最後一個slice,不必進行兩次開啟。
所以出現:
bitstreamFile.clear();
bitstreamFile.seekg(location-streamoff(3));
bytestream.reset();
大家有興趣,可以先增加一個變數,判斷是否是最後一個slice,就不需要執行上面代碼了。
如下代碼是從來不會執行,因為HEVC/H265沒有cavlc,也就不會有slice part A ,slice part B,slice part C ,是現實的編解碼告訴了設計者,slice part A ,slice part B,slice part C 沒有人使用,就被拋棄了,實際的編解碼從來沒有實現slice part A ,slice part B,slice part C 等編解碼的。
if( (m_iMaxTemporalLayer >= 0 && nalu.m_temporalId > m_iMaxTemporalLayer) || !isNaluWithinTargetDecLayerIdSet(&nalu) )
{
if(bPreviousPictureDecoded)
{
bNewPicture = true;
bPreviousPictureDecoded = false;
}
else
{
bNewPicture = false;
}
}
解碼和濾波後當然就是輸出重構資料,xWriteOutput( pcListPic, nalu.m_temporalId ), 如果slice是NAL_UNIT_CODED_SLICE_IDR,NAL_UNIT_CODED_SLICE_IDR_N_LP,NAL_UNIT_CODED_SLICE_BLA_N_LP,NAL_UNIT_CODED_SLICE_BLANT,NAL_UNIT_CODED_SLICE_BLA中的一種,將解碼產生的picture全部清空,沒有任何參考幀,相當於一個新的sequence進行解碼了。
其他的代碼很簡單,請自己分析。