x264学习笔记(1)-函数调用流程
X264的多线程过程

X264的多线程过程,也可以说是并行编码过程。
1. 编译并行编码的x264从X264的帮助命令行可以看到,添加--threads项可以调整运行的线程数,可是当我完成X264编译,视图对手头的YUV进行编码的时候,发现在自己的双核计算机上,只能发挥50%的效率,即使使用--thr eads n 也无济于事,提示就是没有打开pthr ead支持。
Pthr eads定义了一套 C程序语言类型、函数与常量,它以pthread.h头文件和一个线程库实现。
【1】下面就把我在windows上实现pthr ead版本的X264编译过程写作如下:2009年3月的66版本1. 从http://sourcew /pthreads-w in32/下载pthr ead的win32版本,把其中的include和lib加入到VC++的引用目录中去。
2. 在项目属性的“C/C++ -> 预处理器 ->预处理器”中加入HAVE_PTHREAD。
3. 在osdep.h文件,紧接着#ifdef USE_REAL_PTHREAD加入#pragma c omment(lib, "pthr eadVC2.lib")引用pthr eadVC2.lib,重新编译。
2009年10月的77版本4. 在项目属性的“C/C++ -> 预处理器 ->预处理器”中加入SYS_MINGW。
其它版本请自己根据可能的编译错误随机应变。
调整项目属性意味着同时调整libx264和x264两处的属性。
经过如上调整编译出的X264就可以在--threads n //n>=2的时候用完CPU的潜力了。
2. X264的编码基本流程(1)接口变更以前曾经写过文章介绍X264的编程架构并且分析了它的接口,现在进一步看看x264是怎么把YUV图像编程H.264编码的。
在代码分析中,最容易让人头疼的是X264代码随处充斥着的多线程处理和码率控制两方面的代码,所以,这里将先简化过程,忽略掉这些非主体代码。
x264流程详细分析(3)

去块滤波(Deblocking)部分关键函数3.1 deblocking_filter_edgev( x264_t *h, uint8_t *pix, int i_pix_stride, int bS[4], int i_QP )功能对亮度宏块的垂直边界进行边界滤波性能。
输入项目x264_t *h:指向x264_t类型的结构体变量的指针变量;uint8_t *pix:指向uint8_t变量的指针,代表存储像素的内存地址;int i_pix_stride:像素步长,对cif格式的亮度块来说是416,为352+64,64是左右两边分别进行了32个像素的扩边;int bS[4]:边界强度,有0、1、2、3、4五个值。
int i_QP:量化参数程序逻辑如附图10所示:1附图10: deblocking_filter_edgev流程图3.2 deblocking_filter_edgecv( x264_t *h, uint8_t *pix, int i_pix_stride, int bS[4], int i_QP )对色度宏块的垂直边界进行滤波。
输入项目x264_t *h, uint8_t *pix, int i_pix_stride, int bS[4], int i_QP算法;同亮度宏块垂直边界扫描,只不过色度宏块的尺寸比亮度块缩小一倍,为8x8;而色度子块的尺寸为2x2。
3.3 deblocking_filter_edgeh( x264_t *h, uint8_t *pix, int i_pix_stride, int bS[4], int i_QP )功能对亮度宏块的水平边界进行滤波。
输入项目x264_t *h, uint8_t *pix, int i_pix_stride, int bS[4], int i_QP算法;同亮度宏块,只不过是进行水平边界的扫描。
程序逻辑和亮度宏块的类似,略。
3.4 deblocking_filter_edgech( x264_t *h, uint8_t *pix, int i_pix_stride, int bS[4], int i_QP )功能对色度宏块的水平边界进行滤波。
X264用法

----
@cd /d "%~dp0"
x264 -p2 --stat "XXX.stat" -B XXXX [option] -o "%~dpn1_p2.mp4" "%~1"
@pause
注意一般pass 1 都加上--slow-firstpass这个参数。
pass 1压完看下视频是否满足自己要求,不满意就继续
输入:
x264支持输入的文件类型有raw yuv、y4m、avs和任何可以由ffms或lavf打开的文件。raw yuv会用在64位的x264里。有ffms/lavf打开的片子会自动正确的处理vfr问题。avs和ffms/lavf输入不需要指定片子的分辨率。
输出:
x264可以输出没有封装的H.264视频流,扩展名是.264;matroska视频,扩展名是.mkv;flash视频,扩展名是.flv;mp4视频,扩展名是.mp4。mkv、mp4和flv可以是vfr的。
除了2pass,还有多pass模式,在之前分析的基础上再继续分析,理论上会使码率分配更加合理,但实际上2pass已经足够了。
--bitrate 1000 (以1000kbps码率为例)
>x264 --bitrate 1000 --pass 1 --tune animation --preset slower --stats "1pass.stats" -o NUL input.avs
ssim:为提高ssim做了优化的参数;
fastdecode:可以快速解码的参数;
zerolatency:零延迟,用在需要非常低的延迟的情况下,比如电视电话会议的编码。
习笔记(一)(x264编码流程)

