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<=cMax
    2、把语法元素值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};


    TEncSbac表示CABAC编码类,它定义了怎么样把一个语法元素编码成比特;TEncBinCABAC是TEncSbac使用的二进制编码类,是TEncSbac的工具类或者辅助类。

    前面看到了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;}      }    }








1 0
原创粉丝点击