接下来的几篇博客中,具体学习下X264的实现过程。

源代码的分析参考了雷神的博客,感谢雷神!博客链接:https://blog.csdn.net/leixiaohua1020/article/details/45536607

1.概述

X264编码流程图如下所示:

首先我们要创建编码器,初始化编码器参数,然后读入YUV数据进行VCL视频编码,编码后的数据进行NAL打包,循环所有的视频帧,最后释放内存,关闭编码器。

2.应用工程

(1)main()函数是x264控制台应用程序的入口,主函数如下:

  1. //主函数
  2. int main( int argc, char **argv )
  3. {
  4. x264_param_t param; //编码器参数
  5. cli_opt_t opt = {0}; //编码器操作
  6. int ret = 0;
  7. FAIL_IF_ERROR( x264_threading_init(), "unable to initialize threading\n" )
  8. #ifdef _WIN32
  9. FAIL_IF_ERROR( !get_argv_utf8( &argc, &argv ), "unable to convert command line to UTF-8\n" )
  10. GetConsoleTitleW( org_console_title, CONSOLE_TITLE_SIZE );
  11. _setmode( _fileno( stdin ), _O_BINARY ); //二进制格式
  12. _setmode( _fileno( stdout ), _O_BINARY );
  13. _setmode( _fileno( stderr ), _O_BINARY );
  14. #endif
  15.  
  16. if( parse( argc, argv, &param, &opt ) < 0 ) //解析命令行输入
  17. ret = -1;
  18. #ifdef _WIN32
  19. SetConsoleTitleW( org_console_title );
  20. #endif
  21.  
  22. signal( SIGINT, sigint_handler );
  23.  
  24. if( !ret )
  25. ret = encode( &param, &opt ); //编码
  26.  
  27. if( filter.free )
  28. filter.free( opt.hin );
  29. else if( opt.hin )
  30. cli_input.close_file( opt.hin );
  31. if( opt.hout )
  32. cli_output.close_file( opt.hout, 0, 0 );
  33. if( opt.tcfile_out )
  34. fclose( opt.tcfile_out );
  35. if( opt.qpfile )
  36. fclose( opt.qpfile );
  37. #ifdef _WIN32
  38. SetConsoleTitleW( org_console_title );
  39. free( argv );
  40. #endif
  41. return ret;
  42. }

主函数的开始,首先调用parse()解析输入的命令行参数,再调用encode()函数进行编码。

(2)parse()函数用于解析命令行输入的参数。

  1. static int parse(int argc, char **argv, x264_param_t *param, cli_opt_t *opt)

argc:参数个数;argv:指向参数的指针; param:参数结构体; opt:操作类型;

