tishi43/h265_c_reference
Folders and files
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Repository files navigation
写verilog实现H265解码器前为了理解协议实现的一个简化版本的H265解码器C代码,是VS2013的工程,输入文件是test.265,工程里含有一些测试文件,重命令成test.265即可。编译,按F5跑即可,输出out.yuv,以及一些log文件。
主要参考H265标准,ffmpeg和HM,参考的是JCTVC-L1003_v34,
代码尽量和标准写的保持一致,变量名字和标准也保持一致,便于理解,消除看HM和ffmpeg时因为绕弯而带来的困惑,适合初学h265标准的同学们。
举一个cabac decode bin的例子,
HM12.0的代码如下,
Void
TDecBinCABAC::decodeBin( UInt& ruiBin, ContextModel &rcCtxModel )
{
UInt uiLPS = TComCABACTables::sm_aucLPSTable[ rcCtxModel.getState() ][ ( m_uiRange >> 6 ) - 4 ];
m_uiRange -= uiLPS;
UInt scaledRange = m_uiRange << 7;
if( m_uiValue < scaledRange )
{
// MPS path
ruiBin = rcCtxModel.getMps();
rcCtxModel.updateMPS();
if ( scaledRange >= ( 256 << 7 ) )
{
return;
}
m_uiRange = scaledRange >> 6;
m_uiValue += m_uiValue;
if ( ++m_bitsNeeded == 0 )
{
m_bitsNeeded = -8;
m_uiValue += m_pcTComBitstream->readByte();
}
}
else
{
// LPS path
Int numBits = TComCABACTables::sm_aucRenormTable[ uiLPS >> 3 ];
m_uiValue = ( m_uiValue - scaledRange ) << numBits;
m_uiRange = uiLPS << numBits;
ruiBin = 1 - rcCtxModel.getMps();
rcCtxModel.updateLPS();
m_bitsNeeded += numBits;
if ( m_bitsNeeded >= 0 )
{
m_uiValue += m_pcTComBitstream->readByte() << m_bitsNeeded;
m_bitsNeeded -= 8;
}
}
}
FFMPEG的代码,
static av_always_inline int get_cabac_inline(CABACContext *c, uint8_t * const state){
int s = *state;
int RangeLPS= ff_h264_lps_range[2*(c->range&0xC0) + s];
int bit, lps_mask;
c->range -= RangeLPS;
lps_mask= ((c->range<<(CABAC_BITS+1)) - c->low)>>31;
s^=lps_mask;
*state= (ff_h264_mlps_state+128)[s];
bit= s&1;
lps_mask= ff_h264_norm_shift[c->range];
c->range<<= lps_mask;
c->low <<= lps_mask;
if(!(c->low & CABAC_MASK)) {
refill2(c);
}
return bit;
}
下面是完完全全按照spec两幅图来实现的代码,实践证明代码完全按照spec来写,结果也是对的,而HM和ffpeg都有不同程度的绕弯,让人在理解过程中感到蛋腾。
void decode_bin(unsigned char *rbsp, int *binVal, struct context_model *cm)
{
//9.3.4.3.2 Arithmetic decoding process for a binary decision
int ivlLpsRange, qRangeIdx;
qRangeIdx = (ivlCurrRange >> 6) & 3;
ivlLpsRange = rangeTabLps[cm->pStateIdx][qRangeIdx];
ivlCurrRange = ivlCurrRange - ivlLpsRange;
if (ivlOffset >= ivlCurrRange) {
//LPS path
*binVal = 1 - cm->valMps;
ivlOffset -= ivlCurrRange;
ivlCurrRange = ivlLpsRange;
} else {
//MPS path
*binVal = cm->valMps;
}
//9.3.4.3.2.2 State transition process
if(*binVal == cm->valMps){
cm->pStateIdx = transIdxMps[cm->pStateIdx];
} else {
if (cm->pStateIdx == 0)
cm->valMps = 1 - cm->valMps;
cm->pStateIdx = transIdxLps[cm->pStateIdx];
}
//renorm
while (ivlCurrRange < 256) {
ivlCurrRange = ivlCurrRange << 1;
ivlOffset = ivlOffset << 1;
ivlOffset = ivlOffset | read_one_bit(rbsp);
}
}