encodeprocess编码过程理解

1. jm8.6中所涉及的几项关于比特分布的地方:

序列参数集SPS:

parset.c文件中的GernateSPS...

具体宏块编码中的比特分布:

#if TRACE

snprintf(currSE->tracestring, TRACESTRING_SIZE, "Intra mode = %3d %d",currSE->value1,currSE->context);

#endif

还有很相关的一个是和比特计数相关的:

bitCount[BITS_COEFF_Y_MB]+=currSE->len;

rate += currSE->len;

 

int bits;

printf ("%04d(IDR)%8d %1d %2d %7.3f %7.3f %7.3f %7d %5d %3s %3d\n",

frame_no, stat->bit_ctr - stat->bit_ctr_n,0,

img->qp, snr->snr_y, snr->snr_u, snr->snr_v, tmp_time, me_time,

img->fld_flag ? "FLD" : "FRM", intras);

编码中涉及到的片类型(程序中共设定了5个)

typedef enum {

P_SLICE = 0,

B_SLICE,

I_SLICE,

SP_SLICE,

SI_SLICE

} SliceType;

 

这是在程序中自动进行比特使用统计的.

// Update the statistics

stat->bit_use_mb_type [img->type] += bitCount[BITS_MB_MODE];

stat->bit_use_coeffY [img->type] += bitCount[BITS_COEFF_Y_MB] ;

stat->tmp_bit_use_cbp [img->type] += bitCount[BITS_CBP_MB];

stat->bit_use_coeffC [img->type] += bitCount[BITS_COEFF_UV_MB];

stat->bit_use_delta_quant[img->type] += bitCount[BITS_DELTA_QUANT_MB];

 

++stat->mode_use[img->type][currMB->mb_type];

stat->bit_use_mode[img->type][currMB->mb_type]+= bitCount[BITS_INTER_MB];

 

JM中将编码模式转为一个值, 这个值再去利用熵编码进行编码.

Int MBType2Value (Macroblock* currMB)