parse()函数:

  1. //解析命令行输入
  2. static int parse( int argc, char **argv, x264_param_t *param, cli_opt_t *opt )
  3. {
  4. char *input_filename = NULL;
  5. const char *demuxer = demuxer_names[0];
  6. char *output_filename = NULL;
  7. const char *muxer = muxer_names[0];
  8. char *tcfile_name = NULL;
  9. x264_param_t defaults;
  10. char *profile = NULL;
  11. char *vid_filters = NULL;
  12. int b_thread_input = 0;
  13. int b_turbo = 1;
  14. int b_user_ref = 0;
  15. int b_user_fps = 0;
  16. int b_user_interlaced = 0;
  17. cli_input_opt_t input_opt;
  18. cli_output_opt_t output_opt;
  19. char *preset = NULL;
  20. char *tune = NULL;
  21. //初始化参数默认值
  22. x264_param_default( &defaults );
  23. cli_log_level = defaults.i_log_level;
  24.  
  25. memset( &input_opt, 0, sizeof(cli_input_opt_t) );
  26. memset( &output_opt, 0, sizeof(cli_output_opt_t) );
  27. input_opt.bit_depth = 8;
  28. input_opt.input_range = input_opt.output_range = param->vui.b_fullrange = RANGE_AUTO;
  29. int output_csp = defaults.i_csp;
  30. opt->b_progress = 1;
  31.  
  32. /* Presets are applied before all other options. */
  33. for( optind = 0;; )
  34. {
  35. int c = getopt_long( argc, argv, short_options, long_options, NULL );
  36. if( c == -1 )
  37. break;
  38. if( c == OPT_PRESET )
  39. preset = optarg;
  40. if( c == OPT_TUNE )
  41. tune = optarg;
  42. else if( c == '?' )
  43. return -1;
  44. }
  45.  
  46. if( preset && !strcasecmp( preset, "placebo" ) )
  47. b_turbo = 0;
  48. //设置preset,tune
  49. if( x264_param_default_preset( param, preset, tune ) < 0 )
  50. return -1;
  51.  
  52. /* Parse command line options */
  53. //解析命令行选项
  54. for( optind = 0;; )
  55. {
  56. int b_error = 0;
  57. int long_options_index = -1;
  58.  
  59. int c = getopt_long( argc, argv, short_options, long_options, &long_options_index );
  60.  
  61. if( c == -1 )
  62. {
  63. break;
  64. }
  65. //不同的选项做不同的处理
  66. switch( c )
  67. {
  68. case 'h':
  69. help( &defaults, 0 );//"-h"帮助菜单
  70. exit(0);
  71. case OPT_LONGHELP:
  72. help( &defaults, 1 );
  73. exit(0);
  74. case OPT_FULLHELP:
  75. help( &defaults, 2 );
  76. exit(0);
  77. case 'V':
  78. print_version_info();//打印版本信息
  79. exit(0);
  80. case OPT_FRAMES:
  81. param->i_frame_total = X264_MAX( atoi( optarg ), 0 );
  82. break;
  83. case OPT_SEEK:
  84. opt->i_seek = X264_MAX( atoi( optarg ), 0 );
  85. break;
  86. case 'o':
  87. output_filename = optarg;//输出文件路径
  88. break;
  89. case OPT_MUXER:
  90. FAIL_IF_ERROR( parse_enum_name( optarg, muxer_names, &muxer ), "Unknown muxer `%s'\n", optarg )
  91. break;
  92. case OPT_DEMUXER:
  93. FAIL_IF_ERROR( parse_enum_name( optarg, demuxer_names, &demuxer ), "Unknown demuxer `%s'\n", optarg )
  94. break;
  95. case OPT_INDEX:
  96. input_opt.index_file = optarg;
  97. break;
  98. case OPT_QPFILE:
  99. opt->qpfile = x264_fopen( optarg, "rb" );
  100. FAIL_IF_ERROR( !opt->qpfile, "can't open qpfile `%s'\n", optarg )
  101. if( !x264_is_regular_file( opt->qpfile ) )
  102. {
  103. x264_cli_log( "x264", X264_LOG_ERROR, "qpfile incompatible with non-regular file `%s'\n", optarg );
  104. fclose( opt->qpfile );
  105. return -1;
  106. }
  107. break;
  108. case OPT_THREAD_INPUT:
  109. b_thread_input = 1;
  110. break;
  111. case OPT_QUIET:
  112. cli_log_level = param->i_log_level = X264_LOG_NONE;//设置log级别
  113. break;
  114. case 'v':
  115. cli_log_level = param->i_log_level = X264_LOG_DEBUG;//设置log级别
  116. break;
  117. case OPT_LOG_LEVEL:
  118. if( !parse_enum_value( optarg, log_level_names, &cli_log_level ) )
  119. cli_log_level += X264_LOG_NONE;
  120. else
  121. cli_log_level = atoi( optarg );
  122. param->i_log_level = cli_log_level;//设置log级别
  123. break;
  124. case OPT_NOPROGRESS:
  125. opt->b_progress = 0;
  126. break;
  127. case OPT_TUNE:
  128. case OPT_PRESET:
  129. break;
  130. case OPT_PROFILE:
  131. profile = optarg;
  132. break;
  133. case OPT_SLOWFIRSTPASS:
  134. b_turbo = 0;
  135. break;
  136. case 'r':
  137. b_user_ref = 1;
  138. goto generic_option;
  139. case OPT_FPS:
  140. b_user_fps = 1;
  141. param->b_vfr_input = 0;
  142. goto generic_option;
  143. case OPT_INTERLACED:
  144. b_user_interlaced = 1;
  145. goto generic_option;
  146. case OPT_TCFILE_IN:
  147. tcfile_name = optarg;
  148. break;
  149. case OPT_TCFILE_OUT:
  150. opt->tcfile_out = x264_fopen( optarg, "wb" );
  151. FAIL_IF_ERROR( !opt->tcfile_out, "can't open `%s'\n", optarg )
  152. break;
  153. case OPT_TIMEBASE:
  154. input_opt.timebase = optarg;
  155. break;
  156. case OPT_PULLDOWN:
  157. FAIL_IF_ERROR( parse_enum_value( optarg, pulldown_names, &opt->i_pulldown ), "Unknown pulldown `%s'\n", optarg )
  158. break;
  159. case OPT_VIDEO_FILTER:
  160. vid_filters = optarg;
  161. break;
  162. case OPT_INPUT_FMT:
  163. input_opt.format = optarg;//输入文件格式
  164. break;
  165. case OPT_INPUT_RES:
  166. input_opt.resolution = optarg;//输入分辨率
  167. break;
  168. case OPT_INPUT_CSP:
  169. input_opt.colorspace = optarg;//输入色域
  170. break;
  171. case OPT_INPUT_DEPTH:
  172. input_opt.bit_depth = atoi( optarg );//输入颜色位深
  173. break;
  174. case OPT_DTS_COMPRESSION:
  175. output_opt.use_dts_compress = 1;
  176. break;
  177. case OPT_OUTPUT_CSP:
  178. FAIL_IF_ERROR( parse_enum_value( optarg, output_csp_names, &output_csp ), "Unknown output csp `%s'\n", optarg )
  179. // correct the parsed value to the libx264 csp value
  180. #if X264_CHROMA_FORMAT
  181. static const uint8_t output_csp_fix[] = { X264_CHROMA_FORMAT, X264_CSP_RGB };
  182. #else
  183. static const uint8_t output_csp_fix[] = { X264_CSP_I420, X264_CSP_I422, X264_CSP_I444, X264_CSP_RGB };
  184. #endif
  185. param->i_csp = output_csp = output_csp_fix[output_csp];
  186. break;
  187. case OPT_INPUT_RANGE:
  188. FAIL_IF_ERROR( parse_enum_value( optarg, range_names, &input_opt.input_range ), "Unknown input range `%s'\n", optarg )
  189. input_opt.input_range += RANGE_AUTO;
  190. break;
  191. case OPT_RANGE:
  192. FAIL_IF_ERROR( parse_enum_value( optarg, range_names, m->vui.b_fullrange ), "Unknown range `%s'\n", optarg );
  193. input_opt.output_range = param->vui.b_fullrange += RANGE_AUTO;
  194. break;
  195. default:
  196. generic_option:
  197. {
  198. if( long_options_index < 0 )
  199. {
  200. for( int i = 0; long_options[i].name; i++ )
  201. if( long_options[i].val == c )
  202. {
  203. long_options_index = i;
  204. break;
  205. }
  206. if( long_options_index < 0 )
  207. {
  208. /* getopt_long already printed an error message */
  209. return -1;
  210. }
  211. }
  212. //解析以字符串方式输入的参数
  213. //即选项名称和选项值都是字符串
  214. b_error |= x264_param_parse( param, long_options[long_options_index].name, optarg );
  215. }
  216. }
  217.  
  218. if( b_error )
  219. {
  220. const char *name = long_options_index > 0 ? long_options[long_options_index].name : argv[optind-2];
  221. x264_cli_log( "x264", X264_LOG_ERROR, "invalid argument: %s = %s\n", name, optarg );
  222. return -1;
  223. }
  224. }
  225.  
  226. /* If first pass mode is used, apply faster settings. */
  227. if( b_turbo )
  228. x264_param_apply_fastfirstpass( param );
  229.  
  230. /* Apply profile restrictions. */
  231. //设置profile
  232. if( x264_param_apply_profile( param, profile ) < 0 )
  233. return -1;
  234.  
  235. /* Get the file name */
  236. FAIL_IF_ERROR( optind > argc - 1 || !output_filename, "No %s file. Run x264 --help for a list of options.\n",
  237. optind > argc - 1 ? "input" : "output" )
  238. //根据文件名的后缀确定输出的文件格式(raw H264,flv,mp4...)
  239. if( select_output( muxer, output_filename, param ) )
  240. return -1;
  241. FAIL_IF_ERROR( cli_output.open_file( output_filename, &opt->hout, &output_opt ), "could not open output file `%s'\n", output_filename )
  242. //输入文件路径
  243. input_filename = argv[optind++];
  244. video_info_t info = {0};
  245. char demuxername[5];
  246.  
  247. /* set info flags to be overwritten by demuxer as necessary. */
  248. //设置info结构体
  249. info.csp = param->i_csp;
  250. info.fps_num = param->i_fps_num;
  251. info.fps_den = param->i_fps_den;
  252. info.fullrange = input_opt.input_range == RANGE_PC;
  253. info.interlaced = param->b_interlaced;
  254. if( param->vui.i_sar_width > 0 && param->vui.i_sar_height > 0 )
  255. {
  256. info.sar_width = param->vui.i_sar_width;
  257. info.sar_height = param->vui.i_sar_height;
  258. }
  259. info.tff = param->b_tff;
  260. info.vfr = param->b_vfr_input;
  261.  
  262. input_opt.seek = opt->i_seek;
  263. input_opt.progress = opt->b_progress;
  264. input_opt.output_csp = output_csp;
  265. //设置输入文件的格式(yuv,y4m...)
  266. if( select_input( demuxer, demuxername, input_filename, &opt->hin, &info, &input_opt ) )
  267. return -1;
  268.  
  269. FAIL_IF_ERROR( !opt->hin && cli_input.open_file( input_filename, &opt->hin, &info, &input_opt ),
  270. "could not open input file `%s'\n", input_filename )
  271.  
  272. x264_reduce_fraction( &info.sar_width, &info.sar_height );
  273. x264_reduce_fraction( &info.fps_num, &info.fps_den );
  274. x264_cli_log( demuxername, X264_LOG_INFO, "%dx%d%c %u:%u @ %u/%u fps (%cfr)\n", info.width,
  275. info.height, info.interlaced ? 'i' : 'p', info.sar_width, info.sar_height,
  276. info.fps_num, info.fps_den, info.vfr ? 'v' : 'c' );
  277.  
  278. if( tcfile_name )
  279. {
  280. FAIL_IF_ERROR( b_user_fps, "--fps + --tcfile-in is incompatible.\n" )
  281. FAIL_IF_ERROR( timecode_input.open_file( tcfile_name, &opt->hin, &info, &input_opt ), "timecode input failed\n" )
  282. cli_input = timecode_input;
  283. }
  284. else FAIL_IF_ERROR( !info.vfr && input_opt.timebase, "--timebase is incompatible with cfr input\n" )
  285.  
  286. /* init threaded input while the information about the input video is unaltered by filtering */
  287. #if HAVE_THREAD
  288. if( info.thread_safe && (b_thread_input || param->i_threads > 1
  289. || (param->i_threads == X264_THREADS_AUTO && x264_cpu_num_processors() > 1)) )
  290. {
  291. if( thread_input.open_file( NULL, &opt->hin, &info, NULL ) )
  292. {
  293. fprintf( stderr, "x264 [error]: threaded input failed\n" );
  294. return -1;
  295. }
  296. cli_input = thread_input;
  297. }
  298. #endif
  299.  
  300. /* override detected values by those specified by the user */
  301. if( param->vui.i_sar_width > 0 && param->vui.i_sar_height > 0 )
  302. {
  303. info.sar_width = param->vui.i_sar_width;
  304. info.sar_height = param->vui.i_sar_height;
  305. }
  306. if( b_user_fps )
  307. {
  308. info.fps_num = param->i_fps_num;
  309. info.fps_den = param->i_fps_den;
  310. }
  311. if( !info.vfr )
  312. {
  313. info.timebase_num = info.fps_den;
  314. info.timebase_den = info.fps_num;
  315. }
  316. if( !tcfile_name && input_opt.timebase )
  317. {
  318. uint64_t i_user_timebase_num;
  319. uint64_t i_user_timebase_den;
  320. int ret = sscanf( input_opt.timebase, "%"SCNu64"/%"SCNu64, &i_user_timebase_num, &i_user_timebase_den );
  321. FAIL_IF_ERROR( !ret, "invalid argument: timebase = %s\n", input_opt.timebase )
  322. else if( ret == 1 )
  323. {
  324. i_user_timebase_num = info.timebase_num;
  325. i_user_timebase_den = strtoul( input_opt.timebase, NULL, 10 );
  326. }
  327. FAIL_IF_ERROR( i_user_timebase_num > UINT32_MAX || i_user_timebase_den > UINT32_MAX,
  328. "timebase you specified exceeds H.264 maximum\n" )
  329. opt->timebase_convert_multiplier = ((double)i_user_timebase_den / info.timebase_den)
  330. * ((double)info.timebase_num / i_user_timebase_num);
  331. info.timebase_num = i_user_timebase_num;
  332. info.timebase_den = i_user_timebase_den;
  333. info.vfr = 1;
  334. }
  335. if( b_user_interlaced )
  336. {
  337. info.interlaced = param->b_interlaced;
  338. info.tff = param->b_tff;
  339. }
  340. if( input_opt.input_range != RANGE_AUTO )
  341. info.fullrange = input_opt.input_range;
  342.  
  343. //初始化滤镜filter
  344. //filter可以认为是一种“扩展”了的输入源
  345. if( init_vid_filters( vid_filters, &opt->hin, &info, param, output_csp ) )
  346. return -1;
  347.  
  348. /* set param flags from the post-filtered video */
  349. param->b_vfr_input = info.vfr;
  350. param->i_fps_num = info.fps_num;
  351. param->i_fps_den = info.fps_den;
  352. param->i_timebase_num = info.timebase_num;
  353. param->i_timebase_den = info.timebase_den;
  354. param->vui.i_sar_width = info.sar_width;
  355. param->vui.i_sar_height = info.sar_height;
  356.  
  357. info.num_frames = X264_MAX( info.num_frames - opt->i_seek, 0 );
  358. if( (!info.num_frames || param->i_frame_total < info.num_frames)
  359. && param->i_frame_total > 0 )
  360. info.num_frames = param->i_frame_total;
  361. param->i_frame_total = info.num_frames;
  362.  
  363. if( !b_user_interlaced && info.interlaced )
  364. {
  365. #if HAVE_INTERLACED
  366. x264_cli_log( "x264", X264_LOG_WARNING, "input appears to be interlaced, enabling %cff interlaced mode.\n"
  367. " If you want otherwise, use --no-interlaced or --%cff\n",
  368. info.tff ? 't' : 'b', info.tff ? 'b' : 't' );
  369. param->b_interlaced = 1;
  370. param->b_tff = !!info.tff;
  371. #else
  372. x264_cli_log( "x264", X264_LOG_WARNING, "input appears to be interlaced, but not compiled with interlaced support\n" );
  373. #endif
  374. }
  375. /* if the user never specified the output range and the input is now rgb, default it to pc */
  376. int csp = param->i_csp & X264_CSP_MASK;
  377. if( csp >= X264_CSP_BGR && csp <= X264_CSP_RGB )
  378. {
  379. if( input_opt.output_range == RANGE_AUTO )
  380. param->vui.b_fullrange = RANGE_PC;
  381. /* otherwise fail if they specified tv */
  382. FAIL_IF_ERROR( !param->vui.b_fullrange, "RGB must be PC range" )
  383. }
  384.  
  385. /* Automatically reduce reference frame count to match the user's target level
  386. * if the user didn't explicitly set a reference frame count. */
  387. if( !b_user_ref )
  388. {
  389. int mbs = (((param->i_width)+15)>>4) * (((param->i_height)+15)>>4);
  390. for( int i = 0; x264_levels[i].level_idc != 0; i++ )
  391. if( param->i_level_idc == x264_levels[i].level_idc )
  392. {
  393. while( mbs * param->i_frame_reference > x264_levels[i].dpb && param->i_frame_reference > 1 )
  394. param->i_frame_reference--;
  395. break;
  396. }
  397. }
  398. return 0;
  399. }