习笔记(一)(x264编码流程)经过一段时间的学习我对h264也有了一个初步的大体的了解,今天在这里说一下h264中x264的开源code的编码的解析并附一张我自己画的流程图便于大家理解,又不对的地方清大家指教一二,偶必定三顾茅庐寻得真理。
:)首先我们进入x264.c中的main函数.刚开始是读取默认参数,如果你设置了参数的话会修改param的.i_ret = Encode( ¶m, fin, fout );这条语句使过程进入x264.c中的Encode函数. (这个函数就是x264的编码程序)X.264_encode函数.A i_frame_total = 0;if( !fseek( fyuv, 0, SEEK_END ) ){int64_t i_size = ftell( fyuv );fseek( fyuv, 0, SEEK_SET );i_frame_total = i_size / ( param->i_width * param->i_height * 3 / 2 )}这段调用了fseek()函数,对输入的视频文件计算其总帧数。
B. 函数 h = x264_encoder_open( param )对不正确的参数进行修改,并对各结构体参数和cabac编码,预测等需要的参数进行初始化.然后才能进行下一步的编码。
C. 函数 pic = x264_picture_new( h );定义在\CORE\common.c 中.此函数的作用是分给能容纳sizeof(x264_picture_t)字节数的空间,然后进行初始化.这里说明一下x264_picture_t和x264_frame_t的区别.前者是说明一个视频序列中每帧的特点.后者存放每帧实际的象素值.D. 调用fread()函数一次读入一帧,分亮度和色度分别读取.这里要看到c语言中的File文件有一个文件位置指示器,调用fread()函数会使文件指示器自动移位,这就是一帧一帧读取的实现过程.for( i_frame = 0, i_file = 0; i_ctrl_c == 0 ; i_frame++ ){int i_nal;x264_nal_t *nal;int i;/* read a frame */if( fread( pic->plane[0], 1, param->i_width * param->i_height, fyuv ) <= 0 ||fread( pic->plane[1], 1, param->i_width * param->i_height / 4, fyuv ) <= 0 ||fread( pic->plane[2], 1, param->i_width * param->i_height / 4, fyuv ) <= 0 ){break;}这里文件已经指示器发生了位移if( x264_encoder_encode( h, &nal, &i_nal, pic ) < 0 ){fprintf( std err, “x264_encoder_encode failed\n” );}……}E. 进入x264_encoder_encode( h, &nal, &i_nal, pic )函数,该函数定义在/Enc/encoder.c中.函数中先定义了如下三个参数:int i_nal_type; nal存放的数据类型, 可以是sps,pps等多种.int i_nal_ref_idc; nal的优先级,nal重要性的标志位.int i_slice_type; slice的类型的这里先说明一下:我们假设一个视频序列如下:I B B P B B P我们编码是按I P B B P B B的顺序,这就是frame的编号但是编码器如何来区分他们并把他们重新排序呢?我们来看看编码器是如何区分读入的一帧是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 )//判断h是否为B帧然后对其进行下一步操作.我们编完I帧后碰到了一个B帧,这时我们先不对它进编码.而是采用frame= x264_encoder_frame_put_from_picture( h, h->frame_next, pic )函数将这个B帧放进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 */同时还有下面4个函数(定义在\ENCODER\encoder.c中).x264_encoder_frame_put_from_picture();x264_encoder_frame_put() ();x264_encoder_frame_get();x264_frame_copy_picture();这3个数组和4个函数可以说完成了整个帧的类型的判定问题.在不对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帧编码.依此类推.(帧的有关理解学习笔记(二))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帧的话,就更新参考帧列表.G. 下面是写slice的操作./* 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 ){/* 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 );x264_slice_write() (定义在\ENCODER\encoder.c中),这里面是编码的最主要部分..下面这个循环,它是采用for循环对一帧图像的所有块依次进行编码.for( mb_xy = 0, i_skip = 0; mb_xy < h->sps->i_mb_width * h->sps->i_mb_height; mb_xy++ )//h->sps->i_mb_width指的是从宽度上说有多少个宏快. { const int i_mb_y = mb_xy / h->sps->i_mb_width;const int i_mb_x = mb_xy % h->sps->i_mb_width;//这两个变量是定义宏块的位置../* load cache */x264_macroblock_cache_load( h, i_mb_x, i_mb_y );//是把当前宏块的up宏块和left宏块的intra4×4_pred_mode,non_zero_count加载进来,放到一个数组里面,这个数组用来直接得到当前宏块的左侧和上面宏块的相关值.要想得到当前块的预测值,要先知道上面,左面的预测值,它的目的是替代getneighbour函数./* analyse parameters* Slice I: choose I_4×4 or I_16×16 mode* Slice P: choose between using P mode or intra (4×4 or 16×16)* */TIMER_START( i_mtime_analyse );x264_macroblock_analyse( h );//定义在analyse.h中.TIMER_STOP( i_mtime_analyse );/* encode this macrobock -> be carefull it can change the mb type to P_SKIP if needed */TIMER_START( i_mtime_encode );x264_macroblock_encode( h );//定义在Enc/encoder.c中.TIMER_STOP( i_mtime_encode );到这就已经完成编码的主要过程了,后面就是熵编码的过程了.。
x264源码阅读笔记2(zhuantie)

