x264学习笔记(1)-函数调用流程
X264的多线程过程
![X264的多线程过程](https://img.taocdn.com/s3/m/2b2a294acf84b9d528ea7a31.png)
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)
![x264流程详细分析(3)](https://img.taocdn.com/s3/m/4ce221aad1f34693daef3e09.png)
去块滤波(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用法
![X264用法](https://img.taocdn.com/s3/m/66422a00e87101f69e31950a.png)
----
@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编码流程)](https://img.taocdn.com/s3/m/135dc21177c66137ee06eff9aef8941ea76e4b9d.png)
习笔记(一)(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)](https://img.taocdn.com/s3/m/12063413f02d2af90242a8956bec0975f565a447.png)
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)-函数调用流程
![x264学习笔记(1)-函数调用流程](https://img.taocdn.com/s3/m/779bb549a8956bec0975e36b.png)
/* 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程序框架流程分析
![x264程序框架流程分析](https://img.taocdn.com/s3/m/0beda4946bec0975f465e2b5.png)
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)
![X264参数设置(1)](https://img.taocdn.com/s3/m/5616114a49d7c1c708a1284ac850ad02de8007cf.png)
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;值越小越清晰。
x264使用示例(初学者使用)
![x264使用示例(初学者使用)](https://img.taocdn.com/s3/m/215d930f6d175f0e7cd184254b35eefdc8d315bf.png)
x264使用示例(初学者使用)x264使用示例/*** @note x264的编码示例.* 使用x264的版本为libx264-115* 1. 示例是个死循环,会源源不断的编码,然后将数据写文件.* 2. 示例的行为是:编码1000帧后,取空编码缓冲区,然后循环执行这两步.**/#include#include#include#include "stdint.h"extern "C"{#include "x264.h"};unsigned int g_uiPTSFactor = 0;int iNal = 0;x264_nal_t* pNals = NULL;int encode(x264_t* p264, x264_picture_t* pIn, x264_picture_t* pOut);int main(int argc, char** argv){int iResult = 0;x264_t* pX264Handle = NULL;x264_param_t* pX264Param = new x264_param_t;assert(pX264Param);//* 配置参数//* 使用默认参数x264_param_default(pX264Param);//* cpuFlagspX264Param->i_threads = X264_SYNC_LOOKAHEAD_AUTO;//* 取空缓冲区继续使用不死锁的保证.//* video PropertiespX264Param->i_width = 320; //* 宽度.pX264Param->i_height = 240; //* 高度pX264Param->i_frame_total = 0; //* 编码总帧数.不知道用0.pX264Param->i_keyint_max = 10;//* bitstream parameterspX264Param->i_bframe = 5;pX264Param->b_open_gop = 0;pX264Param->i_bframe_pyramid = 0;pX264Param->i_bframe_adaptive = X264_B_ADAPT_TRELLIS;//* 宽高比,有效果,但不是想要的.//pX264Param->vui.i_sar_width = 1080;//pX264Param->vui.i_sar_height = 720;//* LogpX264Param->i_log_level = X264_LOG_DEBUG;//* Rate control ParameterspX264Param->rc.i_bitrate = 1024 * 10;//* 码率(比特率,单位Kbps)//* muxing parameterspX264Param->i_fps_den = 1; //* 帧率分母pX264Param->i_fps_num = 25;//* 帧率分子pX264Param->i_timebase_den = pX264Param->i_fps_num;pX264Param->i_timebase_num = pX264Param->i_fps_den;//* 设置Profile.使用MainProfilex264_param_apply_profile(pX264Param,x264_profile_names[1]);//* 打开编码器句柄,通过x264_encoder_parameters得到设置给X264//* 的参数.通过x264_encoder_reconfig更新X264的参数pX264Handle = x264_encoder_open(pX264Param);assert(pX264Handle);//* 获取整个流的PPS和SPS,不需要可以不调用.iResult = x264_encoder_headers(pX264Handle, &pNals, &iNal);assert(iResult >= 0);//* PPS SPS 总共只有36B,如何解析出来呢?for (int i = 0; i < iNal; ++i){switch (pNals[i].i_type){case NAL_SPS:break;case NAL_PPS:break;default:break;}}//* 获取允许缓存的最大帧数.int iMaxFrames = x264_encoder_maximum_delayed_frames(pX264Handle);//* 编码需要的参数.iNal = 0;pNals = NULL;x264_picture_t* pPicIn = new x264_picture_t;x264_picture_t* pPicOut = new x264_picture_t;x264_picture_init(pPicOut);x264_picture_alloc(pPicIn, X264_CSP_I420, pX264Param->i_width, pX264Param->i_height);pPicIn->img.i_csp = X264_CSP_I420;pPicIn->img.i_plane = 3;//* 创建文件,用于存储编码数据FILE* pFile = fopen("agnt.264","wb");assert(pFile);//* 示例用编码数据.int iDataLen = pX264Param->i_width * pX264Param->i_height;uint8_t* data = new uint8_t[iDataLen];unsigned int uiComponent = 0;while (++uiComponent)//* 构建需要编码的源数据(YUV420色彩格式)::memset(data, uiComponent, iDataLen);::memcpy(pPicIn->img.plane[0], data, iDataLen);::memcpy(pPicIn->img.plane[1], data, iDataLen/4 ); ::memcpy(pPicIn->img.plane[2], data, iDataLen/4);if (uiComponent <= 1000){pPicIn->i_pts = uiComponent + g_uiPTSFactor * 1000; encode(pX264Handle, pPicIn, pPicOut);}else{//* 将缓存的数据取出int iResult = encode(pX264Handle, NULL, pPicOut);if (0== iResult){//break; //* 取空,跳出uiComponent = 0;++g_uiPTSFactor;/* {{ 这个解决不了取空缓冲区,再压缩无B帧的问题x264_encoder_reconfig(pX264Handle, pX264Param); x264_encoder_intra_refresh(pX264Handle);//* }} */}}//* 将编码数据写入文件.for (int i = 0; i < iNal; ++i){fwrite(pNals[i].p_payload, 1, pNals[i].i_payload, pFile);}}//* 清除图像区域x264_picture_clean(pPicIn);x264_picture_clean(pPicOut);//* 关闭编码器句柄x264_encoder_close(pX264Handle);pX264Handle = NULL;delete pPicIn ;pPicIn = NULL;delete pPicOut;pPicOut = NULL;delete pX264Param;pX264Param = NULL;delete [] data;data = NULL;return 0;}int encode(x264_t* pX264Handle, x264_picture_t* pPicIn, x264_picture_t* pPicOut){int iResult = 0;iResult = x264_encoder_encode(pX264Handle, &pNals, &iNal, pPicIn, pPicOut);if (0 == iResult){std::cout<<"编码成功,但被缓存了."<<std::endl;}elseif(iResult < 0){std::cout<<"编码出错"<<std::endl;}elseif (iResult > 0){std::cout<<"得到编码数据"<<std::endl;}/* {{ 作用不明unsigned char* pNal = NULL;for (int i = 0;i < iNal; ++i){int iData = 1024 * 32;x264_nal_encode(pX264Handle, pNal,&pNals[i]);}//* }} *///* 获取X264中缓冲帧数.int iFrames = x264_encoder_delayed_frames(pX264Handle);std::cout<<"当前编码器中缓存数据:"<<iframes<<"帧\n";return iFrames;}</iframes<<"帧\n"; </std::endl;</std::endl;</std::endl;。
[VCB-Studio][教程09]x264参数设置
![[VCB-Studio][教程09]x264参数设置](https://img.taocdn.com/s3/m/e9d8a47abf23482fb4daa58da0116c175f0e1e71.png)
[VCB-Studio][教程09]x264参数设置VCB-Studio教程09 x264参数设置0. 前言本教程旨在讲述x264参数设置的技巧,并侧重于vcb-s定位的使用:10bit,动漫,高码率,高参数。
对于非这类定位的压制,虽有叙述但不详细。
本教程请搭配MeGUI的参数设置面板一起阅读:本教程会按照标签卡(Main->Frame Type->…->Misc)分别讲解,每个标签卡内,按照从左上到左下,到右上到右下的顺序,讲述每个参数的使用,并给出它们在批处理命令行中的写法。
或者,你完全可以在MeGUI中调好,然后复制下方的命令行(去掉开头的program和结尾的--output "output" "input")到批处理。
后续会有教程讲述低码率下8bit压制的技巧。
不要将本篇中讲述的任何参数和逻辑套用在x265上。
1. Main标签卡,x264基础设置Main标签卡里面都是最主要的参数,新手上路,掌握Main标签卡的内容,就足以去一些普通字幕组担任压制了。
Encoding Mode(rc,ratecontrol,码率控制方法):ABR: Average Bit Rate,指定一个平均码率。
x264会试图让整部视频的平均码率达到你的给定值。
如果视频前后编码复杂度相差很大,那么码率的时间分配效果就很差,尤其是到了结尾,为了达到预定的码率值,x264经常不得不采用过高/过低的码率。
所以一般不推荐这个模式。
命令行: --bitrate 1000,1000是码率,单位Kbps。
Const quantizer:cq模式,固定量化模式。
所有P帧(下文有讲)采用一个固定的Quantizer。
Quantizer, 量化,是一种衡量图像压缩程度的方法,用0-69的浮点数表示,0为无损。
图像被压缩的越多,量化值越大,码率越低,注意量化值不一定代表目视质量,比如说一个纯色的图像可以以很高的量化值被量化,占用的体积很小,而一个很复杂的图像就算量化值不高,但是压缩后观感也可能很差。
X264参数设定详细解释
![X264参数设定详细解释](https://img.taocdn.com/s3/m/e5c6fb6e7fd5360cba1adb82.png)
X264参数设定详细解释x264 core:65 r1074M b6bb3d4Syntax: x264 [options] -o outfile infile [widthxheight]语法(命令行写法):x264 [参数] -o 输出文件名输入文件名[宽x高]范例:x264 --crf 26 --ref 3 --mixed-refs --bframes 3 --b-adapt 2 --b-pyramid --weightb --deblock -1:-1 --trellis 2 --partitions all --8x8dct --me umh --threads auto --thread-input --aud --progress --no-psnr --no-ssim -o output.mp4 input.avs 720x480 Infile can be raw YUV 4:2:0 (in which case resolution is required),or YUV4MPEG 4:2:0 (*.y4m),or AVI or Avisynth if compiled with AVIS support (yes).输入文件可以是RAW YUV 4:2:0(在某些情况下分辨率是必需的)或者YUV4MPEG 4:2:0 (*.y4m)或者AVI 或Avisynth(后面的yes说明支持AVS输入)Outfile type is selected by filename:.264 -> Raw bytestream.mkv -> Matroska.mp4 -> MP4 if compiled with GPAC support (yes)输出文件类型由输出文件名指定:.264 -> Raw 格式.mkv -> MKV格式.MP4 -> MP4格式(yes说明支持MP4输出)Options:可选参数:-h, --help List the more commonly used options --longhelp List all options-h, --help 显示常用参数--longhelp 显示全部参数注:-h,--help代表-h与--help两者都可以,-h是简化写法举个例子:x264 -h与x264 --help两者效果等价——————————————————————————————————————Frame-type options:帧-类型选项:-I, --keyint Maximum GOP size [250]说明:指定两个IDR帧之间的最大间隔,默认250推荐值:默认或者FPS的10倍范例:--keyint 300注:[250]代表默认值为250提示:若想使用默认值,不使用参数即可-i, --min-keyint Minimum GOP size [25]说明:指定两个IDR帧之间的最小间隔,默认25推荐值:默认或者FPS的大小范例:--min-keyint 30问题:如何查看FPS?/zh-CN去上面的网站下载mediainfo查看--scenecut How aggressively to insert extra I-frames [40]说明:指定强制使用IDR帧的阀值,值越大强度越高,默认为40推荐值:默认范例:--scenecut 40--pre-scenecut Faster, less precise scenecut detection.Required and implied by multi-threading.说明:效果同scenecut,速度比scenecut快,但是精度稍低,默认设定为当threads>1时,永远使用--pre-scenecut 推荐值:默认-b, --bframes Number of B-frames between I and P [0]说明:设定I帧与P帧之间的最大B帧数量,范围0~16推荐值:3-6范例:--bframes 3--b-adapt Adaptive B-frame decision method [1]Higher values may lower threading efficiency.- 0: Disabled- 1: Fast- 2: Optimal (slow with high --bframes)说明:B帧自适应方法,默认为1- 0: 关闭- 1: 高速- 2: 最优化(--bframes的值越高速度越慢)推荐值:2范例:--b-adapt 2--b-bias Influences how often B-frames are used [0]说明:影响B帧使用的频繁程度,默认为0推荐值:0范例:--b-bias 0--b-pyramid Keep some B-frames as references说明:允许其它帧参考B帧,默认不使用推荐值:开启范例:--b-pyramid--no-cabac Disable CABAC说明:关闭CABAC,默认不使用-r, --ref Number of reference frames [1]说明:设定参考帧的数量,范围0~16,默认值为1,过大的值可能导致无法硬解,参考以下公式计算良好硬解的最大参考帧最大参考帧数量计算公式:maximum ref = 12288 * 1024 / ( width * height * 1.5)推荐值:3-6范例:--ref 3--no-deblock Disable loop filter说明:关闭deblock filter,默认不使用推荐值:默认范例:--no-deblock-f, --deblock Loop filter AlphaC0 and Beta parameters [0:0]说明:设定deblock filter参数,alpha为Deblocking strength,beta为Deblockingthreshold,值越大deblocking效果越好,画面越干净,但是会损失一些细节并有些许模糊,反之亦然,上下限不要超过-3,3,默认0,0推荐值:默认范例:--deblock 0:0--interlaced Enable pure-interlaced mode说明:隔行编码模式,默认关闭范例:--interlaced——————————————————————————————————————Ratecontrol:压缩比控制:-q, --qp Set QP (0=lossless) [26]说明:固定量化模式,值越小质量越好,默认为26,qp = crf + --qcomp 1推荐值:使用crf,见crf部分范例:--qp 26-B, --bitrate Set bitrate (kbit/s)说明:目标码率模式,生成的视频码率大小为指定的bitrate 的值,一般搭配--pass使用推荐值:720P以下码率为800-2100kbps之间,720P为3-6Mbps,1080P为8-15Mbps以上范例:--bitrate 1000--crf Quality-based VBR (nominal QP) 说明:固定压缩因子模式,值越小质量越好,一般搭配--qcomp使用推荐值:16-26范例:--crf 26--qcomp QP curve compression: 0.0 => CBR, 1.0 => CQP [0.60]说明:压缩曲线,范围为0~1之间,数值越小曲线越平坦,与crf搭配使用,默认为0.6推荐值:默认范例:--qcomp 0.6--vbv-maxrate Max local bitrate (kbit/s) [0]说明:设定VBV模式的最大码率,如果需要硬解必须开启VBV模式,默认为0范例:--vbv-maxrate 50000--vbv-bufsize Enable CBR and set size of the VBV buffer (kbit) [0]说明:设定VBV缓冲区的最大尺寸,其大小一般由硬件设备决定,默认为0范例:--vbv-bufsize 50000--vbv-init Initial VBV bufferoccupancy [0.9]说明:设定VBV缓冲区的初始填充尺寸,默认为0.9范例:--vbv-init 0.9--qpmin Set min QP [10]说明:设定qp的下限,默认为10范例:--qpmin 10--qpmax Set max QP [51]说明:设定qp的上限,默认为51范例:--qpmax 51--qpstep Set max QP step [4]说明:设定qp的最大步长,默认为4范例:--qpstep 4--ratetol Allowed variance of average bitrate [1.0]说明:允许最终码率偏离指定平均码率的百分比,只在1pass 中起作用,默认为1.0范例:--ratetol 1.0--ipratio QP factor between I and P [1.40]说明:设定I帧相对于P帧的量化比推荐值:默认范例:--ipratio 1.40--pbratio QP factor between P and B [1.30]说明:设定P帧相对于B帧的量化比推荐值:默认范例:--ipratio 1.30--chroma-qp-offset QP difference between chroma and luma [0]说明:chroma 与luma 的QP差异值,这个值会随着--psy-rd的使用自动调整为-2推荐值:默认范例:--chroma-qp-offset 0--aq-mode AQ method [1]- 0: Disabled- 1: Variance AQ (complexity mask)说明:自适应量化方法,可以改善某些场景过于模糊等问题,默认开启- 0: 关闭- 1: 可变AQ推荐值:默认范例:--aq-mode 1--aq-strength Reduces blocking and blurring in flat andtextured areas. [1.0]- 0.5: weak AQ- 1.5: strong AQ说明:指定AQ的强度,减小低细节宏块的量化值,默认1.0 - 0.5: 较弱的AQ- 1.5: 较强的AQ推荐值:默认范例:--aq-strength 1.0-p, --pass Enable multipass ratecontrol- 1: First pass, creates stats file- 2: Last pass, does not overwrite stats file- 3: Nth pass, overwrites stats file说明:多重压缩模式,1 pass 或N pass生成stats文件,2pass调用生成的stats文件对压缩进行优化,更合理的分配码率,一般没必要进行N pass- 1: 第1 pass,生成stats文件- 2: 最终pass,不覆盖stats文件- 3: 第N pass,覆盖stats文件推荐值:2范例:--pass 2--stats Filename for 2 pass stats["x264_2pass.log"]说明:指定stats文件名,默认为"x264_2pass.log"推荐值:默认范例:--stats "x264_2pass.log"--cplxblur Reduce fluctuations in QP (before curve compression) [20.0]说明:减小QP的波动(在曲线压缩以前),范围0~999推荐值:默认范例:--cplxblur 20--qblur Reduce fluctuations in QP (after curve compression) [0.5]说明:减小QP的波动(在曲线压缩之后),范围0~99推荐值:默认范例:--qblur 0.5--zones //... Tweak the bitrate of someregions of the videoEach zone is of the form,,where is eitherq= (force QP)or b= (bitrate multiplier)说明:调整视频中某一范围内的码率每个区域已以下形式出现,,为下面的任意一个q= (强制QP)或b= (指定bitrate)范例:--zone 0,1000,qp=30/30000,32000,b=0.5--qpfile Force frametypes and QPs说明:强制指定帧类型与QP推荐值:默认Analysis:分析:-A, --partitions Partitions to consider["p8x8,b8x8,i8x8,i4x4"]- p8x8, p4x4, b8x8, i8x8, i4x4- none, all(p4x4 requires p8x8. i8x8 requires --8x8dct.)说明:宏块分割方式,默认["p8x8,b8x8,i8x8,i4x4"]- p8x8, p4x4, b8x8, i8x8, i4x4- none, all(p4x4 需要p8x8. i8x8 需要--8x8dct.)推荐值:默认范例:--partitions "p8x8,b8x8,i8x8,i4x4"--direct Direct MV prediction mode ["spatial"]- none, spatial, temporal, auto说明:Direct预测方法,默认"spatial"- none, spatial, temporal, auto推荐值:"auto"范例:--direct "auto"--direct-8x8 Direct prediction size [1] - 0: 4x4- 1: 8x8- -1: smallest possible according to level说明:Direct预测大小,默认为-1- 0: 4x4- 1: 8x8- -1: 根据Level确定一个最小值范例:--direct 1-w, --weightb Weighted prediction forB-frames说明:允许对B帧进行加权预测范例:--weightb--me Integer pixel motion estimation method ["hex"]- dia: diamond search, radius 1 (fast)- hex: hexagonal search, radius 2- umh: uneven multi-hexagon search- esa: exhaustive search- tesa: hadamard exhaustive search (slow)说明:全像素动态预测方法,越往下精度越高,速度越慢,默认"hex"- dia: 菱形搜索, 半径1 (高速)- hex: 六边形搜索, 半径2- umh: 不规则多边形搜索- esa: 全面搜索- tesa: hadamard变换全面搜索(最慢)推荐值:"umh"范例:--me "umh"--merange Maximum motion vector search range [16]说明:最大动态矢量搜索范围,结合--me使用,对于dia与hex,允许的范围为4~16,umh以上可以超过16,值越大编码速度越慢,默认为16范例:--merange 16--mvrange Maximum motion vector length [-1 (auto)]说明:最大动态矢量长度推荐值:默认范例:--mvrange -1 --mvrange-thread Minimum buffer between threads [-1 (auto)]说明:线程之间的最小缓冲区大小推荐值:默认范例:--mvrange-thread -1 -m, --subme Subpixel motion estimation and mode decision [6]- 0: fullpel only (not recommended)- 1: SAD mode decision, one qpel iteration- 2: SATD mode decision- 3-5: Progressively more qpel- 6: RD mode decision for I/P-frames- 7: RD mode decision for all frames- 8: RD refinement for I/P-frames- 9: RD refinement for all frames说明:子像素动态预测模式策略,值越大效果越好,速度越慢,默认6- 0: 仅fullpel (不推荐)- 1: SAD模式策略, 1 qpel迭代- 2: SATD模式策略- 3-5: 依次qpel增加- 6: I/P-帧RD模式策略- 7: 所有帧RD模式策略- 8: I/P-帧RD refinement模式策略- 9: 所有帧RD refinement模式策略推荐值:6以上范例:--subme 7--psy-rd Strength of psychovisual optimization ["1.0:0.0"]#1: RD (requires subme>=6)#2: Trellis (requires trellis, experimental)说明:视觉优化,--psy-rd 1.0:0.0 代表#1为1.0,#2为0.0,#2还在测试阶段,默认1.0:0.0#1: RD (需要subme>=6)#2: Trellis (需要trellis, 测试阶段)推荐值:#1 (0~1.0),#2 0.0范例:--psy-rd 1.0:0.0--mixed-refs Decide references on a per partition basis说明:对每个宏块区进行参考帧判断,开启后可以提升质量,但会降低速度推荐值:开启范例:--mixed-refs--no-chroma-me Ignore chroma in motion estimation说明:在动态预测中忽略chroma推荐值:默认范例:--no-chroma-me-8, --8x8dct Adaptive spatial transform size说明:自适应空间变换大小推荐值:使用范例:--8x8dct-t, --trellis Trellis RD quantization. Requires CABAC. [0]- 0: disabled- 1: enabled only on the final encode of a MB- 2: enabled on all mode decisions说明:Trllis RD量化.需要CABAC,值越大速度越慢,默认0- 0:关闭- 1:基于宏块- 2:在所有模式策略中使用推荐值:1范例:--trellis 1--no-fast-pskip Disables early SKIP detection on P-frames说明:关闭早期的P帧快速检测,开启可以提升质量,但会减低一些速度,默认不使用推荐值:使用范例:--no-fast-pskip--no-dct-decimate Disables coefficientthresholding on P-frames说明:关闭P帧的系数阀值,默认不使用推荐值:默认范例:--no-dct-decimate--nr Noise reduction [0]说明:降噪,默认0推荐值:默认范例:--nr 0--deadzone-inter Set the size of the inter luma quantization deadzone [21]--deadzone-intra Set the size of the intra luma quantization deadzone [11]Deadzones should be in the range 0 - 32.说明:设定inter/intra luma deadzone 量化值的大小,范围0 - 32推荐值:默认范例:--deadzone-inter 21 --deadzone-intra 11--cqm Preset quant matrices ["flat"]- jvt, flat说明:预设量化矩阵- jvt, flat推荐值:默认范例:--cqm "flat"--cqmfile Read custom quant matricesfrom a JM-compatible fileOverrides any other --cqm* options.说明:读取自定义JM兼容的量化矩阵文件,无视任何以--cqm开头的参数范例:--cqm "mycqm"--cqm4 Set all 4x4 quant matrices Takes a comma-separated list of 16 integers.说明:设定所有4x4量化矩阵,它是一张以逗号分割的16个整数的表范例:--cqm"16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16"--cqm8 Set all 8x8 quant matricesTakes a comma-separated list of 64 integers.说明:设定所有8x8量化矩阵,它是一张以逗号分割的64个整数的表范例:参考--cqm4--cqm4i, --cqm4p, --cqm8i, --cqm8pSet both luma and chroma quant matrices说明:设定luma与chroma的量化矩阵范例:参考--cqm4--cqm4iy, --cqm4ic, --cqm4py, --cqm4pc Set individual quant matrices说明:单独设定量化矩阵范例:参考--cqm4Video Usability Info (Annex E):The VUI settings are not used by the encoder but aremerely suggestions tothe playback equipment. See doc/vui.txt for details. Use at your own risk.视频可用性信息:VUI设置在编码的时候不会用到,它仅仅作用于回放设备。
x264编码器api调用时参数设置
![x264编码器api调用时参数设置](https://img.taocdn.com/s3/m/0acf760315791711cc7931b765ce0508763275a3.png)
x264 编码器API调用时的参数设置x264DLL在调用时主要需要调整的参数包括:第一,profile,也就是使用baseline还是main,还是high编码。
可以通过该接口设置x264_param_apply_profile();第二, 编码复杂度param.i_level_idc=30;第三,图像质量控制param.rc.f_rf_constant = 25;param.rc.f_rf_constant_max = 45;rc.f_rf_constant是实际质量,越大图像越花,越小越清晰。
param.rc.f_rf_constant_max ,图像质量的最大值。
第四, 码率控制一开始我使用恒定码流设置,无论我怎么设置,都无法控制实际码流,后来换成平均码流后,就行了。
param.rc.i_rc_method = X264_RC_ABR;//参数i_rc_method表示码率控制,CQP(恒定质量),CRF(恒定码率),ABR(平均码率)param.rc.i_vbv_max_bitrate=(int)((m_bitRate*1.2)/1000) ; // 平均码率模式下,最大瞬时码率,默认0(与-B设置相同)param.rc.i_bitrate = (int)m_bitRate/1000;x264使用的bitrate需要/1000。
第五,使用实时视频传输时,需要实时发送sps,pps数据param.b_repeat_headers = 1; // 重复SPS/PPS 放到关键帧前面该参数设置是让每个I帧都附带sps/pps。
第六. I帧间隔我是将I帧间隔与帧率挂钩的,以控制I帧始终在指定时间内刷新。
以下是2秒刷新一个I帧param.i_fps_num = (int)m_frameRate;param.i_fps_den = 1;param.i_keyint_max = m_frameRate * 2;第七,编码延迟在使用中,开始总是会有编码延迟,导致我本地编码立即解码回放后也存在巨大的视频延迟,后来发现设置x264_param_default_preset(¶m, "fast" , "zerolatency" );后就能即时编码了。
x264实时编码的码率控制--一个简单的QP调节方法
![x264实时编码的码率控制--一个简单的QP调节方法](https://img.taocdn.com/s3/m/38a5b63acdbff121dd36a32d7375a417866fc170.png)
x264实时编码的码率控制--一个简单的QP调节方法x264实时编码的码率控制一个简单的QP调节方法在用x264做实时视频,由于用在智能手机上,计算能力有限,网络带宽有限,故设计了一个简单的新的码率控制算法,效果还不错,与大家分享。
一般做实时视频,x264作者推荐用vbv-CBR模式,这样来稳定码流,适应带宽。
但对于嵌入式系统,CBR模式计算量较大。
CBR模式需要进行前期的半精度SATD值计算,以得到图像复杂度,进而得到预计的bits(这一SATD还用在slice type的选择,实际这个计算过程的函数就叫slice_type_decision,在lookaheand_get_frame中);通过预计bits与期望bits的比较得到qscale,进而得到本编码帧的QP。
当然若用上mb_tree,aq_mode 还更复杂。
这一前期的计算过程大致占整个计算量的12%-20%(根据参数的不同),但若略去这步,使用固定QP,码率波动较大。
现设计了一个新的模式,利用帧间的相似性,根据上一帧编码的实际bits与期望bits的差,来调节QP(帧级调整)。
实现上的思路也很简单,利用CQP模式的壳,在每帧编码前再次调节QP。
需要采用参数如下:--bframes 0 不要B帧对于嵌入式,手机等系统,建议采用:--scenecut 0---(不需额外增加i 帧,由于实时不需b帧,故一个GOP组中,只有一个IDR 帧及其余的P帧);--subme 1 1/4精度的SAD值--no-8x8dct (这个是high profile, 不需要,同样可加快编码,节省码流)--partition none 不要划分(可提高编码速度,节省码流,psnr基本不变)其余参数可参见,x264 --preset-veryfast的参数配置;好了接下来是帧级的码流控制,即调节QP;1. 在x264参数中添加新码率控制的参数(get_opt函数),如:--newqp2. 这个newqp与CQP,可以起使用。
X264编码流程详细分析
![X264编码流程详细分析](https://img.taocdn.com/s3/m/888fad69a45177232f60a2a0.png)
量化放缩 判断是否为skip模式, 并进行相关处理 x264_mb_encode_8x8
色度编码结束
计算亮度色度句型,以及非零计数
存储cbp: coded_ block_ pattern
检查P/B跳过情况, 进行相应处理
宏块编码结束
Encoder.c: x264_mb_encode_i16x16
反量化所有dct[1][][]中的系数
是
i<16
否 对dct[0][][]中的所有dc系数 进行量化和扫描,解雇存放于 h->dct.luma16x16_dc
重建块(idct,反量化),并将结果 存放入h->mb.pic.p_fdec[0]
宏块编码结束
Encoder.c: x264_mb_encode_i4×4 对4*4子块dct变换从p_src和p_dst 取出数据变换以后放在dct4*4中
X264.c: main
设置打开文件的方式
_setmode
x264_param_default 设置编码器默认参数
读取命令行并分析 Parse 编码Encode
X264.c:Encode
得到总帧数 p_get_frame_total 初始化编码器 x264_encoder_open 设置输出文件参数 p_set_outfile_param 为新图分配空间 x264_picture_alloc
选择帧类型
移动一些B 帧 和一个非B帧到编 码队列中去
获得要被编码 的帧
编码 Do encode
判断是否存放 重建的帧
计算和打印 统计值
更新编码器状态
设置输出 图片属性
Encoder.c: do encode
设置帧上下文 (初始化帧类型) 初始化 (参考列表,bit率控制)
x264阅读笔记之X264_frame
![x264阅读笔记之X264_frame](https://img.taocdn.com/s3/m/59d65ee5a0c7aa00b52acfc789eb172ded639982.png)
x264阅读笔记之X264_frameffmpeg中,libavcodec\libx264.c 文件,X264_frame 函数,用以对一帧图像或者延迟的数据进行压缩编码。
static int X264_frame(AVCodecContext *ctx, uint8_t *buf,int bufsize, void *data){X264Context *x4 = ctx->priv_data;AVFrame *frame = data;x264_nal_t *nal;int nnal, i;x264_picture_t pic_out;// 将 ffmpeg 的 AVFrame 转换为 x264 的 x264_picture_t 类型x4->pic.img.i_csp = X264_CSP_I420; //Csp: color space parameter 色彩空间参数 X264只支持I420x4->pic.img.i_plane = 3; //i_Plane 代表色彩空间的个数。
一般为3,YUVif (frame) { // frame==NULL 意味着,avcodec_encode_video 调用 X264_frame 时,实际传入参数 AVFrame *pict 的值为NULL。
即 X264_frame 是对delayed frame进行处理。
for (i = 0; i < 3; i++) {x4->pic.img.plane[i] = frame->data[i]; //原来有这种对应关系x4->pic.img.i_stride[i] = frame->linesize[i];//原来有这种对应关系}x4->pic.i_pts = frame->pts; //pts居然是由外传入的。
这么说在ffmpeg中,应该有生成pts的地方,可在video_encode_example 中并没有x4->pic.i_type = X264_TYPE_AUTO;}do { //*pi_nal(即此处的nnal) is the number of NAL units outputted in pp_nal(即此处的nal)if (x264_encoder_encode(x4->enc, &nal, &nnal, frame? &x4->pic: NULL, &pic_out) < 0) //pic_out是干嘛的?主要是最终是进入到了x4->out_picreturn -1;bufsize = encode_nals(ctx, buf, bufsize, nal, nnal, 0);if (bufsize < 0)return -1;} while (!bufsize && !frame && x264_encoder_delayed_frames(x4->enc));//与函数开始处相对,这是将x264 的x264_picture_t 类型,转换为 ffmpeg 的 AVFrame 类型。
X264编码过程解析
![X264编码过程解析](https://img.taocdn.com/s3/m/5e4073533b3567ec102d8a1f.png)
初始化static av_cold int X264_init(AVCodecContext *avctx);(1)x264_param_default( x264_param_t *param )作用:对编码器进行参数设定cqm:量化表相关信息csp:量化表相关信息里的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 );(2)static int Parse( int argc, char **argv, x264_param_t *param,cli_opt_t *opt ) 初始化1.getopt_long(nargc, nargv, options, long_options, idx) 得到入口地址的向量与方式的选则2.getopt_internal(nargc, nargv, options) 解析入口地址向量(3)static int Encode( x264_param_t *param, cli_opt_t *opt )/* Create a copy of param */ h->param=param/* VUI */vui信息主要包括帧率、图像尺寸等信息/* Init x264_t */x264_sps_init( h->sps, 0, &h->param );序列图像集x264_pps_init( h->pps, 0, &h->param, h->sps);图像参数集/* Init frames. */ 初始化并开辟帧空间/* init mb cache */ 对前一宏块的信息保存,因为是初始化,所以作为第一个宏块的参考,后面会有x264_macroblock_cache_load( h, i_mb_x, i_mb_y );它是将要编码的宏块的周围的宏块的值读进来, 要想得到当前块的预测值,要先知道上面,左面的预测值/* init cabac adaptive model *//* init CPU functions */ 初始化cpu对各种分块的参数设定/* rate control */1.x264_t *x264_encoder_open ( x264_param_t *param ) 这个函数是对不正确的参数进行修改,并对各结构体参数和cabac编码,预测等需要的参数进行初始化2、p_read_frame( &pic, opt->hin, i_frame + opt->i_seek, param->i_width, param->i_height )读取一帧,并把这帧设为prev3. i_file += Encode_frame( h, opt->hout, &pic );进入核心码层核心编码层的总流程图:(x264.c)1.x264_encoder_encode( h, &nal, &i_nal, pic, &pic_out )对帧进行编码2.i_size = x264_nal_encode( data, &i_data, 1, &nal[i] ) 网络打包编码3.i_file += p_write_nalu( hout, data, i_size ) 把网络包写入到输出文件中去4.返回,对下一帧进行编码编码static int X264_frame(AVCodecContext *ctx, AVPacket *pkt, const AVFrame *frame, int *got_packet)(1).x264_encoder_encode( h, &nal, &i_nal, pic, &pic_out )对帧进行编码1./* 1: Copy the picture to a frame and move it to a buffer */x264_frame_t*fenc=x264_frame_get( h->frames.unused );x264_frame_copy_picture( h, fenc, pic_in );fenc->i_frame = h->frames.i_input++;x264_frame_put( h->frames.next, fenc );x264_frame_init_lowres( h->param.cpu, fenc );//里面包含低象素的扩展,很多for循环,应该是抽头计算和半精度象素的扩展,要认真看(2).264_slicetype_decide( h );对slice类型的判定,里面也要看一下(3).while( IS_X264_TYPE_B( h->frames.next[bframes]->i_type ) ) bframes++;x264_frame_put(h->frames.current,x264_frame_get( &h->frames.next[ bframes] ) );这主要是因为B帧必须等后面的非B帧编码结束后才能编码,所以把暂时不编的一系列B帧存入队列中,一直到非B帧才取出进行编码,之后再进行前面的B帧编码do_encode:(4).建立list0 & list1.我感觉x264_reference_build_list( h, h->fdec->i_poc, i_slice_type );比特率控制初始化x264_ratecontrol_start(h, i_slice_type, h->fenc->i_qpplus1 );(5).创建slice的头部数据x264_slice_init( h, i_nal_type, i_slice_type, i_global_qp );(6)i_frame_size = x264_slices_write( h );这是编码的关键了1. x264_slice_header_write(&h->out.bs,&h->sh,h->i_nal_ref_idc ); /* Slice header */2. 一些初始化工作3. for(mb_xy=h->sh.i_first_mb, i_skip = 0; mb_xy < h->sh.i_last_mb; mb_xy++ )对一个slice中每个宏块进行循环遍历编码,其中const int i_mb_y =mb_xy / h->sps->i_mb_width;和const int i_mb_x = mb_xy %h->sps->i_mb_width;是对宏块位置在slice中的x,y坐标的定位,这个for语句几乎覆盖了整个x264_slices_write()函数4. x264_macroblock_cache_load( h, i_mb_x, i_mb_y ); 它是将要编码的宏块的周围的宏块的值读进来, 要想得到当前块的预测值,要先知道上面,左面的预测值!5. *****x264_macroblock_analyse( h );重点。
【转】关于x264的笔记整理
![【转】关于x264的笔记整理](https://img.taocdn.com/s3/m/e53696f6988fcc22bcd126fff705cc1755275f61.png)
【转】关于x264的笔记整理关于x264的笔记整理来源:[]2010年05月26日当初做x264优化时,1个人在摸索,一点儿点在改进,也记载下了一些东西,现在看来,有的至关琐碎,并且也没多大价值,然而这也是自己当初的一种经历,以后事情了,估计就再不会接触H.264了,现在写下来,或者许能对刚入门的人有点帮助吧。
2008-01-16 9:011.将所有的X264_LOG用LOG_printf代替,去掉common.c中的x264_log,log_default2.所有的fprintf()即对文件的操作应该去掉3.去掉信噪比的计较,因为在解码端也可得到在common.c中param->analyse.b_psnr = 0; //是否使用信噪比4.设置set_en.c中的sps->b_vui = 0;表示vui信息不呈现在码流中sps->b_frame_mbs_only = 1;表示采用所有图像均帧编码5.屏蔽掉:CAVLC_EN.C中的else if( i_mb_type == B_8x8 ),else if( i_mb_type!= B_DIRECT ),else if( i_mb_type == B_DIRECT ),else if( i_mb_type == B_8x8 )6.去掉common.h中的CHECKED_MALLOC中的if(!var)...(即检查分配内存成功与否)7.屏蔽掉ratecontrol_en.c中的x264_ratecontrol_new中的if( h->param.rc.i_rc_method == X264_RC_CRF)..和/* Load stat file and init 2passalgo */if( h->param.rc.b_stat_read )...8.去掉slicetype_decision_en.c中的if( h->param.rc.b_stat_read ) {...}和void x264_slicetype_analyse( x264_t *h )函数,ratecontrol_en.c 中的x264_ratecontrol_slice_type函数9.去掉ratecontrol_en.c中的update_vbv函数中的if( rcc->buffer_fill < 0 &&!rcc->b_2pass )...,rate_estimate_qscale中的if( rcc->b_2pass )..init_pass2(){...}函数10.去掉encoder_en.c中的x264_validate_levels( h )语句和set_en.c中对应的函数以上有一些修改涉及到检查参数的不错性,是因为在保证参数不错性的环境下这些语句是可以去除的2008-01-16 14:431.去掉if( h->param.rc.b_stat_read )开首的语句2.去掉assert()语句3.将CCS中的程序中的fprintf(...)全部去掉,(因为涉及到文件的操作,使用USB口传输很耗时)4.去掉exit(-1)之类报告程序纰缪的函数5.去掉encoder_en中的#ifdef DEBUG_MB_TYPE...#endif之间的语句6.去掉common.c中的x264_param_parse()函数7.去掉analyse_en.c中的static const int i_mb_b_cost_table[19]类似的数组(B帧用到的),以及以if( h->sh.i_type == SLICE_TYPE_B )...开首的语句2008-01-16 19:331.去掉analyse_en.c中的x264_mb_analyse_inter_direct(),x264_mb_analyse_inter_b16x16(),x264_mb_analyse_inter_b8x8,x264_mb_analyse_inter_b16x8,x264_mb_analyse_inter_b8x16()等5个函数,这5个函数是用来进行B帧帧间预测的,不需要用到2.去掉macroblock.c中的if( h->sh.i_type == SLICE_TYPE_B && h->param.b_cabac )...3.去掉所有以if( h->sh.i_type == SLICE_TYPE_B )..开首的语句4.去掉以if( h->param.analyse.b_psnr )...开首计较PSNR的语句5.将以for( i_list = 0; i_list < (h->sh.i_type == SLICE_TYPE_B ?2 : 1 );i_list++ )的轮回去掉,因为不使用P帧只执行一次,不需轮回但需加入i_list = 0;置初值6将#ifdef _BS_H#warning FIXME Multiple inclusion of bs.h#else#define _BS_H改为:#ifndef _BS_H#define _BS_H7.analyse_en.c中的x264_mb_analyse_b_rd(),refine_bidir()函数去掉8.去掉cavlc_en.c中的uint8_t mb_type_b_to_golomb[3][9]和sub_mb_type_b_to_golomb[13]数组9.去掉common.c中的parse_enum(),parse_cqm(),atobool()函数但愿:将encoder_en.c中的if( h->param.rc.psz_stat_out )h->param.rc.psz_stat_out = strdup( h->param.rc.psz_stat_out );如许就不需要自己定义的strdup函数了2008-01-17 9:481.去掉encoder_en.c的x264_encoder_encode()函数中的两个没有用到的变量:i_mb_i和i_mb_p(在CCS编译时的正告提醒这两个变量定义但未被使用,其它有类似的正告也能够考虑去掉)2.去掉frame.c中的x264_frame_new()函数中的以fail:标号开首的三句话(在程序中不会挪用)去掉macroblock.c中的x264_macroblock_cache_init()函数中的以fail:标号开首的两句话3.pridect.c中的UNUNSED变量该怎样处理?(CCS中正告说没有使用,但直接删除在VC下会报错!)4.去掉所有以#ifdef HAVE_MMXEXT ...#endif的语句5.去掉ratecontrol_en.c中的parse_zones相干的三处代码6.去掉encoder_en.c中的x264_encoder_关上()函数中的x264_ratecontrol_summary( h )函数及在ratecontrol.c中的相应代码(因为在这个函数中挪用了if(rc->b_abr)...;7.去掉ratecontrol_en.c中的与rate_estimate_qscale()和get_diff_limited_q()有关的函数8.去掉x264.c中的strtable_lookup()函数,与/* update status line (up to 1000times per input file) */有关的语句*******************1.考虑将ratecontrol_en.h中的声明的函数在程序中被使用的地方全部替换即删掉ratecontrol_en.c文件,不使用码率控制2.去掉以rc->b_abr,b->2pass判断的语句*******************2008-01-17 19:531.去掉ratecontrol_en.c中的get_qscale()和clip_qscale()函数2.去掉encoder_en.c中的关于x264_psnr()的宏及挪用它的函数(不计较信噪比)ratecontrol_en.c中的挪用:1.Searchellong for ''x264_ratecontrol_start''...F:\H.264\youhua 264Vc\encoder_en.c(1350): x264_ratecontrol_start( h,i_slice_type, h->fenc->i_qpplus1 );2.Searchellong for ''x264_ratecontrol_new''...F:\H.264\youhua 264Vc\encoder_en.c(617): if( x264_ratecontrol_new( h) < 0 )3.Searchellong for ''x264_ratecontrol_delete''...F:\H.264\youhua 264Vc\encoder_en.c(1842): x264_ratecontrol_delete( h);4.Searchellong for ''x264_ratecontrol_threads_start''...F:\H.264\youhua 264Vc\encoder_en.c(1124):x264_ratecontrol_threads_start( h );F:\H.264\youhua 264Vc\encoder_en.c(1148):x264_ratecontrol_threads_start( h );5.Searchellong for ''x264_ratecontrol_slice_type''...F:\H.264\youhua 264Vc\slicetype_decision_en.c(381 ):x264_ratecontrol_slice_type( h, h->frames.next[i]->i_frame );(已经被去掉)2008-01-17 20:376.Searchellong for ''x264_ratecontrol_mb''...F:\H.264\youhua 264Vc\encoder_en.c(1074):x264_ratecontrol_mb(h, bs_pos(&h->out.bs) - mb_spos);7.earchellong for ''x264_ratecontrol_qp''...F:\H.264\youhua 264Vc\analyse_en.c(1950): x264_mb_analyse_init( h,&analysis, x264_ratecontrol_qp( h ) );F:\H.264\youhua 264Vc\encoder_en.c(1351): i_global_qp =x264_ratecontrol_qp( h );8.Searchellong for ''x264_ratecontrol_end''...F:\H.264\youhua 264Vc\encoder_en.c(1541): x264_ratecontrol_end( h,i_frame_size * 8 );2008-01-19 9:171.修改x264.c中的Encode_frame()和Encode()函数,修改本来程序中的编码一帧,写一帧到输出文件为编码所有的帧完成后再一次写入到输出文件中(此处给存放输出文件的缓冲只分配了1M巨细)2008-01-20 17:231.在编译选项中添加-k,-mw,可以天生反馈文件信息(.asm)2008-01-22 20:33修改X264.C中的main函数,为输入的文件分配1个缓冲,p_readYUV,5M巨细.先将YUV文件读入到这个缓冲中,然后在编码时每一次读取YU的V分量只需要使用MEMCPY从内存中复制对应的数值即可.两天时间,终于快解决这个输入缓冲的问题!如许做仍然在CCS中仍然是将PC上的YUV 文件通过USB口读取到EVM的SDRAM中,速度仍然很慢,今后要是能使用收集传输应该会快很多,但现在主要是作优化,只要与PC通信的这块可以或者许不占入编码时间就大好了.主要代码如下:/* raw 420 yuv file operation */int open_file_yuv( char *psz_filename, hnd_t *p_handle,x264_param_t*p_param ){....//获得文件的长度fseek(h->fh,0,SEEK_END);i_file_length =ftell(h->fh);fseek(h->fh,0,SEEK_SET);//读取YUV文件fread(p_readYUV,1,i_file_length,h->fh);...}int read_frame_yuv( x264_picture_t *p_pic, hnd_t handle, int i_frame ){yuv_input_t *h = handle;/* if( i_frame != h->next_frame )if( fseek( h->fh, (uint64_t)i_frame * h->width * h->height * 3 /2, SEEK_SET ) )return -1;*//* if( fread( p_pic->img.plane[0], 1, h->width * h->height,h->fh ) <= 0|| fread( p_pic->img.plane[1], 1, h->width * h->height / 4,h->fh ) <= 0|| fread( p_pic->img.plane[2], 1, h->width * h->height / 4,h->fh ) <= 0 )return -1;*///从内存中复制数值memcpy(p_pic->img.plane[0], p_readYUVBuffer , h->width *h->height);memcpy(p_pic->img.plane[1], p_readYUVBuffer+h->width * h->height , h->width * h->height/ 4);memcpy(p_pic->img.plane[2], p_readYUVBuffer +h->width *h->height+ h->width * h->height/ 4, h->width * h->height/ 4);p_readYUVBuffer=p_readYUVBuffer +h->width * h->height+ h->width * h->height/ 4+ h->width * h->height/ 4;h->next_frame = i_frame+1;return 0;}在CCS中的char 换成uint8_t2008-01-23 15:29修改DSP/BIOS,将BIOS的对象分配到IRAM中,将BIOS需要的STACK分配在DDR2FORHEAP中,1.此时在p_readYUVBuffer=x264_malloc(0x500000);分配5M的输入缓冲时占了较长时间(估计有10分钟了)修改COMMON.C中的X264_malloc,将buf = (uint8_t *) malloc( i_size + 15 +sizeof( void ** ) +sizeof( int ) );改为:buf=(uint8_t *)MEM_calloc(DDR2forHeap, i_size + 15 +sizeof( void ** ) +sizeof( int ),0); (使用DSP/BIOS提供的MEM分配函数)2008-01-24 21:011.下午将fread从CCS中读到的数值,用CCS的DATA->SAVE把对应的foreman01.yuv文件生存为DATA格式的foreman01.dat,以后要作测试时,直接使用DATA-->LOAD将其导入到SRAM中即可2.调试时呈现问题:在编码第一帧后又返回到了主函数,如许一直在反复编码一帧!不知道是不是因为将main()和smain()函数放在同1个x264.c文件里面的缘故?是不是main()函数必须单独作1个文件?在log_printf.pjt工程里证明不是因为必须在1个文件里的问题3.以上呈现程序跑飞的缘故原由有多是因为TSK0的任务堆栈过小(默许是1024),在DSP/BIOS里改为了(10000)4.是在encoder_en.c中的x264_slice_write函数中又跑到主函数中去的有多是因为文件操作的缘故原由?2008-02-20 15:471.关于用CCS的DATA->SAVE把对应的foreman01.yuv文件生存为DATA格式的foreman01.dat,以后要作测试时,直接使用DATA-->LOAD将其导入到SRAM中,在CCS中使用VIEW-MEMORY查看内存中的数的读的方法:如DAT文件如下1651 1 85000010 0 11fb80 0xFFC9280A0xFEFEFEF70xFEFEFEFE0xFEFEFEFE0xFEFEFEFE0xFEFEFEFE0xFEFEFEFE0xFEFEFEFE0xFEFEFEFE0xFEFEFEFE0xFEFEFEFE0xE9FEFFFD ....................在SRAM中0x8500 0010存放第1个数0xFFC9280A,第二个数的0xFEFEFEF7的地址为0x8500 0010 + 4*(2-1)=0x8500 0014,第12个数0xE9FEFFFD的地址为0x8500 0010+4*(C-1)=0x8500 003C;最后1个数(第1178496个数)的地址为0x8500 0010 + 4*(11fb80-1)=0x8547 ee0c;(1178496转换为十六进制为0x11fb80);即第n个数的存放地址为0x8500 0010+4*(n(十六进制)-1);2008-02-22 10:21将所有的fseek换成lseek(/dv_rss.asp?s=xhtml&boardid=20&id=118797)仍不克不及解决问题2008-02-22 15:071.尝试方法:恢复1.17号的X264.C文件,即还是采用fopen,fread,从PC上读一帧编码后再从PC上读另一帧,看是否正常使用DATA-LOAD的备份文件为:复件⑷ x264.c在使用DATA-LOAD的程序中似乎可以不需要open_file_yuv..使用类似的程序在VC下可以正常编码..2008-02-22 21:53问题得到解决:将ccs中的macroblock.c用F:\H.264\youhua 264Vc目次下的macroblock.c文件替换,就能够正常编码了可见当时对这两个文件没有同步更新导致1个跨越年度的纰缪!!但天生的文件有800多kb,2008-02-25 16:27将DSP/BIOS中的MEM重新分配,使p_writeBuffer与p_readBuffer不指向同一块内存,天生的文件大为缩小,只有132KB,但是编码30帧的速度仍然需时近5分钟2008-02-26 19:56使用BUILD OPTION:-g -k -o3 -fr"$(Proj_dir)\Debug" -d"_DEBUG" -d"__X264__" -mw -mv6400+ --mem_model:data=far速度30帧约莫需要1分钟对于fps的计较仍未清楚,clock()两次运行同样的程序得到的时间竟然不同!2008-02-27 21:20仍然在研究使用何种方法来获得程序运行的时间,clock()只有用load6x()时才较精确,CLK_getltime()是保举的用法,不过要引入间断新建1个事情(在F:\DSP\CCS\CLK)专门用来测试CLK_getltime()的用法,发明:1.只有用timer0时,定时器间断才能步入,2.使用Profile菜单中的clock->view看钟表时,根据DSP/BIOS中的CLK manager中的Specify input clock rate来,得到的clock不同,默许这项是选中的,27MHZ时得到的cycle数比不选中时(594MHZ)时少的多,这有可能也是采用clock()不精确的缘故原由2008-02-28 9:05跟昨天一样的程序,同样的DSP/BIOS配置,昨天可以用CLK_getltime()获取时间,今天为啥子就返回的是0了1.将dsp264_3.pjt的DSP/BIOS的CLK manager入选中为timer0在x264.c的encode函数中将计较fps,需获得起头时间改为:i_start=CLK_getltime();i_end=CLK_getltime();调试程序时测得i_start=15 ,i_end=55462 (单位ms),fps=31(帧)/i_end-i_start=0.56(帧/秒) 与实际观测附近2008-02-28 19:001.删除mdate.c及相干的X264_mdate()宏,使用CLK_getltime()获取时间(删除common.h中的int64_t x264_mdate( void );encoder_en.c中的#define TIMER_START( d ) \...宏,和encoder_en.c中的挪用了TIMER_START()和TIMER_STOP的宏的行(这在VC上可以成功))2.变量储存类型调解:在VC中long和int 都是定义成32位,(见MSDN)而在CCS中long定义成40位,是以将CCS中的long换成int在VC中同步更新4.去掉if __x264__..else语句5.在x264.c中的encode函数中加入double fps,然而程序运行时却发明fps一直都是未定义变量,对x264.c去掉-O3编译选项,即可认出fps,有多是O3选项优化时将这句代码删掉了...,若去掉O3选项,编码速度很慢!将double fps设为全局变量,可以正常计较,前两天在测试clock()和CLK_getltime(),今天晚上就在测试这个fps的计较了,期间犯过将ms转换为s乘1000的初级纰缪...真是效率低!2008-02-29 11:001.加入-pm和-pm op0和-pm op2 -pm -op3优化选项编译,成果程序又反复编译第一帧了...2.加入-pm -op1优化选项,呈现纰缪:[Linking...] "C:\CCStudio_v3.3\C6000\cgtools\bin\cl6x" -@"Debug.lkf"<Linking>undefined first referencedsymbol in file--------- ----------------_smain F:\\H.264\\dsp264_3\\Debug\\dsp264_3cfg.obj>> error: symbol referencing errors - ''./Debug/dsp264_3.out'' not built在int smain()函数前加入:#pragma FUNC_EXT_CALLED (smain);BUILD无纰缪,但仍然程序反复编译第一帧2008-03-01 10:231.使用-k -o3 -fr"$(Proj_dir)\Debug" -d"_DEBUG" -mt -mw -mv6400+ --mem_model:data=far选项,编码速度为0.60fps2.-k -pm -op0 -o3 -fr"$(Proj_dir)\Debug" -d"_DEBUG" -mt -mw -mv6400+ --mem_model:data=far ...0.46fps,foreman01.264文件>157KB昨天使用-pm -op0选项不成功有多是因为不克不及加之-g选项2008-03-01 15:50参考:F:\H.264\移植\g宋扬-基于TMS320DM642DSP的H_264编码器优化与设计.nh1.项目级优化:使用-o3 -k -mt -mw2008-03-02 11:051.参考F:\H.264\移植\gH_264视频编码技术研究及其在TI_DSPDM642上的使成为事实.nh的<<3.5.3节对X264帧间预测模式选择算法的改进>>修改帧间预测算法,在X264VC中的analyse_en.c中修改,主要是屏蔽了voidx264_macroblock_analyse( x264_t *h )函数中if( analysis.b_mbrd ){/* refine later */}else if( i_partition == D_16x16 ){// x264_me_refine_qpel( h, &analysis.l0.me16x16 ); 与此类似的1/4像素精度搜索i_cost = analysis.l0.me16x16.cost;}最近算法还是不克不及删除!因为这些就是在在选择了最优预测模式后的分像素精度搜索 2008-03-06 14:511.去掉VC版本中pixel.c中void x264_pixel_init()关于PSNR的计较:INIT( ssd,),INIT( sad_x3, );在dsp264_3.pjt中作相应修改2008-03-07 9:001.去掉VC...pixel.c中pixel_sa8d_wxh()...及与sa8d相干的语句,在CCS中亦作相应修改2008-03-12 10:43根据CCS中函数结构和挪用约定,对于x264_pixel_sad_16x16之类的static int name( uint8_t *pix1, int i_stride_pix1, \uint8_t *pix2, int i_stride_pix2 )函数,在传递参数时前四个参数分别是A4,B4,A6,B6,将这些函数形参前后挨次改为static int name( uint8_t *pix1,uint8_t *pix2,int i_stride_pix1,inti_stride_pix2 ) 似乎更利于CCS的并行运行.因为对于pix1,pix2分别是参考宏块,分别采用A4,B4,作为寄存器挪用,可以减少一次T通道的使用(但有可能会增长X的使用了)先在VC中更改形参的次序,以"h->pixf.sad"为关键字搜索,修改如下地方:F:\H.264\youhua 264Vc\me_en.c(52): /*int cost = h->pixf.sad[i_pixel](m->p_fenc[0], FENC_STRIDE,\&p_fref[(my)*m->i_stride[0]+(mx)], m->i_stride[0] )\+ BITS_MVD(mx,my);\*/修改为:int cost = h->pixf.sad[i_pixel]( m->p_fenc[0],&p_fref[(my)*m->i_stride[0]+(mx)],\FENC_STRIDE, m->i_stride[0] )\+ BITS_MVD(mx,my);\ 即交换第二个和第三个形参的位置.以下修改类似F:\H.264\youhua 264Vc\me_en.c(62): int cost = h->pixf.sad[i_pixel](m->p_fenc[0], FENC_STRIDE, src, stride ) \F:\H.264\youhua 264Vc\me_en.c(70): h->pixf.sad_x3[i_pixel]( m->p_fenc[0],\F:\H.264\youhua 264Vc\me_en.c(83): h->pixf.sad_x4[i_pixel]( m->p_fenc[0],\F:\H.264\youhua 264Vc\me_en.c(101): h->pixf.sad_x4[i_pixel]( m->p_fenc[0],\F:\H.264\youhua 264Vc\me_en.c(468): const int enc_dc = h->pixf.sad[i_pixel]( m->p_fenc[0], FENC_STRIDE, zero, 16 );F:\H.264\youhua 264Vc\me_en.c(544): int cost = h->pixf.sad[i_pixel](m->p_fenc[0], FENC_STRIDE, src, stride ) \F:\H.264\youhua 264Vc\me_en.c(622): h->pixf.sad_x4[i_pixel]( m->p_fenc[0], src0, src1, src2, src3, stride, costs );修改了:me_en.c中的#define COST_MV( mx, my )\,#define COST_MV_PRED( mx,my ) \,const int enc_dc = h->pixf.sad[i_pixel]( m->p_fenc[0], zero, FENC_STRIDE,16 );#define COST_MV_SAD( mx, my ) \和pixel.c中的static int name( uint8_t *pix1, uint8_t *pix2, \int i_stride_pix1, int i_stride_pix2 )及SAD_x3和SAD_x4中的参数挨次和pixel.h中,添加typedef int (*x264_pixel_sad_t) ( uint8_t *, uint8_t *,int, int );及将x264_pixel_function_t结构体中的x264_pixel_cmp_t sad[7];改为:x264_pixel_sad_t sad[7];作以上修改后,release版速度只有8.94fps,不知是不是重启后速度会快些以此修改dsp264_3中对应的文件,将原有文件作备份(后缀名后加08311)2008-03-13 9:021.经昨日修改然后,在X264_PIXEL_sad_16x16线性汇编中的软件流水信息显示的是ii= 3 Schedule found with 5 iterations in parallel.C中的...是ii = 4 Schedule found with 4 iterations in parallel比未经修改的速度应该有提升,然而程序运行总出错:编译一帧后即停止运行2.经昨日修改的VC今天重启电脑运行,速度可以到达15.74fps,是以昨日所作修改对程序不会造成影响2008-03-15 20:131.田晓东-基于TMS320DM642的H_264视频编码.kdh4.4节,储存器的高度策略,有讲到分配的程序举例,和SAD16X8的线性汇编2008-03-16 9:501.g王澎-H_264视频编码器在DSP平台上的移植和优化.kdh5.4.1节短整型J无论是在精度还是范围上都能餍足需求。
x264编码 教程
![x264编码 教程](https://img.taocdn.com/s3/m/eacb8efff705cc175527091f.png)
MP4介绍与基本AVC编码(x264)教程首先简单介绍一下MPEG是什么:MPEG是Motion Picture Expert Group的缩写,简单讲就是个行业里的组织,专门对数字内容做出业界规范的组织。
其实从MPEG1开始我们就广泛认识到这个组织和他们的标准了。
VCD就是其中最主要的代表。
在当时亚洲国家VCD格式十分流行,如果没记错VCD这个具体的格式是从日本而来的,并遵守MPEG1规格。
之后便是MPEG2,具体代表是DVD。
知道现在都是主流数码格式。
说道这里大家应该开始明白MPEG这个组织其实他的责任就是推广每一代新的数字媒体规范或是规格,而不是实际的产品。
换句白话就是说,政府来规定符合什么样标准的汽车可以上路,然后各个汽车公司按照这个具体的标准来制作自己的汽车,通过政府规定的汽车才可以上路。
张三李四都可以开发自己符合mpeg规格的codec和container(这个是什么我之后会解释),并且理论上拿到别人同样按照这个规格开发的产品上照样可以工作。
具体例子就好比制作DVD的方法千千万万,好莱坞用来做大片,个人也可以把自家拍的DV刻成DVD。
理论上讲都可以在放在任何DVD机里播放(这里不考虑个别不兼容问题)。
这也是为什么明明XVID编码的dvdrip大家用ffdshow也可以照样看。
所以说这就是规格统一的好处!!说了这么多转入正题:我们要讨论的MP4格式。
从名字就可以看出来她是高于MPEG1、2的新一代数字媒体格式(本人到现在还不知道为什么没有3直接从2跳到4了,知道达人请在后面的帖子里补充)现在就具体介绍一下MP4的具体规格:(很多内容来源与,英文好的朋友我强烈建议去那里看看,消费层用户里面那里是很权威的)- ISO 14496-1 (Systems) - 户动界面(有点像DVD里的菜单)- ISO 14496-2 (Video) - ASP(Advanced Simple Profile)就是其中一种,代表产品有Xvid,Divx5等等。
x264的nal流程
![x264的nal流程](https://img.taocdn.com/s3/m/7edf03b365ce0508763213af.png)
x264中的NAL流程目前,主要是在分析NAL,做抓包实验,所以对NAL的格式要求比较高,这个过程中读了《新一代视频编码》,以前也读过,这是没有遇到实际的问题,读的时候也是似懂非懂的,囫囵吞枣,现在要分析,要用了才知道这些相关文档是要好好读的,ES流也是要好好分析的。
在上一篇中关于函数指针和指针函数的理论知识中,我主要是来看看x264中的NAL是怎么在封装的。
在x264中使用的函数指针,关于NAL部分的下面的一些:static int (*p_write_nalu)( hnd_t handle, uint8_t *p_nal, int i_size );,在这里可以看到p_write_nalu)是一个指针函数,p_write_nalu = write_nalu_bsf;即对于p_write_nalu其所指向的函数write_nalu_bsf,类似的还有:p_write_nalu = write_nalu_mp4;p_write_nalu = write_nalu_mkv;//每一个NALU都是由header+payload组成的,在header的结构是可以参考264的相关文档enum nal_unit_type_e{NAL_UNKNOWN = 0,NAL_SLICE = 1,NAL_SLICE_DPA = 2,NAL_SLICE_DPB = 3,NAL_SLICE_DPC = 4,NAL_SLICE_IDR = 5, /* ref_idc != 0 */NAL_SEI = 6, /* ref_idc == 0 */NAL_SPS = 7,NAL_PPS = 8,NAL_AUD = 9,/* ref_idc == 0 for 6,9,10,11,12 */};enum nal_priority_e{NAL_PRIORITY_DISPOSABLE = 0,NAL_PRIORITY_LOW = 1,NAL_PRIORITY_HIGH = 2,NAL_PRIORITY_HIGHEST = 3,};//NAL结构typedef struct{int i_ref_idc; /* nal_priority_e */int i_type; /* nal_unit_type_e *//* Size of payload in bytes. */int i_payload; //负载的大小/* If param->b_annexb is set, Annex-B bytestream with 4-byte startcode.* Otherwise, startcode is replaced with a 4-byte size.* This size is the size used in mp4/similar muxing; it is equal to i_payload-4 */uint8_t *p_payload;//如果是字节流格式的NAL时所用到的前缀4bytes} x264_nal_t;下面主要是跟踪x264,得到NAL的封装流程,如下:1.//NALstatic void x264_nal_start( x264_t *h, int i_type, int i_ref_idc ){x264_nal_t *nal = &h->out.nal[h->out.i_nal];nal->i_ref_idc = i_ref_idc;nal->i_type = i_type;nal->i_payload= 0;nal->p_payload= &h->out.p_bitstream[bs_pos( &h->out.bs ) / 8];}//下面是对bs_pos函数的注解static inline int bs_pos( bs_t *s ){return( 8 * (s->p - s->p_start) + (WORD_SIZE*8) - s->i_left ); / /获取当前的NALU的地址?????????- s->i_left}//bs_s的结构typedef struct bs_s{uint8_t *p_start;uint8_t *p;uint8_t *p_end;intptr_t cur_bits;int i_left; /* i_count number of available bits */int i_bits_encoded; /* RD only */} bs_t;2.static int x264_encoder_encapsulate_nals( x264_t *h ) //NAL封装int nal_size = 0, i;uint8_t *nal_buffer;for( i = 0; i < h->out.i_nal; i++ )nal_size += h->out.nal[i].i_payload;/* Worst-case NAL unit escaping: reallocate the buffer if it's too small. */if( h->nal_buffer_size < nal_size * 3/2 + h->out.i_nal * 4 ){uint8_t *buf = x264_malloc( nal_size * 2 + h->out.i_nal * 4 );if( !buf )return -1;x264_free( h->nal_buffer );h->nal_buffer = buf;}nal_buffer = h->nal_buffer;for( i = 0; i < h->out.i_nal; i++ ){int size = x264_nal_encode( nal_buffer, h->param.b_annexb, &h->out.nal[i] );h->out.nal[i].i_payload = size;h->out.nal[i].p_payload = nal_buffer;nal_buffer += size;}return nal_buffer - h->nal_buffer;}3.在2.中有:int x264_nal_encode( uint8_t *dst, int b_annexb, x264_nal_t *nal ){uint8_t *src = nal->p_payload; //为了同意结构还是将字节流格式的前缀作为指针的初始值uint8_t *end = nal->p_payload + nal->i_payload;uint8_t *orig_dst = dst;int i_count = 0, size;/* long nal start code (we always use long ones) */if( b_annexb ) //这里是进行字节流格式的码流编码,有开始前缀码,对于RTP封装则不需要前缀码{*dst++ = 0x00;*dst++ = 0x00;*dst++ = 0x00;*dst++ = 0x01;else /* save room for size later */dst += 4;/* nal header */*dst++ = ( 0x00 << 7 ) | ( nal->i_ref_idc << 5 ) | nal->i_type;//第一个bit的设置是由编码器自己控制的while( src < end ){if( i_count == 2 && *src <= 0x03 ){*dst++ = 0x03;//伪起始码i_count = 0;}if( *src == 0 )i_count++;elsei_count = 0;*dst++ = *src++;}size = (dst - orig_dst) - 4; //减4主要是前面的前缀码所占的字节/* Write the size header for mp4/etc */if( !b_annexb ){/* Size doesn't include the size of the header we're writing now. */orig_dst[0] = size>>24;orig_dst[1] = size>>16;orig_dst[2] = size>> 8;orig_dst[3] = size>> 0;}return size+4; //+4}4.static int x264_nal_end( x264_t *h ){x264_nal_t *nal = &h->out.nal[h->out.i_nal];nal->i_payload = &h->out.p_bitstream[bs_pos( &h->out.bs )/ 8] - nal ->p_payload;h->out.i_nal++;/* if number of allocated nals is not enough, re-allocate a larger one. */if( h->out.i_nal >= h->out.i_nals_allocated ){x264_nal_t *new_out = x264_malloc( sizeof(x264_nal_t) * (h->out.i_nals_allocated*2) );if( !new_out )return -1;memcpy( new_out, h->out.nal, sizeof(x264_nal_t) * (h->out.i_nals_allocated) );x264_free( h->out.nal );h->out.nal = new_out;h->out.i_nals_allocated *= 2;}return 0;}5.p_write_naluint write_nalu_bsf( hnd_t handle, uint8_t *p_nalu, int i_size ){if( fwrite( p_nalu, i_size, 1, (FILE*)handle ) > 0 ) //就是把p_nalu里面的1*i_size的字节输出到handle里面return i_size;return -1;}实验跟踪:在编码第一个I帧的时候,要编码的NALU的个数为4个,这里主要是指编码的类型为:SEI,SPS,PPS,I 帧的NAL的编码,对于一个I帧,也就是对这个GOP中的图像序列参数,图像参数进行编码,即有如下:/* 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 );if( x264_sei_version_write( h, &h->out.bs ) )return -1;if( x264_nal_end( h ) )return -1;overhead += h->out.nal[h->out.i_nal-1].i_payload + NALU_OVERHEAD;}/* generate sequence parameters */x264_nal_start( h, NAL_SPS, NAL_PRIORITY_HIGHEST );x264_sps_write( &h->out.bs, h->sps );if( x264_nal_end( h ) )return -1;overhead += h->out.nal[h->out.i_nal-1].i_payload + NALU_OVERHEAD;/* generate picture parameters */x264_nal_start( h, NAL_PPS, NAL_PRIORITY_HIGHEST );x264_pps_write( &h->out.bs, h->pps );if( x264_nal_end( h ) )return -1;overhead += h->out.nal[h->out.i_nal-1].i_payload + NALU_OVERHEAD; }int x264_encoder_headers( x264_t *h, x264_nal_t **pp_nal, int *pi_nal ) {int frame_size = 0;/* init bitstream context */h->out.i_nal = 0;bs_init( &h->out.bs, h->out.p_bitstream, h->out.i_bitstream );/* Write SEI, SPS and PPS. */x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE );if( x264_sei_version_write( h, &h->out.bs ) )return -1;if( x264_nal_end( h ) )return -1;/* generate sequence parameters */x264_nal_start( h, NAL_SPS, NAL_PRIORITY_HIGHEST );x264_sps_write( &h->out.bs, h->sps );if( x264_nal_end( h ) )return -1;/* generate picture parameters */x264_nal_start( h, NAL_PPS, NAL_PRIORITY_HIGHEST );x264_pps_write( &h->out.bs, h->pps );if( x264_nal_end( h ) )return -1;bs_flush( &h->out.bs );frame_size = x264_encoder_encapsulate_nals( h );/* now set output*/*pi_nal = h->out.i_nal;*pp_nal = &h->out.nal[0];h->out.i_nal = 0;return frame_size;}总结一下:对于NALU,并不是所谓的一帧对应一个NALU,而是对于SLICE而言,一个slice就封装层一个nal,具体一帧中有几个nalu则是可以再pps中参数中进行设定的,每遇到一个IDR,则此时就将对应的SPS,PPS进行一个更新,NAL的生成过程:先是对header里面的8个bits进行设定,然后是Payload,中间的细节,过于复杂,目前还不能够钻的很深,就先理解到这里。
1、H264视频压缩函数调用的基本流程
![1、H264视频压缩函数调用的基本流程](https://img.taocdn.com/s3/m/28087afb4bfe04a1b0717fd5360cba1aa8118c0d.png)
1、H.264 视频压缩函数调用的基本流程1首先检测本地环境是否支持使用H.264 视频压缩,XIS_IsSupportH264;2然后启动视频采集,XIS_StartV ideoCapture;3创建H.264 编码器,XIS_CreateH264Encoder;4设置H.264 视频压缩数据回调函数,XIS_SetH264EncoderCallback;5连接视频采集设备和H.264 编码器,XIS_SetV ideoCaptureEncoderOutput;6连接一旦建立,即开始进行H.264 编码操作;7如果需要中止编码器的运行,需要在XIS_SetV ideoCaptureEncoderOutput 上将编码器指针设为空值,即可断开视频采集设备和H.264 编码器的连接;8然后销毁H.264 编码器,XIS_DestroyH264Encoder;9最后停止视频采集,XIS_StopV ideoCapture;2、H.264 编码器的硬件加速特性在本地硬件环境允许的情况下,H.264 编码器会启用硬件加速特性,可以大大降低编码时对CPU 的占用,可以使用XIS_IsSupportH264HD 检查本地硬件环境是否支持硬件编码。
同时满足以下条件的硬件环境都可以支持硬件加速特性:∙视频采集设备驱动程序更新到最新版本;∙使用Intel Sandy Bridge / Ivy Bridge 架构的CPU (酷睿3代或4代) 和Intel 集成显卡;∙Intel 显卡驱动程序更新到2012.10.17 (Build 9.17.10.2875)及其以后版本;∙操作系统为Windows 7/8(如果操作系统为WindowsXP,则只能用软件压缩,不能使用硬件加速)。
如果本地硬件环境不支持硬件编码,但XIS_IsSupportH264 返回TRUE,此时调用XIS_CreateH264Encoder 会创建H.264 软件编码器。
- 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 );