CAVLC全称Context-Adaptive Variable-Length Coding,中文译为“基于上下文自适应的可变长编码”。
一、初始化
图中,初始化包含了
- 将数据按 zig-zag 序列重新排序;
- 计算非零系数的数目(TotalCoeffs);
- 计算拖尾系数的数目(TrailingOnes);
- 计算最后一个非零系数前零的数目(TotalZeros)。
拖尾系数是指序列中±1的数目,最多3个。如果超过3个,则最后3个为拖尾系数,其余的当做普通的非零系数处理。
例1. 设有一个4×4块数据
| 0 | 3 | -1 | 0 |
| 0 | -1 | 1 | 0 |
| 1 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 |
将其按zig-zag排序后为:
0,3,0,1,-1,-1,0,1,0,0,0,0,0,0,0,0
可计算出:TotalCoeffs = 5,TrailingOnes = 3,TotalZeros = 3。
二、计算NC
NC (Number Current,当前块值)。对于色度的直流系数,NC = -1。其余的情况则根据当前块左边4×4块的非零系数数目NA和当前块上方4×4块的非零系数数目NB求得。当前块的值为:NA、NB中与其同属一个片且可用的块的值的平均值,若没有同属一个片且可用的块则为0。
根据NC的值查表获取编码值,表1存放在JM中文件vlc.c中函数 int writeSyntaxElement_NumCoeffTrailingOnesChromaDC(VideoParameters *p_Vid, SyntaxElement *se, DataPartition *dp) 内。对应的是色度的直流系数。
int writeSyntaxElement_NumCoeffTrailingOnesChromaDC(VideoParameters *p_Vid, SyntaxElement *se, DataPartition *dp) { static const byte lentab[3][4][17] = { //YUV420 {{ 2, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 1, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 3, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //YUV422 {{ 1, 7, 7, 9, 9,10,11,12,13, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 2, 7, 7, 9,10,11,12,12, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 3, 7, 7, 9,10,11,12, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 5, 6, 7, 7,10,11, 0, 0, 0, 0, 0, 0, 0, 0}}, //YUV444 {{ 1, 6, 8, 9,10,11,13,13,13,14,14,15,15,16,16,16,16}, { 0, 2, 6, 8, 9,10,11,13,13,14,14,15,15,15,16,16,16}, { 0, 0, 3, 7, 8, 9,10,11,13,13,14,14,15,15,16,16,16}, { 0, 0, 0, 5, 6, 7, 8, 9,10,11,13,14,14,15,15,16,16}} }; static const byte codtab[3][4][17] = { //YUV420 {{ 1, 7, 4, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 1, 6, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //YUV422 {{ 1,15,14, 7, 6, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 1,13,12, 5, 6, 6, 6, 5, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 1,11,10, 4, 5, 5, 4, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 1, 1, 9, 8, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0}}, //YUV444 {{ 1, 5, 7, 7, 7, 7,15,11, 8,15,11,15,11,15,11, 7, 4}, { 0, 1, 4, 6, 6, 6, 6,14,10,14,10,14,10, 1,14,10, 6}, { 0, 0, 1, 5, 5, 5, 5, 5,13, 9,13, 9,13, 9,13, 9, 5}, { 0, 0, 0, 3, 3, 4, 4, 4, 4, 4,12,12, 8,12, 8,12, 8}} }; int yuv = p_Vid->yuv_format - 1; // se->value1 : numcoeff // se->value2 : numtrailingones se->len = lentab[yuv][se->value2][se->value1]; se->inf = codtab[yuv][se->value2][se->value1]; .... }
NC ≠ –1时查表2,表2对应函数如下:
int writeSyntaxElement_NumCoeffTrailingOnes(SyntaxElement *se, DataPartition *dp) { static const byte lentab[3][4][17] = { { // 0702 { 1, 6, 8, 9,10,11,13,13,13,14,14,15,15,16,16,16,16}, { 0, 2, 6, 8, 9,10,11,13,13,14,14,15,15,15,16,16,16}, { 0, 0, 3, 7, 8, 9,10,11,13,13,14,14,15,15,16,16,16}, { 0, 0, 0, 5, 6, 7, 8, 9,10,11,13,14,14,15,15,16,16}, }, { { 2, 6, 6, 7, 8, 8, 9,11,11,12,12,12,13,13,13,14,14}, { 0, 2, 5, 6, 6, 7, 8, 9,11,11,12,12,13,13,14,14,14}, { 0, 0, 3, 6, 6, 7, 8, 9,11,11,12,12,13,13,13,14,14}, { 0, 0, 0, 4, 4, 5, 6, 6, 7, 9,11,11,12,13,13,13,14}, }, { { 4, 6, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9, 9,10,10,10,10}, { 0, 4, 5, 5, 5, 5, 6, 6, 7, 8, 8, 9, 9, 9,10,10,10}, { 0, 0, 4, 5, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,10,10,10}, { 0, 0, 0, 4, 4, 4, 4, 4, 5, 6, 7, 8, 8, 9,10,10,10}, }, }; static const byte codtab[3][4][17] = { { { 1, 5, 7, 7, 7, 7,15,11, 8,15,11,15,11,15,11, 7,4}, { 0, 1, 4, 6, 6, 6, 6,14,10,14,10,14,10, 1,14,10,6}, { 0, 0, 1, 5, 5, 5, 5, 5,13, 9,13, 9,13, 9,13, 9,5}, { 0, 0, 0, 3, 3, 4, 4, 4, 4, 4,12,12, 8,12, 8,12,8}, }, { { 3,11, 7, 7, 7, 4, 7,15,11,15,11, 8,15,11, 7, 9,7}, { 0, 2, 7,10, 6, 6, 6, 6,14,10,14,10,14,10,11, 8,6}, { 0, 0, 3, 9, 5, 5, 5, 5,13, 9,13, 9,13, 9, 6,10,5}, { 0, 0, 0, 5, 4, 6, 8, 4, 4, 4,12, 8,12,12, 8, 1,4}, }, { {15,15,11, 8,15,11, 9, 8,15,11,15,11, 8,13, 9, 5,1}, { 0,14,15,12,10, 8,14,10,14,14,10,14,10, 7,12, 8,4}, { 0, 0,13,14,11, 9,13, 9,13,10,13, 9,13, 9,11, 7,3}, { 0, 0, 0,12,11,10, 9, 8,13,12,12,12, 8,12,10, 6,2}, }, }; int vlcnum = se->len; // se->value1 : numcoeff // se->value2 : numtrailingones if (vlcnum == 3) // nC >= 8 { se->len = 6; // 4 + 2 bit FLC if (se->value1 > 0) { se->inf = ((se->value1-1) << 2) | se->value2; } else { se->inf = 3; } } else // nC >=0 && nC < 8 { se->len = lentab[vlcnum][se->value2][se->value1]; se->inf = codtab[vlcnum][se->value2][se->value1]; } .... }
例2. 接例1,若NC = 0时,求TotalCoeffs和TrailingOnes的编码以及每个拖尾系数的符号。
TotalCofeffs = 5,TraillingOnes = 3,NC = 0,查表2,得编码为0000100。
拖尾系数从高频到低频(逆序)分别为:+1,-1,-1。正号用0表示,负号用1表示得编码为011。
所以至此编码为 0000100 011。
三、非零系数幅值编码
只编码不包含拖尾系数的非零系数。计算方法为:
1. 将有符号的Level[i]转化为无符号的LevelCode:
if(Level[i] > 0) // + LevelCode = (Level[i] << 1) - 2; else // - LevelCode = (Level[i] << 1) - 1;
2. 计算前缀与后缀:
LevelPrefix = LevelCode / (1 << SuffixLength); LevelSuffix = LevelCode % (1 << SuffixLength);
3. 根据前缀计算bit string :
bitStringLen = LevelPrefix + 1; // length of bit string bitStringCode = 1; // value of bit string, constant 1
4. 更新SuffixLength,根据SuffixLength确定后缀长度,初始状态SuffixLength = 0:
if(SuffixLength == 0) SuffixLength++; else if((LevelCode > (3 << SuffixLength - 1)) && (SuffixLength < 6)) SuffixLength++;
例3. 接例2,对非零系数幅值编码。
序列中非零系数有:3,1。从高频开始,先编码1。
初始状态SuffixLength = 0,Level[0] = 1,可得LevelCode = 0,计算前缀LevelPrefix = 0,得bit string = 1。由于Level[0]为第一个数,没有后缀。
更新SuffixLength,SuffixLength = 1。同样可得3的LevelCode = 4,LevelPrefix = 2,得bit string = 001。LevelSuffix = 0,SuffixLength = 1,所以后缀为0。编码为0010。
至此编码为 0000100 011 10010。
四、最后一个非零系数前零数目编码
根据最后一个非零系数前零数目TotalZeros与非零系数数目可以查表3获得编码值,表3对应下面函数。
int writeSyntaxElement_TotalZeros(SyntaxElement *se, DataPartition *dp) { static const byte lentab[TOTRUN_NUM][16] = { { 1,3,3,4,4,5,5,6,6,7,7,8,8,9,9,9}, { 3,3,3,3,3,4,4,4,4,5,5,6,6,6,6}, { 4,3,3,3,4,4,3,3,4,5,5,6,5,6}, { 5,3,4,4,3,3,3,4,3,4,5,5,5}, { 4,4,4,3,3,3,3,3,4,5,4,5}, { 6,5,3,3,3,3,3,3,4,3,6}, { 6,5,3,3,3,2,3,4,3,6}, { 6,4,5,3,2,2,3,3,6}, { 6,6,4,2,2,3,2,5}, { 5,5,3,2,2,2,4}, { 4,4,3,3,1,3}, { 4,4,2,1,3}, { 3,3,1,2}, { 2,2,1}, { 1,1}, }; static const byte codtab[TOTRUN_NUM][16] = { {1,3,2,3,2,3,2,3,2,3,2,3,2,3,2,1}, {7,6,5,4,3,5,4,3,2,3,2,3,2,1,0}, {5,7,6,5,4,3,4,3,2,3,2,1,1,0}, {3,7,5,4,6,5,4,3,3,2,2,1,0}, {5,4,3,7,6,5,4,3,2,1,1,0}, {1,1,7,6,5,4,3,2,1,1,0}, {1,1,5,4,3,3,2,1,1,0}, {1,1,1,3,3,2,2,1,0}, {1,0,1,3,2,1,1,1,}, {1,0,1,3,2,1,1,}, {0,1,1,2,1,3}, {0,1,1,1,1}, {0,1,1,1}, {0,1,1}, {0,1}, }; int vlcnum = se->len; // se->value1 : TotalZeros se->len = lentab[vlcnum][se->value1]; se->inf = codtab[vlcnum][se->value1]; .... }
色度的直流系数则为表4,对应下面函数:
int writeSyntaxElement_TotalZerosChromaDC(VideoParameters *p_Vid, SyntaxElement *se, DataPartition *dp) { static const byte lentab[3][TOTRUN_NUM][16] = { //YUV420 {{ 1,2,3,3}, { 1,2,2}, { 1,1}}, //YUV422 {{ 1,3,3,4,4,4,5,5}, { 3,2,3,3,3,3,3}, { 3,3,2,2,3,3}, { 3,2,2,2,3}, { 2,2,2,2}, { 2,2,1}, { 1,1}}, //YUV444 {{ 1,3,3,4,4,5,5,6,6,7,7,8,8,9,9,9}, { 3,3,3,3,3,4,4,4,4,5,5,6,6,6,6}, { 4,3,3,3,4,4,3,3,4,5,5,6,5,6}, { 5,3,4,4,3,3,3,4,3,4,5,5,5}, { 4,4,4,3,3,3,3,3,4,5,4,5}, { 6,5,3,3,3,3,3,3,4,3,6}, { 6,5,3,3,3,2,3,4,3,6}, { 6,4,5,3,2,2,3,3,6}, { 6,6,4,2,2,3,2,5}, { 5,5,3,2,2,2,4}, { 4,4,3,3,1,3}, { 4,4,2,1,3}, { 3,3,1,2}, { 2,2,1}, { 1,1}} }; static const byte codtab[3][TOTRUN_NUM][16] = { //YUV420 {{ 1,1,1,0}, { 1,1,0}, { 1,0}}, //YUV422 {{ 1,2,3,2,3,1,1,0}, { 0,1,1,4,5,6,7}, { 0,1,1,2,6,7}, { 6,0,1,2,7}, { 0,1,2,3}, { 0,1,1}, { 0,1}}, //YUV444 {{1,3,2,3,2,3,2,3,2,3,2,3,2,3,2,1}, {7,6,5,4,3,5,4,3,2,3,2,3,2,1,0}, {5,7,6,5,4,3,4,3,2,3,2,1,1,0}, {3,7,5,4,6,5,4,3,3,2,2,1,0}, {5,4,3,7,6,5,4,3,2,1,1,0}, {1,1,7,6,5,4,3,2,1,1,0}, {1,1,5,4,3,3,2,1,1,0}, {1,1,1,3,3,2,2,1,0}, {1,0,1,3,2,1,1,1,}, {1,0,1,3,2,1,1,}, {0,1,1,2,1,3}, {0,1,1,1,1}, {0,1,1,1}, {0,1,1}, {0,1}} }; int vlcnum = se->len; int yuv = p_Vid->yuv_format - 1; // se->value1 : TotalZeros se->len = lentab[yuv][vlcnum][se->value1]; se->inf = codtab[yuv][vlcnum][se->value1]; .... }
例4. 接例3,对最后一个非零系数前零数目编码。
TotalCoeffs = 5,TotalZeros = 3,查表3得TotalZeros的编码为:111。
至此,编码为:0000100 011 10010 111。
五、对每个非零系数前零数目编码
- 从高频开始,逆序编码。
- 最后一个非零系数前零数目不需要编码;
- ZerosLeft储存还没编码的零数目,RunBefore储存当前非零系数前零数目。
- 查表5获得,表5对应函数如下:
int writeSyntaxElement_Run(SyntaxElement *se, DataPartition *dp) { static const byte lentab[TOTRUN_NUM][16] = { {1,1}, {1,2,2}, {2,2,2,2}, {2,2,2,3,3}, {2,2,3,3,3,3}, {2,3,3,3,3,3,3}, {3,3,3,3,3,3,3,4,5,6,7,8,9,10,11}, }; static const byte codtab[TOTRUN_NUM][16] = { {1,0}, {1,1,0}, {3,2,1,0}, {3,2,1,1,0}, {3,2,3,2,1,0}, {3,0,1,3,2,5,4}, {7,6,5,4,3,2,1,1,1,1,1,1,1,1,1}, }; int vlcnum = se->len; // se->value1 : RunBefore // vlcnum : ZerosLeft - 1, means {1,1} correspond to vlcnum = 0, zerosLeft = 1 se->len = lentab[vlcnum][se->value1]; se->inf = codtab[vlcnum][se->value1]; .... }
例5. 接例4,序列为:0,3,0,1,-1,-1,0,1,0,0,0,0,0,0,0,0。
根据表5的信息可得:
| Source | ZeroLeft | RunBefore | Code |
| 1 | 3 | 1 | 10 |
| -1 | 2 | 0 | 1 |
| -1 | 2 | 0 | 1 |
| 1 | 2 | 1 | 01 |
| 3 | 1 | 1 | N/A |
至此,编码为:0000100 011 10010 111 101101。

浙公网安备 33010602011771号