转:CABAC之手把手教你H.264编码

CABAC之手把手教你H.264编码
    
     首先要说明的是CABAC的生命期是SLICE,因此本篇所讲的也是一个SLICE里CABAC的流
程,其次对于我们来说场模式几乎用不到,所以本文的编码流程只使用帧模式,因此实际上用
到的表只有277个, 当然如果我写成399, 不是说里面所有表都用到的. 这里只是声明一下
这个问题, 如果大家实际操作的时候发现模型表序号始终不过276那是很正常的. 本文参考
了T264的代码, 应此一帧里只有一个SLICE. 而本文用的变量则采用标准里的变量.本文不
会讲CABAC的原理, 想要了解原理请参考《Context-based adaptive binary arithmetic
coding in the H.264AVC video compression standard》
注意:我用的标准是BS的正式标准,可能里面的序号和大家的不一样,但是内容应该是一样.
大家对照着就可以了

片级: 即以下步骤在片期间只做1次

1,   在一帧的开始的时候, 先看是否字节对齐, 没有对齐则补1, 直到字节对齐

2,   先根据SliceQP算出399个模型表里的pStateIdx和valMPS, 构成一张初始表,根据标准
9.3.1.1里的公式, 同时可以参考T264_cabac_context_init函数. 这张表不要和模型表弄
混,虽然都是399维的, 但我们宏块级编码过程中实际用的只是这张表, 而不是标准里的那
张模型表Table 9-23, 9-23这张表是用来算由pStateIdx和valMPS构成的初始表的.

3,   然后就是初始化CABAC的初值, 下界指针,区间范围 (0x1FE)等等,可参考T264_cabac
_encode_init函数.

宏块级: 以下则是每个宏块都要做一次的, 这一级中会处理很多的语法元素, 这里我只用
前2个语法元素做为例子: mb_skip_flag, mb_type

4,   重整区间, 确保在区间在28-29内.这里的区间概念来自BAC,如果不明白先google算术
编码,然后再看上面那个参考文献.

5,   首先mb_skip_flag标志进行CABAC编码, 由于这个元素本身就是2值的,所以直接就可
以进行上下文模型选择了:

1. 由标准Table 9-24知道, P帧(这里要注意是slice_type==P, 不是mb_type)的这个元素
用11号表, B帧用24号表. 可以参考T264中的T264_cabac_mb_skip函数.

2. 由于这个元素只有1个bit, 因此只要算第一个bit的ctxIdxInc就可以了, 参考标准Tab
le 9-29, 可以看到表中11 和 24号表确实只有第一个bit是可用的, 根据9.3.3.1.1子条款
可以知道这一位的ctxIdxInc可能是0, 1, 2中的一个, 在T264中简单的说就是看左边和上
边宏块是否是Skip模式, 有一个是skip模式ctxIdxInc就加1. 也就是全不是skip为0, 有一
个skip则ctxIdxInc就是1,全是skip则ctxIdxInc就是2.

3. 下来就是算术编码部分了, 简单提一下基本原理: 在CABAC中为了减少RLPS = R*pLPS
这个区间变换公式的开销, 用128个有限状态(实际可用的为126)代替pLPS , 用rangeTab这
张表代替了RLPS, 见标准Table 9-33. 可以参考T264中的T264_cabac_encode_decision函
数, 看一下具体流程:

Ø          获得当前bin的pStateIdx和valMPS(来自片级计算的那张初始表)

Ø          根据标准9.3.3.2.1子条款, 求得qCodIRangeIdx用来索引表rangeTab
, 即可以求得变换后的区间了.

Ø          修正区间codIRange = codIRange – codIRangeLPS

Ø          判断当前的bin是否为最有可能的值, 如果不是(binVal!= valMPS),则
更新区间下边界codILow = codILow + codIRange, 同时修正区间codIRange = codIRange
LPS;然后判断当前状态, 如果已经达到状态0, 是则把valMPS的值取反(即0,1互换), 如果
还有没达到0, 则进行LPS的状态迁移,具体参看标准Table 9-34的状态迁移表中的transId
xLPS

