common fps counter

This commit is contained in:
Maxim Devaev
2024-03-30 12:15:59 +02:00
parent f8a703f166
commit 70fa6548fe
6 changed files with 135 additions and 51 deletions

View File

@@ -36,6 +36,7 @@
#include "../libs/logging.h"
#include "../libs/frame.h"
#include "../libs/memsink.h"
#include "../libs/fps.h"
#include "../libs/signal.h"
#include "../libs/options.h"
@@ -221,16 +222,13 @@ static int _dump_sink(
const useconds_t interval_us = interval * 1000000;
us_frame_s *frame = us_frame_init();
us_fps_s *fps = us_fps_init("SINK");
us_memsink_s *sink = NULL;
if ((sink = us_memsink_init_opened("input", sink_name, false, 0, false, 0, sink_timeout)) == NULL) {
goto error;
}
unsigned fps = 0;
unsigned fps_accum = 0;
long long fps_second = 0;
long double last_ts = 0;
while (!_g_stop) {
@@ -240,7 +238,6 @@ static int _dump_sink(
key_required = false;
const long double now = us_get_now_monotonic();
const long long now_second = us_floor_ms(now);
char fourcc_str[8];
US_LOG_VERBOSE("Frame: %s - %ux%u -- online=%d, key=%d, kr=%d, gop=%u, latency=%.3Lf, backlog=%.3Lf, size=%zu",
@@ -254,13 +251,7 @@ static int _dump_sink(
US_LOG_DEBUG(" stride=%u, grab_ts=%.3Lf, encode_begin_ts=%.3Lf, encode_end_ts=%.3Lf",
frame->stride, frame->grab_ts, frame->encode_begin_ts, frame->encode_end_ts);
if (now_second != fps_second) {
fps = fps_accum;
fps_accum = 0;
fps_second = now_second;
US_LOG_PERF_FPS("A new second has come; captured_fps=%u", fps);
}
fps_accum += 1;
us_fps_bump(fps);
if (ctx->v_output != NULL) {
ctx->write(ctx->v_output, frame);
@@ -287,6 +278,7 @@ static int _dump_sink(
error:
US_DELETE(sink, us_memsink_destroy);
us_fps_destroy(fps);
us_frame_destroy(frame);
US_LOG_INFO("Bye-bye");
return retval;

60
src/libs/fps.c Normal file
View File

@@ -0,0 +1,60 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPEG-HTTP streamer. #
# #
# Copyright (C) 2018-2024 Maxim Devaev <mdevaev@gmail.com> #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
# #
*****************************************************************************/
#include "fps.h"
#include "types.h"
#include "tools.h"
#include "logging.h"
us_fps_s *us_fps_init(const char *name) {
us_fps_s *fps;
US_CALLOC(fps, 1);
fps->name = us_strdup(name);
return fps;
}
void us_fps_destroy(us_fps_s *fps) {
free(fps->name);
free(fps);
}
void us_fps_bump(us_fps_s *fps) {
const sll now_sec_ts = us_floor_ms(us_get_now_monotonic());
if (now_sec_ts != fps->ts) {
US_LOG_PERF_FPS("FPS: %s: %u", fps->name, fps->accum);
atomic_store(&fps->current, fps->accum);
fps->accum = 0;
fps->ts = now_sec_ts;
}
++fps->accum;
}
void us_fps_reset(us_fps_s *fps) {
us_fps_bump(fps); // Just show the log record
fps->accum = 0;
}
uint us_fps_get(us_fps_s *fps) {
return atomic_load(&fps->current);
}

43
src/libs/fps.h Normal file
View File

@@ -0,0 +1,43 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPEG-HTTP streamer. #
# #
# Copyright (C) 2018-2024 Maxim Devaev <mdevaev@gmail.com> #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
# #
*****************************************************************************/
#pragma once
#include <stdatomic.h>
#include "types.h"
typedef struct {
char *name;
uint accum;
ldf ts;
atomic_uint current;
} us_fps_s;
us_fps_s *us_fps_init(const char *name);
void us_fps_destroy(us_fps_s *fps);
void us_fps_bump(us_fps_s *fps);
void us_fps_reset(us_fps_s *fps);
uint us_fps_get(us_fps_s *fps);

View File

@@ -120,6 +120,7 @@ us_server_s *us_server_init(us_stream_s *stream) {
us_server_exposed_s *exposed;
US_CALLOC(exposed, 1);
exposed->frame = us_frame_init();
exposed->queued_fps = us_fps_init("MJPEG-QUEUED");
us_server_runtime_s *run;
US_CALLOC(run, 1);
@@ -168,6 +169,7 @@ void us_server_destroy(us_server_s *server) {
});
US_LIST_ITERATE(run->stream_clients, client, { // cppcheck-suppress constStatement
us_fps_destroy(client->fps);
free(client->key);
free(client->hostport);
free(client);
@@ -175,6 +177,7 @@ void us_server_destroy(us_server_s *server) {
US_DELETE(run->auth_token, free);
us_fps_destroy(run->exposed->queued_fps);
us_frame_destroy(run->exposed->frame);
free(run->exposed);
free(server->run);
@@ -514,7 +517,7 @@ static void _http_callback_state(struct evhttp_request *request, void *v_server)
us_bool_to_string(online),
stream->cap->desired_fps,
captured_fps,
ex->queued_fps,
us_fps_get(ex->queued_fps),
run->stream_clients_count
);
@@ -523,7 +526,7 @@ static void _http_callback_state(struct evhttp_request *request, void *v_server)
"\"%" PRIx64 "\": {\"fps\": %u, \"extra_headers\": %s, \"advance_headers\": %s,"
" \"dual_final_frames\": %s, \"zero_data\": %s, \"key\": \"%s\"}%s",
client->id,
client->fps,
us_fps_get(client->fps),
us_bool_to_string(client->extra_headers),
us_bool_to_string(client->advance_headers),
us_bool_to_string(client->dual_final_frames),
@@ -590,6 +593,13 @@ static void _http_callback_stream(struct evhttp_request *request, void *v_server
client->hostport = _http_get_client_hostport(request);
client->id = us_get_now_id();
{
char *name;
US_ASPRINTF(name, "MJPEG-CLIENT-%" PRIx64, client->id);
client->fps = us_fps_init(name);
free(name);
}
US_LIST_APPEND_C(run->stream_clients, client, run->stream_clients_count);
if (run->stream_clients_count == 1) {
@@ -626,15 +636,7 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
us_server_s *const server = client->server;
us_server_exposed_s *const ex = server->run->exposed;
const ldf now_ts = us_get_now_monotonic();
const sll now_sec_ts = us_floor_ms(now_ts);
if (now_sec_ts != client->fps_ts) {
client->fps = client->fps_accum;
client->fps_accum = 0;
client->fps_ts = now_sec_ts;
}
client->fps_accum += 1;
us_fps_bump(client->fps);
struct evbuffer *buf;
_A_EVBUFFER_NEW(buf);
@@ -716,6 +718,7 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
us_get_now_real(),
(client->extra_headers ? "" : RN)
);
const ldf now_ts = us_get_now_monotonic();
if (client->extra_headers) {
_A_EVBUFFER_ADD_PRINTF(buf,
"X-UStreamer-Online: %s" RN
@@ -736,7 +739,7 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
ex->dropped,
ex->frame->width,
ex->frame->height,
client->fps,
us_fps_get(client->fps),
ex->frame->grab_ts,
ex->frame->encode_begin_ts,
ex->frame->encode_end_ts,
@@ -793,6 +796,7 @@ static void _http_callback_stream_error(struct bufferevent *buf_event, short wha
struct evhttp_connection *conn = evhttp_request_get_connection(client->request);
US_DELETE(conn, evhttp_connection_free);
us_fps_destroy(client->fps);
free(client->key);
free(client->hostport);
free(client);
@@ -839,17 +843,9 @@ static void _http_send_stream(us_server_s *server, bool stream_updated, bool fra
});
if (queued) {
static uint queued_fps_accum = 0;
static sll queued_fps_ts = 0;
const sll now_sec_ts = us_floor_ms(us_get_now_monotonic());
if (now_sec_ts != queued_fps_ts) {
ex->queued_fps = queued_fps_accum;
queued_fps_accum = 0;
queued_fps_ts = now_sec_ts;
}
queued_fps_accum += 1;
us_fps_bump(ex->queued_fps);
} else if (!has_clients) {
ex->queued_fps = 0;
us_fps_reset(ex->queued_fps);
}
}

View File

@@ -31,6 +31,7 @@
#include "../../libs/types.h"
#include "../../libs/frame.h"
#include "../../libs/list.h"
#include "../../libs/fps.h"
#include "../encoder.h"
#include "../stream.h"
@@ -50,9 +51,8 @@ typedef struct us_stream_client_sx {
bool need_initial;
bool need_first_frame;
bool updated_prev;
uint fps_accum;
sll fps_ts;
uint fps;
us_fps_s *fps;
US_LIST_STRUCT(struct us_stream_client_sx);
} us_stream_client_s;
@@ -67,8 +67,7 @@ typedef struct us_snapshot_client_sx {
typedef struct {
us_frame_s *frame;
uint captured_fps;
uint queued_fps;
us_fps_s *queued_fps;
uint dropped;
ldf expose_begin_ts;
ldf expose_cmp_ts;

View File

@@ -42,6 +42,7 @@
#include "../libs/memsink.h"
#include "../libs/capture.h"
#include "../libs/unjpeg.h"
#include "../libs/fps.h"
#ifdef WITH_V4P
# include "../libs/drm/drm.h"
#endif
@@ -175,9 +176,7 @@ void us_stream_loop(us_stream_s *stream) {
# endif
# undef CREATE_WORKER
uint captured_fps_accum = 0;
sll captured_fps_ts = 0;
uint captured_fps = 0;
us_fps_s *fps = us_fps_init("CAP");
US_LOG_INFO("Capturing ...");
@@ -190,16 +189,9 @@ void us_stream_loop(us_stream_s *stream) {
default: goto close; // Any error
}
const sll now_sec_ts = us_floor_ms(us_get_now_monotonic());
if (now_sec_ts != captured_fps_ts) {
captured_fps = captured_fps_accum;
captured_fps_accum = 0;
captured_fps_ts = now_sec_ts;
US_LOG_PERF_FPS("A new second has come; captured_fps=%u", captured_fps);
}
captured_fps_accum += 1;
us_fps_bump(fps);
_stream_set_capture_state(stream, cap->run->width, cap->run->height, true, us_fps_get(fps));
_stream_set_capture_state(stream, cap->run->width, cap->run->height, true, captured_fps);
# ifdef WITH_GPIO
us_gpio_set_stream_online(true);
# endif
@@ -229,6 +221,8 @@ void us_stream_loop(us_stream_s *stream) {
}
close:
us_fps_destroy(fps);
atomic_store(&threads_stop, true);
# define DELETE_WORKER(x_ctx) if (x_ctx != NULL) { \