x264源码阅读笔记2(zhuantie)写参数集x264_sps_write()和x264_pps_write()以及其中基本的bs_write()的过程。
挺有意思,挺巧妙的。
他们就是负责码流写入的过程,这个不同于写字节,直接COPY内存,用C语言实现对位的操作真的显得比较笨拙,但是这里代码还是很巧妙的。
说基本的,static inline void bs_write( bs_t *s, int i_count, uint32_t i_bits )这个函数的作用就是,向s里写入i_bits流的前i_count位,s当然是以字节为单位了,所以够8个位就写下个,哎呀太麻烦了,引别人写的把,不知道他这个是什么时候版本,但是大概意思差不多。
酬和看。
函数bs_writestatic inline void bs_write( bs_t *s, int i_count, uint32_t i_bits ) {while( i_count > 0 ){if( s->p >= s->p_end ){break;}i_count--;if( ( i_bits >> i_count )&0x01 ){*s->p |= 1 << ( s->i_left - 1 );}else{*s->p &= ~( 1 << ( s->i_left - 1 ) );}s->i_left--;if( s->i_left == 0 ){s->p++;s->i_left = 8;}}}函数功能:i_count是循环的次数,i_bits是要编码的数,i_left是当前空闲码流的位数。
将i_bits编为i_count位的码流,每次循环,I_count和I_left都会减1,I_count和I_left并不一定相等。
当i_left==0时,s->p指针指向下一个码流单元,i_left更新为8。
x264学习笔记(1)-函数调用流程

/* 1: Copy the picture to a frame and move it to a buffer */
x264_stack_align( x264_slicetype_decide, h )
/* 2: Select frame types */
x264_frame_push()
/* 3: move some B-frames and 1 non-B to encode queue */
i_file = 0; b_ctrl_c == 0 &&
(i_frame < i_frame_total || i_frame_total ==
0); )
parse_qpfile() Encode_frame()
i_frame++;
分析量化表,如果有 编码一帧
打印输出信息
编码循环结束 Encode_frame( )
Flush delayed B-frames
x264_mdate() x264_picture_clean( &pic );
x264_encoder_close( h ); x264_free( mux_buffer ); p_close_infile( opt->hin ); p_close_outfile( opt->hout );
Encode()函 数,编码器
主循环
main() (x264.c)
x264_param_default()
Parse()
分析命令行参数,并 打开输入输出文件
Encode()
x264_encoder_open()
初始化编码器参数,以及 编码器用到的函数
x264程序框架流程分析