具体流程:

(1)调用x264_param_default()为存参数的结构体x264_param_t赋默认值;

(2)调用x264_param_default_preset()为x264_param_t赋值;

(3)在循环中调用getopt_long()逐个解析输入的参数,并做相应的处理;

(4)调用select_input()解析输出文件格式;

(5)调用select_output()解析输入文件格式;

x264_param_default():设置x264_param_t结构体的默认值(X264的API)。

  1. void x264_param_default(x264_param_t *param)
  2. {
  3. memset( param, 0, sizeof( x264_param_t ) ); //开辟内存空间
  4. param->cpu = x264_cpu_detect(); //CPU自动检测
  5. param->i_threads = X264_THREADS_AUTO; //并行编码线程为0
  6. param->b_deterministic = 1; //允许非确定性时线程优化
  7. param->i_sync_lookahead = X264_SYNC_LOOKAHEAD_AUTO; //自动选择线程超前缓冲大小-1
  8.  
  9. //视频属性
  10. param->i_csp = X264_CSP_I420; //设置输入的视频采样的格式0x0001yuv 4:2:0 planar
  11. param->i_width = 0; //宽度
  12. param->i_height = 0; //高度
  13. param->vui.i_sar_width = 0;
  14. param->vui.i_sar_height= 0; //设置长宽比
  15. param->vui.i_overscan = 0; //过扫描线,默认undef(不设置),可选:show(观看)crop(去除)
  16. param->vui.i_vidformat = 5; //undef视频格式
  17. param->vui.b_fullrange = 0; //off
  18. param->vui.i_colorprim = 2; //undef原始色度格式
  19. param->vui.i_transfer = 2; //undef 转换方式
  20. param->vui.i_colmatrix = 2; //undef 色度矩阵设置
  21. param->vui.i_chroma_loc= 0; //left center 色度样本指定,范围0~5,默认0
  22. param->i_fps_num = 25; //帧率
  23. param->i_fps_den = 1; //用两个整型的数的比值,来表示帧率
  24. param->i_level_idc = -1; //level值的设置
  25. param->i_slice_max_size = 0; //每片字节的最大数,包括预计的NAL开销.
  26. param->i_slice_max_mbs = 0; //每片宏块的最大数,重写 i_slice_count
  27. param->i_slice_count = 0; //每帧的像条数目: 设置矩形像条.
  28.  
  29. //编码参数
  30. param->i_frame_reference = 3; //参考帧的最大帧数。
  31. param->i_keyint_max = 250; //在此间隔设置IDR关键帧
  32. param->i_keyint_min = 25; //场景切换少于次值编码位I, 而不是 IDR.
  33. param->i_bframe = 3; //两个参考帧之间的B帧数目
  34. param->i_scenecut_threshold = 40; //如何积极地插入额外的I帧
  35. param->i_bframe_adaptive = X264_B_ADAPT_FAST; //自适应B帧判定1
  36. param->i_bframe_bias = 0; //控制插入B帧判定,范围-100~+100,越高越容易插入B帧
  37. param->b_bframe_pyramid = 0; //允许部分B为参考帧
  38. param->b_deblocking_filter = 1; //去块效应相关
  39. param->i_deblocking_filter_alphac0 = 0; //[-6, 6] -6 亮度滤波器, 6 强
  40. param->i_deblocking_filter_beta = 0; //[-6, 6] 同上
  41. param->b_cabac = 1; //cabac的开关
  42. param->i_cabac_init_idc = 0;
  43.  
  44. //码率控制
  45. param->rc.i_rc_method = X264_RC_CRF; //恒定码率
  46. param->rc.i_bitrate = 0; //设置平均码率大小
  47. param->rc.f_rate_tolerance = 1.0;
  48. param->rc.i_vbv_max_bitrate = 0; //平均码率模式下,最大瞬时码率,默认0(与-B设置相同)
  49. param->rc.i_vbv_buffer_size = 0; //码率控制缓冲区的大小,单位kbit,默认0
  50. param->rc.f_vbv_buffer_init = 0.9;
  51. param->rc.i_qp_constant = 23; //最小qp值
  52. param->rc.f_rf_constant = 23;
  53. param->rc.i_qp_min = 10; //允许的最小量化值
  54. param->rc.i_qp_max = 51; //允许的最大量化值
  55. param->rc.i_qp_step = 4; //帧间最大量化步长
  56. param->rc.f_ip_factor = 1.4;
  57. param->rc.f_pb_factor = 1.3;
  58. param->rc.i_aq_mode = X264_AQ_VARIANCE;
  59. param->rc.f_aq_strength = 1.0;
  60. param->rc.i_lookahead = 40;
  61. param->rc.b_stat_write = 0;
  62. param->rc.psz_stat_out = "x264_2pass.log";
  63. param->rc.b_stat_read = 0;
  64. param->rc.psz_stat_in = "x264_2pass.log";
  65. param->rc.f_qcompress = 0.6;
  66. param->rc.f_qblur = 0.5; //时间上模糊量化
  67. param->rc.f_complexity_blur = 20; //时间上模糊复杂性
  68. param->rc.i_zones = 0;
  69. param->rc.b_mb_tree = 1;
  70.  
  71. //日志
  72. param->pf_log = x264_log_default;
  73. param->p_log_private = NULL;
  74. param->i_log_level = X264_LOG_INFO; //默认为“Info”
  75.  
  76. //分析
  77. param->analyse.intra = X264_ANALYSE_I4x4 | X264_ANALYSE_I8x8;
  78. param->analyse.inter = X264_ANALYSE_I4x4 | X264_ANALYSE_I8x8 | X264_ANALYSE_PSUB16x16 | X264_ANALYSE_BSUB16x16;
  79. param->analyse.i_direct_mv_pred = X264_DIRECT_PRED_SPATIAL;//空间预测模式
  80. param->analyse.i_me_method = X264_ME_HEX; //运动估计算法HEX
  81. param->analyse.f_psy_rd = 1.0;
  82. param->analyse.b_psy = 1;
  83. param->analyse.f_psy_trellis = 0;
  84. param->analyse.i_me_range = 16; //运动估计范围
  85. param->analyse.i_subpel_refine = 7; //亚像素运动估计质量
  86. param->analyse.b_mixed_references = 1; //允许每个宏块的分区在P帧有它自己的参考号
  87. param->analyse.b_chroma_me = 1; //亚像素色度运动估计和P帧的模式选择
  88. param->analyse.i_mv_range_thread = -1; //线程之间的最小空间
  89. param->analyse.i_mv_range = -1; //运动矢量最大长度set from level_idc
  90. param->analyse.i_chroma_qp_offset = 0; //色度量化步长偏移量
  91. param->analyse.b_fast_pskip = 1; //快速P帧跳过检测
  92. param->analyse.b_weighted_bipred = 1; //为b帧隐式加权
  93. param->analyse.b_dct_decimate = 1; //在P-frames转换参数域
  94. param->analyse.b_transform_8x8 = 1; //帧间分区
  95. param->analyse.i_trellis = 1; //Trellis量化,对每个8x8的块寻找合适的量化值,需要CABAC,默认0
  96. param->analyse.i_luma_deadzone[0] = 21; //帧间亮度量化中使用的无效区大小
  97. param->analyse.i_luma_deadzone[1] = 11; //帧内亮度量化中使用的无效区大小
  98. param->analyse.b_psnr = 0; //是否显示PSNR
  99. param->analyse.b_ssim = 0; //是否显示SSIM
  100.  
  101. //量化
  102. param->i_cqm_preset = X264_CQM_FLAT; //自定义量化矩阵(CQM),初始化量化模式为flat 0
  103. memset( param->cqm_4iy, 16, 16 );
  104. memset( param->cqm_4ic, 16, 16 );
  105. memset( param->cqm_4py, 16, 16 );
  106. memset( param->cqm_4pc, 16, 16 );
  107. memset( param->cqm_8iy, 16, 64 );
  108. memset( param->cqm_8py, 16, 64 ); //开辟空间
  109. param->b_repeat_headers = 1; //在每个关键帧前放置SPS/PPS
  110. param->b_aud = 0; //生成访问单元分隔符
  111. }

