转自:http://alphamailpost.blog.163.com/blog/static/201118081201281103931932/
x264代码详细阅读第一之x264.c http://www.usr.cc/thread-52097-1-2.html x264代码详细阅读第二之common.c http://www.usr.cc/thread-52098-1-2.html x264代码详细阅读第三之encoder.c http://www.usr.cc/thread-52100-1-2.html
1.x264代码详细阅读第一之x264.c 一个x264的图像分两个层,一个是视频编码层(VCL),一个是网络提取层(NAL)。 对图像一个帧进行VCL编码,用的是x264_encoder_encode函数,这个函数输入一个pic结构,这个结构包含了输入数像的大小得信息,还一个缓冲区指针,指向一个存有一帧图像的缓冲。函数会对这个缓冲区中的数据编码,编码后数据存入nal数组. 然后再对得到的nal缓冲区再进行NAL编码,这样得到的数据即可在网上传,也可以存为文件。 下面是编码一帧,并输出一帧数据的代码。要编码的数据存入inBufs->bufs[0]指向的缓冲区中,大小是176*144*1.5字节。输入 存入outBufs->bufs[0]指向缓冲区。其中i_nal是指VCL编码一帧后产生的nal层数据包的个数。请参考附件中的6.3.3节。
附件是x264的协议标准。因为读代码经常用到,所以加上了附件。
- char *temp=outBufs->bufs[0];
- pic.img.plane[0] = (uint8_t *)inBufs->bufs[0];
- pic.img.plane[1] = pic.img.plane[0] + 176* 144;
- pic.img.plane[2] = pic.img.plane[1] + 176 *144 / 4;
- x264_encoder_encode( h, &nal, &i_nal, &pic, &pic_out );
- for( i = 0; i < i_nal; i++ )
- {
- int i_size;
-
- if( mux_buffer_size < nal[i].i_payload * 3/2 + 4 )
- {
- mux_buffer_size = nal[i].i_payload * 2 + 4;
- x264_free( mux_buffer );
- mux_buffer = x264_malloc( mux_buffer_size );
- }
-
- i_size = mux_buffer_size;
- x264_nal_encode( mux_buffer, &i_size, 1, &nal[i] );
- memcpy(temp, mux_buffer,i_size);
- temp+=i_size;
- bufSize+=i_size;
- i_file +=i_size;
- }
mdate函数定义于mdate.c中,这个函数只在x264.c中调用了,在开始编码时获得一次,结束时再获得一次,两次时间取差,得到编码用的总时 间,用于打印编译效率。并非编码算法的核心。移植为dsp算法时也用不到,所以删掉mdate.c和相应与mdate相关的变量及打印语句。
mdate.c中的全部内容:
- /*****************************************************************************
- * mdate.c: h264 encoder
- *****************************************************************************
- * Copyright (C) 2003 Laurent Aimar
- * $Id: mdate.c,v 1.1 2004/06/03 19:27:07 fenrir Exp $
- *
- * Authors: Laurent Aimar <[email protected]>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
- *****************************************************************************/
-
- #if !(defined(_MSC_VER) || defined(__MINGW32__))
- //#include <sys/time.h>
- #else
- #include <sys/types.h>
- #include <sys/timeb.h>
- #endif
- #include <time.h>
-
- #include "osdep.h"
-
- far int64_t x264_mdate( void )
- {
- #if !(defined(_MSC_VER) || defined(__MINGW32__))
- // struct timeval tv_date;
- //
- // gettimeofday( &tv_date, NULL );
- // return( (int64_t) tv_date.tv_sec * 1000000 + (int64_t) tv_date.tv_usec );
- #else
- struct _timeb tb;
- _ftime(&tb);
- return ((int64_t)tb.time * (1000) + (int64_t)tb.millitm) * (1000);
- #endif
- }
-
再次调用的代码: i_start=x264_mdate(); i_end = x264_mdate();
x264中开始编码到结束的流程
首先 这三个结构的添充要完成: x264_param_t param; cli_opt_t opt; x264_picture_t pic;
填充param首先给各个域一个默认值,调用:x264_param_default( ¶m ); 然后自己相应的做一些改变,最常用的有:
- param.rc.i_qp_constant = 30;//帧率
- param.rc.i_rc_method = X264_RC_CQP ;//rate control method
- param.i_width=176;//测试文件图像大小。
- param.i_height=144;
-
这四个改了其他的不用变,然后就可以用这个param来打开一个encoder了,打开方法: h = x264_encoder_open( ¶m ) ;
然后是编码,编码之前要添充好pic:
- pic.i_type = X264_TYPE_AUTO;
- pic.i_qpplus1 = 0;
- pic.img.i_csp =X264_CSP_I420;
- pic.img.i_plane = 3;
- pic.img.i_stride[0] = 176;
- pic.img.i_stride[1] = 176 / 2;
- pic.img.i_stride[2] = 176 / 2;
- pic.i_type = X264_TYPE_AUTO;
- pic.i_qpplus1 = 0;
这些指胆图象的大小,格式。 还要指明输入一帧数据所有的缓冲区:
- pic.img.plane[0] = (uint8_t *)inBufs->bufs[0];
- pic.img.plane[1] = pic.img.plane[0] + 176* 144;
- pic.img.plane[2] = pic.img.plane[1] + 176 *144 / 4;
一个图像通常有三个plane,对于4:2:0的图片来说:第一个plane是Y信息,176x144大小。另两个分别是U和V,各是四分之一个Y信息的大小。
opt是用来解析命令行传入的选项的,如果测试阶段,都在文件中指定,用不到。
另外,编译时还要初始化一个pic_out和nal结构,pic_out用于encoder_encode的参数。nal则用于承载VCL编码后的数,编成多个nal帧。显然nal是一个数组,i_nal是数组下标的最大值,即数组边界。 看nal编码的代码就知道了:
- for( i = 0; i < i_nal; i++ )
- {
- int i_size;
-
- if( mux_buffer_size < nal[i].i_payload * 3/2 + 4 )
- {
- mux_buffer_size = nal[i].i_payload * 2 + 4;
- x264_free( mux_buffer );
- mux_buffer = x264_malloc( mux_buffer_size );
- }
-
- i_size = mux_buffer_size;
- x264_nal_encode( mux_buffer, &i_size, 1, &nal[i] );
- memcpy(temp, mux_buffer,i_size);
- temp+=i_size;
- bufSize+=i_size;
- i_file +=i_size;
- }
编码完成后要释放pic中的缓冲区:
- void x264_picture_clean( x264_picture_t *pic )
- {
- x264_free( pic->img.plane[0] );
-
- /* just to be safe */
- memset( pic, 0, sizeof( x264_picture_t ) );
- }
并关闭encoder: x264_encoder_close( h );
2.x264代码详细阅读第二之common.c
x264.c中向外调用的函数只有六个,三个在common.c中,三个在encoder.c中:
- x264_param_default( ¶m );
- x264_encoder_open( ¶m ) ; encoder.c
- x264_encoder_encode( h, &nal, &i_nal, &pic, &pic_out ); encoder.c
- x264_nal_encode( mux_buffer, &i_size, 1, &nal[i] );
- x264_picture_clean( &pic );
- x264_encoder_close( h ); encoder.c
x264_param_default函数
这个函数将param结构所有成员赋了一次初值,可真够难为它的了.... 要了解param各个域的意思,非要对x264及视频格式有点了解才行,没有的可以借这个机会了解:
void x264_param_default( x264_param_t *param ) { /* */ memset( param, 0, sizeof( x264_param_t ) ); //清空,这个谁都懂。
/* CPU autodetect */ param->cpu = x264_cpu_detect(); //cpu是什么,这个函数在cpu.c中, //这个文件中就是这个函数的各种实现,主要是判断CPU有没有特殊指令的支持, //大家看一下它的可选的值就知道了,cpu域是一个掩码,对davinci,我们没优化的情况下,返回0。 param->i_threads = 1; //支持的线程数 param->b_deterministic = 1;//多线程的情况下是否支持非确定性优化.
/* Video properties 视频属性*/ param->i_csp = X264_CSP_I420; //格式 param->i_width = 0; param->i_height = 0; param->vui.i_sar_width = 0;//视频可用性信息(Video Usablity Info)中的SAR宽度 param->vui.i_sar_height= 0; param->vui.i_overscan = 0; /* undef */ param->vui.i_vidformat = 5; /* undef */ param->vui.b_fullrange = 0; /* off */ param->vui.i_colorprim = 2; /* undef */ param->vui.i_transfer = 2; /* undef */ param->vui.i_colmatrix = 2; /* undef */ param->vui.i_chroma_loc= 0; /* left center */ param->i_fps_num = 25; param->i_fps_den = 1; param->i_level_idc = 51; /* as close to "unrestricted" as we can get */
/* Encoder parameters */ param->i_frame_reference = 1; param->i_keyint_max = 250; param->i_keyint_min = 25; param->i_bframe = 0; param->i_scenecut_threshold = 40; param->b_bframe_adaptive = 1; param->i_bframe_bias = 0; param->b_bframe_pyramid = 0;
param->b_deblocking_filter = 1; param->i_deblocking_filter_alphac0 = 0; param->i_deblocking_filter_beta = 0;
param->b_cabac = 1; param->i_cabac_init_idc = 0;
param->rc.i_rc_method = X264_RC_NONE; param->rc.i_bitrate = 0; param->rc.f_rate_tolerance = 1.0; param->rc.i_vbv_max_bitrate = 0; param->rc.i_vbv_buffer_size = 0; param->rc.f_vbv_buffer_init = 0.9; param->rc.i_qp_constant = 26; param->rc.f_rf_constant = 0; param->rc.i_qp_min = 10; param->rc.i_qp_max = 51; param->rc.i_qp_step = 4; param->rc.f_ip_factor = 1.4; param->rc.f_pb_factor = 1.3;
param->rc.b_stat_write = 0; param->rc.psz_stat_out = "x264_2pass.log"; param->rc.b_stat_read = 0; param->rc.psz_stat_in = "x264_2pass.log"; param->rc.psz_rc_eq = "blurCplx^(1-qComp)"; param->rc.f_qcompress = 0.6; param->rc.f_qblur = 0.5; param->rc.f_complexity_blur = 20; param->rc.i_zones = 0;
/* Log */ param->pf_log = x264_log_default; param->p_log_private = NULL; param->i_log_level = X264_LOG_INFO;
/* */ param->analyse.intra = X264_ANALYSE_I4x4 | X264_ANALYSE_I8x8; param->analyse.inter = X264_ANALYSE_I4x4 | X264_ANALYSE_I8x8 | X264_ANALYSE_PSUB16x16 | X264_ANALYSE_BSUB16x16; param->analyse.i_direct_mv_pred = X264_DIRECT_PRED_SPATIAL; param->analyse.i_me_method = X264_ME_HEX; param->analyse.i_me_range = 16; param->analyse.i_subpel_refine = 5; param->analyse.b_chroma_me = 1; param->analyse.i_mv_range_thread = -1; param->analyse.i_mv_range = -1; // set from level_idc param->analyse.i_direct_8x8_inference = -1; // set from level_idc param->analyse.i_chroma_qp_offset = 0; param->analyse.b_fast_pskip = 1; param->analyse.b_dct_decimate = 1; param->analyse.i_luma_deadzone[0] = 21; param->analyse.i_luma_deadzone[1] = 11; param->analyse.b_psnr = 1; param->analyse.b_ssim = 1;
param->i_cqm_preset = X264_CQM_FLAT; memset( param->cqm_4iy, 16, 16 ); memset( param->cqm_4ic, 16, 16 ); memset( param->cqm_4py, 16, 16 ); memset( param->cqm_4pc, 16, 16 ); memset( param->cqm_8iy, 16, 64 ); memset( param->cqm_8py, 16, 64 );
param->b_repeat_headers = 1; param->b_aud = 0; }
x264_nal_encode
这个函数运行于x264_encoder_encode之后,encoder_encode指缓冲区中的帧编译后,变成了i_nal个片段,这些片段被加上一个nal头,就变成一个nalu,即nal单元.nal头是一个字节的数据.
- int x264_nal_encode( void *p_data, int *pi_data, int b_annexeb, x264_nal_t *nal )
- {
- uint8_t *dst = p_data;
- uint8_t *src = nal->p_payload;
- uint8_t *end = &nal->p_payload[nal->i_payload];
-
- int i_count = 0;
-
- /* FIXME this code doesn't check overflow */
-
- if( b_annexeb )
- {
- /* long nal start code (we always use long ones)*/ //加起始码四字节.
- *dst++ = 0x00;
- *dst++ = 0x00;
- *dst++ = 0x00;
- *dst++ = 0x01;
- }
-
- /* nal header */
- *dst++ = ( 0x00 << 7 ) | ( nal->i_ref_idc << 5 ) | nal->i_type; //加头一字节
-
- while( src < end )
- {
- if( i_count == 2 && *src <= 0x03 )
- {
- *dst++ = 0x03;
- i_count = 0;
- }
- if( *src == 0 )
- {
- i_count++;
- }
- else
- {
- i_count = 0;
- }
- *dst++ = *src++;
- }
- *pi_data = dst - (uint8_t*)p_data;
-
- return *pi_data;
- }
x264_picture_clean( &pic ); 这个就是release掉pic->plane[0]中指向的缓冲区
x264代码详细阅读第三之encoder.c
上篇 x264代码详细阅读第二之common.c 讲到x264.c中的六个函数,还有三个函数定义在encoder.c中。
一楼看x264_encoder_open函数: 先是创建一个x264_t的结构,给指针h,并且0初始化:
x264_t *h = x264_malloc( sizeof( x264_t ) ); memset( h, 0, sizeof( x264_t ) );
将x264_param_t的结构复制到x264_t中的param域中:
memcpy( &h->param, param, sizeof( x264_param_t ) );
然后验证h中的各个域,这个函数其实验证的全是param中的参数,它不仅验证,还要做修正,就是把可能出错的参数根据猜测做一些修改,提供一些容错能力。 x264_validate_parameters( h ) x264_cqm_parse_file( h, h->param.psz_cqm_file ) //这个函数首先把文件内容读到内存中,并根据文件内容,获得一个量化矩阵,本来是有一个jvt的默认量化矩阵的,因此这个函数所做的事情其实可以不做, 移植到DSP的话也不可能让DSP自己读取文件,所以是一定要注释掉的。 嘿嘿,这三个参数的意思我搞了好久才明白个大概的,视频编码有个2pass的变码率压缩技术,这个技术要对影片的数据过两次,第一次分析数据,看看应该怎么压缩,然后再过一遍,这一遍才真正的压缩,而第一次分析得到的数据就存在上面的三个结构里。
if( h->param.rc.psz_stat_out ) h->param.rc.psz_stat_out = strdup( h->param.rc.psz_stat_out );//这个是传出状态数据 if( h->param.rc.psz_stat_in ) h->param.rc.psz_stat_in = strdup( h->param.rc.psz_stat_in );//这个是传入状态数据 if( h->param.rc.psz_rc_eq ) h->param.rc.psz_rc_eq = strdup( h->param.rc.psz_rc_eq );//这个是rate control 方程
上面这一段移植的时候是要修改的,因为strdup函数在DSP的编译器cgtools中的定义与标准C的不一样的,不好用,所以我们在文件头部定义一个strdup1函数:
char * strdup1(const char *s) { char *t=NULL; if(s&&(t=(char *)malloc(strlen(s)+1))) strcpy(t,s); return t; }
/* VUI */ if( h->param.vui.i_sar_width > 0 && h->param.vui.i_sar_height > 0 ) { 这个sar_width和sar_height是用来表示宽高比的,不是表示正常的宽高像素值.
int i_w = param->vui.i_sar_width; int i_h = param->vui.i_sar_height;
x264_reduce_fraction( &i_w, &i_h ); //这个函数是把两个数字变得互质,也就是说把16:12变成4:3 while( i_w > 65535 || i_h > 65535 ) //如果都转人成互质的数了,还这么大,那就没有办法了,只能以牺牲精度的方式不断除2了. { i_w /= 2; i_h /= 2; } h->param.vui.i_sar_width = 0; //原来的数清零,以备把i_w,i_h的值传回来. h->param.vui.i_sar_height = 0; if( i_w == 0 || i_h == 0 ) { x264_log( h, X264_LOG_WARNING, "cannot create valid sample aspect ratio\n" ); } else { x264_log( h, X264_LOG_INFO, "using SAR=%d/%d\n", i_w, i_h ); h->param.vui.i_sar_width = i_w; //这里传回改变后的宽高比值 h->param.vui.i_sar_height = i_h; } } x264_reduce_fraction( &h->param.i_fps_num, &h->param.i_fps_den ); //对fps_num和fps_den也转成互质,i_fps_num是fps分子,i_fps_den是fps分母,这两个数合起来组成fps,即fps=i_fps_num/i_fps_den. /* Init x264_t */ h->i_frame = 0; h->i_frame_num = 0; h->i_idr_pic_id = 0; 初始化SPS PPS的数据结构,关于SPS和PPS可以参考: http://www.usr.cc/blog-16-3091.html和 http://www.usr.cc/blog-16-3090.html
h->sps = &h->sps_array[0]; x264_sps_init( h->sps, h->param.i_sps_id, &h->param );
h->pps = &h->pps_array[0]; x264_pps_init( h->pps, h->param.i_sps_id, &h->param, h->sps);
x264_validate_levels( h );这个函数用于验证一些参数是否大于某个level. 这些参数包括:frame size(帧大小), DPB size(Decoded Picture Buffer)解码后图像缓冲区大小(关于DPB参考这个附件: H.264 DPB summery.pdf ), VBV bitrate, VBV buffer( 参考这里),MV range( 参考),MB bitrate.
if( x264_cqm_init( h ) < 0 ) { x264_free( h ); return NULL; }
初始化量化矩阵。 h->mb.i_mb_count = h->sps->i_mb_width * h->sps->i_mb_height;//mb是宏块,macroblock /* Init frames. 初始化帧相关的内容 */
h->frames.i_delay = h->param.i_bframe + h->param.i_threads - 1; h->frames.i_max_ref0 = h->param.i_frame_reference; h->frames.i_max_ref1 = h->sps->vui.i_num_reorder_frames; h->frames.i_max_dpb = h->sps->vui.i_max_dec_frame_buffering; h->frames.b_have_lowres = !h->param.rc.b_stat_read && ( h->param.rc.i_rc_method == X264_RC_ABR || h->param.rc.i_rc_method == X264_RC_CRF || h->param.b_bframe_adaptive || h->param.b_pre_scenecut );
h->frames.i_last_idr = - h->param.i_keyint_max; h->frames.i_input = 0; h->frames.last_nonb = NULL;
h->i_ref0 = 0; h->i_ref1 = 0; x264_rdo_init( ); //预计算abs_level_m1编码代价 /* init CPU functions 初始化CPU功能*/
x264_predict_16x16_init( h->param.cpu, h->predict_16x16 ); x264_predict_8x8c_init( h->param.cpu, h->predict_8x8c ); x264_predict_8x8_init( h->param.cpu, h->predict_8x8 ); x264_predict_4x4_init( h->param.cpu, h->predict_4x4 );
这里实际上四个函数调用中的第二个参数都是一个数组,即h->predict_NxN是一个数组,数组元素是函数指针。这四个函数初始的正这些指针的值。这些指针指向的函数都是完成相应大小的宏块的预测所用的函数。这些函数内容类似于: pf[I_PRED_16x16_V ] = predict_16x16_v; 分别是七种预测模式的预测函数。 pf[I_PRED_16x16_H ] = predict_16x16_h; pf[I_PRED_16x16_DC] = predict_16x16_dc; pf[I_PRED_16x16_P ] = predict_16x16_p; pf[I_PRED_16x16_DC_LEFT]= predict_16x16_dc_left; pf[I_PRED_16x16_DC_TOP ]= predict_16x16_dc_top; pf[I_PRED_16x16_DC_128 ]= predict_16x16_dc_128;
x264_pixel_init( h->param.cpu, &h->pixf );
初始化像素预测数的指针。
x264_dct_init( h->param.cpu, &h->dctf );
初始化离散余弦变换的函数指针。
x264_zigzag_init( h->param.cpu, &h->zigzagf, h->param.b_interlaced );
初始化Z形扫描函数指针
x264_mc_init( h->param.cpu, &h->mc );
初始化运动补偿相应函数指针
x264_csp_init( h->param.cpu, h->param.i_csp, &h->csp );
初始化YUV格式转换的一些函数指针 x264_quant_init( h, h->param.cpu, &h->quantf );// 初始化量化器函数指针 x264_deblock_init( h->param.cpu, &h->loopf );//不知道做什么的 x264_dct_init_weights();//dct权值 mbcmp_init( h ); x264_log( h, X264_LOG_INFO, "using cpu capabilities: %s%s%s%s%s%s%s%s\n", param->cpu&X264_CPU_MMX ? "MMX " : "", param->cpu&X264_CPU_MMXEXT ? "MMXEXT " : "", param->cpu&X264_CPU_SSE ? "SSE " : "", param->cpu&X264_CPU_SSE2 ? "SSE2 " : "", param->cpu&X264_CPU_SSSE3 ? "SSSE3 " : "", param->cpu&X264_CPU_3DNOW ? "3DNow! " : "", param->cpu&X264_CPU_ALTIVEC ? "Altivec " : "", param->cpu ? "" : "none!" ); h->out.i_nal = 0;//i_nal,即nal段的数目初始化为0 h->out.i_bitstream = X264_MAX( 1000000, h->param.i_width * h->param.i_height * 4 * ( h->param.rc.i_rc_method == X264_RC_ABR ? pow( 0.95, h->param.rc.i_qp_min ) : pow( 0.95, h->param.rc.i_qp_constant ) * X264_MAX( 1, h->param.rc.f_ip_factor ))); h->thread[0] = h;//多线程方面的东西 h->i_thread_num = 0; for( i = 1; i < h->param.i_threads; i++ ) h->thread[i] = x264_malloc( sizeof(x264_t) ); for( i = 0; i < h->param.i_threads; i++ ) { if( i > 0 ) *h->thread[i] = *h; h->thread[i]->fdec = x264_frame_pop_unused( h ); h->thread[i]->out.p_bitstream = x264_malloc( h->out.i_bitstream ); if( x264_macroblock_cache_init( h->thread[i] ) < 0 ) return NULL; } if( x264_ratecontrol_new( h ) < 0 ) return NULL; #ifdef DEBUG_DUMP_FRAME { /* create or truncate the reconstructed video file */ FILE *f = fopen( "fdec.yuv", "w" ); if( f ) fclose( f ); else { x264_log( h, X264_LOG_ERROR, "can't write to fdec.yuv\n" ); x264_free( h ); return NULL; } } #endif return h; [tr][td] 资源:[/td][/tr] [tr][td] 115网盘附件下载:[/td][/tr] [tr][td] H.264 DPB summery.pdf (120.48KB) [/td][/tr] [/table] x264_encoder_encode函数
x264_t *thread_current, *thread_prev, *thread_oldest;//这三个指针记录是哪一个线程,单线程用不到。 int i_nal_type; int i_nal_ref_idc; int i_global_qp;
if( h->param.i_threads > 1) //如果是多线程 { int i = ++h->i_thread_phase; int t = h->param.i_threads; thread_current = h->thread[ i%t ]; thread_prev = h->thread[ (i-1)%t ]; thread_oldest = h->thread[ (i+1)%t ]; x264_thread_sync_context( thread_current, thread_prev ); x264_thread_sync_ratecontrol( thread_current, thread_prev, thread_oldest ); h = thread_current; // fprintf(stderr, "current: %p prev: %p oldest: %p \n", thread_current, thread_prev, thread_oldest); } else { thread_current = thread_prev = thread_oldest = h; }
这一段显然只要留一句话就行了。
// ok to call this before encoding any frames, since the initial values of fdec have b_kept_as_ref=0 x264_reference_update( h ); h->fdec->i_lines_completed = -1;
更新引用数: static inline void x264_reference_update( x264_t *h ) { int i;
if( h->fdec->i_frame >= 0 )//fdec指向正在被重建的帧,i_frame是其序号 h->i_frame++; //i_frame为当然帧的序号
if( !h->fdec->b_kept_as_ref )//正在被重建的帧不被kept as ref,即不被用作参考帧 { if( h->param.i_threads > 1 )//如果是多线程的话 { x264_frame_push_unused( h, h->fdec );//将己经不用了的帧加入到unused数组中,x264_t中的有个frames结构,里面有三个数组,unused,current,next,分别是未被使用的(用完了在这里回收),正要被编码的,和没有决定是什么类型(待编码的)。这是一条龙的。 h->fdec = x264_frame_pop_unused( h );//之所以要先push再pop,是就是为了多线程的同步。 } return; }
/* move lowres copy of the image to the ref frame */ for( i = 0; i < 4; i++) { XCHG( uint8_t*, h->fdec->lowres[i], h->fenc->lowres[i] ); XCHG( uint8_t*, h->fdec->buffer_lowres[i], h->fenc->buffer_lowres[i] ); }
/* adaptive B decision needs a pointer, since it can't use the ref lists */ if( h->sh.i_type != SLICE_TYPE_B )//片的类型不是B帧 h->frames.last_nonb = h->fdec;//上一个不是B的帧就是这个帧
/* move frame in the buffer */ x264_frame_push( h->frames.reference, h->fdec );//将上一个重建帧加入到参考帧列表。 if( h->frames.reference[h->frames.i_max_dpb] )//i_max_dpb是解码图片缓冲区的数目,这句成立代表解码缓冲区満。 x264_frame_push_unused( h, x264_frame_shift( h->frames.reference ) );//就把参考数组中最低的拿出来放到不用的数组里面。 h->fdec = x264_frame_pop_unused( h );//从未被使用的帧中拿出一个来用,放到重建帧指针上。 }
/* no data out */ 因为还没解码中数据来, *pi_nal = 0; //nal数据段数为0 *pp_nal = NULL; //nal数组第一个元素为NULL TIMER_START( i_mtime_encode_frame ); //这个要定义了DEBUGBENCHMARK才要用,否则这句没用
if( pic_in != NULL ) {//对输入的图片进行处理 /* 1: Copy the picture to a frame and move it to a buffer */ x264_frame_t *fenc = x264_frame_pop_unused( h );//首先从未被使用的帧中拿出一个来,用于编码帧 x264_frame_copy_picture( h, fenc, pic_in );//从pic结构中复制图像数据到fenc中 if( h->param.i_width != 16 * h->sps->i_mb_width || h->param.i_height != 16 * h->sps->i_mb_height ) x264_frame_expand_border_mod16( h, fenc );
fenc->i_frame = h->frames.i_input++;//输入的帧的数目即为编码帧的数目加一
x264_frame_push( h->frames.next, fenc );//把这个编码帧加入到next,即将要编码的帧的数组中
if( h->frames.b_have_lowres ) x264_frame_init_lowres( h->param.cpu, fenc );
if( h->frames.i_input <= h->frames.i_delay + 1 - h->param.i_threads ) { /* Nothing yet to encode */ /* waiting for filling bframe buffer */ pic_out->i_type = X264_TYPE_AUTO; return 0; } }
[quote] if( h->frames.current[0] == NULL ) //如果当前编码帧队列己经空了,这时要添加新的帧了 { int bframes = 0; /* 2: Select frame types */ if( h->frames.next[0] == NULL ) //如果待编码帧队列为空,则当前帧是最后一个帧,用encoder_frame_end来编码 { x264_encoder_frame_end( thread_oldest, thread_current, pp_nal, pi_nal, pic_out ); return 0; } x264_slicetype_decide( h ); //决定片的类型 /* 3: move some B-frames and 1 non-B to encode queue 本段引用中以下所有代码都是为了向h->frames.current中添加一个非B帧和多个B帧*/ while( IS_X264_TYPE_B( h->frames.next[bframes]->i_type ) ) //测试待编码帧队列,遇到B帧时,测试的是连续的B帧,遇到第一非B帧时停止 bframes++ ;//b帧计数加一 x264_frame_push( h->frames.current, x264_frame_shift( &h->frames.next[bframes] ) ); //向当前编码帧队列中添加一个非B帧 /* FIXME: when max B-frames > 3, BREF may no longer be centered after GOP closing */ //下面的if中是向待编码帧队列中乱取一个帧,加入当前编码帧队列,并且标记为参考帧,BREF:B帧,且为参考帧 if( h->param.b_bframe_pyramid && bframes > 1 )//B帧
- /* ------------------------ Create slice header ----------------------- */
- x264_slice_init( h, i_nal_type, i_global_qp );//初始化片,创建片头,一个片包含一个或多个宏块,片头要包含这些信息
-
- if( i_nal_ref_idc != NAL_PRIORITY_DISPOSABLE )
- h->i_frame_num++;//帧数增加
-
- /* ---------------------- Write the bitstream -------------------------- */
- /* Init bitstream context */
- //写入比特流,初始化比特流环境
- h->out.i_nal = 0;//输出的nal个数为0
- bs_init( &h->out.bs, h->out.p_bitstream, h->out.i_bitstream ); //初始化码流结构
-
- typedef struct bs_s
- {
- uint8_t *p_start;
- uint8_t *p;
- uint8_t *p_end;
-
- int i_left; /* i_count number of available bits */
- int i_bits_encoded; /* RD only */
- } bs_t;
-
- static inline void bs_init( bs_t *s, void *p_data, int i_data )
- {
- s->p_start = p_data;//将p_start指上输出码流的缓冲区首部
- s->p = p_data;//p指向数据本身
- s->p_end = s->p + i_data;//p_end指向数据的底部,i_data应该是数据的个数.
- s->i_left = 8;//i_left是编码的比特数为8,也就是一个字节.
- }
- if(h->param.b_aud){
- int pic_type;
-
- if(h->sh.i_type == SLICE_TYPE_I) //片的类型是I片,即采用的是帧内编码,里面全是I宏块.
- pic_type = 0;
- else if(h->sh.i_type == SLICE_TYPE_P) //片的类型是P,里面是I宏块或P宏块,采用帧内或单向的帧间编码
- pic_type = 1;
- else if(h->sh.i_type == SLICE_TYPE_B) //片的类型是B,里面是B宏块,双向帧间编码
- pic_type = 2;
- else
- pic_type = 7; //其他片类型
-
- x264_nal_start(h, NAL_AUD, NAL_PRIORITY_DISPOSABLE);//开始写一个nal数据段
- bs_write(&h->out.bs, 3, pic_type);//写入
- bs_rbsp_trailing(&h->out.bs);//加上RBSP尾部
- x264_nal_end(h);//结束一个数据段
- }
- static inline void bs_write(struct bs_s * s, int i_count, uint32_t i_bits )
- {
- if( s->p >= s->p_end - 4 )
- return;
- while( i_count > 0 )
- {
- if( i_count < 32 )
- i_bits &= (1<<i_count)-1;
- if( i_count < s->i_left )
- {
- *s->p = (*s->p << i_count) | i_bits;//写入i_count个位,这些位的值为i_bits
- s->i_left -= i_count;
- break;
- }
- else
- {
- *s->p = (*s->p << s->i_left) | (i_bits >> (i_count - s->i_left));
- i_count -= s->i_left;
- s->p++;
- s->i_left = 8;
- }
- }
- }
- /* Write SPS and PPS 写入序列参数集和图像参数集*/
- if( i_nal_type == NAL_SLICE_IDR && h->param.b_repeat_headers )
- {
- if( h->fenc->i_frame == 0 )
- {
- /* identify ourself */
- x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE );
- x264_sei_version_write( h, &h->out.bs );
- x264_nal_end( h );
- }
-
- /* 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 );
- }
- /* Write frame */
- if( h->param.i_threads > 1 )
- {
- x264_pthread_create( &h->thread_handle, NULL, (void*)x264_slices_write, h );//创建一个线程来写入一帧数据
- h->b_thread_active = 1;
- }
- else
- x264_slices_write( h );//写入一帧数据
|
请发表评论