HEVC中的CABAC
来源:互联网 发布:html5shiv.min.js下载 编辑:程序博客网 时间:2024/04/28 16:04
HEVC中的CABAC
CABAC(上下文自适应的二进制算术编码)基于算术编码,在HEVC中,除了参数集、SEI和slice头部之外,其余的所有数据都使用CABAC来进行熵编码。
CABAC有三个步骤:
1、初始化,构建上下文概率模型
2、根据上下文概率模型获取语法元素的概率,对语法元素进行熵编码
3、根据编码结果更新上下文概率模型
初始化
初始化上下文模型,就是指初始化和上下文模型有关的两个变量:MPS和δ。MPS是最大概率符号,它表示待编码符号可能出现符号,对于二进制算术编码来说,MPS是0或者1,相反,LPS表示待编码符号不可能出现的符号,对于二进制算术编码来说,LPS也是0或1;δ表示概率的状态索引,它的值与LPS的概率值是相对应的,δ值随着LPS概率的更新而变化。MPS和δ唯一的确定上下文模型的状态,以及后续如何对上下模型进行更新。
计算MPS和δ需要一个初始值initValue,initValue的值与slice的类型、初始量化参数有关。HEVC为每一个语法元素都定义了不同的initValue,为了方便,可以通过slice的类型和量化参数查表来得到initValue的值。initValue表示起始概率值。
我们以MPS和δ来表示上下文概率模型,或者说,上下文概率模型的主要参数是MPS和δ。
初始化的入口函数
它的主要功能是:获取QP和slice的类型,然后调用ContextModel3DBuffer::initBuffer进行上下文概率模型的初始化。
Void TEncSbac::resetEntropy (){Int iQp = m_pcSlice->getSliceQp();SliceType eSliceType = m_pcSlice->getSliceType();Int encCABACTableIdx = m_pcSlice->getPPS()->getEncCABACTableIdx();if (!m_pcSlice->isIntra() && (encCABACTableIdx==B_SLICE || encCABACTableIdx==P_SLICE) && m_pcSlice->getPPS()->getCabacInitPresentFlag()){eSliceType = (SliceType) encCABACTableIdx;}// 初始化各个模型的缓存// split标志的上下文m_cCUSplitFlagSCModel.initBuffer ( eSliceType, iQp, (UChar*)INIT_SPLIT_FLAG );// *** 省略其他// new structurem_uiLastQp = iQp;// 二值化的一些操作m_pcBinIf->start();return;}
根据slice的类型获取存放initValue的表格
需要注意的是,initValue的值是和slice的类型有关的,因此计算initValue的时候需要使用slice的类型。为了提高计算速度,HEVC实现的时候,使用查表的方式来代替直接计算,ctxModel(例如:INIT_SPLIT_FLAG等)中存放了是与slice类型有关的initValue。
得到存放initValue的表格之后,以QP和initValue为参数调用ContextModel::init,计算MPS和δ。
Void ContextModel3DBuffer::initBuffer( SliceType sliceType, Int qp, UChar* ctxModel ){ ctxModel += sliceType * m_sizeXYZ; // 根据当前slice的类型(I,P,B)选择对应的context,为什么这么做,下面会解释 // 根据sliceType计算initType并将context指针移动到正确的位置上,这个initType用于索引context model,且由slice_type来决定for ( Int n = 0; n < m_sizeXYZ; n++ ){m_contextModel[ n ].init( qp, ctxModel[ n ] );// 完成context的各个状态变量的初始化工作m_contextModel[ n ].setBinsCoded( 0 );}}下面是存放initValue的表格,每个语法元素对应一个表格:
// initial probability for cu_transquant_bypass flagstatic const UCharINIT_CU_TRANSQUANT_BYPASS_FLAG[3][NUM_CU_TRANSQUANT_BYPASS_FLAG_CTX] ={{ 154 }, { 154 }, { 154 }, };// initial probability for split flagstatic const UChar INIT_SPLIT_FLAG[3][NUM_SPLIT_FLAG_CTX] = {{ 107, 139, 126, },{ 107, 139, 126, }, { 139, 141, 157, }, };static const UChar INIT_SKIP_FLAG[3][NUM_SKIP_FLAG_CTX] = {{ 197, 185, 201, }, { 197, 185, 201, }, { CNU, CNU, CNU, }, };static const UCharINIT_MERGE_FLAG_EXT[3][NUM_MERGE_FLAG_EXT_CTX] = {{ 154, }, { 110, }, { CNU, }, };static const UChar INIT_MERGE_IDX_EXT[3][NUM_MERGE_IDX_EXT_CTX] = {{ 137, }, { 122, }, { CNU, }, };static const UChar INIT_PART_SIZE[3][NUM_PART_SIZE_CTX] = {{ 154, 139, 154, 154 },{ 154, 139, 154, 154 },{ 184, CNU, CNU, CNU },};static const UCharINIT_PRED_MODE[3][NUM_PRED_MODE_CTX] = {{ 134, }, { 149, }, { CNU, }, };static const UChar INIT_INTRA_PRED_MODE[3][NUM_ADI_CTX] = {{ 183, }, { 154, }, { 184, }, };static const UChar INIT_CHROMA_PRED_MODE[3][NUM_CHROMA_PRED_CTX] = {{ 152, 139, }, { 152, 139, }, { 63, 139, }, };static const UChar INIT_INTER_DIR[3][NUM_INTER_DIR_CTX] = {{ 95, 79, 63, 31, 31, }, { 95, 79, 63, 31, 31, }, { CNU, CNU, CNU, CNU, CNU, }, };static const UChar INIT_MVD[3][NUM_MV_RES_CTX] = {{ 169, 198, }, { 140, 198, }, { CNU, CNU, }, };static const UChar INIT_REF_PIC[3][NUM_REF_NO_CTX] = {{ 153, 153 }, { 153, 153 }, { CNU, CNU }, };static const UChar INIT_DQP[3][NUM_DELTA_QP_CTX] = {{ 154, 154, 154, }, { 154, 154, 154, }, { 154, 154, 154, }, };static const UChar INIT_QT_CBF[3][2*NUM_QT_CBF_CTX] = {{ 153, 111, CNU, CNU, 149, 92, 167, 154 },{ 153, 111, CNU, CNU, 149, 107, 167, 154 },{ 111, 141, CNU, CNU, 94, 138, 182, 154 },};static const UChar INIT_QT_ROOT_CBF[3][NUM_QT_ROOT_CBF_CTX] = {{ 79, }, { 79, }, { CNU, }, };static const UChar INIT_LAST[3][2*NUM_CTX_LAST_FLAG_XY] = {{ 125, 110, 124, 110, 95, 94, 125, 111, 111, 79, 125, 126, 111, 111, 79,108, 123, 93, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, }, { 125, 110, 94, 110, 95, 79, 125, 111, 110, 78, 110, 111, 111, 95, 94,108, 123, 108, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU,}, { 110, 110, 124, 125, 140, 153, 125, 127, 140, 109, 111, 143, 127, 111, 79, 108, 123, 63, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, }, };static const UChar INIT_SIG_CG_FLAG[3][2 * NUM_SIG_CG_FLAG_CTX] = {{ 121, 140, 61, 154, }, { 121, 140, 61, 154, }, { 91, 171, 134, 141, }, };static const UChar INIT_SIG_FLAG[3][NUM_SIG_FLAG_CTX] = {{ 170, 154, 139, 153, 139, 123, 123, 63, 124, 166, 183, 140, 136, 153, 154, 166, 183, 140, 136, 153, 154, 166, 183, 140, 136, 153, 154, 170, 153, 138, 138, 122, 121, 122, 121, 167, 151, 183, 140, 151, 183, 140, }, { 155, 154, 139, 153, 139, 123, 123, 63, 153, 166, 183, 140, 136, 153, 154, 166, 183, 140, 136, 153, 154, 166, 183, 140, 136, 153, 154, 170, 153, 123, 123, 107, 121, 107, 121, 167, 151, 183, 140, 151, 183, 140, }, { 111, 111, 125, 110, 110, 94, 124, 108, 124, 107, 125, 141, 179, 153, 125, 107, 125, 141, 179, 153, 125, 107, 125, 141, 179, 153, 125, 140, 139, 182, 182, 152, 136, 152, 136, 153, 136, 139, 111, 136, 139, 111, }, };static const UChar INIT_ONE_FLAG[3][NUM_ONE_FLAG_CTX] = {{ 154, 196, 167, 167, 154, 152, 167, 182, 182, 134, 149, 136, 153, 121, 136, 122, 169, 208, 166, 167, 154, 152, 167, 182, }, { 154, 196, 196, 167, 154, 152, 167, 182, 182, 134, 149, 136, 153, 121, 136, 137, 169, 194, 166, 167, 154, 167, 137, 182, }, { 140, 92, 137, 138, 140, 152, 138, 139, 153, 74, 149, 92, 139, 107, 122, 152, 140, 179, 166, 182, 140, 227, 122, 197, }, };static const UChar INIT_ABS_FLAG[3][NUM_ABS_FLAG_CTX] = {{ 107, 167, 91, 107, 107, 167, }, { 107, 167, 91, 122, 107, 167, }, { 138, 153, 136, 167, 152, 152, }, };static const UChar INIT_MVP_IDX[3][NUM_MVP_IDX_CTX] = {{ 168 },{ 168 },{ CNU }, };static const UChar INIT_SAO_MERGE_FLAG[3][NUM_SAO_MERGE_FLAG_CTX] = {{ 153, }, { 153, }, { 153, }, };static const UChar INIT_SAO_TYPE_IDX[3][NUM_SAO_TYPE_IDX_CTX] = {{ 160, },{ 185, },{ 200, },};static const UCharINIT_TRANS_SUBDIV_FLAG[3][NUM_TRANS_SUBDIV_FLAG_CTX] ={{ 224, 167, 122, },{ 124, 138, 94, },{ 153, 138, 138, },};static const UCharINIT_TRANSFORMSKIP_FLAG[3][2*NUM_TRANSFORMSKIP_FLAG_CTX] = {{ 139, 139}, { 139, 139}, { 139, 139}, };//! \}
根据initValue和量化参数计算MPS和δ
在下面的函数中,slope、offset、initState都是中间变量,mpState表示MPS,m_ucState表示δ
Void ContextModel::init( Int qp, Int initValue ){ // 选取中间值qp = Clip3(0, 51, qp);// 与draft 9.3.1.1基本呈一一对应关系Int slope = (initValue>>4)*5 - 45;// mInt offset = ((initValue&15)<<3)-16;// nInt initState = min( max( 1, ( ( ( slope * qp ) >> 4 ) + offset ) ), 126 );// preCtxState UInt mpState = (initState >= 64 );// valMPS m_ucState = ( (mpState? (initState - 64):(63 - initState)) <<1) + mpState;// pStateIdx,与(9-5)式略有不同,这里的m_ucState的值实际上是draft中pStateIdx<<1+valMPS,这么做的目的应该是为了节省内存}
获取概率进行熵编码
语法元素对应的上下文模型初始化完成之后,开始进行二进制算术编码。二进制算术编码是对语法元素对应的二进制比特串进行算术编码。二进制算术编码包含两种方式:旁路方式和常规方式。在旁路编码方式中,二进制串的符号的概率是相同的,也不需要更新上下文概率模型;在常规方式中,二进制串中符号的概率可以由上下文模型中得到,对每一个符号编码完成之后都需要对上下文模型进行更新。使用常规方式还是旁路方式是由语法元素决定的,HEVC文档指明了哪些语法元素使用旁路方式哪些语法元素使用常规方式。
在对语法元素进行编码之前需要对它进行二进制化。
二进制化
理论上,HEVC的二进制方法有:
1、一元码
2、截断一元码
3、指数哥伦布码
4、截断莱斯码
5、定长码
由于在实际中,由于很多语法元素的值都是0或者1,因此,很多语法元素不需要二进制化就可以直接进行编码,只有少部分才会进行二进制化。例如mvp-index、delta-qp等语法元素使用截断一元码进行二进制化;mvd等语法元素使用指数哥伦布来进行二进制化。
一元码
假设语法的元素值是x,那么它对应的一元码由前缀x个1和后缀一个0构成:11...10。假设x=5,那么它的一元码是111110
/*** 一元码的实现*/Void TEncSbac::xWriteUnarySymbol( UInt uiSymbol, ContextModel* pcSCModel, Int iOffset ){m_pcBinIf->encodeBin( uiSymbol ? 1 : 0, pcSCModel[0] );if( 0 == uiSymbol){return;}while( uiSymbol-- ){m_pcBinIf->encodeBin( uiSymbol ? 1 : 0, pcSCModel[ iOffset ] );}return;}
截断一元码
1、把语法元素之转换成一元码,假如语法元素值是x,那么它的一元码由起始的x个1和最后一个0组成。
2、给定一个最大的可能值cMax,bins的长度不能超过cMax,如果超过,那么就对bins的尾部进行截断
3、例如,给定一个语法元素的值是5,cMax是4
(1)5对应的一元码是111110
(2)由于一元码的长度大于cMax,因此需要对它进行截断
(3)截断之后为1111,因此5对应的截断一元码是1111(当cMax等于4时)
/*** uiSymbol:语法元素值** uiMaxSymbol:cMax*/Void TEncSbac::xWriteUnaryMaxSymbol( UInt uiSymbol, ContextModel* pcSCModel, Int iOffset, UInt uiMaxSymbol ){if (uiMaxSymbol == 0){return;}// 先编码第一个编码一元码的第一个比特m_pcBinIf->encodeBin( uiSymbol ? 1 : 0, pcSCModel[ 0 ] );if ( uiSymbol == 0 ){return;}// 判断是否需要截断Bool bCodeLast = ( uiMaxSymbol > uiSymbol );// 编码x个1(一元码)while( --uiSymbol ){m_pcBinIf->encodeBin( 1, pcSCModel[ iOffset ] );}// 不需要截断,直接在后面添加0if( bCodeLast ){m_pcBinIf->encodeBin( 0, pcSCModel[ iOffset ] );}return;}
截断莱斯码
HEVC的实现中好像没有用到,这里不再介绍k阶指数哥伦布码
它由前缀和后缀构成,前缀和一元码有点相似,由p个1和一个0构成,其中p=log2[x/(2^k)+1];后缀是q对应的二进制数,其中q=x+2^k*(1-2^p);HEVC中最常用的是1阶指数哥伦布码。/*** K阶指数哥伦布 二进制化** uiSymbol:语法元素值** uiCount:阶数K** HEVC最常用的是1阶*/Void TEncSbac::xWriteEpExGolomb( UInt uiSymbol, UInt uiCount ){UInt bins = 0;Int numBins = 0;while( uiSymbol >= (UInt)(1<<uiCount) ){bins = 2 * bins + 1;numBins++;uiSymbol -= 1 << uiCount;uiCount ++;}bins = 2 * bins + 0;numBins++;bins = (bins << uiCount) | uiSymbol;numBins += uiCount;assert( numBins <= 32 );m_pcBinIf->encodeBinsEP( bins, numBins );}
定长码
1、给定一个与语法元素值x和一个参数cMax,限定0<=x<=cMax2、把语法元素值x转换成二进制bins,这就是它的定长码
3、HEVC中没有给出具体的实现,因为使用定长码的语法元素大部分值都是0或者1,可以直接编码
常规编码
对于常规编码方式,编码完一个比特符号之后需要更新上下文模型和编码区间。为了减少更新上下文模型时候的计算量,HEVC定义了几个表格,分别是transMPS、transLPS和rangeLPS,transMPS和transLPS的作用是更新δ,rangeLPS存储了LPS对应的子区间。是当为了方便描述,假设当前编码的符号是bin,把当前的编码区间的长度和下界设置为length和low。
上下文模型的更新分为两个步骤:
1、更新上下文模型。上下文模型的更新主要是对上下文模型的δ和MPS变量进行更新。如果bin等于MPS,那么δ_new=transMPS[δ];否则δ_new=transLPS[δ],而且如果δ等于0,需要互换MPS和LPS
2、更新编码区间。主要是移动编码区间的下界low或者重新计算区间的长度length。首先计算LPS的子区间的长度length_lps=rangeLPS[δ][(length?6)&3],对应的MPS的子区间的长度length_mps=length-length_lps;如果比特符号x等MPS,那么区间下界low不变,length更新为length_mps;否则,low=low+length_mps,length更新为length_lps。
Void TEncBinCABAC::encodeBin( UInt binValue, ContextModel &rcCtxModel ){// 调试的打印信息,省略***// 比特计数,如果开启了计数功能就计数m_uiBinsCoded += m_binCountIncrement;// 设置已经编码的标志rcCtxModel.setBinsCoded( 1 );// 查表获取LPS对应的子区间的长度UInt uiLPS = TComCABACTables::sm_aucLPSTable[ rcCtxModel.getState() ][ ( m_uiRange >> 6 ) & 3 ];// m_uiRange表示MPS对应的子区间的长度m_uiRange -= uiLPS;// 如果二进制符号不等于MPSif( binValue != rcCtxModel.getMps() ){// numBits用于重归一化Int numBits = TComCABACTables::sm_aucRenormTable[ uiLPS >> 3 ];// RenormE // 更新low=low+length_mps,使用“<< numBits”的目的是重归一化m_uiLow = ( m_uiLow + m_uiRange ) << numBits;// codILow = codILow + codIRange// 更新length = length_lps,“<< numBits”的目的是重归一化m_uiRange = uiLPS << numBits;// codIRange = codIRangeLPS// 使用rangeLPS表格对δ进行更新rcCtxModel.updateLPS();// pStateIdx = transIdxLPS[pStateIdx] // m_bitsLeft -= numBits; }else// binVal == valMPS,概率索引值将增大,即LPS的概率减小{/*** 下界low不变,length更新为length_mps(m_uiRange已经等于length_mps)*/// 使用transMPS表格对δ进行更新rcCtxModel.updateMPS();// pStateIdx = transIdxLPS[pStateIdx] // 如果length大于等于256,那么不用重归一化,直接返回if ( m_uiRange >= 256 ){return;}// 重归一化m_uiLow <<= 1;m_uiRange <<= 1;m_bitsLeft--;}// 尝试写到比特流中,先判断当前缓冲区中的空闲空间是否足够,不足的话就写到比特流中,腾出空间testAndWriteOut();}编码流程:
1、首先计算LPS对应的子区间的长度,通过查表得到:length_lps=rangeLPS[δ][(length?6)&3],对应的代码是:
UInt uiLPS = TComCABACTables::sm_aucLPSTable[ rcCtxModel.getState() ][ ( m_uiRange >> 6 ) & 3 ];其中rcCtxModel.getState()返回δ
2、计算MPS对应的子区间的长度:m_uiRange -= uiLPS;
3、如果二进制符号不等于MPS,low=low+length_mps,length更新为length_lps
4、如果二进制符号等于MPS,下界low不变,length更新为length_mps
transMPS表格
// 即transMPS表格const UChar ContextModel::m_aucNextStateMPS[ 128 ] ={2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65,66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81,82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97,98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113,114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 124, 125, 126, 127};
transLPS表格
// 即transLPS表格const UChar ContextModel::m_aucNextStateLPS[ 128 ] ={1, 0, 0, 1, 2, 3, 4, 5, 4, 5, 8, 9, 8, 9, 10, 11,12, 13, 14, 15, 16, 17, 18, 19, 18, 19, 22, 23, 22, 23, 24, 25,26, 27, 26, 27, 30, 31, 30, 31, 32, 33, 32, 33, 36, 37, 36, 37,38, 39, 38, 39, 42, 43, 42, 43, 44, 45, 44, 45, 46, 47, 48, 49,48, 49, 50, 51, 52, 53, 52, 53, 54, 55, 54, 55, 56, 57, 58, 59,58, 59, 60, 61, 60, 61, 60, 61, 62, 63, 64, 65, 64, 65, 66, 67,66, 67, 66, 67, 68, 69, 68, 69, 70, 71, 70, 71, 70, 71, 72, 73,72, 73, 72, 73, 74, 75, 74, 75, 74, 75, 76, 77, 76, 77, 126, 127};
rangeLPS表格
// 即rangeLPSconst UChar TComCABACTables::sm_aucLPSTable[64][4] ={{ 128, 176, 208, 240},{ 128, 167, 197, 227},{ 128, 158, 187, 216},{ 123, 150, 178, 205},{ 116, 142, 169, 195},{ 111, 135, 160, 185},{ 105, 128, 152, 175},{ 100, 122, 144, 166},{ 95, 116, 137, 158},{ 90, 110, 130, 150},{ 85, 104, 123, 142},{ 81, 99, 117, 135},{ 77, 94, 111, 128},{ 73, 89, 105, 122},{ 69, 85, 100, 116},{ 66, 80, 95, 110},{ 62, 76, 90, 104},{ 59, 72, 86, 99},{ 56, 69, 81, 94},{ 53, 65, 77, 89},{ 51, 62, 73, 85},{ 48, 59, 69, 80},{ 46, 56, 66, 76},{ 43, 53, 63, 72},{ 41, 50, 59, 69},{ 39, 48, 56, 65},{ 37, 45, 54, 62},{ 35, 43, 51, 59},{ 33, 41, 48, 56},{ 32, 39, 46, 53},{ 30, 37, 43, 50},{ 29, 35, 41, 48},{ 27, 33, 39, 45},{ 26, 31, 37, 43},{ 24, 30, 35, 41},{ 23, 28, 33, 39},{ 22, 27, 32, 37},{ 21, 26, 30, 35},{ 20, 24, 29, 33},{ 19, 23, 27, 31},{ 18, 22, 26, 30},{ 17, 21, 25, 28},{ 16, 20, 23, 27},{ 15, 19, 22, 25},{ 14, 18, 21, 24},{ 14, 17, 20, 23},{ 13, 16, 19, 22},{ 12, 15, 18, 21},{ 12, 14, 17, 20},{ 11, 14, 16, 19},{ 11, 13, 15, 18},{ 10, 12, 15, 17},{ 10, 12, 14, 16},{ 9, 11, 13, 15},{ 9, 11, 12, 14},{ 8, 10, 12, 14},{ 8, 9, 11, 13},{ 7, 9, 11, 12},{ 7, 9, 10, 12},{ 7, 8, 10, 11},{ 6, 8, 9, 11},{ 6, 7, 9, 10},{ 6, 7, 8, 9},{ 2, 2, 2, 2}};
重归一化的表格
// 归一化的时候使用到的表格const UChar TComCABACTables::sm_aucRenormTable[32] ={6, 5, 4, 4,3, 3, 3, 3,2, 2, 2, 2,2, 2, 2, 2,1, 1, 1, 1,1, 1, 1, 1,1, 1, 1, 1,1, 1, 1, 1};
旁路编码
旁路编码也叫等概率编码,它不使用上下文概率模型,二进制符号0和1的概率都是1/2。为了是区间划分操作更加简便,不直接对区间长度进行二等分,而是采用保存编码区间长度不变,使区间下限low的值加倍的方法来实现区间的划分,效果是一样的。
Void TEncBinCABAC::encodeBinEP( UInt binValue ){// 调试的打印信息,省略***m_uiBinsCoded += m_binCountIncrement;// low的值加倍m_uiLow <<= 1;// 如果符号值等于1if( binValue ){// 更新low=low + lengthm_uiLow += m_uiRange;}// 重归一化m_bitsLeft--;testAndWriteOut();}
把数据写到比特流中
先看看TEncBinCABAC的定义
class TEncBinCABAC : public TEncBinIf{// *** 省略了类的函数和接口Void testAndWriteOut();Void writeOut();// 比特处理接口:比特流的处理TComBitIf* m_pcTComBitIf;// 二进制算数编码的下限UInt m_uiLow;// 二进制算数编码的范围UInt m_uiRange;// 缓冲区UInt m_bufferedByte; // 已经缓存的数据的长度Int m_numBufferedBytes;// 缓冲区中剩余的比特数Int m_bitsLeft;// 已经编码的比特数UInt m_uiBinsCoded;// 增长的比特数,和m_uiBinsCoded一起进行计数Int m_binCountIncrement;#if FAST_BIT_ESTUInt64 m_fracBits;#endif};
前面看到了TEncBinCABAC::encodeBin函数中会调用testAndWriteOut函数,testAndWriteOut函数的作用是尝试往比特流中写入数据。
/*** 尝试写到比特流中** 先判断当前缓冲区中的空闲空间是否足够,不足的话就写到比特流中,腾出空间*/Void TEncBinCABAC::testAndWriteOut(){if ( m_bitsLeft < 12 ){writeOut();}}
/*** 把已经编码的数据写到比特流中*/Void TEncBinCABAC::writeOut(){UInt leadByte = m_uiLow >> (24 - m_bitsLeft);m_bitsLeft += 8;m_uiLow &= 0xffffffffu >> m_bitsLeft;if ( leadByte == 0xff ){m_numBufferedBytes++;}else{if ( m_numBufferedBytes > 0 ){UInt carry = leadByte >> 8;UInt byte = m_bufferedByte + carry;m_bufferedByte = leadByte & 0xff;m_pcTComBitIf->write( byte, 8 );byte = ( 0xff + carry ) & 0xff;while ( m_numBufferedBytes > 1 ){m_pcTComBitIf->write( byte, 8 );m_numBufferedBytes--;}}else{m_numBufferedBytes = 1;m_bufferedByte = leadByte;} } }
- HEVC中的CABAC
- CABAC中的二值化方法
- 【HEVC学习与研究】27、CABAC解析语法元素SAO
- 【HEVC学习与研究】27、CABAC解析语法元素SAO
- HEVC函数入门(23)——熵编码&CABAC
- 18-回顾:h264中的CABAC
- CABAC
- Cabac
- H264中的CABAC及FFMPEG中的实现
- H.264中的CABAC编码原理
- HEVC中的CU结构
- HEVC中的TZSearch过程
- HEVC中的英语简写
- HEVC中的PPS解析
- HEVC 中的EGk
- HEVC中的PPS解析
- HEVC中的PPS解析
- HEVC中的OPEN-GOP
- HDU6025-Coprime Sequence-简单数学
- Makefile函数补充
- Android绘图机制与处理技巧(一)2D绘图技巧与XML绘图
- C语言~三子棋游戏的实现
- Oracle启动监听报错:The listener supports no services解决
- HEVC中的CABAC
- 选择排序之Java实现
- 第十一周:[Leetcode]139. Word Break
- 文章标题
- IDEA中文显示口口口口口...解决方法
- Linux的启动和关机的流程、引导加载程序
- C#实现二分法查找算法
- Gym-100712J 桶排序思想&反向思维
- PE解析器的编写(四)——数据目录表的解析