1、x264程序框架流程分析(1) 进入x264.c 的int main( int argc, char **argv ) 函数main 函数中主要有以下三个主要的步骤,分别调用了3个函数。
第一步、对编码器进行参数设定。
函数实现如下:x264_param_default( x264_param_t *param );此函数在common.c中定义,完成一个x264_param_t 结构体的初始化。
第二步、分析参数,读入运行参数完成文件打开。
函数实现如下:Parse( argc, argv, x264_param_t *param, &opt );此函数在x264.c 中定义。
VC的运行参数我们可以设置:“-o out.264 football.yuv 352x288”则具体到Parse函数,其输入的参数argc == 5 ,这个数值大小等于要放下下面的argv所需要的二维数组的行数。
参数argc和argv在进入main 函数之前就已经确定了。
argv 为字符串char **的类型的,相当于一个二维数组,其具体内容如下:argv[0][] ==“F:\x264-snapshot-20070331-2245\build\win32\bin\x264.exe”argv[1][] == “-o”argv[2][] == “out.264”argv[3][] == “football.yuv”argv[4][] == “352x288”第三步、开始编码。
函数实现如下:Encode( x264_param_t *param, &opt ); 此函数在x264.c 中定义。
(2) static int Parse( int argc, char **argv, x264_param_t *param, cli_opt_t *opt )分析参数,读入运行参数。
其中比较重要的语句是int c = getopt_long( argc, argv, "8A:B:b:f:hI:i:m:o:p:q:r:t:Vvw",long_options, &long_options_index);getopt_long()解析入口地址的向量,最后int c 得到的是运行参数(“-o out.264 football.yuv 352x288”)中前面“-o”中“o”的ASCII值即c = 111 。
X264参数设置(1)