x264_param_default_preset():设置x264的preset和tune(libx264的API)。

  1. //设置preset,tune
  2. int x264_param_default_preset( x264_param_t *param, const char *preset, const char *tune )
  3. {
  4. x264_param_default( param );
  5.  
  6. //设置preset
  7. if( preset && x264_param_apply_preset( param, preset ) < 0 )
  8. return -1;
  9.  
  10. //设置tune
  11. if( tune && x264_param_apply_tune( param, tune ) < 0 )
  12. return -1;
  13. return 0;
  14. }

在代码里,调用了函数x264_param_apply_preset()和x264_param_apply_tune()分别设置preset和tune。

x264_param_apply_preset():

  1. //设置preset
  2. static int x264_param_apply_preset( x264_param_t *param, const char *preset )
  3. {
  4. char *end;
  5. int i = strtol( preset, &end, 10 );
  6. if( *end == 0 && i >= 0 && i < sizeof(x264_preset_names)/sizeof(*x264_preset_names)-1 )
  7. preset = x264_preset_names[i];
  8.  
  9. //几种不同的preset设置不同的参数
  10. if( !strcasecmp( preset, "ultrafast" ) )
  11. {
  12. param->i_frame_reference = 1; //参考帧的最大帧数设为1
  13. param->i_scenecut_threshold = 0;
  14. param->b_deblocking_filter = 0; //不使用去块滤波
  15. param->b_cabac = 0; //不使用CABAC
  16. param->i_bframe = 0; //不使用B帧
  17. param->analyse.intra = 0;
  18. param->analyse.inter = 0;
  19. param->analyse.b_transform_8x8 = 0; //不使用8x8DCT
  20. param->analyse.i_me_method = X264_ME_DIA;//运动算法的选择,使用“Diamond”
  21. param->analyse.i_subpel_refine = 0;
  22. param->rc.i_aq_mode = 0;
  23. param->analyse.b_mixed_references = 0;
  24. param->analyse.i_trellis = 0;
  25. param->i_bframe_adaptive = X264_B_ADAPT_NONE;
  26. param->rc.b_mb_tree = 0;
  27. param->analyse.i_weighted_pred = X264_WEIGHTP_NONE;//不使用加权
  28. param->analyse.b_weighted_bipred = 0;
  29. param->rc.i_lookahead = 0;
  30. }
  31. else if( !strcasecmp( preset, "superfast" ) )
  32. {
  33. param->analyse.inter = X264_ANALYSE_I8x8|X264_ANALYSE_I4x4;
  34. param->analyse.i_me_method = X264_ME_DIA; //钻石模板
  35. param->analyse.i_subpel_refine = 1; //亚像素运动估计质量为1
  36. param->i_frame_reference = 1; //参考帧最大帧数为1
  37. param->analyse.b_mixed_references = 0;
  38. param->analyse.i_trellis = 0;
  39. param->rc.b_mb_tree = 0;
  40. param->analyse.i_weighted_pred = X264_WEIGHTP_SIMPLE;
  41. param->rc.i_lookahead = 0;
  42. }
  43. else if( !strcasecmp( preset, "veryfast" ) )
  44. {
  45. param->analyse.i_me_method = X264_ME_HEX; //六边形模板
  46. param->analyse.i_subpel_refine = 2;
  47. param->i_frame_reference = 1;
  48. param->analyse.b_mixed_references = 0;
  49. param->analyse.i_trellis = 0;
  50. param->analyse.i_weighted_pred = X264_WEIGHTP_SIMPLE;
  51. param->rc.i_lookahead = 10;
  52. }
  53. else if( !strcasecmp( preset, "faster" ) )
  54. {
  55. param->analyse.b_mixed_references = 0;
  56. param->i_frame_reference = 2; //参考帧最大帧数设为2
  57. param->analyse.i_subpel_refine = 4;
  58. param->analyse.i_weighted_pred = X264_WEIGHTP_SIMPLE;
  59. param->rc.i_lookahead = 20;
  60. }
  61. else if( !strcasecmp( preset, "fast" ) )
  62. {
  63. param->i_frame_reference = 2;
  64. param->analyse.i_subpel_refine = 6;
  65. param->analyse.i_weighted_pred = X264_WEIGHTP_SIMPLE;
  66. param->rc.i_lookahead = 30;
  67. }
  68. else if( !strcasecmp( preset, "medium" ) )
  69. {
  70. /* Default is medium */
  71. }
  72. else if( !strcasecmp( preset, "slow" ) )
  73. {
  74. param->analyse.i_me_method = X264_ME_UMH; //UMH相对复杂
  75. param->analyse.i_subpel_refine = 8; //亚像素运动估计质量为8
  76. param->i_frame_reference = 5; //参考帧的组大帧数设为5
  77. param->i_bframe_adaptive = X264_B_ADAPT_TRELLIS;
  78. param->analyse.i_direct_mv_pred = X264_DIRECT_PRED_AUTO;
  79. param->rc.i_lookahead = 50;
  80. }
  81. else if( !strcasecmp( preset, "slower" ) )
  82. {
  83. param->analyse.i_me_method = X264_ME_UMH;
  84. param->analyse.i_subpel_refine = 9;
  85. param->i_frame_reference = 8; //参考帧的最大帧数设为8
  86. param->i_bframe_adaptive = X264_B_ADAPT_TRELLIS;
  87. param->analyse.i_direct_mv_pred = X264_DIRECT_PRED_AUTO;
  88. param->analyse.inter |= X264_ANALYSE_PSUB8x8;
  89. param->analyse.i_trellis = 2;
  90. param->rc.i_lookahead = 60;
  91. }
  92. else if( !strcasecmp( preset, "veryslow" ) )
  93. {
  94. param->analyse.i_me_method = X264_ME_UMH;
  95. param->analyse.i_subpel_refine = 10;
  96. param->analyse.i_me_range = 24;
  97. param->i_frame_reference = 16;
  98. param->i_bframe_adaptive = X264_B_ADAPT_TRELLIS;
  99. param->analyse.i_direct_mv_pred = X264_DIRECT_PRED_AUTO;
  100. param->analyse.inter |= X264_ANALYSE_PSUB8x8;
  101. param->analyse.i_trellis = 2;
  102. param->i_bframe = 8; //两个参考帧之间B帧为8
  103. param->rc.i_lookahead = 60;
  104. }
  105. else if( !strcasecmp( preset, "placebo" ) )
  106. {
  107. param->analyse.i_me_method = X264_ME_TESA; //TESA很慢
  108. param->analyse.i_subpel_refine = 11;
  109. param->analyse.i_me_range = 24; //运动估计范围设为24
  110. param->i_frame_reference = 16; //参考帧的最大帧数设为16
  111. param->i_bframe_adaptive = X264_B_ADAPT_TRELLIS;
  112. param->analyse.i_direct_mv_pred = X264_DIRECT_PRED_AUTO;
  113. param->analyse.inter |= X264_ANALYSE_PSUB8x8;
  114. param->analyse.b_fast_pskip = 0;
  115. param->analyse.i_trellis = 2;
  116. param->i_bframe = 16; //两个参考帧之间B帧为16
  117. param->rc.i_lookahead = 60;
  118. }
  119. else
  120. {
  121. x264_log( NULL, X264_LOG_ERROR, "invalid preset '%s'\n", preset );
  122. return -1;
  123. }
  124. return 0;
  125. }

