From de2324b9ad89aa5fbeb0cb8ef499d74bb9bcef14 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Wed, 15 Mar 2017 23:55:06 +0100 Subject: [PATCH] Support CRF for x264, similar to the CQP mode we already use in Quick Sync Video. --- flags.cpp | 18 ++++++++++++++++++ flags.h | 5 ++++- mainwindow.cpp | 2 +- x264_encoder.cpp | 12 ++++++++++-- 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/flags.cpp b/flags.cpp index ee575c5..acd0541 100644 --- a/flags.cpp +++ b/flags.cpp @@ -25,6 +25,7 @@ enum LongOption { OPTION_X264_SPEEDCONTROL, OPTION_X264_SPEEDCONTROL_VERBOSE, OPTION_X264_BITRATE, + OPTION_X264_CRF, OPTION_X264_VBV_BUFSIZE, OPTION_X264_VBV_MAX_BITRATE, OPTION_X264_PARAM, @@ -86,6 +87,7 @@ void usage() fprintf(stderr, " --x264-speedcontrol-verbose output speedcontrol debugging statistics\n"); fprintf(stderr, " --x264-bitrate x264 bitrate (in kilobit/sec, default %d)\n", DEFAULT_X264_OUTPUT_BIT_RATE); + fprintf(stderr, " --x264-crf=VALUE quality-based VBR (-12 to 51), incompatible with --x264-bitrate and VBV\n"); fprintf(stderr, " --x264-vbv-bufsize x264 VBV size (in kilobits, 0 = one-frame VBV,\n"); fprintf(stderr, " default: same as --x264-bitrate, that is, one-second VBV)\n"); fprintf(stderr, " --x264-vbv-max-bitrate x264 local max bitrate (in kilobit/sec per --vbv-bufsize,\n"); @@ -157,6 +159,7 @@ void parse_flags(int argc, char * const argv[]) { "x264-speedcontrol", no_argument, 0, OPTION_X264_SPEEDCONTROL }, { "x264-speedcontrol-verbose", no_argument, 0, OPTION_X264_SPEEDCONTROL_VERBOSE }, { "x264-bitrate", required_argument, 0, OPTION_X264_BITRATE }, + { "x264-crf", required_argument, 0, OPTION_X264_CRF }, { "x264-vbv-bufsize", required_argument, 0, OPTION_X264_VBV_BUFSIZE }, { "x264-vbv-max-bitrate", required_argument, 0, OPTION_X264_VBV_MAX_BITRATE }, { "x264-param", required_argument, 0, OPTION_X264_PARAM }, @@ -288,6 +291,9 @@ void parse_flags(int argc, char * const argv[]) case OPTION_X264_BITRATE: global_flags.x264_bitrate = atoi(optarg); break; + case OPTION_X264_CRF: + global_flags.x264_crf = atof(optarg); + break; case OPTION_X264_VBV_BUFSIZE: global_flags.x264_vbv_buffer_size = atoi(optarg); break; @@ -481,4 +487,16 @@ void parse_flags(int argc, char * const argv[]) if (global_flags.max_input_queue_frames > 10) { fprintf(stderr, "WARNING: --max-input-queue-frames has little effect over 10.\n"); } + + if (!isinf(global_flags.x264_crf)) { // CRF mode is selected. + if (global_flags.x264_bitrate != -1) { + fprintf(stderr, "ERROR: --x264-bitrate and --x264-crf are mutually incompatible.\n"); + exit(1); + } + if (global_flags.x264_vbv_max_bitrate != -1 && global_flags.x264_vbv_buffer_size != -1) { + fprintf(stderr, "WARNING: VBV settings are ignored with --x264-crf.\n"); + } + } else if (global_flags.x264_bitrate == -1) { + global_flags.x264_bitrate = DEFAULT_X264_OUTPUT_BIT_RATE; + } } diff --git a/flags.h b/flags.h index 6ca9794..bd962fe 100644 --- a/flags.h +++ b/flags.h @@ -1,6 +1,8 @@ #ifndef _FLAGS_H #define _FLAGS_H +#include + #include #include #include @@ -32,7 +34,8 @@ struct Flags { std::string x264_tune = X264_DEFAULT_TUNE; bool x264_speedcontrol = false; bool x264_speedcontrol_verbose = false; - int x264_bitrate = DEFAULT_X264_OUTPUT_BIT_RATE; // In kilobit/sec. + int x264_bitrate = -1; // In kilobit/sec. -1 = not set = DEFAULT_X264_OUTPUT_BIT_RATE. + float x264_crf = HUGE_VAL; // From 51 - QP_MAX_SPEC to 51. HUGE_VAL = not set = use x264_bitrate instead. int x264_vbv_max_bitrate = -1; // In kilobits. 0 = no limit, -1 = same as (CBR). int x264_vbv_buffer_size = -1; // In kilobits. 0 = one-frame VBV, -1 = same as (one-second VBV). std::vector x264_extra_param; // In “key[,value]” format. diff --git a/mainwindow.cpp b/mainwindow.cpp index a1c2915..136890d 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -210,7 +210,7 @@ MainWindow::MainWindow() ui->timecode_stream_action->setChecked(global_flags.display_timecode_in_stream); ui->timecode_stdout_action->setChecked(global_flags.display_timecode_on_stdout); - if (global_flags.x264_video_to_http) { + if (global_flags.x264_video_to_http && isinf(global_flags.x264_crf)) { connect(ui->x264_bitrate_action, &QAction::triggered, this, &MainWindow::x264_bitrate_triggered); } else { ui->x264_bitrate_action->setEnabled(false); diff --git a/x264_encoder.cpp b/x264_encoder.cpp index 0c1ecbc..dbdef5b 100644 --- a/x264_encoder.cpp +++ b/x264_encoder.cpp @@ -30,6 +30,9 @@ namespace { void update_vbv_settings(x264_param_t *param) { + if (global_flags.x264_bitrate == -1) { + return; + } if (global_flags.x264_vbv_buffer_size < 0) { param->rc.i_vbv_buffer_size = param->rc.i_bitrate; // One-second VBV. } else { @@ -127,8 +130,13 @@ void X264Encoder::init_x264() param.vui.i_colmatrix = 6; // BT.601/SMPTE 170M. } - param.rc.i_rc_method = X264_RC_ABR; - param.rc.i_bitrate = global_flags.x264_bitrate; + if (!isinf(global_flags.x264_crf)) { + param.rc.i_rc_method = X264_RC_CRF; + param.rc.f_rf_constant = global_flags.x264_crf; + } else { + param.rc.i_rc_method = X264_RC_ABR; + param.rc.i_bitrate = global_flags.x264_bitrate; + } update_vbv_settings(¶m); if (param.rc.i_vbv_max_bitrate > 0) { // If the user wants VBV control to cap the max rate, it is -- 2.39.2