Ø          如果当前的binVal值等于valMPS, 就比较简单了, 直接进行状态迁移
, MPS的状态迁移也很简单, 直接数据+1就可以了(最大63),具体参考标准Table 9-34的状
态迁移表中的transIdxMPS, 状态的转移其实就是修改了399个模型的初始表.

Ø          区间重整, 如果区间过小则输出一些bit, 这样可以不用把数据全部编
码完再输出, 可以编一部分输出一些.

Ø          已编码的2进制值bin总数+1, 即SymCnt+1, 这个值用于字节填充, 可
以参考标准9.3.4.6, 其中的BinCountsInNALunits就是指这个值,至此算术编码部分就结束
了.

6, 下面就是开始编码第二个语法元素了: mb_type

1)      不同于上一个语法元素mb_skip_flag, mb_type这个语法元素本身并不是二值化的
, 因此编码的第一步是进行二值化, 查阅标准Table 9-24可知mb_type的二值化方法需要参
考9.3.2.5子条款, 由该条款可知mb_type的二值化相对简单, 可以直接参考表得到, 例如
I片中某宏块mb_type为I_16x16时则二值化后bin0=1, 且非I_PCM则bin1=0, 亮度AC无非0系
数则bin2=0, 色度有非0系数则bin3=1, 色度DC,AC都无非0系数时,bin4=0, 帧内预测模式
为0时则bin5=0, bin6=0, 因此我们就得到了标准中I_16x16_0_1_0的二值化串为1001000


2)      下来是上下文模型选择, 由Table 9-24可知 I的mb_type元素起始表是3号表, 然
后参考标准表Table 9-29, 计算bin串中每个bin的ctxIdxInc值,由表可知bin0要参考9.3.
3.1.1.3才能确定ctxIdxInc值, 简单的说就是如果上边和左边的块模式有一个不是I_4x4则
ctxIdxInc++, 这样就有0,1,2这3种可能的结果了; bin1的ctxIdx固定是276, 直接就调用
了encoder_terminal模块, 即标准Figure 9-11的右分支;bin2的ctxIdxInc是3;bin3的ctx
IdxInc是4,; bin4和bin5要参考9.3.3.1.2子条款, 由该条款可知如果bin3是1则bin4的ct
xIdxInc=5,bin5的ctxIdxInc=6,如果bin3=0则bin4的ctxIdxInc =6,bin5的ctxIdxInc =7,
由于I_16x16_0_1_0的二值化串为1001000, 其中bin3=1, 由此可知此时bin4的ctxIdxInc
=5, bin5的ctxIdxInc =6; 最后由表9-29 中bin6的ctxIdxInc为7.至此二进制串所有位的
表号可以用3+的ctxIdxInc来得到了.

3)      下来就是算术编码部分,这个部分于mb_skip_flag的算术编码部分步骤一样.这里
要提的是, 第一步获得当前bin的pStateIdx和valMPS的模型表已经被更新了(是表被更新,
但不一定是当前表), 还记得么? 是在上一个语法元素的状态转移的时候更新的.

7, 下面的语法元素就是按照标准7.3.5的语法一一编码的, 其过程和上面2个语法元素的编
码过程大同小异,就不一一细述了.


……如此做完所有的宏块里的语法元素

又回到片级:

8, 写入end_of_slice_flag的标志,即完成标准Figure 9-11的左分支, 然后调用T264_cab
ac_encode_flush函数, 输出bit

9, 进行byte stuffing, 参考标准9.3.4.6 Byte stuffing process里的公式可以求得k,
在码流中填充k次的0x000003

10, 最后是模型更新,更换模型表(更改PB帧所用的模型表,而I帧所用的不变)
 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值