通过调节preset参数可以调整编码速度和质量之间的平衡。它的具体参数有ultrafast、superfast、veryfast、faster、fast、medium、slow、slower、placebo。这些参数的编码速度从快到慢,默认值是medium。

x264_param_apply_tune():

  1. //设置tune
  2. static int x264_param_apply_tune( x264_param_t *param, const char *tune )
  3. {
  4. char *tmp = x264_malloc( strlen( tune ) + 1 );
  5. if( !tmp )
  6. return -1;
  7. tmp = strcpy( tmp, tune );
  8. //分解一个字符串为一个字符串数组。第2个参数为分隔符
  9. char *s = strtok( tmp, ",./-+" );
  10. int psy_tuning_used = 0;
  11.  
  12. //设置
  13. //这里是循环的,可以设置多次
  14. while( s )
  15. {
  16. if( !strncasecmp( s, "film", 4 ) )
  17. {
  18. if( psy_tuning_used++ ) goto psy_failure;
  19. param->i_deblocking_filter_alphac0 = -1;
  20. param->i_deblocking_filter_beta = -1;
  21. param->analyse.f_psy_trellis = 0.15;
  22. }
  23. else if( !strncasecmp( s, "animation", 9 ) )
  24. {
  25. if( psy_tuning_used++ ) goto psy_failure;
  26. param->i_frame_reference = param->i_frame_reference > 1 ? param->i_frame_reference*2 : 1;
  27. param->i_deblocking_filter_alphac0 = 1;
  28. param->i_deblocking_filter_beta = 1;
  29. param->analyse.f_psy_rd = 0.4;
  30. param->rc.f_aq_strength = 0.6;
  31. param->i_bframe += 2;
  32. }
  33. else if( !strncasecmp( s, "grain", 5 ) )
  34. {
  35. if( psy_tuning_used++ ) goto psy_failure;
  36. param->i_deblocking_filter_alphac0 = -2;
  37. param->i_deblocking_filter_beta = -2;
  38. param->analyse.f_psy_trellis = 0.25;
  39. param->analyse.b_dct_decimate = 0;
  40. param->rc.f_pb_factor = 1.1;
  41. param->rc.f_ip_factor = 1.1;
  42. param->rc.f_aq_strength = 0.5;
  43. param->analyse.i_luma_deadzone[0] = 6;
  44. param->analyse.i_luma_deadzone[1] = 6;
  45. param->rc.f_qcompress = 0.8;
  46. }
  47. else if( !strncasecmp( s, "stillimage", 10 ) )
  48. {
  49. if( psy_tuning_used++ ) goto psy_failure;
  50. param->i_deblocking_filter_alphac0 = -3;
  51. param->i_deblocking_filter_beta = -3;
  52. param->analyse.f_psy_rd = 2.0;
  53. param->analyse.f_psy_trellis = 0.7;
  54. param->rc.f_aq_strength = 1.2;
  55. }
  56. else if( !strncasecmp( s, "psnr", 4 ) )
  57. {
  58. if( psy_tuning_used++ ) goto psy_failure;
  59. param->rc.i_aq_mode = X264_AQ_NONE;
  60. param->analyse.b_psy = 0;
  61. }
  62. else if( !strncasecmp( s, "ssim", 4 ) )
  63. {
  64. if( psy_tuning_used++ ) goto psy_failure;
  65. param->rc.i_aq_mode = X264_AQ_AUTOVARIANCE;
  66. param->analyse.b_psy = 0;
  67. }
  68. else if( !strncasecmp( s, "fastdecode", 10 ) )
  69. {
  70. param->b_deblocking_filter = 0;
  71. param->b_cabac = 0;
  72. param->analyse.b_weighted_bipred = 0;
  73. param->analyse.i_weighted_pred = X264_WEIGHTP_NONE;
  74. }
  75. else if( !strncasecmp( s, "zerolatency", 11 ) )
  76. {
  77. //zerolatency速度快
  78. param->rc.i_lookahead = 0;
  79. param->i_sync_lookahead = 0;
  80. param->i_bframe = 0; //不使用B帧
  81. param->b_sliced_threads = 1;
  82. param->b_vfr_input = 0;
  83. param->rc.b_mb_tree = 0;
  84. }
  85. else if( !strncasecmp( s, "touhou", 6 ) )
  86. {
  87. if( psy_tuning_used++ ) goto psy_failure;
  88. param->i_frame_reference = param->i_frame_reference > 1 ? param->i_frame_reference*2 : 1;
  89. param->i_deblocking_filter_alphac0 = -1;
  90. param->i_deblocking_filter_beta = -1;
  91. param->analyse.f_psy_trellis = 0.2;
  92. param->rc.f_aq_strength = 1.3;
  93. if( param->analyse.inter & X264_ANALYSE_PSUB16x16 )
  94. param->analyse.inter |= X264_ANALYSE_PSUB8x8;
  95. }
  96. else
  97. {
  98. x264_log( NULL, X264_LOG_ERROR, "invalid tune '%s'\n", s );
  99. x264_free( tmp );
  100. return -1;
  101. }
  102. if( 0 )
  103. {
  104. psy_failure:
  105. x264_log( NULL, X264_LOG_WARNING, "only 1 psy tuning can be used: ignoring tune %s\n", s );
  106. }
  107. s = strtok( NULL, ",./-+" );
  108. }
  109. x264_free( tmp );
  110. return 0;
  111. }

