源码阅读 x264 - 帧内预测
本文分析 x264
库中的帧内预测的 C
语言函数。
16x16 预测
x264
代码通过 x264_predict_16x16_init()
函数初始化 Intra16x16
帧内预测汇编函数,初始化的预测模式如下:
intra16x16Predmode |
Name of Intra16x16PredMode |
Note |
Function |
---|---|---|---|
0 |
Intra_16x16_Vertical |
由上边像素推出相应像素值 | x264_predict_16x16_v_c |
1 |
Intra_16x16_Horicontal |
由左边像素推出相应像素值 | x264_predict_16x16_h_c |
2 |
Intra_16x16_DC |
由上边和左边像素平均值推出相应像素值 | x264_predict_16x16_dc_c |
3 |
Intra_16x16_Plane |
利用线性 plan 函数及左、上像素推出相应像素值,适用于亮度变化平缓区域 | x264_predict_16x16_p_c |
Intra_16x16_Vertical
在 SPEC 中,关于该预测模式的定义如下:
x264
中关于模式 Intra_4x4_Vertical
的代码如下:
/* common/predict.c */
void x264_predict_16x16_v_c(pixel *src) {
// pixel4 实际上是 uint32_t(占用 32bit),存储 4 个像素的值(每个像素占用 8bit)
pixel4 v0 = MPIXEL_X4(&src[ 0 - FDEC_STRIDE]);
pixel4 v1 = MPIXEL_X4(&src[ 4 - FDEC_STRIDE]);
pixel4 v2 = MPIXEL_X4(&src[ 8 - FDEC_STRIDE]);
pixel4 v3 = MPIXEL_X4(&src[12 - FDEC_STRIDE]);
/*
* Vertical 预测方式
* |X1 X2 X3 X4
* --+-----------
* |X1 X2 X3 X4
* |X1 X2 X3 X4
* |X1 X2 X3 X4
* |X1 X2 X3 X4
*
* 展开宏定义如下:
* uint32_t v0 = ((x264_union32_t*)(&src[ 0-FDEC_STRIDE]))->i;
* uint32_t v1 = ((x264_union32_t*)(&src[ 4-FDEC_STRIDE]))->i;
* uint32_t v2 = ((x264_union32_t*)(&src[ 8-FDEC_STRIDE]))->i;
* uint32_t v3 = ((x264_union32_t*)(&src[12-FDEC_STRIDE]))->i;
* x264_union32_t 的定义如下:
* typedef union {uint64_t i; uint32_t d[2]; uint16_t w[4]; uint8_t b[8]; } MAY_ALIAS x264_union64_t;
* 即将一行 16 字节数据分成 4 次,每次取出 4 个像素(一共 16 个像素),分别赋值给 v0,v1,v2,v3
* 取出的值源自于 16x16 块上面的一行像素
* 0| 4 8 12 16
* || v0 | v1 | v2 | v3 |
* ---++==========+==========+==========+==========+
* ||
* ||
*/
for (int i = 0; i < 16; i++) {
MPIXEL_X4(src + 0) = v0;
MPIXEL_X4(src + 4) = v1;
MPIXEL_X4(src + 8) = v2;
MPIXEL_X4(src + 12) = v3;
/* 展开宏定义如下:
* ((x264_union32_t*)(src + 0))->i = v0;
* ((x264_union32_t*)(src + 4))->i = v1;
* ((x264_union32_t*)(src + 8))->i = v2;
* ((x264_union32_t*)(src + 12))->i = v3;
* 即分成 4 次,每次赋值 4 个像素
*/
src += FDEC_STRIDE;
}
}
Intra_16x16_Horicontal
在 SPEC 中,关于该预测模式的定义如下:
x264
中关于模式 Intra_16x16_Horicontal
的代码如下:
#define PIXEL_SPLAT_X4(x) ((x)*0x01010101U)
void x264_predict_16x16_h_c( pixel *src ) {
for ( int i = 0; i < 16; i++ ) {
const pixel4 v = PIXEL_SPLAT_X4( src[-1] );
MPIXEL_X4( src+ 0 ) = v;
MPIXEL_X4( src+ 4 ) = v;
MPIXEL_X4( src+ 8 ) = v;
MPIXEL_X4( src+12 ) = v;
/* 展开宏定义如下:
* uint32_t v = src[-1] * 0x01010101U;
* ((x264_union32_t*)(src + 0))->i = v;
* ((x264_union32_t*)(src + 4))->i = v;
* ((x264_union32_t*)(src + 8))->i = v;
* ((x264_union32_t*)(src + 12))->i = v;
*/
src += FDEC_STRIDE;
}
}
Intra_16x16_DC
在 SPEC 中,关于该预测模式的定义如下:
x264
中关于模式 Intra_16x16_DC
的代码如下:
#define PREDICT_16x16_DC(v)\
for( int i = 0; i < 16; i++ )\
{\
MPIXEL_X4( src+ 0 ) = v;\
MPIXEL_X4( src+ 4 ) = v;\
MPIXEL_X4( src+ 8 ) = v;\
MPIXEL_X4( src+12 ) = v;\
src += FDEC_STRIDE;\
}
void x264_predict_16x16_dc_c( pixel *src ) {
int dc = 0;
for( int i = 0; i < 16; i++ ) {
dc += src[-1 + i * FDEC_STRIDE];
dc += src[i - FDEC_STRIDE];
}
pixel4 dcsplat = PIXEL_SPLAT_X4( ( dc + 16 ) >> 5 );
PREDICT_16x16_DC( dcsplat );
/* 展开宏定义如下:
* uint32_t dcsplat = ((dc + 16) >> 5) * 0x01010101U;
* for ( int i = 0; i < 16; i++ ) {
* ((x264_union32_t*)(src + 0))->i = v;
* ((x264_union32_t*)(src + 4))->i = v;
* ((x264_union32_t*)(src + 8))->i = v;
* ((x264_union32_t*)(src + 12))->i = v;
* src += FDEC_STRIDE;
* }
*/
}
Intra_16x16_Plane
在 SPEC 中,关于该预测模式的定义如下:
x264
中关于模式 Intra_16x16_Plane
的代码如下:
#define PIXEL_MAX 255
static ALWAYS_INLINE pixel x264_clip_pixel(int x) {
return ((x & ~PIXEL_MAX) ? (-x)>>31 & PIXEL_MAX : x );
}
void x264_predict_16x16_p_c(pixel *src) {
int H = 0, V = 0;
/* calculate H and V */
for (int i = 0; i <= 7; i++) {
H += (i + 1) * ( src[ 8 + i - FDEC_STRIDE ] - src[6 - i - FDEC_STRIDE] );
V += (i + 1) * ( src[-1 + (8+i) * FDEC_STRIDE] - src[-1 + (6-i) * FDEC_STRIDE] );
}
int a = 16 * (src[-1 + 15*FDEC_STRIDE] + src[15 - FDEC_STRIDE] );
int b = (5 * H + 32) >> 6;
int c = (5 * V + 32) >> 6;
int i00 = a - b * 7 - c * 7 + 16;
for (int y = 0; y < 16; y++) {
int pix = i00;
for (int x = 0; x < 16; x++) {
src[x] = x264_clip_pixel( pix >> 5 );
pix += b;
}
src += FDEC_STRIDE;
i00 += c;
}
}
4x4 预测
x264
中对 4x4
的预测模式如下:
Intra4x4PredMode[luma4x4BlkIdx] |
Name of Intra4x4PredMode[luma4x4BlkIdx] |
x264 Function |
---|---|---|
0 |
Intra_4x4_Vertical |
x264_predict_4x4_v_c() |
1 |
Intra_4x4_Horizontal |
x264_predict_4x4_h_c() |
2 |
Intra_4x4_DC |
x264_predict_4x4_dc_c() |
3 |
Intra_4x4_Diagonal_Down_Left |
predict_4x4_ddl_c() |
4 |
Intra_4x4_Diagonal_Down_Right |
predict_4x4_ddr_c() |
5 |
Intra_4x4_Vertical_Right |
predict_4x4_vr_c() |
6 |
Intra_4x4_Horizontal_Down |
predict_4x4_hd_c() |
7 |
Intra_4x4_Vertical_Left |
predict_4x4_vl_c() |
8 |
Intra_4x4_Horizontal_Up |
predict_4x4_hu_c() |
Intra_4x4_Vertical
在 SPEC 中,关于该预测模式的定义如下:
x264
中关于模式 Intra_4x4_Vertical
的代码如下:
#define SRC(x,y) src[(x)+(y)*FDEC_STRIDE]
#define SRC_X4(x,y) MPIXEL_X4( &SRC(x,y) )
#define PREDICT_4x4_DC(v)\
SRC_X4(0,0) = SRC_X4(0,1) = SRC_X4(0,2) = SRC_X4(0,3) = v;
void x264_predict_4x4_v_c(pixel *src) {
PREDICT_4x4_DC(SRC_X4(0,-1));
/* 展开宏定义如下:
* ((x264_union32_t*)(&src[0+0*FDEC_STRIDE])->i = \
* ((x264_union32_t*)(&src[0+1*FDEC_STRIDE])->i = \
* ((x264_union32_t*)(&src[0+2*FDEC_STRIDE])->i = \
* ((x264_union32_t*)(&src[0+3*FDEC_STRIDE])->i = ((x264_union32_t*)(&src[0+(-1)*FDEC_STRIDE])->i;
*/
}
Intra_4x4_Horizontal
在 SPEC 中,关于该预测模式的定义如下:
x264
中关于模式 Intra_4x4_Horizontal
的代码如下:
#define M32(src) (((x264_union32_t*)(src))->i)
#define MPIXEL_X4(src) M32(src)
#define SRC(x,y) src[(x)+(y)*FDEC_STRIDE]
#define SRC_X4(x,y) MPIXEL_X4( &SRC(x,y) )
#define PIXEL_SPLAT_X4(x) ((x)*0x01010101U)
void x264_predict_4x4_h_c(pixel *src) {
SRC_X4(0,0) = PIXEL_SPLAT_X4( SRC(-1,0) );
SRC_X4(0,1) = PIXEL_SPLAT_X4( SRC(-1,1) );
SRC_X4(0,2) = PIXEL_SPLAT_X4( SRC(-1,2) );
SRC_X4(0,3) = PIXEL_SPLAT_X4( SRC(-1,3) );
/* 展开宏定义如下:
* ((x264_union32_t*)(&src[0+0*FDEC_STRIDE])->i = src[-1+0*FDEC_STRIDE]*0x01010101U;
* ((x264_union32_t*)(&src[0+1*FDEC_STRIDE])->i = src[-1+1*FDEC_STRIDE]*0x01010101U;
* ((x264_union32_t*)(&src[0+2*FDEC_STRIDE])->i = src[-1+2*FDEC_STRIDE]*0x01010101U;
* ((x264_union32_t*)(&src[0+3*FDEC_STRIDE])->i = src[-1+3*FDEC_STRIDE]*0x01010101U;
*/
}
Intra_4x4_DC
在 SPEC 中,关于该预测模式的定义如下:
x264
中关于模式 Intra_4x4_DC
的代码如下:
typedef uint32_t pixel4;
#define PREDICT_4x4_DC(v) SRC_X4(0,0) = SRC_X4(0,1) = SRC_X4(0,2) = SRC_X4(0,3) = v;
void x264_predict_4x4_dc_c(pixel *src) {
pixel4 dc = PIXEL_SPLAT_X4((SRC(-1,0) + SRC(-1,1) + SRC(-1,2) + SRC(-1,3) +
SRC(0,-1) + SRC(1,-1) + SRC(2,-1) + SRC(3,-1) + 4) >> 3 );
PREDICT_4x4_DC(dc);
/* 展开宏定义如下:
* uint32_t dc = (( src[-1+0*FDEC_STRIDE] + src[-1+1*FDEC_STRIDE] +
src[-1+2*FDEC_STRIDE] + src[-1+3*FDEC_STRIDE] +
src[0+(-1)*FDEC_STRIDE] + src[1+(-1)*FDEC_STRIDE] +
src[2+(-1)*FDEC_STRIDE] + src[3+(-1)*FDEC_STRIDE] ) >> 3 ) * 0x01010101U;
* ((x264_union32_t*)(&src[(0)+(0)*FDEC_STRIDE])->i = \
* ((x264_union32_t*)(&src[(0)+(1)*FDEC_STRIDE])->i = \
* ((x264_union32_t*)(&src[(0)+(2)*FDEC_STRIDE])->i = \
* ((x264_union32_t*)(&src[(0)+(3)*FDEC_STRIDE])->i = dc;
*/
}
Intra_4x4_Diagonal_Down_Left
在 SPEC 中,关于该预测模式的定义如下:
x264
中关于模式 Intra_4x4_Diagonal_Down_Left
的代码如下:
#define PREDICT_4x4_LOAD_TOP\
int t0 = SRC(0,-1);\
int t1 = SRC(1,-1);\
int t2 = SRC(2,-1);\
UNUSED int t3 = SRC(3,-1);
#define PREDICT_4x4_LOAD_TOP_RIGHT\
int t4 = SRC(4,-1);\
int t5 = SRC(5,-1);\
int t6 = SRC(6,-1);\
UNUSED int t7 = SRC(7,-1);
#define F2(a,b,c) (((a)+2*(b)+(c)+2)>>2)
static void predict_4x4_ddl_c(pixel *src) {
PREDICT_4x4_LOAD_TOP
PREDICT_4x4_LOAD_TOP_RIGHT
SRC(0,0)= F2(t0,t1,t2);
SRC(1,0)=SRC(0,1)= F2(t1,t2,t3);
SRC(2,0)=SRC(1,1)=SRC(0,2)= F2(t2,t3,t4);
SRC(3,0)=SRC(2,1)=SRC(1,2)=SRC(0,3)= F2(t3,t4,t5);
SRC(3,1)=SRC(2,2)=SRC(1,3)= F2(t4,t5,t6);
SRC(3,2)=SRC(2,3)= F2(t5,t6,t7);
SRC(3,3)= F2(t6,t7,t7);
/* 展开宏定义如下:
* int t0 = src[0+(-1)*FDEC_STRIDE];
* int t1 = src[1+(-1)*FDEC_STRIDE];
* int t2 = src[2+(-1)*FDEC_STRIDE];
* int t3 = src[3+(-1)*FDEC_STRIDE];
* int t4 = src[4+(-1)*FDEC_STRIDE];
* int t5 = src[5+(-1)*FDEC_STRIDE];
* int t6 = src[6+(-1)*FDEC_STRIDE];
* int t7 = src[7+(-1)*FDEC_STRIDE];
* src[0+0*FDEC_STRIDE] = (t0+2*t1+t2+2) >> 2;
* src[1+0*FDEC_STRIDE] = src[0+1*FDEC_STRIDE] = (t1+2*t2+t3+2) >> 2;
* src[2+0*FDEC_STRIDE] = src[1+1*FDEC_STRIDE] = src[0+2*FDEC_STRIDE] = (t2+2*t3+t4+2) >> 2;
* src[3+0*FDEC_STRIDE] = src[2+1*FDEC_STRIDE] = src[1+2*FDEC_STRIDE] = src[0+3*FDEC_STRIDE] = (t3+2*t4+t5+2) >> 2;
* src[3+1*FDEC_STRIDE] = src[2+2*FDEC_STRIDE] = src[1+3*FDEC_STRIDE] = (t4+2*t5+t6+2) >> 2;
* src[3+2*FDEC_STRIDE] = src[2+3*FDEC_STRIDE] = (t5+2*t6+t7+2) >> 2;
* src[3+3*FDEC_STRIDE] = (t6+2*t7+t7+2) >> 2;
*/
}
Intra_4x4_Diagonal_Down_Right
在 SPEC 中,关于该预测模式的定义如下:
x264
中关于模式 Intra_4x4_Diagonal_Down_Right
的代码如下:
#define PREDICT_4x4_LOAD_LEFT\
int l0 = SRC(-1,0);\
int l1 = SRC(-1,1);\
int l2 = SRC(-1,2);\
UNUSED int l3 = SRC(-1,3);
static void predict_4x4_ddr_c( pixel *src ) {
int lt = SRC(-1,-1);
PREDICT_4x4_LOAD_LEFT
PREDICT_4x4_LOAD_TOP
SRC(3,0)= F2(t3,t2,t1);
SRC(2,0)=SRC(3,1)= F2(t2,t1,t0);
SRC(1,0)=SRC(2,1)=SRC(3,2)= F2(t1,t0,lt);
SRC(0,0)=SRC(1,1)=SRC(2,2)=SRC(3,3)= F2(t0,lt,l0);
SRC(0,1)=SRC(1,2)=SRC(2,3)= F2(lt,l0,l1);
SRC(0,2)=SRC(1,3)= F2(l0,l1,l2);
SRC(0,3)= F2(l1,l2,l3);
/* 展开宏定义如下,后续类似代码不再进行宏定义展开:
* int lt = src[-1+(-1)*FDEC_STRIDE];
* int l0 = src[-1+0*FDEC_STRIDE];
* int l1 = src[-1+1*FDEC_STRIDE];
* int l2 = src[-1+2*FDEC_STRIDE];
* int t0 = src[0+(-1)*FDEC_STRIDE];
* int t1 = src[1+(-1)*FDEC_STRIDE];
* int t2 = src[2+(-1)*FDEC_STRIDE];
* int t3 = src[3+(-1)*FDEC_STRIDE];
* src[3+0*FDEC_STRIDE] = (t3+2*t2+t1+2) >> 2;
* src[2+0*FDEC_STRIDE] = src[3+1*FDEC_STRIDE] = (t2+2*t1+t0+2) >> 2;
* src[1+0*FDEC_STRIDE] = src[2+1*FDEC_STRIDE] = src[3+2*FDEC_STRIDE] = (t1+2*t0+lt+2) >> 2;
* src[0+0*FDEC_STRIDE] = src[1+1*FDEC_STRIDE] = src[2+2*FDEC_STRIDE] = src[3+3*FDEC_STRIDE] = (t0+2*lt+l0+2) >> 2;
* src[0+1*FDEC_STRIDE] = src[1+2*FDEC_STRIDE] = src[2+3*FDEC_STRIDE] = (lt+2*l0+l1+2) >> 2;
* src[0+2*FDEC_STRIDE] = src[1+3*FDEC_STRIDE] = (l0+2*l1+l2+2) >> 2;
* src[0+3*FDEC_STRIDE] = (l1+2*l2+l3+2) >> 2;
*/
}
Intra_4x4_Vertical_Right
在 SPEC 中,关于该预测模式的定义如下:
x264
中关于模式 Intra_4x4_Vertical_Right
的代码如下:
static void predict_4x4_vr_c( pixel *src ) {
int lt = SRC(-1,-1);
PREDICT_4x4_LOAD_LEFT
PREDICT_4x4_LOAD_TOP
SRC(0,3)= F2(l2,l1,l0);
SRC(0,2)= F2(l1,l0,lt);
SRC(0,1)=SRC(1,3)= F2(l0,lt,t0);
SRC(0,0)=SRC(1,2)= F1(lt,t0);
SRC(1,1)=SRC(2,3)= F2(lt,t0,t1);
SRC(1,0)=SRC(2,2)= F1(t0,t1);
SRC(2,1)=SRC(3,3)= F2(t0,t1,t2);
SRC(2,0)=SRC(3,2)= F1(t1,t2);
SRC(3,1)= F2(t1,t2,t3);
SRC(3,0)= F1(t2,t3);
}
Intra_4x4_Horizontal_Down
在 SPEC 中,关于该预测模式的定义如下:
x264
中关于模式 Intra_4x4_Horizontal_Down
的代码如下:
static void predict_4x4_hd_c( pixel *src ) {
int lt= SRC(-1,-1);
PREDICT_4x4_LOAD_LEFT
PREDICT_4x4_LOAD_TOP
SRC(0,3)= F1(l2,l3);
SRC(1,3)= F2(l1,l2,l3);
SRC(0,2)=SRC(2,3)= F1(l1,l2);
SRC(1,2)=SRC(3,3)= F2(l0,l1,l2);
SRC(0,1)=SRC(2,2)= F1(l0,l1);
SRC(1,1)=SRC(3,2)= F2(lt,l0,l1);
SRC(0,0)=SRC(2,1)= F1(lt,l0);
SRC(1,0)=SRC(3,1)= F2(t0,lt,l0);
SRC(2,0)= F2(t1,t0,lt);
SRC(3,0)= F2(t2,t1,t0);
}
Intra_4x4_Vertical_Left
在 SPEC 中,关于该预测模式的定义如下:
x264
中关于模式 Intra_4x4_Vertical_Left
的代码如下:
static void predict_4x4_vl_c( pixel *src ) {
PREDICT_4x4_LOAD_TOP
PREDICT_4x4_LOAD_TOP_RIGHT
SRC(0,0)= F1(t0,t1);
SRC(0,1)= F2(t0,t1,t2);
SRC(1,0)=SRC(0,2)= F1(t1,t2);
SRC(1,1)=SRC(0,3)= F2(t1,t2,t3);
SRC(2,0)=SRC(1,2)= F1(t2,t3);
SRC(2,1)=SRC(1,3)= F2(t2,t3,t4);
SRC(3,0)=SRC(2,2)= F1(t3,t4);
SRC(3,1)=SRC(2,3)= F2(t3,t4,t5);
SRC(3,2)= F1(t4,t5);
SRC(3,3)= F2(t4,t5,t6);
}
Intra_4x4_Horizontal_Up
在 SPEC 中,关于该预测模式的定义如下:
x264
中关于模式 Intra_4x4_Horizontal_Up
的代码如下:
static void predict_4x4_hu_c( pixel *src ) {
PREDICT_4x4_LOAD_LEFT
SRC(0,0)= F1(l0,l1);
SRC(1,0)= F2(l0,l1,l2);
SRC(2,0)=SRC(0,1)= F1(l1,l2);
SRC(3,0)=SRC(1,1)= F2(l1,l2,l3);
SRC(2,1)=SRC(0,2)= F1(l2,l3);
SRC(3,1)=SRC(1,2)= F2(l2,l3,l3);
SRC(3,2)=SRC(1,3)=SRC(0,3)=
SRC(2,2)=SRC(2,3)=SRC(3,3)= l3;
}
8x8 预测
x264 中对 8x8 的预测模式如下:
intra8x8Predmodei[luma8x8BlkIdx] |
Name of Intra8x8PredMode[luma8x8BlkIdx] |
Function |
---|---|---|
0 | Intra_8x8_Vertical |
|
1 | Intra_8x8_Horizontal |
|
2 | Intra_8x8_DC |
|
3 | Intra_8x8_Diagonal_Down_Left |
|
4 | Intra_8x8_Diagonal_Down_Right |
|
5 | Intra_8x8_Vertical_Right |
|
6 | Intra_8x8_Horizontal_Down |
|
7 | Intra_8x8_Vertical_Left |
|
8 | Intra_8x8_Horizontal_Up |