H264视频图像压缩编码算法主要基于DCT 变换和熵编码等基本算法。视频数据一般分成4×4像素块,先经DCT 变换,再进行量化和游程编码,最后得到编码后的压缩数据。
经过量化得到的4×4的DCT 系数具有一定的特征:较大的非零系数集中于4×4 块的左上角,而值为零的系数大多在右下角。所以,如果按表1 的顺序来扫描这个4×4块,映射成1× 16 的序列,则非零系数相对集中,通过游程编码就可以达到压缩的目的。上述用来扫描4×4块DCT 系数的表就叫做ZigZag 表,扫描的过程就称作ZigZag扫描。解码算法只要用这种规定的顺序来扫描,就可以正确的解码任何符合H264标准的码流。
/*
原扫描顺序(表1:ZigZag 表):
x 0 1 2 3
y 0 00 01 05 06
1 02 04 07 12
2 03 08 11 13
3 09 10 14 15
置乱后的扫描顺序(也即秘钥):
x 0 1 2 3
y 0 00 04 13 12
1 07 01 02 06
2 15 08 11 05
3 09 14 10 15
*/
之字形置乱加密的主导思想:在编码时,采用自己设计的一个随机置乱表代替标准中规定的ZigZag 扫描表,这样编出的视频数据如果用标准中的ZigZag表来扫描解码的话,得到的图像将是加密后的图像。要想得到正确的图像,必须要用编码时使用的表。因此,可以把编码时使用的扫描表当作密钥,从而实现视频加密。
代码修改:
1、 编码端(针对X264的代码进行修改)
将macroblock.c中的:
/*
static inline void scan_zigzag_4x4full( int level[16], int16_t dct[4][4] )
{
ZIG( 0,0,0) ZIG( 1,0,1) ZIG( 2,1,0) ZIG( 3,2,0)
ZIG( 4,1,1) ZIG( 5,0,2) ZIG( 6,0,3) ZIG( 7,1,2)
ZIG( 8,2,1) ZIG( 9,3,0) ZIG(10,3,1) ZIG(11,2,2)
ZIG(12,1,3) ZIG(13,2,3) ZIG(14,3,2) ZIG(15,3,3)
}//原scan_zigzag_4x4full扫描顺序
static inline void scan_zigzag_4x4( int level[15], int16_t dct[4][4] )
{
ZIG( 0,0,1) ZIG( 1,1,0) ZIG( 2,2,0)
ZIG( 3,1,1) ZIG( 4,0,2) ZIG( 5,0,3) ZIG( 6,1,2)
ZIG( 7,2,1) ZIG( 8,3,0) ZIG( 9,3,1) ZIG(10,2,2)
ZIG(11,1,3) ZIG(12,2,3) ZIG(13,3,2) ZIG(14,3,3)
}//原scan_zigzag_4x4扫描顺序
*/
修改为:
static inline void scan_zigzag_4x4full( int level[16], int16_t dct[4][4] )
{
ZIG( 0,0,0) ZIG( 4,0,1) ZIG( 7,1,0) ZIG(15,2,0)
ZIG( 1,1,1) ZIG(13,0,2) ZIG(12,0,3) ZIG( 2,1,2)
ZIG( 8,2,1) ZIG( 9,3,0) ZIG(14,3,1) ZIG(11,2,2)
ZIG( 6,1,3) ZIG( 5,2,3) ZIG(10,3,2) ZIG( 3,3,3)
}//置乱后的scan_zigzag_4x4full扫描顺序
static inline void scan_zigzag_4x4( int level[15], int16_t dct[4][4] )
{
ZIG( 3,0,1) ZIG( 6,1,0) ZIG(14,2,0)
ZIG( 0,1,1) ZIG(12,0,2) ZIG(11,0,3) ZIG( 1,1,2)
ZIG( 7,2,1) ZIG( 8,3,0) ZIG(13,3,1) ZIG(10,2,2)
ZIG( 5,1,3) ZIG( 4,2,3) ZIG( 9,3,2) ZIG( 2,3,3)
}//置乱后的扫描顺序,由置乱后的scan_zigzag_4x4full减1即可.
2、 在解码端;(针对从FFMPEG中抽取的264解码代码进行修改)
修改头文件h264data.h中的代码:
/*
static const uint8_t zigzag_scan[16]={
0+0*4, 1+0*4, 0+1*4, 0+2*4,
1+1*4, 2+0*4, 3+0*4, 2+1*4,
1+2*4, 0+3*4, 1+3*4, 2+2*4,
3+1*4, 3+2*4, 2+3*4, 3+3*4,
};//原zigzag扫描顺序
修改为:
static const uint8_t zigzag_scan[16]={
0+0*4, 1+1*4, 2+1*4, 3+3*4,
1+0*4, 3+2*4, 3+1*4, 0+1*4,
1+2*4, 0+3*4, 2+3*4, 2+2*4,
3+0*4, 2+0*4, 1+3*4, 0+2*4,
};//置乱后的扫描顺序
*/
缺点体现在以下两点:
1、 由于只使用了一张置乱序列表,且为4X4的,所以破解置乱表的难度不高。即使使用两张表,破解难度也只是增加了一倍,只要破解出一个表,另一个表也就不难了。
2、 对于DC 系数,其值远大于AC 系数值,因此乱序后很容易被识别。
改进方案:
1、 在每次出现I帧的时候改变置乱序列表,提高置乱序列表的破解难度。或者用序列发生器,每帧都分配一个随机置乱序列表。
2、 对DC我们一次提取一帧图像里面的所有DC系数,用高强度的加密算法AES加密,加密后的DC再按顺序返回到码流中。到解码端,再按照同样的操作提取DC系数,然后解密。(适用于加密传输的应用中)