tune的参数值有:

film:电影、真人类型;

animation:动画;

grain:需要保留大量的颗粒时使用;

stillimage:静态图像编码时使用;

psnr:为提高峰值信噪比(psnr)做优化的参数;

ssim:为提高ssim做优化的参数(ssim:衡量两图图像相似度的指标,值越大越好,最大为1);

fastdecode:可以快速解码的参数;

zerolatency:零延迟,可以减少音视频的不同步;

x264_param_apply_profile():设置x264的profile(x264的API)。

  1. //设置profile
  2. int x264_param_apply_profile( x264_param_t *param, const char *profile )
  3. {
  4. if( !profile )
  5. return 0;
  6. //字符串到整型
  7. int p = profile_string_to_int( profile );
  8. //检查profile设置是否正确
  9. if( p < 0 )
  10. {
  11. x264_log( NULL, X264_LOG_ERROR, "invalid profile: %s\n", profile );
  12. return -1;
  13. }
  14. if( p < PROFILE_HIGH444_PREDICTIVE && ((param->rc.i_rc_method == X264_RC_CQP && param->rc.i_qp_constant <= 0) ||
  15. (param->rc.i_rc_method == X264_RC_CRF && (int)(param->rc.f_rf_constant + QP_BD_OFFSET) <= 0)) )
  16. {
  17. x264_log( NULL, X264_LOG_ERROR, "%s profile doesn't support lossless\n", profile );
  18. return -1;
  19. }
  20. if( p < PROFILE_HIGH444_PREDICTIVE && (param->i_csp & X264_CSP_MASK) >= X264_CSP_I444 )
  21. {
  22. x264_log( NULL, X264_LOG_ERROR, "%s profile doesn't support 4:4:4\n", profile );
  23. return -1;
  24. }
  25. if( p < PROFILE_HIGH422 && (param->i_csp & X264_CSP_MASK) >= X264_CSP_I422 )
  26. {
  27. x264_log( NULL, X264_LOG_ERROR, "%s profile doesn't support 4:2:2\n", profile );
  28. return -1;
  29. }
  30. if( p < PROFILE_HIGH10 && BIT_DEPTH > 8 )
  31. {
  32. x264_log( NULL, X264_LOG_ERROR, "%s profile doesn't support a bit depth of %d\n", profile, BIT_DEPTH );
  33. return -1;
  34. }
  35. //根据不同的Profile做设置
  36. //Baseline基本型
  37. if( p == PROFILE_BASELINE )
  38. {
  39. //不支持DCT8x8
  40. param->analyse.b_transform_8x8 = 0;
  41. //不使用CABAC
  42. param->b_cabac = 0;
  43. param->i_cqm_preset = X264_CQM_FLAT;
  44. param->psz_cqm_file = NULL;
  45. //没有B帧
  46. param->i_bframe = 0;
  47. //没有加权
  48. param->analyse.i_weighted_pred = X264_WEIGHTP_NONE;
  49. //不支持隔行扫描
  50. if( param->b_interlaced )
  51. {
  52. x264_log( NULL, X264_LOG_ERROR, "baseline profile doesn't support interlacing\n" );
  53. return -1;
  54. }
  55. if( param->b_fake_interlaced )
  56. {
  57. x264_log( NULL, X264_LOG_ERROR, "baseline profile doesn't support fake interlacing\n" );
  58. return -1;
  59. }
  60. }
  61. //Main主型
  62. else if( p == PROFILE_MAIN )
  63. {
  64. //不支持DCT8x8
  65. param->analyse.b_transform_8x8 = 0;
  66. param->i_cqm_preset = X264_CQM_FLAT;
  67. param->psz_cqm_file = NULL;
  68. }
  69. return 0;
  70. }

其函数内部调用profile_string_to_int():

  1. static int profile_string_to_int( const char *str )
  2. {
  3. if( !strcasecmp( str, "baseline" ) )
  4. return PROFILE_BASELINE;
  5. if( !strcasecmp( str, "main" ) )
  6. return PROFILE_MAIN;
  7. if( !strcasecmp( str, "high" ) )
  8. return PROFILE_HIGH;
  9. if( !strcasecmp( str, "high10" ) )
  10. return PROFILE_HIGH10;
  11. if( !strcasecmp( str, "high422" ) )
  12. return PROFILE_HIGH422;
  13. if( !strcasecmp( str, "high444" ) )
  14. return PROFILE_HIGH444_PREDICTIVE;
  15. return -1;
  16. }

(3)最后根据设定的编码器参数,对输入的视频文件进行H264视频算法编码,编码后的H264码流写入文件。

