(转)x264源码分析(1):main、parse、encode、x264_encoder_open函数代码分析
转自:http://nkwavelet.blog.163.com/blog/static/2277560382013103010312144/
x264版本: x264-snapshot-20140226-2245
1. 首先对主函数进行分析,main函数很简洁,主要有三个步骤,见下图:
2. 接下来分析一下Parse函数中的主要过程:
// 该函数的详细分析见:x264编码器默认参数值
/* Presets are applied before all other options. */
for( optind = 0;; )
{ // 获取preset 和tune
int c = getopt_long( argc, argv, short_options, long_options, NULL );
... ... ... ...
}
// 如果preset设置为placebo,此设置获取更大的psnr值,但是速度非常慢,通常不采用此设置
b_turbo = 0;
// 首先设置缺省的编码器参数,然后根据给定的preset和tune修改部分编码器参数
// 该函数代码详细分析见:x264_param_xxxx 系列函数代码分析
if ( x264_param_default_preset( param, preset, tune ) < 0 )
return -1;
/* 解释命令行参数 */
for( optind = 0;; )
{ ... ... ... ... }
/* If first pass mode is used, apply faster settings. */
if ( b_turbo )
x264_param_apply_fastfirstpass( param );
/* Apply profile restrictions. 主要用于检测设置的profile与其他的编码器参数是否有冲突 */
// 该函数代码详细分析见:x264_param_xxxx 系列函数代码分析
if ( x264_param_apply_profile( param, profile ) < 0 )
return -1;
// 根据输出文件名的后缀名选择相应的文件操作函数集合
if ( select_output( muxer, output_filename, param ) )
return -1;
// 打开输出文件,如果输出文件是flv格式,则此处调用的函数
// 就是 output/flv.c 文件中的open_file 函数
cli_output.open_file( output_filename, &opt->hout, &output_opt );
// 选择输入文件打开方式,有avs, y4m, ffms, lavf, raw等方式,
// 具体方式还依赖编译选项的支持
if( select_input( demuxer, demuxername, input_filename, &opt->hin, &info, &input_opt ) )
return -1;
// 打开输入文件
cli_input.open_file( input_filename, &opt->hin, &info, &input_opt );
// 计算宽高比和帧率,即约分数表示
x264_reduce_fraction( &info.sar_width, &info.sar_height );
x264_reduce_fraction( &info.fps_num, &info.fps_den );
// 初始化视频filter参数,该函数定义在x264.c中
if( init_vid_filters( vid_filters, &opt->hin, &info, param, output_csp ) )
return -1;
/* set param flags from the post-filtered video */
param->b_vfr_input = info.vfr;
param->i_fps_num = info.fps_num;
param->i_fps_den = info.fps_den;
param->i_timebase_num = info.timebase_num;
param->i_timebase_den = info.timebase_den;
param->vui.i_sar_width = info.sar_width;
param->vui.i_sar_height = info.sar_height;
/* Automatically reduce reference frame count to match the user's target
level if the user didn't explicitly set a reference frame count. */
param->i_frame_reference = .......
}
{
x264_t *h = NULL; // 结构体x264_t的定义在common/common.h文件中
x264_picture_t pic; // 结构体x264_picture_t的定义在x264.h文件中
cli_pic_t cli_pic; // 结构体cli_pic_t的定义在input/input.h文件中
// 并对各结构体参数和cabac编码、预测等需要的参数进行初始化,打开编码器句柄。
// 通过x264_encoder_parameters得到设置给x264的参数,通过x264_encoder_reconfig更新x264参数
x264_t *h = x264_encoder_open( param );
x264_encoder_parameters( h, param );
ticks_per_frame = (int64_t)param->i_timebase_den * param->i_fps_den
ticks_per_frame = X264_MAX( ticks_per_frame, 1 );
{
// Write SPS/PPS/SEI
x264_nal_t *headers;
int i_nal;
x264_encoder_headers( h, &headers, &i_nal );
cli_output.write_headers( opt->hout, headers );
break;
x264_picture_init( &pic );
convert_cli_to_lib_pic( &pic, &cli_pic );
i_frame_size = encode_frame( h, opt->hout, &pic, &last_dts );
// 此处将函数encode_frame展开来分析
{
x264_picture_t pic_out;
x264_nal_t *nal;
int i_nal;
int i_frame_size = 0;
// 该函数详细分析见:x264_encoder_encode 函数代码分析
i_frame_size = x264_encoder_encode( h, &nal, &i_nal, pic, &pic_out );
if ( i_frame_size )
{ // 将一帧数据写入到输出文件中
i_frame_size = cli_output.write_frame( hout, nal[0].p_payload, i_frame_size, &pic_out );
*last_dts = pic_out.i_dts;
}
return i_frame_size;
}
{
b_ctrl_c = 1; /* lie to exit the loop */
retval = -1;
}
else if ( i_frame_size )
{
i_file += i_frame_size;
i_frame_output++;
if( i_frame_output == 1 )
first_dts = prev_dts = last_dts;
}
if ( filter.release_frame( opt->hin, &cli_pic, i_frame + opt->i_seek ) )
break;
while( !b_ctrl_c && x264_encoder_delayed_frames( h ) )
{
i_frame_size = encode_frame( h, opt->hout, NULL, &last_dts );
... ... ... ...
}
x264_encoder_close( h );
cli_output.close_file( opt->hout, largest_pts, second_largest_pts );
{
/* 结构体x264_t的定义在common/common.h中 */
x264_t *h;
/* Create a copy of param, 创建一个param拷贝 */
memcpy( &h->param, param, sizeof(x264_param_t) );
/* x264线程初始化 */
x264_threading_init();
/* 该函数位于encoder/encoder.c中,主要用于检查h->param中的参数设置是否有冲突 */
if( x264_validate_parameters( h, 1 ) < 0 )
goto fail;
/* 如果提供了外部量化矩阵文件,则读取分析该量化矩阵文件 */
if( x264_cqm_parse_file( h, h->param.psz_cqm_file ) < 0 )
goto fail;
/* 用于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 );
/* 将fps和timebase化为既约分数 */
x264_reduce_fraction( &h->param.i_fps_num, &h->param.i_fps_den );
x264_reduce_fraction( &h->param.i_timebase_num, &h->param.i_timebase_den );
/* Init x264_t */
/* 初始化sps和pps,这两个函数位于encoder/set.c中 */
x264_sps_init( h->sps, h->param.i_sps_id, &h->param );
x264_pps_init( h->pps, h->param.i_sps_id, &h->param, h->sps );
/* 检查设定的level是否有效,该函数及数组x264_levels[] 定义定义在encoder/set.c中 */
h->chroma_qp_table = i_chroma_qp_table + 12 + h->pps->i_chroma_qp_index_offset;
/* 初始化量化矩阵 */
if( x264_cqm_init( h ) < 0 )
goto fail;
/* 每行每列及一帧总的宏块数目 */
h->mb.i_mb_width = h->sps->i_mb_width;
h->mb.i_mb_height = h->sps->i_mb_height;
h->mb.i_mb_count = h->mb.i_mb_width * h->mb.i_mb_height;
h->mb.chroma_h_shift = CHROMA_FORMAT == CHROMA_420 || CHROMA_FORMAT == CHROMA_422;
h->mb.chroma_v_shift = CHROMA_FORMAT == CHROMA_420;
/* Adaptive MBAFF and subme 0 are not supported as we require halving motion
* vectors during prediction, resulting in hpel mvs.
* The chosen solution is to make MBAFF non-adaptive in this case. */
// 忽略隔行,因此下面变量值通常为0
h->mb.b_adaptive_mbaff = PARAM_INTERLACED && h->param.analyse.i_subpel_refine;
/* Init frames. 初始化一些帧设置,包括延迟帧数、参考帧数、关键帧间隔等 */
// i_bframe_adaptive的默认值为X264_B_ADAPT_FAST,rc.b_stat_read通常取值0
if( h->param.i_bframe_adaptive == X264_B_ADAPT_TRELLIS && !h->param.rc.b_stat_read )
h->frames.i_delay = X264_MAX(h->param.i_bframe,3)*4;
else
h->frames.i_delay = h->param.i_bframe;
// mb_tree默认是开启的,i_vbv_buffer_size默认值为0,
// rc.i_lookahead默认值为40,但是对于不同的preset,rc.i_lookahead取不同的值
// rc.i_lookahead的值还可以通过命令行参数--rc-lookahead设定
if ( h->param.rc.b_mb_tree || h->param.rc.i_vbv_buffer_size )
h->frames.i_delay = X264_MAX( h->frames.i_delay, h->param.rc.i_lookahead );
h->frames.i_delay += h->i_thread_frames - 1; // h->i_thread_frames通常取值1
h->frames.i_delay += h->param.i_sync_lookahead; // h->param.i_sync_lookahead通常取值0
h->frames.i_delay += h->param.b_vfr_input; // h->param.b_vfr_input默认值为1
// param.i_bframe_pyramid的值可以通过命令行参数--b-pyramid设定,可以取值0、1、2
// --b-pyramid表示是否允许B帧作为参考帧,默认值为normal=2,none=0表示不允许B帧作为参考帧
h->frames.i_bframe_delay = h->param.i_bframe ? (h->param.i_bframe_pyramid ? 2 : 1) : 0;
h->frames.i_max_ref0 = h->param.i_frame_reference;
h->frames.i_max_ref1 = X264_MIN( h->sps->vui.i_num_reorder_frames, h->param.i_frame_reference );
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.i_bframe_adaptive
|| h->param.i_scenecut_threshold
|| h->param.rc.b_mb_tree
|| h->param.analyse.i_weighted_pred ); // h->frames.b_have_lowres通常取值1
h->frames.b_have_lowres |= h->param.rc.b_stat_read && h->param.rc.i_vbv_buffer_size > 0;
h->frames.b_have_sub8x8_esa = !!(h->param.analyse.inter & X264_ANALYSE_PSUB8x8);
// i_keyint_max默认值250,上一个idr帧和上一个关键帧的序号,初始化为负值
h->frames.i_last_idr =
h->frames.i_last_keyframe = - h->param.i_keyint_max;
h->frames.i_input = 0;
h->frames.i_largest_pts = h->frames.i_second_largest_pts = -1; // 初始化帧的最大pts和次大pts值为-1
h->frames.i_poc_last_open_gop = -1;
/* Allocate room for max refs plus a few extra just in case. */
CHECKED_MALLOCZERO( h->frames.unused[1], (h->i_thread_frames + X264_REF_MAX + 4) * sizeof(x264_frame_t *) );
CHECKED_MALLOCZERO( h->frames.current, (h->param.i_sync_lookahead + h->param.i_bframe
+ h->i_thread_frames + 3) * sizeof(x264_frame_t *) );
if ( h->param.analyse.i_weighted_pred > 0 ) // 如果开启了P帧加权预测,则还需要申请额外的帧空间;默认是开启的
CHECKED_MALLOCZERO( h->frames.blank_unused, h->i_thread_frames * 4 * sizeof(x264_frame_t *) );
h->i_cpb_delay = h->i_coded_fields = h->i_disp_fields = 0;
h->i_prev_duration = ((uint64_t)h->param.i_fps_den * h->sps->vui.i_time_scale)
/ ((uint64_t)h->param.i_fps_num * h->sps->vui.i_num_units_in_tick);
h->i_disp_fields_last_frame = -1;
/* precalculate the cost of coding various combinations of bits in a single context,该函数定义在encoder/rdo.c中 */
x264_rdo_init();
/* init CPU functions */
x264_predict_8x8c_init( h->param.cpu, h->predict_8x8c ); // 该函数定义在common/predict.c中
x264_predict_8x16c_init( h->param.cpu, h->predict_8x16c ); // 该函数定义在common/predict.c中
x264_predict_8x8_init( h->param.cpu, h->predict_8x8, &h->predict_8x8_filter ); // 该函数定义在common/predict.c中
x264_predict_4x4_init( h->param.cpu, h->predict_4x4 ); // 该函数定义在common/predict.c中
x264_pixel_init( h->param.cpu, &h->pixf ); // 该函数定义在common/pixel.c中
x264_zigzag_init( h->param.cpu, &h->zigzagf_progressive, &h->zigzagf_interlaced ); // 该函数定义在common/dct.c中
memcpy( &h->zigzagf, PARAM_INTERLACED ? &h->zigzagf_interlaced : &h->zigzagf_progressive, sizeof(h->zigzagf) );
x264_mc_init( h->param.cpu, &h->mc, h->param.b_cpu_independent ); // 该函数定义在common/mc.c中
x264_quant_init( h, h->param.cpu, &h->quantf ); // 该函数定义在common/quant.c中
x264_deblock_init( h->param.cpu, &h->loopf, PARAM_INTERLACED ); // 该函数定义在common/deblock.c中
if( h->param.b_cabac )
x264_cabac_init( h ); // 该函数定义在common/cabac.c中
else // 函数x264_cavlc_init定义在common/vlc.c中
x264_stack_align( x264_cavlc_init, h ); // 该函数定义在common/cpu.h中
mbcmp_init( h ); // 该函数定义在encoder/encoder.c中
chroma_dsp_init( h ); // 该函数定义在encoder/encoder.c中
/* 输出cpu可以利用的多媒体指令集,例如mmx、sse2、sse3等 */
p = buf + sprintf( buf, "using cpu capabilities:" );
for( int i = 0; x264_cpu_names[i].flags; i++ ) // 数组x264_cpu_names定义在common/cpu.c中
{ ... ... }
if( !h->param.cpu )
p += sprintf( p, " none!" );
x264_log( h, X264_LOG_INFO, "%s\n", buf );
/* 生成数组logs,其中logs[0] = 0.718、 logs[i] = 2 * log2(i + 1) + 1.718,
其中log2表示以2为底的对数,该函数定义在encoder/analyse.c中 */
/* 初始化不同qp值所对应的代价,该函数定义在encoder/analyse.c中 */
if ( x264_analyse_init_costs( h, logs, qp ) )
goto fail;
if ( x264_analyse_init_costs( h, logs, X264_LOOKAHEAD_QP ) )
goto fail;
x264_free( logs );
static const uint16_t cost_mv_correct[7] = { 24, 47, 95, 189, 379, 757, 1515 };
/* Checks for known miscompilation issues. */
if( h->cost_mv[X264_LOOKAHEAD_QP][2013] != cost_mv_correct[BIT_DEPTH-8] )
goto fail;
/* Must be volatile or else GCC will optimize it out. */
if( x264_clz( temp ) != 23 )
goto fail;
h->out.i_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->nal_buffer_size = h->out.i_bitstream * 3/2 + 4 + 64; /* +4 for startcode, +64 for nal_escape assembly padding */
// 有关线程池、线程锁定等代码
... ...
if ( x264_lookahead_init( h, i_slicetype_length ) ) // 该函数定义在encoder/lookahead.c中
goto fail;
for( int i = 0; i < h->param.i_threads; i++ )
if ( x264_macroblock_thread_allocate( h->thread[i], 0 ) < 0 ) // 该函数定义在common/macroblock.c中
goto fail;
/* 初始化码率控制的一些参数,该函数详细分析见:x264_ratecontrol_new 函数代码分析(码率控制) */
goto fail;
/* 输出profile、level等一些日志信息 */
... ...
(转)x264源码分析(1):main、parse、encode、x264_encoder_open函数代码分析的更多相关文章
- 51ak带你看MYSQL5.7源码1:main入口函数
从事DBA工作多年 MYSQL源码也是头一次接触 尝试记录下自己看MYSQL5.7源码的历程 目录: 51ak带你看MYSQL5.7源码1:main入口函数 51ak带你看MYSQL5.7源码2:编译 ...
- Netty 源码解析(五): Netty 的线程池分析
今天是猿灯塔“365篇原创计划”第五篇. 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty 源码解析(一): 开始 Netty 源码解析(二): Netty 的 Channel Netty ...
- AngularJS源码解析4:Parse解析器的详解
$ParseProvider简介 此服务提供者也是angularjs中用的比较多的,下面我们来详细的说下这个provider. function $ParseProvider() { var cach ...
- 源码详解系列(五) ------ C3P0的使用和分析(包括JNDI)
简介 c3p0是用于创建和管理连接,利用"池"的方式复用连接减少资源开销,和其他数据源一样,也具有连接数控制.连接可靠性测试.连接泄露控制.缓存语句等功能.目前,hibernate ...
- Qt Creator 源码学习笔记04,多插件实现原理分析
阅读本文大概需要 8 分钟 插件听上去很高大上,实际上就是一个个动态库,动态库在不同平台下后缀名不一样,比如在 Windows下以.dll结尾,Linux 下以.so结尾 开发插件其实就是开发一个动态 ...
- 【源码分享】WPF漂亮界面框架实现原理分析及源码分享
1 源码下载 2 OSGi.NET插件应用架构概述 3 漂亮界面框架原理概述 4 漂亮界面框架实现 4.1 主程序 4.2 主程序与插件的通讯 4.2.1 主程序获取插件注册的服务 4.2 ...
- 曹工力荐:调试 jdk 中 rt.jar 包部分的源码(可自由增加注释,修改代码并debug)
背景 大家知道,jdk安装的目录下,一般会有个src.zip包,这个包基本对应了rt.jar这个包.rt.jar这个包里面,就放了jdk中,jdk采用java实现的那部分类库代码,比如java.lan ...
- [PHP源码阅读]trim、rtrim、ltrim函数
trim系列函数是用于去除字符串中首尾的空格或其他字符.ltrim函数只去除掉字符串首部的字符,rtrim函数只去除字符串尾部的字符. 我在github有对PHP源码更详细的注解.感兴趣的可以围观一下 ...
- Linux源码与编译出的目标文件汇编代码的一致性问题
start_kernel是内核启动时比较重要的一个函数,然而我发现一个问题,我编译出来的目标文件中的汇编代码与C源码并不完全对应,这是怎么一回事呢? asmlinkage void __init st ...
随机推荐
- add printer driver error 1802修复说明
1.重启电脑后 ,将服务"Print Spooler"服务重新启动2.srclient.dll文件拷贝到c盘 windows/system32目录下3.连接好打印机USB接口,重装 ...
- Unix lrzsz命令 上传本地文件到服务器 / 发送文件到客户端
第三方教程:https://www.jb51.net/article/73690.htm 安装命令: $ yum install lrzsz 本地上传文件到服务器,如果是xshell,直接拖拽文件进入 ...
- linux怎么查看一个文件夹的大小
linux查看一个文件夹的大小的命令为: -lh 该文件夹的完整路径 例,查询/var文件夹的大小: -lh /var du 递归查询该路径下所有文件的大小(若不加任何参数,则显示文件夹内的所有文件, ...
- unity, multi pass shader中的surface pass
今天尝试写一个multi pass shader,但其中有一个Pass是surface pass,总是莫名奇妙地报错.后来看到下面帖子: http://forum.unity3d.com/thread ...
- nexus代理地址整理
为给大家提供方便构建开源软件,分享下自己研究一些开源软件的时候,为方便构建搭建的nexus仓库以及代理地 名称 地址 apache-content-releases https://repositor ...
- 在C#中使用WMI查询进程的用户信息
这是一个使用WMI查询信息的例子.看之前请对WMI有一个简单的了解,可以百度,或者查看我上一篇:WMI测试器 主要代码:(需要添加对System.Management的引用) //创建Win32_Pr ...
- 批处理学习笔记7 - 管道连接符"|"
|就是把左边作为值传递给右边.有一些命令运用它比较方便 @echo off ping baidu.com | find "TTL" pause 这段命令就是把左边ping的结果传递 ...
- 解决方式-在Mac系统中,Eclipse无法导入含有中文路径的project
1.改动eclipse.app/Contents/Info.plist.查找 <key>CFBundleExecutable<key> 在其上方加入下面代码 <? xml ...
- nginx检查报错 error while loading shared libraries: libprofiler.so.0: cannot open shared object file: No such file or directory
在centos7.3上编译安装nginx-1.12.2 启动测试出错 [root@web02 local]# /usr/local/nginx/sbin/nginx -t /usr/local/ngi ...
- java.io.PrintWriter 中 write() 与 print() 的区别
最终都是重写了抽象类Writer里面的write方法print方法可以将各种类型的数据转换成字符串的形式输出.重载的write方法只能输出字符.字符数组.字符串等与字符相关的数据.