# Mixer objects
AUDIO_MIXER_OBJS = audio_mixer.o alsa_input.o alsa_pool.o ebu_r128_proc.o stereocompressor.o resampling_queue.o flags.o correlation_measurer.o filter.o input_mapping.o state.pb.o
-OBJS += chroma_subsampler.o v210_converter.o mixer.o metrics.o pbo_frame_allocator.o context.o ref_counted_frame.o theme.o httpd.o flags.o image_input.o alsa_output.o disk_space_estimator.o print_latency.o timecode_renderer.o tweaked_inputs.o $(AUDIO_MIXER_OBJS)
+OBJS += chroma_subsampler.o v210_converter.o mixer.o basic_stats.o metrics.o pbo_frame_allocator.o context.o ref_counted_frame.o theme.o httpd.o flags.o image_input.o alsa_output.o disk_space_estimator.o print_latency.o timecode_renderer.o tweaked_inputs.o $(AUDIO_MIXER_OBJS)
# Streaming and encoding objects
OBJS += quicksync_encoder.o x264_encoder.o x264_dynamic.o x264_speed_control.o video_encoder.o metacube2.o mux.o audio_encoder.o ffmpeg_raii.o ffmpeg_util.o
# DeckLink
OBJS += decklink_capture.o decklink_util.o decklink_output.o decklink/DeckLinkAPIDispatch.o
-KAERU_OBJS = kaeru.o x264_encoder.o mux.o metrics.o flags.o audio_encoder.o x264_speed_control.o print_latency.o x264_dynamic.o ffmpeg_raii.o ffmpeg_capture.o ffmpeg_util.o httpd.o metacube2.o
+KAERU_OBJS = kaeru.o x264_encoder.o mux.o basic_stats.o metrics.o flags.o audio_encoder.o x264_speed_control.o print_latency.o x264_dynamic.o ffmpeg_raii.o ffmpeg_capture.o ffmpeg_util.o httpd.o metacube2.o
# bmusb
ifeq ($(EMBEDDED_BMUSB),yes)
--- /dev/null
+#include "basic_stats.h"
+#include "metrics.h"
+
+#include <assert.h>
+#include <sys/resource.h>
+
+using namespace std;
+using namespace std::chrono;
+
+bool uses_mlock = false;
+
+BasicStats::BasicStats(bool verbose)
+ : verbose(verbose)
+{
+ start = steady_clock::now();
+
+ metric_start_time_seconds = get_timestamp_for_metrics();
+ global_metrics.add("frames_output_total", &metric_frames_output_total);
+ global_metrics.add("frames_output_dropped", &metric_frames_output_dropped);
+ global_metrics.add("start_time_seconds", &metric_start_time_seconds, Metrics::TYPE_GAUGE);
+ global_metrics.add("memory_used_bytes", &metrics_memory_used_bytes);
+ global_metrics.add("memory_locked_limit_bytes", &metrics_memory_locked_limit_bytes);
+}
+
+void BasicStats::update(int frame_num, int stats_dropped_frames)
+{
+ steady_clock::time_point now = steady_clock::now();
+ double elapsed = duration<double>(now - start).count();
+
+ metric_frames_output_total = frame_num;
+ metric_frames_output_dropped = stats_dropped_frames;
+
+ if (frame_num % 100 != 0) {
+ return;
+ }
+
+ if (verbose) {
+ printf("%d frames (%d dropped) in %.3f seconds = %.1f fps (%.1f ms/frame)",
+ frame_num, stats_dropped_frames, elapsed, frame_num / elapsed,
+ 1e3 * elapsed / frame_num);
+ }
+
+ // Check our memory usage, to see if we are close to our mlockall()
+ // limit (if at all set).
+ rusage used;
+ if (getrusage(RUSAGE_SELF, &used) == -1) {
+ perror("getrusage(RUSAGE_SELF)");
+ assert(false);
+ }
+ metrics_memory_used_bytes = used.ru_maxrss * 1024;
+
+ if (uses_mlock) {
+ rlimit limit;
+ if (getrlimit(RLIMIT_MEMLOCK, &limit) == -1) {
+ perror("getrlimit(RLIMIT_MEMLOCK)");
+ assert(false);
+ }
+ metrics_memory_locked_limit_bytes = limit.rlim_cur;
+
+ if (verbose) {
+ if (limit.rlim_cur == 0) {
+ printf(", using %ld MB memory (locked)",
+ long(used.ru_maxrss / 1024));
+ } else {
+ printf(", using %ld / %ld MB lockable memory (%.1f%%)",
+ long(used.ru_maxrss / 1024),
+ long(limit.rlim_cur / 1048576),
+ float(100.0 * (used.ru_maxrss * 1024.0) / limit.rlim_cur));
+ }
+ }
+ } else {
+ metrics_memory_locked_limit_bytes = 0.0 / 0.0;
+ if (verbose) {
+ printf(", using %ld MB memory (not locked)",
+ long(used.ru_maxrss / 1024));
+ }
+ }
+
+ if (verbose) {
+ printf("\n");
+ }
+}
+
+
--- /dev/null
+#ifndef _BASIC_STATS_H
+#define _BASIC_STATS_H
+
+// Holds some metrics for basic statistics about uptime, memory usage and such.
+
+#include <stdint.h>
+
+#include <atomic>
+#include <chrono>
+
+extern bool uses_mlock;
+
+class BasicStats {
+public:
+ BasicStats(bool verbose);
+ void update(int frame_num, int stats_dropped_frames);
+
+private:
+ std::chrono::steady_clock::time_point start;
+ bool verbose;
+
+ // Metrics.
+ std::atomic<int64_t> metric_frames_output_total{0};
+ std::atomic<int64_t> metric_frames_output_dropped{0};
+ std::atomic<double> metric_start_time_seconds{0.0 / 0.0};
+ std::atomic<int64_t> metrics_memory_used_bytes{0};
+ std::atomic<double> metrics_memory_locked_limit_bytes{0.0 / 0.0};
+};
+
+#endif // !defined(_BASIC_STATS_H)
// This is experimental code, not yet supported.
#include "audio_encoder.h"
+#include "basic_stats.h"
#include "defs.h"
#include "flags.h"
#include "ffmpeg_capture.h"
Mixer *global_mixer = nullptr;
X264Encoder *global_x264_encoder = nullptr;
+int frame_num = 0;
+BasicStats *global_basic_stats = nullptr;
MuxMetrics stream_mux_metrics;
int write_packet(void *opaque, uint8_t *buf, int buf_size, AVIODataMarkerType type, int64_t time)
video_pts = av_rescale_q(video_pts, video_timebase, AVRational{ 1, TIMEBASE });
int64_t frame_duration = TIMEBASE * video_format.frame_rate_den / video_format.frame_rate_nom;
x264_encoder->add_frame(video_pts, frame_duration, video->get_current_frame_ycbcr_format().luma_coefficients, video_frame.data + video_offset, ts);
+ global_basic_stats->update(frame_num++, /*dropped_frames=*/0);
}
if (audio_frame.len > 0) {
// FFmpegCapture takes care of this for us.
video.start_bm_capture();
video.change_rate(2.0); // Be sure never to really fall behind, but also don't dump huge amounts of stuff onto x264.
+ BasicStats basic_stats(/*verbose=*/false);
+ global_basic_stats = &basic_stats;
httpd.start(9095);
signal(SIGUSR1, adjust_bitrate);
#include <QSurfaceFormat>
#include <string>
+#include "basic_stats.h"
#include "context.h"
#include "flags.h"
#include "image_input.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
-#include <sys/resource.h>
#include <algorithm>
#include <chrono>
#include <condition_variable>
#include "DeckLinkAPI.h"
#include "LinuxCOM.h"
#include "alsa_output.h"
+#include "basic_stats.h"
#include "bmusb/bmusb.h"
#include "bmusb/fake_capture.h"
#include "chroma_subsampler.h"
using namespace bmusb;
Mixer *global_mixer = nullptr;
-bool uses_mlock = false;
namespace {
set_output_card_internal(global_flags.output_card);
}
- metric_start_time_seconds = get_timestamp_for_metrics();
-
output_jitter_history.register_metrics({{ "card", "output" }});
- global_metrics.add("frames_output_total", &metric_frames_output_total);
- global_metrics.add("frames_output_dropped", &metric_frames_output_dropped);
- global_metrics.add("start_time_seconds", &metric_start_time_seconds, Metrics::TYPE_GAUGE);
- global_metrics.add("memory_used_bytes", &metrics_memory_used_bytes);
- global_metrics.add("memory_locked_limit_bytes", &metrics_memory_locked_limit_bytes);
}
Mixer::~Mixer()
}
}
- steady_clock::time_point start, now;
- start = steady_clock::now();
-
+ BasicStats basic_stats(/*verbose=*/true);
int stats_dropped_frames = 0;
while (!should_quit) {
++frame_num;
pts_int += frame_duration;
- now = steady_clock::now();
- double elapsed = duration<double>(now - start).count();
-
- metric_frames_output_total = frame_num;
- metric_frames_output_dropped = stats_dropped_frames;
-
- if (frame_num % 100 == 0) {
- printf("%d frames (%d dropped) in %.3f seconds = %.1f fps (%.1f ms/frame)",
- frame_num, stats_dropped_frames, elapsed, frame_num / elapsed,
- 1e3 * elapsed / frame_num);
- // chain->print_phase_timing();
-
- // Check our memory usage, to see if we are close to our mlockall()
- // limit (if at all set).
- rusage used;
- if (getrusage(RUSAGE_SELF, &used) == -1) {
- perror("getrusage(RUSAGE_SELF)");
- assert(false);
- }
-
- if (uses_mlock) {
- rlimit limit;
- if (getrlimit(RLIMIT_MEMLOCK, &limit) == -1) {
- perror("getrlimit(RLIMIT_MEMLOCK)");
- assert(false);
- }
-
- if (limit.rlim_cur == 0) {
- printf(", using %ld MB memory (locked)",
- long(used.ru_maxrss / 1024));
- } else {
- printf(", using %ld / %ld MB lockable memory (%.1f%%)",
- long(used.ru_maxrss / 1024),
- long(limit.rlim_cur / 1048576),
- float(100.0 * (used.ru_maxrss * 1024.0) / limit.rlim_cur));
- }
- metrics_memory_locked_limit_bytes = limit.rlim_cur;
- } else {
- printf(", using %ld MB memory (not locked)",
- long(used.ru_maxrss / 1024));
- metrics_memory_locked_limit_bytes = 0.0 / 0.0;
- }
-
- printf("\n");
-
- metrics_memory_used_bytes = used.ru_maxrss * 1024;
- }
-
+ basic_stats.update(frame_num, stats_dropped_frames);
+ // if (frame_num % 100 == 0) chain->print_phase_timing();
if (should_cut.exchange(false)) { // Test and clear.
video_encoder->do_cut(frame_num);
std::vector<uint32_t> mode_scanlist[MAX_VIDEO_CARDS];
unsigned mode_scanlist_index[MAX_VIDEO_CARDS]{ 0 };
std::chrono::steady_clock::time_point last_mode_scan_change[MAX_VIDEO_CARDS];
-
- // Metrics.
- std::atomic<int64_t> metric_frames_output_total{0};
- std::atomic<int64_t> metric_frames_output_dropped{0};
- std::atomic<double> metric_start_time_seconds{0.0 / 0.0};
- std::atomic<int64_t> metrics_memory_used_bytes{0};
- std::atomic<double> metrics_memory_locked_limit_bytes{0.0 / 0.0};
};
extern Mixer *global_mixer;
-extern bool uses_mlock;
#endif // !defined(_MIXER_H)