encode()函数:编码YUV为H264,源代码如下:

  1. static int encode( x264_param_t *param, cli_opt_t *opt ) //编码器(内部有循环用于一帧一帧编码)
  2. {
  3. x264_t *h = NULL; //编码器句柄
  4. x264_picture_t pic; //当前的编码帧
  5. cli_pic_t cli_pic;
  6. const cli_pulldown_t *pulldown = NULL;
  7.  
  8. int i_frame = 0; //编码帧数目统计
  9. int i_frame_output = 0;
  10. int64_t i_end, i_previous = 0, i_start = 0; //编码时间统计
  11. int64_t i_file = 0; //当前NAL打包的长度
  12. int i_frame_size; //编码后的码流长度
  13. int64_t last_dts = 0;
  14. int64_t prev_dts = 0;
  15. int64_t first_dts = 0;
  16.  
  17. int pts_warning_cnt = 0;
  18. int64_t largest_pts = -1;
  19. int64_t second_largest_pts = -1;
  20. int64_t ticks_per_frame;
  21. double duration;
  22. double pulldown_pts = 0;
  23. int retval = 0;
  24.  
  25. opt->b_progress &= param->i_log_level < X264_LOG_DEBUG; //调试信息等级
  26.  
  27. if( opt->i_pulldown && !param->b_vfr_input )
  28. {
  29. param->b_pulldown = 1;
  30. param->b_pic_struct = 1;
  31. pulldown = &pulldown_values[opt->i_pulldown];
  32. param->i_timebase_num = param->i_fps_den;
  33. FAIL_IF_ERROR2( fmod( param->i_fps_num * pulldown->fps_factor, 1 ), "unsupported framerate for chosen pulldown\n" )
  34. param->i_timebase_den = param->i_fps_num * pulldown->fps_factor;
  35. }
  36.  
  37. h = x264_encoder_open( param ); //打开编码器
  38. FAIL_IF_ERROR2( !h, "x264_encoder_open failed\n" );
  39.  
  40. x264_encoder_parameters( h, param ); //获得参数
  41. FAIL_IF_ERROR2( cli_output.set_param( opt->hout, param ), "can't set outfile param\n" );
  42.  
  43. i_start = x264_mdate(); //计时,读取当前系统时间
  44. ticks_per_frame = (int64_t)param->i_timebase_den * param->i_fps_den / param->i_timebase_num / param->i_fps_num;
  45. FAIL_IF_ERROR2( ticks_per_frame < 1 && !param->b_vfr_input, "ticks_per_frame invalid: %"PRId64"\n",ticks_per_frame )
  46. ticks_per_frame = X264_MAX( ticks_per_frame, 1 );
  47.  
  48. //如果不是在每个keyframe前面都增加SPS/PPS/SEI的话,就在整个码流前面加SPS/PPS/SEI;Header指的就是SPS/PPS/SEI
  49. if( !param->b_repeat_headers )
  50. {
  51. x264_nal_t *headers;
  52. int i_nal;
  53.  
  54. FAIL_IF_ERROR2( x264_encoder_headers( h, &headers, &i_nal ) < 0, "x264_encoder_headers failed\n" )
  55. FAIL_IF_ERROR2( (i_file = cli_output.write_headers( opt->hout, headers )) < 0, "error writing headers to output file\n" );
  56. }
  57.  
  58. if( opt->tcfile_out )
  59. fprintf( opt->tcfile_out, "# timecode format v2\n" );
  60.  
  61. for( ; !b_ctrl_c && (i_frame < param->i_frame_total || !param->i_frame_total); i_frame++ ) //循环编码所有帧
  62. {
  63. if( filter.get_frame( opt->hin, &cli_pic, i_frame + opt->i_seek ) ) //获取1帧YUV数据,存于cli_pic
  64. break;
  65.  
  66. x264_picture_init( &pic ); //初始化x264_picture_t结构体pic
  67. convert_cli_to_lib_pic( &pic, &cli_pic );
  68.  
  69. if( !param->b_vfr_input )
  70. pic.i_pts = i_frame;
  71.  
  72. if( opt->i_pulldown && !param->b_vfr_input )
  73. {
  74. pic.i_pic_struct = pulldown->pattern[ i_frame % pulldown->mod ];
  75. pic.i_pts = (int64_t)( pulldown_pts + 0.5 );
  76. pulldown_pts += pulldown_frame_duration[pic.i_pic_struct];
  77. }
  78. else if( opt->timebase_convert_multiplier )
  79. pic.i_pts = (int64_t)( pic.i_pts * opt->timebase_convert_multiplier + 0.5 );
  80.  
  81. if( pic.i_pts <= largest_pts )
  82. {
  83. if( cli_log_level >= X264_LOG_DEBUG || pts_warning_cnt < MAX_PTS_WARNING )
  84. x264_cli_log( "x264", X264_LOG_WARNING, "non-strictly-monotonic pts at frame %d (%"PRId64" <= %"PRId64")\n",i_frame, pic.i_pts, largest_pts );
  85. else if( pts_warning_cnt == MAX_PTS_WARNING )
  86. x264_cli_log( "x264", X264_LOG_WARNING, "too many nonmonotonic pts warnings, suppressing further ones\n" );
  87. pts_warning_cnt++;
  88. pic.i_pts = largest_pts + ticks_per_frame;
  89. }
  90.  
  91. second_largest_pts = largest_pts;
  92. largest_pts = pic.i_pts;
  93. if( opt->tcfile_out )
  94. fprintf( opt->tcfile_out, "%.6f\n", pic.i_pts * ((double)param->i_timebase_num / param->i_timebase_den) * 1e3 );
  95.  
  96. if( opt->qpfile )
  97. parse_qpfile( opt, &pic, i_frame + opt->i_seek );
  98.  
  99. prev_dts = last_dts;
  100.  
  101. i_frame_size = encode_frame( h, opt->hout, &pic, &last_dts ); //编码pic中存储的1帧YUV数据
  102. if( i_frame_size < 0 )
  103. {
  104. b_ctrl_c = 1; /* lie to exit the loop */
  105. retval = -1;
  106. }
  107. else if( i_frame_size )
  108. {
  109. i_file += i_frame_size;
  110. i_frame_output++;
  111. if( i_frame_output == 1 )
  112. first_dts = prev_dts = last_dts;
  113. }
  114.  
  115. if( filter.release_frame( opt->hin, &cli_pic, i_frame + opt->i_seek ) ) //释放处理完的YUV数据
  116. break;
  117.  
  118. if( opt->b_progress && i_frame_output )
  119. i_previous = print_status( i_start, i_previous, i_frame_output, param->i_frame_total, i_file, param, 2 * last_dts - prev_dts - first_dts );
  120. }
  121.  
  122. while( !b_ctrl_c && x264_encoder_delayed_frames( h ) ) //x264_encoder_delayed_frames()返回剩余的帧的个数
  123. {
  124. prev_dts = last_dts;
  125.  
  126. i_frame_size = encode_frame( h, opt->hout, NULL, &last_dts ); //编码
  127. if( i_frame_size < 0 )
  128. {
  129. b_ctrl_c = 1;
  130. retval = -1;
  131. }
  132. else if( i_frame_size )
  133. {
  134. i_file += i_frame_size;
  135. i_frame_output++;
  136. if( i_frame_output == 1 )
  137. first_dts = prev_dts = last_dts;
  138. }
  139.  
  140. if( opt->b_progress && i_frame_output ) //输出一些统计信息
  141. i_previous = print_status( i_start, i_previous, i_frame_output, param->i_frame_total, i_file, param, 2 * last_dts - prev_dts - first_dts );
  142. }
  143. fail:
  144. if( pts_warning_cnt >= MAX_PTS_WARNING && cli_log_level < X264_LOG_DEBUG )
  145. x264_cli_log( "x264", X264_LOG_WARNING, "%d suppressed nonmonotonic pts warnings\n", pts_warning_cnt-MAX_PTS_WARNING );
  146.  
  147. if( i_frame_output == 1 )
  148. duration = (double)param->i_fps_den / param->i_fps_num;
  149. else if( b_ctrl_c )
  150. duration = (double)(2 * last_dts - prev_dts - first_dts) * param->i_timebase_num / param->i_timebase_den;
  151. else
  152. duration = (double)(2 * largest_pts - second_largest_pts) * param->i_timebase_num / param->i_timebase_den;
  153.  
  154. i_end = x264_mdate(); //获取系统时间,计时
  155.  
  156. if( opt->b_progress )
  157. fprintf( stderr, " \r" );
  158.  
  159. if( h )
  160. x264_encoder_close( h ); //关闭编码器
  161. fprintf( stderr, "\n" );
  162.  
  163. if( b_ctrl_c )
  164. fprintf( stderr, "aborted at input frame %d, output frame %d\n", opt->i_seek + i_frame, i_frame_output );
  165.  
  166. cli_output.close_file( opt->hout, largest_pts, second_largest_pts );
  167. opt->hout = NULL;
  168.  
  169. if( i_frame_output > 0 )
  170. {
  171. double fps = (double)i_frame_output * (double)1000000/(double)( i_end - i_start );
  172. fprintf( stderr, "encoded %d frames, %.2f fps, %.2f kb/s\n", i_frame_output, fps,(double) i_file * 8 / ( 1000 * duration ) );
  173. }
  174. return retval;
  175. }

encode()函数的基本流程:

调用x264_encoder_open()函数打开编码器;

调用x264_encoder_parameters()获得当前参数集x264_param_t;

调用x264_encoder_headers()在码流前面加入SPS/PPS/SEI信息;

调用encode_frame()进入循环一帧一帧的编码;