0
设置VBV模式的最大码率,强烈建议如果使用,则用2-pass bitrate
Vbv-buffsize
0
设置VBV的最大Buffer大小
Vbv-init
0
初始化buffer值
qpmin
10
Qp最小量化值,qp值越小,视频越清晰,码率越高,一般qp小于16即接近无损压码
qpmax
Aq-mode
1
0:不使用AQ自适应模式,
1:使用
推荐:使用缺省值
Beta deblocking值越低,被去块化(deblock)的方块越少。增加Beta值,环状物(ringing)就越少,而降低Beta则DCT块越少(矛盾)?。/showthread.php?t=109747
如果你不需要编码很细,不介意偶尔的块,可以用-2:-1;如果你喜好更明亮的画面,且不介意一点点模糊,那就用1:2;动画则用低beta值;推荐用缺省值
X264参数设置(1)
X264参数设置
注:I帧:关键帧,P帧:预报帧,B帧:I帧和P帧之间的双向插值帧
名称
缺省值设置IDR帧的最大间隔为250帧,IDR帧就是可备用于拖拽的帧,IDR帧一定是I帧,反之不成立。最好是设成帧率的10倍。显然改值越小,P,B帧越少。
min-keyint
interlace
无
交错编码,无详细说明,缺省值
以下是码率控制
码率控制方法有三种:qp, bitrate, crf,他们是互斥的。三种方法目标不同:qp:固定量化子,bitrate:固定文件大小,crf固定视频“质量”
qp
无
固定量化模式(CQ),数值是针对P帧的量化值,I,B帧则根据ipratio和pbratio算出;0值表示无损压缩;推荐使用crf方法替代qp;值越小越清晰。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
(i_frame < i_frame_total || i_frame_total ==
0); )
parse_qpfile() Encode_frame()
i_frame++;
分析量化表,如果有 编码一帧
打印输出信息
编码循环结束 Encode_frame( )
x264_reference_build_list( h, h->fdec->i_poc ) /* build ref list 0/1 */
x264_ratecontrol_start( h, h->fenc->i_qpplus1 );
i_file += Encode_frame( h, opt->hout, &pic );
i_frame++;
… } 这是一个按 frame 计算的循环,逐帧编码,直到编完所有的帧; p_read_frame( &pic, opt->hin, i_frame + opt->i_seek ):从源文件中读取一个要编码 的帧数据; Encode_frame( h, opt->hout, &pic ):进行一帧编码; 流程图如下:
return
2. 函数 x264_encoder_open( param ): 这个函数主要作用是: 1) 检查编码参数4) 初始化量化表; 5) 初始化 RDO; 6) 初始化部分编码中用到的函数(函数指针); 函数流程如下:
初始化编码过程 中所用到的函
数,初始化部分
参数
h->thread[i]->fdec = x264_frame_pop_unused( h ); h->thread[i]->out.p_bitstream = x264_malloc( h>out.i_bitstream ); x264_macroblock_cache_init( h->thread[i] )
x264_init_vlc_tables(); 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 ); x264_mc_init( h->param.cpu, &h->mc ); x264_quant_init( h, h->param.cpu, &h->quantf ); x264_deblock_init( h->param.cpu, &h->loopf ); x264_dct_init_weights(); mbcmp_init( h );
/* 1: Copy the picture to a frame and move it to a buffer */
x264_stack_align( x264_slicetype_decide, h )
/* 2: Select frame types */
x264_frame_push()
/* 3: move some B-frames and 1 non-B to encode queue */
x264_encoder_open(x264_param_t *param)
x264_validate_parameters()
检查编码参数
x264_cqm_parse_file()
分析quant matrices, 如果有配置
x264_sps_init()
初始化SPS参数集
x264_pps_init() x264_validate_levels()
x264_ratecontrol_new() return
初始化ratecontrol
3. x264_encoder_encode( h, &nal, &i_nal, pic, &pic_out ) 函数 Encode_frame()中主要调用 x264_encoder_encode(),这是编码器的编码主流 程,这个函数的调用流程如下: 1) x264_reference_update( h ) 更新参考帧队列,碰到不用作参考的 B 帧的时候,不做更新; 2) 根据上面的结果,对帧 buffer 进行管理; 3) x264_stack_align( x264_slicetype_decide, h ) 决定当前编码的 slice 的类型; 4) x264_frame_shift( h->frames.current ) 获得要编码的当前帧,注意:前面 p_read_frame()函数是按照显示顺序逐帧将数 据从源文件中读取进来的,但是并不会立即编码,会预先存放在 buffer 中,有 B 帧 的时候,编码顺序和显示顺序是不一样的;这里才会按照编码顺序从 buffer 中 load 一个需要编码的帧数据; 5) x264_reference_build_list( h, h->fdec->i_poc ) 在一个 slice 编码前,需要初始化参考帧队列,ref_list0 和 ref_list1; 6) x264_ratecontrol_start( ) 和 i_global_qp = x264_ratecontrol_qp( h ) 码率控制,对编码流程无影响; 7) x264_macroblock_bipred_init( h ) 如果是 B slice 则调用此函数,主要根据 POC 值初始化双向参考帧队列,以及预测权 重系数; 8) x264_slice_init( h, i_nal_type, i_global_qp ) 建立一个 slice header; 9) x264_nal_start(),x264_sps_write(),x264_pps_write(),x264_nal_end(); 这个好理解,SPS, PPS; 10) x264_slices_write( h ) 这个是主要编码函数,才是重点,该函数开始进行一个 slice 的 bit stream 的真正编 码;后面介绍
X264 源代码版本:x264-snapshot-20090319-2245
1. 函数主流程 函数的入口在 x264.c 中 main(): 1) 进入 main()函数 后,首 先执行 2 个 函数:x264_param_default( ¶m ) 和 Parse( argc, argv, ¶m, &opt ); x264_param_default( ¶m ):设置参数集 param 的缺省值; Parse( argc, argv, ¶m, &opt ):这里主要分析命令行参数,打开输入输出文件, 同时根据环境设置初始化某些函数指针; 2)之后进入函数 Encode( x264_param_t *param, cli_opt_t *opt ); 3 ) 函 数 Encode( x264_param_t *param, cli_opt_t *opt ) 首 先 调 用 x264_encoder_open( param ):后面论述 4)x264_picture_alloc( &pic, X264_CSP_I420, param->i_width, param->i_height ): 这个函数主要 malloc 一个编码 buffer,分 3 个部分,分别存放 Y,U,V; 5)之后就进去编码的一个主循环,一个 for 循环 /* Encode frames */ for( i_frame = 0, i_file = 0; b_ctrl_c == 0 && (i_frame < i_frame_total || i_frame_total == 0); ) { if( p_read_frame( &pic, opt->hin, i_frame + opt->i_seek ) ) break;
x264_mdate()
malloc一个picture buffer (i_width*i_height*1.5), img.plane[0],[1],[2]分别指向 Y,U,V, 并初始化i_stride
p_read_frame()
读取一个编码帧的原始数据
for循环:逐帧编码 for( i_frame = 0,
x264_encoder_encode ()的流程如下:
x264_encoder_encode( x264_t *h, x264_nal_t **pp_nal, int *pi_nal, x264_picture_t *pic_in, x264_picture_t *pic_out )
x264_reference_update()
Encode()函 数,编码器
主循环
main() (x264.c)
x264_param_default()
Parse()
分析命令行参数,并 打开输入输出文件
Encode()
x264_encoder_open()
初始化编码器参数,以及 编码器用到的函数
p_set_outfile_param() x264_picture_alloc()
context
/* init CPU functions */ 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, &h>predict_8x8_filter ); x264_predict_4x4_init( h->param.cpu, h->predict_4x4 ); if( !h->param.b_cabac );