#include <string.h>
#include <unistd.h>
#include <x264.h>
+#include <atomic>
#include <cstdint>
+#include <mutex>
#include "defs.h"
#include "flags.h"
+#include "metrics.h"
#include "mux.h"
#include "print_latency.h"
#include "timebase.h"
namespace {
+// X264Encoder can be restarted if --record-x264-video is set, so make these
+// metrics global.
+atomic<int64_t> metric_x264_queued_frames{0};
+atomic<int64_t> metric_x264_max_queued_frames{X264_QUEUE_LENGTH};
+atomic<int64_t> metric_x264_dropped_frames{0};
+atomic<int64_t> metric_x264_output_frames_i{0};
+atomic<int64_t> metric_x264_output_frames_p{0};
+atomic<int64_t> metric_x264_output_frames_b{0};
+Histogram metric_x264_crf;
+LatencyHistogram x264_latency_histogram;
+once_flag x264_metrics_inited;
+
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 {
: wants_global_headers(oformat->flags & AVFMT_GLOBALHEADER),
dyn(load_x264_for_bit_depth(global_flags.x264_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;
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) {
lock_guard<mutex> lock(mu);
if (free_frames.empty()) {
fprintf(stderr, "WARNING: x264 queue full, dropping frame with pts %ld\n", pts);
+ ++metric_x264_dropped_frames;
return;
}
lock_guard<mutex> lock(mu);
queued_frames.push(qf);
queued_frames_nonempty.notify_all();
+ metric_x264_queued_frames = queued_frames.size();
}
}
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
perror("nice()");
// No exit; it's not fatal.
}
+ pthread_setname_np(pthread_self(), "x264_encode");
init_x264();
x264_init_done = true;
qf.data = nullptr;
}
+ metric_x264_queued_frames = queued_frames.size();
frames_left = !queued_frames.empty();
}
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;
+ } else {
+ ++metric_x264_output_frames_p;
+ }
+
+ 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];
frames_being_encoded.erase(pic.i_pts);
static int frameno = 0;
print_latency("Current x264 latency (video inputs → network mux):",
received_ts, (pic.i_type == X264_TYPE_B || pic.i_type == X264_TYPE_BREF),
- &frameno);
+ &frameno, &x264_latency_histogram);
} else {
assert(false);
}