调用printf_status()输出编码后的统计信息;

调用x264_encode_close()关闭编码器;

encode_frame()函数:编码一帧数据,内部调用x264_encoder_encode()函数。代码:

  1. static int encode_frame( x264_t *h, hnd_t hout, x264_picture_t *pic, int64_t *last_dts )
  2. {
  3. x264_picture_t pic_out; //待编码帧
  4. x264_nal_t *nal; //NAL打包指针
  5. int i_nal; //NAL的数目
  6. int i_frame_size = 0;
  7.  
  8. i_frame_size = x264_encoder_encode( h, &nal, &i_nal, pic, &pic_out ); //编码x264_picture_t为x264_nal_t
  9. FAIL_IF_ERROR( i_frame_size < 0, "x264_encoder_encode failed\n" ); //编码失败,输出当前信息
  10.  
  11. if( i_frame_size )
  12. {
  13. i_frame_size = cli_output.write_frame( hout, nal[0].p_payload, i_frame_size, &pic_out );
  14. *last_dts = pic_out.i_dts;
  15. }
  16. return i_frame_size;
  17. }

printf_status()函数:输出一帧数据编码后的统计信息。代码:

  1. static int64_t print_status( int64_t i_start, int64_t i_previous, int i_frame, int i_frame_total, int64_t i_file, x264_param_t *param, int64_t last_ts )
  2. {
  3. char buf[200];
  4. int64_t i_time = x264_mdate();
  5. if( i_previous && i_time - i_previous < UPDATE_INTERVAL )
  6. return i_previous;
  7. int64_t i_elapsed = i_time - i_start;
  8. double fps = i_elapsed > 0 ? i_frame * 1000000. / i_elapsed : 0;
  9. double bitrate;
  10. if( last_ts )
  11. bitrate = (double) i_file * 8 / ( (double) last_ts * 1000 * param->i_timebase_num / param->i_timebase_den );
  12. else
  13. bitrate = (double) i_file * 8 / ( (double) 1000 * param->i_fps_den / param->i_fps_num );
  14. if( i_frame_total )
  15. {
  16. int eta = i_elapsed * (i_frame_total - i_frame) / ((int64_t)i_frame * 1000000);
  17. sprintf( buf, "x264 [%.1f%%] %d/%d frames, %.2f fps, %.2f kb/s, eta %d:%02d:%02d",
  18. 100. * i_frame / i_frame_total, i_frame, i_frame_total, fps, bitrate,
  19. eta/3600, (eta/60)%60, eta%60 );
  20. }
  21. else
  22. sprintf( buf, "x264 %d frames: %.2f fps, %.2f kb/s", i_frame, fps, bitrate );
  23.  
  24. fprintf( stderr, "%s \r", buf+5 );
  25. x264_cli_set_console_title( buf );
  26. fflush( stderr ); // needed in windows
  27. return i_time;
  28. }

运行编码函数,编码成功时,我们可以看到这个函数打印的一些信息。

X264-应用工程的更多相关文章

  1. ffmpeg x264安装

    fmpeg安装第三方编码器(encoder)库,ffmpeg编码h264(完) ffmpeg安装第三方编码器(encoder)库 关键词:ffmpeg.编码h264.第三方encoder 安装好了ff ...

  2. 【FFMPEG】【ARM-Linux开发】fmpeg安装第三方编码器(encoder)库,ffmpeg编码h264(完)

    fmpeg安装第三方编码器(encoder)库,ffmpeg编码h264(完) ffmpeg安装第三方编码器(encoder)库 关键词:ffmpeg.编码h264.第三方encoder 安装好了ff ...

  3. Linux下编译带x264的ffmpeg的配置方法,包含SDL2

    一.环境准备 ffmpeg下载:http://www.ffmpeg.org/download.html x264下载:http://download.videolan.org/x264/snapsho ...

  4. WebRTC VideoEngine超详细教程(三)——集成X264编码和ffmpeg解码

    转自:http://blog.csdn.net/nonmarking/article/details/47958395 本系列目前共三篇文章,后续还会更新 WebRTC VideoEngine超详细教 ...

  5. x264测试代码

    建立一个工程,将头文件,库文件加载到工程,测试代码如下:#include <iostream>#include <string>#include "stdint.h& ...

  6. Android camera采集视频 X264编码

    参考 http://blog.csdn.net/zblue78/article/details/6058147 感谢 ExperiencesOfCode 硬件平台:CPU Intel G630 @2. ...

  7. Mac系统下编译支持Android平台的最新X264编码器

    Mac系统下编译支持Android平台的最新X264编码器 原文来自 http://www.mingjianhua.com,转载请注明出处 1.首先去官网下载最新的x264源代码,解压到任意目录 ht ...

  8. 编译vs下可调试的ffmpeg和x264

    以前随手记的笔记,翻出来,整理下哈 ffmpeg 在windows上的编译还是比较麻烦的,而且如果mingw-gcc编译的话,是无法在vs下调试的 所以以前刚开始玩ffmpeg的时候,费了一些功夫,用 ...

  9. (转)windows下编译最新的x264

    二:<windows下编译最新的x264> X264更新的比较快,每天都有更新,但算法模块,基本结构是没有多大变化的.x264都是用C语言写的包括C99,但C99语法是在VC中是没法用的( ...

  10. [X264] MinGW编译x264,VC中调用libx264.dll-------------<参考转>

    1. 下载并按照MinGW,最好就缺省按照    http://sourceforge.net/projects/ ... ler/mingw-get-inst/    把C:\MinGW\bin添加 ...

随机推荐

  1. 编译安装 proxychains-ng proxychains4

    下载 [root@localhost html]# git clone https://github.com/rofl0r/proxychains-ng.git 编译安装 [root@localhos ...

  2. Python—运算符的类型

    Python语言支持以下类型的运算符 算术运算符 比较运算符 赋值运算符 逻辑运算符 位运算符 成员运算符(in / not in) 身份运算符(is / is not) Python算术运算符 运算 ...

  3. 自动化部署-svn hook触发构建

    目的 之前是通过轮询的形式,2分钟更新一次svn,即时性不高,现在想要实现提交代码时直接触发构建 方案 使用svn的服务器hook,当有代码提交时请求jenkins api实现构建 具体实现 1.je ...

  4. git 入门教程之 git bash 竟然不支持 tree 命令

    开门见山 git bash 是 Windows 用户安装 git 时默认安装的命令行工具,不仅界面漂亮功能也不错,大多数情况下可以替代 Windows 原生的 cmd 命令行. 然而,git bash ...

  5. 关于 Noip的考纲

    关于 \(\text{Noip}\) 的考纲 先放一张图 : 此图包含了 \(\text{Noip}\) 自开始到结束 的所有真题的考察知识点 算法分类标准主要来自于 \(\text{Luogu}\) ...

  6. Note | Ubuntu18.04安装与配置

    目标: 在服务器上配置最新的Ubuntu稳定版本18.04 LTS.18.04比16.04好看很多,非常建议. 有3块硬盘:2块4TB机械硬盘,1块2TB固态硬盘.计划将固态硬盘作为主硬盘,其余两块机 ...

  7. svn merge操作

    使用SVN做Merge操作时,会包含6个选项,下面就这6个选项给出详细的说明: 1.Merge a range of revisions 此类型应用最为广泛,主要是把源分支中的修改合并到目标分支上来. ...

  8. 解决 Ubuntu16.04 + opencv4.1 源码编译错误 Makefile:160: recipe for target 'all' failed

    最近源码编译 opencv,出现下面的错误 [ %] Built target opencv_dnn Makefile:: recipe for target 'all' failed google ...

  9. Leetcode练习题Remove Element

    Leetcode练习题Remove Element Question: Given an array nums and a value val, remove all instances of tha ...

  10. torch_13_自定义数据集实战

    1.将图片的路径和标签写入csv文件并实现读取 # 创建一个文件,包含image,存放方式:label pokemeon\\mew\\0001.jpg,0 def load_csv(self,file ...