五年前,为了在fpga上实现h.265, 花了一段时间来看懂h.265的spec,现在分享出来有点晚了,h.266也出来了。
代码分享在github,https://github.com/tishi43/h265_c_reference
也欢迎到我的github主页https://github.com/tishi43看看,定期上传一些代码,
这是一个简化版本的h.265代码,中间没有任何优化,为了和协议保持完全一致,变量名字和协议里保持一致,流程和协议一致,适合初学者拿来理解协议。
参考了HM和ffmpeg,参考的时候,发现代码和spec不完全对得起来,其中有绕弯。还有做了一些优化,优化的地方跟spec有些偏离了,有些地方看好一会才理解过来。
下面是cabac decode bin的例子,
举一个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两幅图来实现的代码
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);
}
}