{

static const int dir1offset[3] = { 1, 2, 3};

static const int dir2offset[3][3] = {{ 0, 4, 8}, // 1. block forward

{ 6, 2, 10}, // 1. block backward

{12, 14, 16}}; // 1. block bi-directional

 

int mbtype, pdir0, pdir1;

 

if (img->type!=B_SLICE)

{

if (currMB->mb_type==I4MB) return (img->type==I_SLICE ? 0 : 6);

else if (currMB->mb_type==I16MB) return (img->type==I_SLICE ? 0 : 6) + img->i16offset;

else if (currMB->mb_type==P8x8)

{

if (input->symbol_mode==UVLC && ZeroRef (currMB)) return 5;

else return 4;

}

else return currMB->mb_type;

}

函数中的ZeroRef用来判断当前的宏块中的每一个4x4块是否存在参考,如果参考帧都是0的话, 说明他们没有参考帧.(代码中是对宏块的每一个4x4块都存储一个运动矢量的.)

在函数writeMotionInfo2NAL中是将当前宏块的运动矢量进行熵编码, 这个函数调用了函数writeReferenceFrame来写参考帧号, 调用 函数int writeMotionVector8x8来写运动矢量(一个宏块分为4个8x8块, 调用4次函数writeMotionVector8x8), 在函数writeMotionVector8x8中要对4个4x4块计算mvd然后进行相应的熵编码.

 

编码图像的编号

for (img->number=0; img->number < input->no_frames; img->number++)

这个编号指的是I/P帧的编号.

定义在global.h中的全局变量frame_no是编码图像的原始编号(属于播放顺序不是编码顺序)

在配置文件中给出的量:

FramesToBeEncoded = 10

这指的是要进行编码的I/P帧的数量, 不包括B帧

 

编码的过程:

for循环, 利用img->number来控制循环次数:

(0)编码I帧, 判断是否要编码B帧, 如果配置文件允许编码B帧, 此时也不能编码B帧, 因为现在只编码了一帧, B帧是双向参考的, 所以需要已经被编码的帧数(其实就是img->number代表的含义)必须大于1才行.

(1)编码P帧, 此时判断是否编码B帧, 发现符合条件(配置文件中允许编码B帧,并且被编码帧数大于1), 进行编码B帧. 在配置文件中的NumberBFrames=2指明的是编码B帧的数量, 所以在这儿要循环NumberBFrames次. 相当于在这儿编码NumberBFrames个B帧.

(2)编码P帧, 同样编码NumberBFrames个B帧

(3)编码P帧, 同样编码NumberBFrames个B帧

(4)编码P帧, 同样编码NumberBFrames个B帧

......

(FramesToBeEncoded-1)编码P帧, 同样编码NumberBFrames个B帧

循环结束的条件是I/P帧数之和等于FramesToBeEncoded

这种情况下的编码序列是(FramesToBeEncoded=10, NumberBFrames=2,FrameSkip=2,IntraPeriod=0,IDRIntraEnable=0):

 

img->number 

0 

1 

  

2 

  

3 

  

4 

  

... 

9 

  

编码顺序

0 

1 

2 

3 

4 

5 

6 

7 

8 

9 

10 

11 

15 

..... 

25 

26 

27 

帧类型

I 

P 

B 

B 

P 

B 

B 

P 

B 

B 

P 

B 

B 

....... 

P 

B 

B 

播放顺序(原始顺序)frame_no

0 

3 

1 

2 

6 

4 

5 

9 

7 

8 

12 

10 

11 

 

27 

25 

26 

img->frame_num 

0 

1 

2 

2 

2 

3 

3 

3

4 

4 

4 

5 

5 

....... 

9 

10 

10 

img->number 

0 

1 

1 

1 

2 

2 

2 

3 

3 

3 

4 

4 

4 

...... 

9 

9 

9 

 

在配置文件中的FrameSkip是来制定两个P帧之间的间隔, 不包括之间的B帧数(B帧数量是由NumberBFrames来决定的), 在代码中frame_no = start_tr_in_this_IGOP + IMG_NUMBER * (input->jumpd + 1);

两个P(I)帧之间的间隔(跨越的帧数)是要大于等于B帧的数量的, 即frameSkip>=NumberBFrames的.

这种情况下的编码序列是(FramesToBeEncoded=10, NumberBFrames=2,FrameSkip=3,IntraPeriod=0,IDRIntraEnable=0):

img->number 

0 

1 

  

2 

  

3 

  

4 

  

... 

9 

  

编码顺序

0 

1 

2 

3 

4 

5 

6 

7 

8 

9 

10 

11 

15 

..... 

25 

26 

27 

帧类型

I 

P 

B 

B 

P 

B 

B 

P 

B 

B 

P 

B 

B 

....... 

P 

B 

B 

播放顺序(原始顺序)

0 

4 

1 

2 

8 

5 

6 

12 

9 

10 

16 

13 

14 

... 

36 

33 

34 

img->frame_num 

0 

1 

2 

2 

2 

3 

3 

3 

4 

4 

4 

5 

5 

....... 

9 

10 

10 

img->number 

0 

1 

1 

1 

2 

2 

2 

3 

3 

3 

4 

4 

4 

...... 

9 

9 

9 

 

对于配置文件中的IntraPeriod理解也很简单的, 因为在编码的过程中img->framenum控制编码的I/P帧的顺序,并且IntraPeriod也是针对P帧来说的(是和FramesToBeEncoded相关的). 在编码的过程中, 利用SetImgType函数来确定当前要进行编码的帧的类型(不包括B帧,只是判断当前帧是设定为I帧还是P帧): 首先第一帧肯定是I帧, 然后在编码下一帧(不考虑B帧)的时候要根据IntraPeriod这个量来决定当前帧是否要编为I帧,如果IntraPeriod不进行设定的话, 只有第一帧是I帧,然后后面的(FramesToBeEncoded-1)帧都是P帧, 如果对IntraPeriod进行了设定的话,需要利用img->framenum和IntraPeriod进行计算判断是否设置当前帧为I帧还是P帧

 

配置文件中的IDRIntraEnable是用来设定在进行I帧编码的时候是否使用IDR刷新的方式, 这个设置是要和IntraPeriod一同起作用的, 即如果IntraPeriod没有设定(值为0)的话,即使设定了IDRIntraEnable(值为1)的话, 也不会进行IDR刷新的. 在代码中可以看到:

input->intra_period && input->idr_enable

在函数static int CalculateFrameNumber()中计算frame_no, 因为在进行编码的过程中, 是依据frame_no来获取要进行编码的帧的原始数据的. img->frame_num

img->number是I/P编码循环的控制变量

从上面的表中, 可以看到img->frame_num和img->number在I/P帧对应的值是一样的, 只是在B 帧的地方不一样.

 

total_frame_buffer可以计算出该编码过程一共编码了多少帧, 这个变量在函数encode_one_frame 函数中完成了递增的.

编码的过程:

for(img->number=0; img->number < input->no_frames(FramesToBeEncoded); img->number++)

{

计算img->frame_num

SetImgType:根据intra_period设定当前要编码的帧是设定为I帧还是P帧

encode_one_frame():编码一帧(I/P)

 

encode_one_frame

{

在函数CalculateFrameNumber中计算当前要编码帧在原始视频序列中的序号(frame_no)

获取要编码帧数据

frame_picture{

code_a_picture{

while()

{encode_one_slice()};

}

 

}

 

}

判断接下来是否要编码B帧

if ((input->successive_Bframe != 0) && (IMG_NUMBER > 0))

{

设定编码的类型:img->type = B_SLICE;

计算frame_num(其实让frame_num++)

 

编码一定数量的B帧

for(img->b_frame_to_code=1; img->b_frame_to_code<=input->successive_Bframe(NumberBFrames); img

->b_frame_to_code++)

{

encode_one_frame():编码B帧

}

}

}


posted @ 2012-07-28 14:54  Mr.Rico  阅读(2327)  评论(0编辑  收藏  举报