x264源代码分析(二)
时间:2006-07-17 来源:lzhw_lucky
这个函数前面有一段注释(如下):
****************************************************************************
* x264_encoder_encode:
* XXX: i_poc : is the poc of the current given picture
* i_frame : is the number of the frame being coded
* ex: type frame poc
* I 0 2*0//poc是实际的帧的位置.
* P 1 2*3//frame是编码的顺序.
* B 2 2*1
* B 3 2*2
* P 4 2*6
* B 5 2*4
* B 6 2*5
****************************************************************************/
要搞清poc和frame的区别.
假设一个视频序列如下:
I B B P B B P
我们编码是按I P B B P B B的顺序,这就是frame的编号.
而我们视频序列的播放序号是POC的序号,这里是乘以了2.
函数中先定义了如下三个参数:
int i_nal_type;
nal存放的数据类型, 可以是sps,pps等多种.
int i_nal_ref_idc;
nal的优先级,nal重要性的标志位.
前面两个参数虽然简单,但如果不参照标准,也不容易理解,所以标准中的句法表是很重要的,可以说是最关键的.
int i_slice_type;
slice的类型,在x264中我的感觉好像一帧只有一个slice.如果确定了帧的类型,slice的类型也就确定了.
我们来看看编码器是如何区分读入的一帧是I帧,P帧,或者B帧,这个过程需要好好理解.
还以I B B P B B P为例.
if( h->i_frame % (h->param.i_iframe * h->param.i_idrframe) == 0 ){
确定这是立即刷新片.
}
这里很好理解.
但到了if( h->param.i_bframe > 0 )//可以B帧编码时.
就有问题了.
注意我们编完I帧后碰到了一个B帧,这时我们先不对它进编码.而是采用frame = x264_encoder_frame_put_from_picture( h, h->frame_next, pic )函数将这个B帧放进h->frame_next中.
好,这里出现了h->frame_next,在h中同时定义了下面几个帧数组用以实现帧的管理.
x264_frame_t *bframe_current[X264_BFRAME_MAX]; /* store the sequence of b frame being encoded */
x264_frame_t *frame_next[X264_BFRAME_MAX+1]; /* store the next sequence of frames to be encoded *///搞清意义,下一个帧,而不一定是B帧.
x264_frame_t *frame_unused[X264_BFRAME_MAX+1]; /* store unused frames */
注意区分这3个数组.
同时还有下面4个函数(定义在\ENCODER\encoder.c中).
x264_encoder_frame_put_from_picture();
x264_encoder_frame_put();
x264_encoder_frame_get();
x264_frame_copy_picture();
这3个数组和4个函数可以说完成了整个帧的类型的判定问题.这个里面if ,else语句较多,容易使人迷惑.但我们只要把握下面一个观点就可以看清实质:在不对P帧进行编码之前,我们不对B帧进行编码,只是把B帧放进缓冲区(就是前面提到的数组).
比如视频序列:I B B P B B P
先确立第一个帧的类型,然后进行编码.然后是2个B帧,我们把它放进缓冲区数组.然后是P帧,我们可以判定它的类型并进行编码.同时,我们将缓冲区的B帧放进h->bframe_current[i],不过这时P帧前的两个B帧并没有编码.当读到P帧后面的第一个B帧时,我们实际上才将h->bframe_current数组中的第一个B帧编码,也就是将在I帧后面的第一个B帧(说成P帧前面的第一个B帧容易误解J)编码.
依此类推,把握好上面4个函数的调用流程和指针操作的用法,就可以将帧的类型判定这个问题搞明白了.
F. 然后是速率控制(先不说这个,因为它对编码的流程影响不大),看看建立参考帧列表的操作,也就是
x264_reference_build_list( h, h->fdec->i_poc ); (定义在\ENCODER\encoder.c中).
光看这个函数是不行的,它是和后面的这个函数(如下)一起配合工作的.
if( i_nal_ref_idc != NAL_PRIORITY_DISPOSABLE )//B帧时.
{
x264_reference_update( h );
}
If条件是判断当前帧是否是B帧,如果是的话就不更新参考列表,因为B帧本来就不能作为参考帧嘛!如果是I帧或P帧的话,我们就更新参考帧列表.
我们看到了一个for循环,两个do—while循环.这是实现的关键,具体看代码,不好用语言说明白.
G. 进入另一个复杂的领域:写slice的操作,刚开使挺简单,如我下面的注释.
/* ---------------------- Write the bitstream -------------------------- */
/* Init bitstream context */
h->out.i_nal = 0;//out的声明在bs.h中.
bs_init( &h->out.bs, h->out.p_bitstream, h->out.i_bitstream );//空出8位.
/* Write SPS and PPS */
if( i_nal_type == NAL_SLICE_IDR )//不是每次都要写SPS and PPS,只有碰见立即刷新片时才写.
{
/* generate sequence parameters */
x264_nal_start( h, NAL_SPS, NAL_PRIORITY_HIGHEST );
x264_sps_write( &h->out.bs, h->sps );
x264_nal_end( h );
/* generate picture parameters */
x264_nal_start( h, NAL_PPS, NAL_PRIORITY_HIGHEST );
x264_pps_write( &h->out.bs, h->pps );
x264_nal_end( h );
}
不过看下面那个函数(就进入了复杂的领域).