X-Git-Url: https://git.sesse.net/?p=nageru;a=blobdiff_plain;f=nageru%2Fx264_encoder.cpp;h=6c46c98c689dbdd99e16275e9d3466a883e8dd9e;hp=8463d1bae285744a420aa47a1155504eb3f07b00;hb=f34a3e1bbc207541842e0b54d5418d95bafc8e5b;hpb=392f9d1ccb835c05a3874c4bea163788b2c37024 diff --git a/nageru/x264_encoder.cpp b/nageru/x264_encoder.cpp index 8463d1b..6c46c98 100644 --- a/nageru/x264_encoder.cpp +++ b/nageru/x264_encoder.cpp @@ -14,10 +14,10 @@ #include "defs.h" #include "flags.h" -#include "metrics.h" -#include "mux.h" +#include "shared/metrics.h" +#include "shared/mux.h" #include "print_latency.h" -#include "timebase.h" +#include "shared/timebase.h" #include "x264_dynamic.h" #include "x264_speed_control.h" @@ -43,7 +43,17 @@ atomic metric_x264_output_frames_p{0}; atomic metric_x264_output_frames_b{0}; Histogram metric_x264_crf; LatencyHistogram x264_latency_histogram; -once_flag x264_metrics_inited; + +atomic metric_x264_disk_queued_frames{0}; +atomic metric_x264_disk_max_queued_frames{X264_QUEUE_LENGTH}; +atomic metric_x264_disk_dropped_frames{0}; +atomic metric_x264_disk_output_frames_i{0}; +atomic metric_x264_disk_output_frames_p{0}; +atomic metric_x264_disk_output_frames_b{0}; +Histogram metric_x264_disk_crf; +LatencyHistogram x264_disk_latency_histogram; + +once_flag x264_metrics_inited, x264_disk_metrics_inited; void update_vbv_settings(x264_param_t *param) { @@ -64,24 +74,40 @@ void update_vbv_settings(x264_param_t *param) } // namespace -X264Encoder::X264Encoder(AVOutputFormat *oformat) +X264Encoder::X264Encoder(const AVOutputFormat *oformat, bool use_separate_disk_params) : wants_global_headers(oformat->flags & AVFMT_GLOBALHEADER), - dyn(load_x264_for_bit_depth(global_flags.x264_bit_depth)) + use_separate_disk_params(use_separate_disk_params), + dyn(load_x264_for_bit_depth(global_flags.bit_depth)) { - call_once(x264_metrics_inited, [](){ - global_metrics.add("x264_queued_frames", &metric_x264_queued_frames, Metrics::TYPE_GAUGE); - global_metrics.add("x264_max_queued_frames", &metric_x264_max_queued_frames, Metrics::TYPE_GAUGE); - global_metrics.add("x264_dropped_frames", &metric_x264_dropped_frames); - global_metrics.add("x264_output_frames", {{ "type", "i" }}, &metric_x264_output_frames_i); - global_metrics.add("x264_output_frames", {{ "type", "p" }}, &metric_x264_output_frames_p); - global_metrics.add("x264_output_frames", {{ "type", "b" }}, &metric_x264_output_frames_b); - - metric_x264_crf.init_uniform(50); - global_metrics.add("x264_crf", &metric_x264_crf); - x264_latency_histogram.init("x264"); - }); - - size_t bytes_per_pixel = global_flags.x264_bit_depth > 8 ? 2 : 1; + if (use_separate_disk_params) { + call_once(x264_disk_metrics_inited, []{ + global_metrics.add("x264_queued_frames", {{ "encode", "separate_disk" }}, &metric_x264_disk_queued_frames, Metrics::TYPE_GAUGE); + global_metrics.add("x264_max_queued_frames", {{ "encode", "separate_disk" }}, &metric_x264_disk_max_queued_frames, Metrics::TYPE_GAUGE); + global_metrics.add("x264_dropped_frames", {{ "encode", "separate_disk" }}, &metric_x264_disk_dropped_frames); + global_metrics.add("x264_output_frames", {{ "encode", "separate_disk" }, { "type", "i" }}, &metric_x264_disk_output_frames_i); + global_metrics.add("x264_output_frames", {{ "encode", "separate_disk" }, { "type", "p" }}, &metric_x264_disk_output_frames_p); + global_metrics.add("x264_output_frames", {{ "encode", "separate_disk" }, { "type", "b" }}, &metric_x264_disk_output_frames_b); + + metric_x264_disk_crf.init_uniform(50); + global_metrics.add("x264_crf", {{ "encode", "separate_disk" }}, &metric_x264_disk_crf); + x264_disk_latency_histogram.init("x264_disk"); + }); + } else { + call_once(use_separate_disk_params ? x264_disk_metrics_inited : x264_metrics_inited, []{ + global_metrics.add("x264_queued_frames", {{ "encode", "regular" }}, &metric_x264_queued_frames, Metrics::TYPE_GAUGE); + global_metrics.add("x264_max_queued_frames", {{ "encode", "regular" }}, &metric_x264_max_queued_frames, Metrics::TYPE_GAUGE); + global_metrics.add("x264_dropped_frames", {{ "encode", "regular" }}, &metric_x264_dropped_frames); + global_metrics.add("x264_output_frames", {{ "encode", "regular" }, { "type", "i" }}, &metric_x264_output_frames_i); + global_metrics.add("x264_output_frames", {{ "encode", "regular" }, { "type", "p" }}, &metric_x264_output_frames_p); + global_metrics.add("x264_output_frames", {{ "encode", "regular" }, { "type", "b" }}, &metric_x264_output_frames_b); + + metric_x264_crf.init_uniform(50); + global_metrics.add("x264_crf", {{ "encode", "regular" }}, &metric_x264_crf); + x264_latency_histogram.init("x264"); + }); + } + + size_t bytes_per_pixel = global_flags.bit_depth > 8 ? 2 : 1; frame_pool.reset(new uint8_t[global_flags.width * global_flags.height * 2 * bytes_per_pixel * X264_QUEUE_LENGTH]); for (unsigned i = 0; i < X264_QUEUE_LENGTH; ++i) { free_frames.push(frame_pool.get() + i * (global_flags.width * global_flags.height * 2 * bytes_per_pixel)); @@ -112,8 +138,13 @@ void X264Encoder::add_frame(int64_t pts, int64_t duration, YCbCrLumaCoefficients { lock_guard lock(mu); if (free_frames.empty()) { - fprintf(stderr, "WARNING: x264 queue full, dropping frame with pts %ld\n", pts); - ++metric_x264_dropped_frames; + if (use_separate_disk_params) { + fprintf(stderr, "WARNING: x264 queue full (disk encoder), dropping frame with pts %" PRId64 "\n", pts); + ++metric_x264_disk_dropped_frames; + } else { + fprintf(stderr, "WARNING: x264 queue full, dropping frame with pts %" PRId64 "\n", pts); + ++metric_x264_dropped_frames; + } return; } @@ -121,37 +152,45 @@ void X264Encoder::add_frame(int64_t pts, int64_t duration, YCbCrLumaCoefficients free_frames.pop(); } - size_t bytes_per_pixel = global_flags.x264_bit_depth > 8 ? 2 : 1; + size_t bytes_per_pixel = global_flags.bit_depth > 8 ? 2 : 1; memcpy(qf.data, data, global_flags.width * global_flags.height * 2 * bytes_per_pixel); { lock_guard lock(mu); queued_frames.push(qf); queued_frames_nonempty.notify_all(); - metric_x264_queued_frames = queued_frames.size(); + if (use_separate_disk_params) { + metric_x264_disk_queued_frames = queued_frames.size(); + } else { + metric_x264_queued_frames = queued_frames.size(); + } } } void X264Encoder::init_x264() { x264_param_t param; - dyn.x264_param_default_preset(¶m, global_flags.x264_preset.c_str(), global_flags.x264_tune.c_str()); + if (use_separate_disk_params) { + dyn.x264_param_default_preset(¶m, global_flags.x264_separate_disk_preset.c_str(), global_flags.x264_separate_disk_tune.c_str()); + } else { + dyn.x264_param_default_preset(¶m, global_flags.x264_preset.c_str(), global_flags.x264_tune.c_str()); + } param.i_width = global_flags.width; param.i_height = global_flags.height; param.i_csp = X264_CSP_NV12; - if (global_flags.x264_bit_depth > 8) { + if (global_flags.bit_depth > 8) { param.i_csp |= X264_CSP_HIGH_DEPTH; } param.b_vfr_input = 1; param.i_timebase_num = 1; param.i_timebase_den = TIMEBASE; param.i_keyint_max = 50; // About one second. - if (global_flags.x264_speedcontrol) { + if (!use_separate_disk_params && global_flags.x264_speedcontrol) { param.i_frame_reference = 16; // Because speedcontrol is never allowed to change this above what we set at start. } #if X264_BUILD >= 153 - param.i_bitdepth = global_flags.x264_bit_depth; + param.i_bitdepth = global_flags.bit_depth; #endif // NOTE: These should be in sync with the ones in quicksync_encoder.cpp (sps_rbsp()). @@ -165,14 +204,18 @@ void X264Encoder::init_x264() param.vui.i_colmatrix = 6; // BT.601/SMPTE 170M. } - if (!isinf(global_flags.x264_crf)) { + const double crf = use_separate_disk_params ? global_flags.x264_separate_disk_crf : global_flags.x264_crf; + const int bitrate = use_separate_disk_params ? global_flags.x264_separate_disk_bitrate : global_flags.x264_bitrate; + if (!isinf(crf)) { param.rc.i_rc_method = X264_RC_CRF; - param.rc.f_rf_constant = global_flags.x264_crf; + param.rc.f_rf_constant = crf; } else { param.rc.i_rc_method = X264_RC_ABR; - param.rc.i_bitrate = global_flags.x264_bitrate; + param.rc.i_bitrate = bitrate; + } + if (!use_separate_disk_params) { + update_vbv_settings(¶m); } - 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 // also reasonable to assume that they are fine with the stream @@ -205,7 +248,8 @@ void X264Encoder::init_x264() // be on the safe side. Shouldn't affect quality in any meaningful way. param.rc.i_qp_min = 5; - for (const string &str : global_flags.x264_extra_param) { + const vector &extra_param = use_separate_disk_params ? global_flags.x264_separate_disk_extra_param : global_flags.x264_extra_param; + for (const string &str : extra_param) { const size_t pos = str.find(','); if (pos == string::npos) { if (dyn.x264_param_parse(¶m, str.c_str(), nullptr) != 0) { @@ -221,7 +265,7 @@ void X264Encoder::init_x264() } } - if (global_flags.x264_bit_depth > 8) { + if (global_flags.bit_depth > 8) { dyn.x264_param_apply_profile(¶m, "high10"); } else { dyn.x264_param_apply_profile(¶m, "high"); @@ -232,10 +276,10 @@ void X264Encoder::init_x264() x264 = dyn.x264_encoder_open(¶m); if (x264 == nullptr) { fprintf(stderr, "ERROR: x264 initialization failed.\n"); - exit(1); + abort(); } - if (global_flags.x264_speedcontrol) { + if (!use_separate_disk_params && global_flags.x264_speedcontrol) { speed_control.reset(new X264SpeedControl(x264, /*f_speed=*/1.0f, X264_QUEUE_LENGTH, /*f_buffer_init=*/1.0f)); } @@ -284,7 +328,11 @@ void X264Encoder::encoder_thread_func() qf.data = nullptr; } - metric_x264_queued_frames = queued_frames.size(); + if (use_separate_disk_params) { + metric_x264_disk_queued_frames = queued_frames.size(); + } else { + metric_x264_queued_frames = queued_frames.size(); + } frames_left = !queued_frames.empty(); } @@ -313,7 +361,7 @@ void X264Encoder::encode_frame(X264Encoder::QueuedFrame qf) dyn.x264_picture_init(&pic); pic.i_pts = qf.pts; - if (global_flags.x264_bit_depth > 8) { + if (global_flags.bit_depth > 8) { pic.img.i_csp = X264_CSP_NV12 | X264_CSP_HIGH_DEPTH; pic.img.i_plane = 2; pic.img.plane[0] = qf.data; @@ -360,15 +408,27 @@ void X264Encoder::encode_frame(X264Encoder::QueuedFrame qf) if (num_nal == 0) return; - if (IS_X264_TYPE_I(pic.i_type)) { - ++metric_x264_output_frames_i; - } else if (IS_X264_TYPE_B(pic.i_type)) { - ++metric_x264_output_frames_b; + if (use_separate_disk_params) { + if (IS_X264_TYPE_I(pic.i_type)) { + ++metric_x264_disk_output_frames_i; + } else if (IS_X264_TYPE_B(pic.i_type)) { + ++metric_x264_disk_output_frames_b; + } else { + ++metric_x264_disk_output_frames_p; + } + + metric_x264_disk_crf.count_event(pic.prop.f_crf_avg); } else { - ++metric_x264_output_frames_p; - } + if (IS_X264_TYPE_I(pic.i_type)) { + ++metric_x264_output_frames_i; + } else if (IS_X264_TYPE_B(pic.i_type)) { + ++metric_x264_output_frames_b; + } else { + ++metric_x264_output_frames_p; + } - metric_x264_crf.count_event(pic.prop.f_crf_avg); + metric_x264_crf.count_event(pic.prop.f_crf_avg); + } if (frames_being_encoded.count(pic.i_pts)) { ReceivedTimestamps received_ts = frames_being_encoded[pic.i_pts];