H.264 CAVLC学习笔记
编码过程:1) 初始值设定:2) 编码coeff_token:3) 编码所有TrailingOnes的符号:4) 编码除了拖尾系数以外非零系数幅值Levels:(1)将有符号的Level转换成无符号的levelCode(2)计算level_prefix:(3)计算level_suffix:(4)根据suffixLength的值来确定后缀的长度;(5)更新suffixLength值
5)编码最后一个非零系数前零的数目(TotalZeros):6) 对每个非零系数前零的个数(RunBefore)进行编码:7)mv等参数
解码过程:1、Coeff_token2、Level[i]3、total_zeros4、剩下的元素用0补齐5、mv等参数
本文和复旦大学开源的h264_enc代码对应
编码过程:
假设有一个4*4数据块 { 0, 3, -1, 0, 0, -1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0 } 数据重排列:0,3,0,1,-1,-1,0,1,0……
1) 初始值设定:
//TotalCoefZero.v //TrailingOne.v //NC_compute.v
非零系数的数目(TotalCoeffs) = 5;
非零系数为{3,1,-1,-1,1}
拖尾系数的数目(TrailingOnes)= 3;
(+1)和(-1)在非零系数中的数量是4个,trailingones的范围是[0,3]
所以最后trailingones的取值是3
最后一个非零系数前零的数目(Total_zeros) = 3;
最后一个非零系数是1,前面有3个0,所以total_zeros取值是3
Nc = 1
Nc:当前块值(Number current)。除色度的直流系数外,其它系数类型Nc值是根据当前左边
4x4块的非零系数数目(NA)和当前块上面4x4的非零系数的数目(NB)求得的。
如果左边存在4x4块,上边也存在4x4块,Nc = (NA + NB + 1) >> 1
如果左边不存在4x4块,上边存在4x4块,Nc = NB
如果左边存在4x4块,上边不存在4x4块,Nc = NA
如果左边和上边都不存在4x4块,Nc = 0
当调用色度直流系数时,Nc由YUV格式确定
4:2:0时,Nc = -1
4:2:2时,Nc = -2
YUV格式会确定另外一个参数
4:2:0时,ChromaArrayType = 1
4:2:2时,ChromaArrayType = 2
4:4:4时,ChromaArrayType = 3
other,ChromaArrayType = 0
suffixLength = 0;
i = TotalCoeffs = 5;
2) 编码coeff_token:
//Coeff_token_enc.v
查标准(Rec.ITU-T H.264)Table 9-5,可得:
If (TotalCoeffs == 5 && TrailingOnes == 3 && 0 <= NC < 2)
coeff_token = 0000 100;
code_bit = 0000 100;
3) 编码所有TrailingOnes的符号:
// Coeff_Sign_packer.v
逆序编码,三个拖尾系数的符号依次是+(0),-(1),-(1);
即:
TrailingOne sign[2] = 0;
TrailingOne sign[1] = 1;
TrailingOne sign[0] = 1;
tmpCodeBit = 0000 1000 11;
tmpCodeBit = {code_bit,TrailingOne sign[2],TrailingOne sign[1],TrailingOne sign[0]}
Coeff_Sign_packer.v输出的是CoeffSignCodeBit,
如上所示,tmpCodeBit的位数是10bit,
CoeffsignCodeBit = {tmpCodeBit,(19-10){1'b0}}
CoeffsignCodeBit = 0000 1000 1100 0000 000
与拖尾系数对应的levels为{1,-1,-1}
当TrailingOne sign为0时,对应level为1
当TrailingOne sign为1时,对应level为-1
4) 编码除了拖尾系数以外非零系数幅值Levels:
//level_enc.v 过程如下:
(1)将有符号的Level转换成无符号的levelCode
如果Level是正的,levelCode = (Level<<1) – 2;
如果Level是负的,levelCode = - (Level<<1) – 1;
如果TrailingOnes小于3,那么第一个非TrailingOnes的非零系数必不为 +1、-1,
为了节省编码比特,将其幅值减1。也就是如果level为正,level=level-1;
否则,level=level+1。然后再按(1)
(2)计算level_prefix:
如果TotalCoeffs>10,且TrailingOnes<3,那么suffixlength初始化为1;
否则suffixlength初始化为0
level_prefix = levelCode / (1<<suffixLength);
查表9-6可得所对应的bit string;
(3)计算level_suffix:
level_suffix = levelCode % (1<<suffixLength);
(4)根据suffixLength的值来确定后缀的长度;
(5)更新suffixLength值
更新suffixLength函数如下:
If ( suffixLength == 0 )
suffixLength++;
else if ( levelCode > (3<<(suffixLength-1)) && suffixLength <6)
suffixLength++;
回到例子中,依然按照逆序,
按逆序除了拖尾系数之外的非零系数为1,3;suffixLength的初始值为0
Level[i--] = 1;(i-- = 1)
levelCode = (Level[i] << 1)- 2 = (1 << 1) - 2 = 2 - 2 = 0;
level_prefix =levelCode / (1<< suffixLength)= 0 / (1 << 0) = 0
level_suffix = levelCode%(1 << suffixLength)= 0 % (1 << 0) = 0
查表9-6,可得level_prefix = 0时对应的bit string = 1;
因为suffixLength初始化为0,故该Level没有后缀;
因为suffixLength = 0,故suffixLength++;
Code = 0000 1000 111;
Code = {tmpCodeBit, bit string};
编码下一个Level:Level[0] = 3;
levelCode = (Level[i] << 1)- 2 = (3 << 1) - 2 = 6 - 2 = 4;
level_prefix =levelCode / (1<< suffixLength)= 4 / (1 << 1) = 2;
查表得bit string = 001;
level_suffix = levelCode%(1 << suffixLength)= 4 % (1 << 1)=0;
SuffixLength = 1,level_suffix的Code = 0;
Code = 0000 1000 1110 010;
Code = {Code,bit string,level_suffix}
i = 0,编码Level结束。
5)编码最后一个非零系数前零的数目(TotalZeros):
// TotalZeros_enc.v
TotalZeros指的是在最后一个非零系数前零的数目,此非零系数指的是按照正向扫描的最后一
个非零系数。例如:已知一串系数0 0 5 0 3 0 0 0 1 0 0 -1 0 0 0 0,最后一个非零系数是-1,
TotalZeros的值等于 2+3+1+2=8。因为非零系数数目TotalCoeffs是已知的,这就决定了
TotalZeros可能的最大值。编码见 H264标准Table9-7,Table9-8,Table9-9
查表9-7,当TotalCoeffs = 5,total_zero = 3时,bit string = 111;
tzVlcIndex = TotalCoeffs
Code = 0000 1000 1110 0101 11
Code = {Code,bit string}
6) 对每个非零系数前零的个数(RunBefore)进行编码:
ZerosLeft:当前系数之前所有零的个数;
Run_before:紧接当前系数前零的个数;
每个非零系数前零的个数RunBefore是按照倒序来进行编码的,从最高频的非零系数开始。
RunBefore在以下两种情况下是不需要编码的:
1、最后一个非零系数(在低频位置上)前零的个数;
2、如果没有剩余的零需要编码(即所有的RunBefore求和等于TotalZeros)时,没有必要再进行 RunBefore的编码。
每个非零系数前零的个数的编码是依赖于ZeroLeft的值,ZeroLeft的初始值等于TotalZeros,在每个非零系数的RunBefore值编码后进行更新。
按照逆序进行编码: 0,3,0,1,-1,-1,0,1,0,0,0,0,0,0,0,0
第一个非零系数1,之前所有零的个数ZerosLeft = 3,1之前零的个数 run_before = 1,
ZerosLeft == 3 && run_before == 1 ,查表得到run_before[4] = 10;
第二个非零系数-1,ZerosLeft = 2, run_before = 0,
ZerosLeft == 2 && run_before == 0 ,查表得到run_before[3] = 1;
第三个非零系数-1,ZerosLeft = 2, run_before = 0,
ZerosLeft == 2 && run_before == 0 ,查表得到run_before[2] = 1;
第四个非零系数1,ZerosLeft = 2, run_before = 1,
ZerosLeft == 2 &&run_before == 1 ,查表得到run_before[1] = 01;
第五个非零系数3,ZerosLeft = 1, run_before = 1,run_before[0]不需要码流来表示;
Code = 0000 1000 1110 0101 1110 1101;
Code = {Code,run_before[4],run_before[3],run_before[2],run_before[1]}
编码完毕。
7)mv等参数
//Bit_stream_packer.v P135
mv等参数,先要进行编码,生成17bit的编码数据(headcode)。先将headcode后面
补59bit 0补成76bit输出,再输出由4x4矩阵生成的编码数据。
解码过程:
{ 0,3,-1,0, 0,-1,1,0, 1,0,0,0, 0,0,0,0 }NC=1,编码后得到的序列为:0000 1000 1110 010 11110 1101
1、Coeff_token
查表得到Coeff_token(TrailingOnes,TotalCoeff)的值;
NC=1,查表9-5,依次对应序列中的每一位,表中存在0000100,不存在00001000,
0000100对应的TrailingOnes = 3, TotalCoeff = 5。
输出序列:无
2、Level[i]
if(totalCoeff > 0){
suffixLength = 0;
for(i=0;i < TotalCoeff;i++){
}
traling_ones_sign_flag = {0 1 1}
其长度是TrailingOnes = 3,位于0000100后那3bit
i = 0 时,0<3 --> trailing_ones_sign_flag = 0 , level[0] = 1-2*0=1;
i = 1 时,1<3 --> trailing_ones_sign_flag = 1 , level[1] = 1-2*1=-1;
i = 2 时,2<3 --> trailing_ones_sign_flag = 1 , level[2] = 1-2*1=-1;
i = 3 时, 查表9-6 码流 1 对应level_prefix = 0,
suffixLength初始值为0
计算levelSuffixSize:(后缀是长度为levelSuffixSize的无符号整数)
除了以下两种情况levelSuffixSize = SuffixLength:
1)level_prefix == 14 && suffixLength == 0时,levelSuffixSize = 4;
2)level_prefix >= 15时,levelSuffixSize = level_prefix - 3;
此时,用例得出的levelSuffixSize = 0
计算level_suffix:
如果levelSuffixSize>0,level_suffix为编码序列后续的levelSuffixSize位bit
如果levelSuffixSize=0,level_suffix = 0
此时,用例得出的level_suffix = 0
levelCode = Min( 15, level_prefix ) << suffixLength ) + level_suffix
if (level_prefix >= 15 && suffixLength == 0)
levelCode = levelCode + 15
if (level_prefix >= 16)
levelCode = levelCode + (1<<( level_prefix − 3 )) − 4096
if (i == TrailingOnes && TrailingOnes < 3)
levelCode = levelCode + 2
此时levelCode = 0<<0+0 = 0
如果levelCode是奇数
level[i] = (-levelCode - 1) >> 1
如果levelCode是偶数
level[i] = (levelCode + 2) >> 1
此时,level[3] = (levelCode + 2) >> 1 = (0+2)>>1 = 1;
更新suffixLength
If ( suffixLength == 0 )
suffixLength++;
else if ( levelCode > (3<<(suffixLength-1)) && suffixLength <6)
suffixLength++;
此时,suffixLength = 1;
i = 4 时, 查表9-6 码流 001 对应level_prefix = 2,
levelSuffixSize = 1
levelSuffix为0000 1000 1110 01后面的那1bit,即为0
levelCode = 2 << 1 + 0 = 4 ;
level[4] = (levelCode + 2) >> 1 = (4+1) >> 1 = 3;
输出序列:level[i--] = 3,1,-1,-1,1
3、total_zeros
根据TotalCoeffs = 5 ,输入码流 111 ,查表9-7得到Total_Zeros = 3;
zeroLeft = total_zeros = 3;
for(i = 0; i <TotalCoeff:i++){
}
i = 0,zeroLeft = 3,根据码流 10 ,查表9-10得到run_before = 1, run[0] = run_before = 1;
zeroLeft = zeroLeft - run[0] = 3 - 1 =2;
i = 1,zeroLeft = 2,根据码流 1,查表9-10得到run_before = 0, run[1] = run_before = 0;
zeroLeft = zeroLeft - run[1] = 2 - 0 =2;
i = 2 , zeroLeft = 2, 根据码流 1,查表9-10得到run_before = 0, run[2] = run_before = 0;
zeroLeft = zeroLeft - run[2] = 2 - 0 =2;
i = 3 , zeroLeft = 2, 根据码流 01,查表9-10得到run_before = 1,run[3] = run_before = 1;
zeroLeft = zeroLeft - run[3] = 2 - 1 =1;
run[TotalCoeff-1] = run[4] = zeroLeft = 1;
coeffNum = -1;
for(i = TotalCoeff - 1; i >= 0:i++){
}
i = 4, coeffNum = coeffNum + (run[4] + 1) = -1 + (1 + 1) = 1; coeffLevel[1] = level[4] = 3;
i = 3, coeffNum = coeffNum + (run[3] + 1) = 1 + (1 + 1) = 3; coeffLevel[3] = level[3] = 1;
i = 2, coeffNum = coeffNum + (run[2] + 1) = 3 + (0 + 1) = 4; coeffLevel[4] = level[2] = -1;
i = 1, coeffNum = coeffNum + (run[1] + 1) = 4 + (0 + 1) = 5; coeffLevel[5] = level[1] = -1;
i = 0, coeffNum = coeffNum + (run[0] + 1) = 5 + (1 + 1) = 7; coeffLevel[7] = level[0] = 1;
输出序列:coeffLevel[i++] = 0,3,0,1,-1,-1,0,1;
4、剩下的元素用0补齐
输出序列为0,3,0,1,-1,-1,0,1,0,0,0,0,0,0,0,0,
按照Zigzag序列可得到4*4矩阵
{
0,3,-1,0,
0,-1,1,0,
1,0,0,0,
0,0,0,0
}
解码完毕!
5、mv等参数
mv等参数是在headerbit中,由17bit编码和59bit0组成,要确保一次性将所有
header读入,每次读出的数据位宽必须为80bits(10bytes)。