Metrics global_metrics;
+namespace {
+
+string serialize_name(const string &name, const vector<pair<string, string>> &labels)
+{
+ if (labels.empty()) {
+ return "nageru_" + name;
+ }
+
+ string label_str;
+ for (const pair<string, string> &label : labels) {
+ if (!label_str.empty()) {
+ label_str += ",";
+ }
+ label_str += label.first + "=\"" + label.second + "\"";
+ }
+ return "nageru_" + name + "{" + label_str + "}";
+}
+
+} // namespace
+
void Metrics::add(const string &name, const vector<pair<string, string>> &labels, atomic<int64_t> *location, Metrics::Type type)
{
Metric metric;
types[name] = type;
}
+void Metrics::add_histogram(const string &name, const vector<pair<string, string>> &labels, atomic<int64_t> *location, size_t num_elements)
+{
+ Histogram histogram;
+ histogram.name = name;
+ histogram.labels = labels;
+ histogram.location_int64 = location;
+ histogram.num_elements = num_elements;
+
+ lock_guard<mutex> lock(mu);
+ histograms.push_back(histogram);
+}
+
string Metrics::serialize() const
{
stringstream ss;
}
}
for (const Metric &metric : metrics) {
- string name;
- if (metric.labels.empty()) {
- name = "nageru_" + metric.name;
- } else {
- name = "nageru_" + metric.name + "{";
- bool first = true;
- for (const pair<string, string> &label : metric.labels) {
- if (!first) {
- name += ",";
- }
- first = false;
- name += label.first + "=\"" + label.second + "\"";
- }
- name += "}";
- }
+ string name = serialize_name(metric.name, metric.labels);
if (metric.data_type == DATA_TYPE_INT64) {
ss << name << " " << metric.location_int64->load() << "\n";
ss << name << " " << metric.location_double->load() << "\n";
}
}
+ for (const Histogram &histogram : histograms) {
+ ss << "# TYPE nageru_" << histogram.name << " histogram\n";
+
+ int64_t sum = 0, count = 0;
+ for (size_t i = 0; i < histogram.num_elements; ++i) {
+ char buf[16];
+ snprintf(buf, sizeof(buf), "%lu", i);
+ vector<pair<string, string>> labels = histogram.labels;
+ labels.emplace_back("le", buf);
+
+ int64_t val = histogram.location_int64[i].load();
+ sum += i * val;
+ count += val;
+ ss << serialize_name(histogram.name + "_bucket", labels) << " " << count << "\n";
+ }
+
+ ss << serialize_name(histogram.name + "_sum", histogram.labels) << " " << sum << "\n";
+ ss << serialize_name(histogram.name + "_count", histogram.labels) << " " << count << "\n";
+ }
return ss.str();
}
void add(const std::string &name, const std::vector<std::pair<std::string, std::string>> &labels, std::atomic<int64_t> *location, Type type = TYPE_COUNTER);
void add(const std::string &name, const std::vector<std::pair<std::string, std::string>> &labels, std::atomic<double> *location, Type type = TYPE_COUNTER);
+ // Only integer histogram, ie. keys are 0..(N-1).
+ void add_histogram(const std::string &name, const std::vector<std::pair<std::string, std::string>> &labels, std::atomic<int64_t> *location, size_t num_elements);
+
std::string serialize() const;
private:
};
};
+ // TODO: This needs to be more general.
+ struct Histogram {
+ std::string name;
+ std::vector<std::pair<std::string, std::string>> labels;
+ std::atomic<int64_t> *location_int64; // First bucket.
+ size_t num_elements;
+ };
+
mutable std::mutex mu;
std::map<std::string, Type> types;
std::vector<Metric> metrics;
+ std::vector<Histogram> histograms;
};
extern Metrics global_metrics;
#include <type_traits>
#include "flags.h"
+#include "metrics.h"
using namespace std;
using namespace std::chrono;
stat.max_buffer = 0;
stat.avg_preset = 0.0;
stat.den = 0;
+
+ metric_x264_speedcontrol_buffer_available_seconds = buffer_fill * 1e-6;
+ metric_x264_speedcontrol_buffer_size_seconds = buffer_size * 1e-6;
+ global_metrics.add_histogram("x264_speedcontrol_preset_used_frames", {}, metric_x264_speedcontrol_preset_used_frames, SC_PRESETS);
+ global_metrics.add("x264_speedcontrol_buffer_available_seconds", &metric_x264_speedcontrol_buffer_available_seconds, Metrics::TYPE_GAUGE);
+ global_metrics.add("x264_speedcontrol_buffer_size_seconds", &metric_x264_speedcontrol_buffer_size_seconds, Metrics::TYPE_GAUGE);
+ global_metrics.add("x264_speedcontrol_idle_frames", &metric_x264_speedcontrol_idle_frames);
+ global_metrics.add("x264_speedcontrol_late_frames", &metric_x264_speedcontrol_late_frames);
}
X264SpeedControl::~X264SpeedControl()
// Note that the two first and the two last are also used for extrapolation
// should the desired time be outside the range. Thus, it is disadvantageous if
// they are chosen so that the timings are too close to each other.
-#define SC_PRESETS 26
static const sc_preset_t presets[SC_PRESETS] = {
#define I4 X264_ANALYSE_I4x4
#define I8 X264_ANALYSE_I8x8
set_buffer_size(new_buffer_size);
}
buffer_fill = buffer_size * new_buffer_fill;
+ metric_x264_speedcontrol_buffer_available_seconds = buffer_fill * 1e-6;
steady_clock::time_point t;
first = false;
}
buffer_fill = buffer_size;
+ metric_x264_speedcontrol_buffer_available_seconds = buffer_fill * 1e-6;
+ ++metric_x264_speedcontrol_idle_frames;
} else if (buffer_fill <= 0) { // oops, we're late
// fprintf(stderr, "speedcontrol underflow (%.6f sec)\n", buffer_fill/1e6);
+ ++metric_x264_speedcontrol_late_frames;
}
{
}
dyn.x264_encoder_reconfig(x264, &p);
preset = new_preset;
+
+ ++metric_x264_speedcontrol_preset_used_frames[new_preset];
}
// some cleanup, but it's much, much better than just using a static preset.
#include <stdint.h>
+#include <atomic>
#include <chrono>
#include <functional>
#include "x264_dynamic.h"
+#define SC_PRESETS 26
+
class X264SpeedControl {
public:
// x264: Encoding object we are using; must be opened. Assumed to be
} stat;
std::function<void(x264_param_t *)> override_func = nullptr;
+
+ // Metrics.
+ std::atomic<int64_t> metric_x264_speedcontrol_preset_used_frames[SC_PRESETS]{{0}};
+ std::atomic<double> metric_x264_speedcontrol_buffer_available_seconds{0.0};
+ std::atomic<double> metric_x264_speedcontrol_buffer_size_seconds{0.0};
+ std::atomic<int64_t> metric_x264_speedcontrol_idle_frames{0};
+ std::atomic<int64_t> metric_x264_speedcontrol_late_frames{0};
};