From fb9a8e31b1e15880bad7e9684fde310d4932ebc7 Mon Sep 17 00:00:00 2001 From: Devaev Maxim Date: Sun, 30 Sep 2018 02:49:11 +0300 Subject: [PATCH] --drop-same-frames to reduce the outgoing traffic --- src/http.c | 77 ++++++++++++++++++++++++++++++++++++++++++------------ src/http.h | 4 +++ src/main.c | 8 +++++- 3 files changed, 72 insertions(+), 17 deletions(-) diff --git a/src/http.c b/src/http.c index 474631a..91af65a 100644 --- a/src/http.c +++ b/src/http.c @@ -52,10 +52,10 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c static void _http_callback_stream_error(struct bufferevent *buf_event, short what, void *v_ctx); static void _http_exposed_refresh(int fd, short event, void *v_server); -static void _http_queue_send_stream(struct http_server_t *server); +static void _http_queue_send_stream(struct http_server_t *server, const bool updated); -static void _expose_new_picture(struct http_server_t *server); -static void _expose_blank_picture(struct http_server_t *server); +static bool _expose_new_picture(struct http_server_t *server); +static bool _expose_blank_picture(struct http_server_t *server); struct http_server_t *http_server_init(struct stream_t *stream) { @@ -70,6 +70,7 @@ struct http_server_t *http_server_init(struct stream_t *stream) { run->exposed = exposed; run->refresh_interval.tv_sec = 0; run->refresh_interval.tv_usec = 30000; // ~30 refreshes per second + run->drop_same_frames_blank = 10; A_CALLOC(server, 1); server->host = "localhost"; @@ -108,12 +109,16 @@ void http_server_destroy(struct http_server_t *server) { } int http_server_listen(struct http_server_t *server) { + server->run->drop_same_frames_blank = max_u(server->drop_same_frames, server->run->drop_same_frames_blank); + LOG_DEBUG("Binding HTTP to [%s]:%d ...", server->host, server->port); evhttp_set_timeout(server->run->http, server->timeout); + if (evhttp_bind_socket(server->run->http, server->host, server->port) != 0) { LOG_PERROR("Can't listen HTTP on [%s]:%d", server->host, server->port) return -1; } + LOG_INFO("Listening HTTP on [%s]:%d", server->host, server->port); return 0; } @@ -229,6 +234,7 @@ static void _http_callback_stream(struct evhttp_request *request, void *v_server client->server = server; client->request = request; client->need_initial = true; + client->need_first_frame = true; if (server->run->stream_clients == NULL) { server->run->stream_clients = client; @@ -321,23 +327,26 @@ static void _http_callback_stream_error(UNUSED struct bufferevent *buf_event, UN free(client); } -static void _http_queue_send_stream(struct http_server_t *server) { +static void _http_queue_send_stream(struct http_server_t *server, const bool updated) { struct stream_client_t *client; struct evhttp_connection *conn; struct bufferevent *buf_event; for (client = server->run->stream_clients; client != NULL; client = client->next) { conn = evhttp_request_get_connection(client->request); - if (conn != NULL) { + if (conn != NULL && (updated || client->need_first_frame)) { buf_event = evhttp_connection_get_bufferevent(conn); bufferevent_setcb(buf_event, NULL, _http_callback_stream_write, _http_callback_stream_error, (void *)client); bufferevent_enable(buf_event, EV_READ|EV_WRITE); + client->need_first_frame = false; } } } static void _http_exposed_refresh(UNUSED int fd, UNUSED short what, void *v_server) { struct http_server_t *server = (struct http_server_t *)v_server; + bool updated = false; + bool queue_send = false; #define LOCK_STREAM \ A_PTHREAD_M_LOCK(&server->run->stream->mutex); @@ -349,41 +358,66 @@ static void _http_exposed_refresh(UNUSED int fd, UNUSED short what, void *v_serv LOG_DEBUG("Refreshing HTTP exposed ..."); LOCK_STREAM; if (server->run->stream->picture.size > 0) { // If online - _expose_new_picture(server); + updated = _expose_new_picture(server); UNLOCK_STREAM; } else { UNLOCK_STREAM; - _expose_blank_picture(server); + updated = _expose_blank_picture(server); } - _http_queue_send_stream(server); + queue_send = true; } else if (!server->run->exposed->online) { LOG_DEBUG("Refreshing HTTP exposed (BLANK) ..."); - _http_queue_send_stream(server); + updated = _expose_blank_picture(server); + queue_send = true; + } + + if (queue_send) { + _http_queue_send_stream(server, updated); } # undef LOCK_STREAM # undef UNLOCK_STREAM } -void _expose_new_picture(struct http_server_t *server) { +static bool _expose_new_picture(struct http_server_t *server) { + assert(server->run->stream->picture.size > 0); + server->run->exposed->fps = server->run->stream->fps; + +# define MEM_STREAM_TO_EXPOSED \ + server->run->exposed->picture.data, server->run->stream->picture.data, \ + server->run->stream->picture.size * sizeof(*server->run->exposed->picture.data) + + if (server->drop_same_frames) { + if ( + server->run->exposed->online + && server->run->exposed->dropped < server->drop_same_frames + && server->run->exposed->picture.size == server->run->stream->picture.size + && !memcmp(MEM_STREAM_TO_EXPOSED) + ) { + LOG_PERF("HTTP: dropped same frame number %u", server->run->exposed->dropped); + ++server->run->exposed->dropped; + return false; // Not updated + } + } + if (server->run->exposed->picture.allocated < server->run->stream->picture.allocated) { A_REALLOC(server->run->exposed->picture.data, server->run->stream->picture.allocated); server->run->exposed->picture.allocated = server->run->stream->picture.allocated; } - memcpy( - server->run->exposed->picture.data, server->run->stream->picture.data, - server->run->stream->picture.size * sizeof(*server->run->exposed->picture.data) - ); + memcpy(MEM_STREAM_TO_EXPOSED); + +# undef MEM_STREAM_TO_EXPOSED server->run->exposed->picture.size = server->run->stream->picture.size; server->run->exposed->width = server->run->stream->width; server->run->exposed->height = server->run->stream->height; - server->run->exposed->fps = server->run->stream->fps; server->run->exposed->online = true; + server->run->exposed->dropped = 0; + return true; // Updated } -void _expose_blank_picture(struct http_server_t *server) { +static bool _expose_blank_picture(struct http_server_t *server) { if (server->run->exposed->online || server->run->exposed->picture.size == 0) { if (server->run->exposed->picture.allocated < BLANK_JPG_SIZE) { A_REALLOC(server->run->exposed->picture.data, BLANK_JPG_SIZE); @@ -400,5 +434,16 @@ void _expose_blank_picture(struct http_server_t *server) { server->run->exposed->height = BLANK_JPG_HEIGHT; server->run->exposed->fps = 0; server->run->exposed->online = false; + goto updated; } + + if (server->run->exposed->dropped < server->run->drop_same_frames_blank) { + LOG_PERF("HTTP: dropped same frame (BLANK) number %u", server->run->exposed->dropped); + ++server->run->exposed->dropped; + return false; // Not updated + } + + updated: + server->run->exposed->dropped = 0; + return true; // Updated } diff --git a/src/http.h b/src/http.h index 9477e03..1ca7b4c 100644 --- a/src/http.h +++ b/src/http.h @@ -33,6 +33,7 @@ struct stream_client_t { struct http_server_t *server; struct evhttp_request *request; bool need_initial; + bool need_first_frame; struct stream_client_t *prev; struct stream_client_t *next; @@ -44,6 +45,7 @@ struct exposed_t { unsigned height; unsigned fps; bool online; + unsigned dropped; }; struct http_server_runtime_t { @@ -55,11 +57,13 @@ struct http_server_runtime_t { struct timeval refresh_interval; struct stream_client_t *stream_clients; + unsigned drop_same_frames_blank; }; struct http_server_t { char *host; unsigned port; + unsigned drop_same_frames; unsigned fake_width; unsigned fake_height; unsigned timeout; diff --git a/src/main.c b/src/main.c index 93e4d7f..c0b29a2 100644 --- a/src/main.c +++ b/src/main.c @@ -40,7 +40,7 @@ #include "http.h" -static const char _short_opts[] = "d:x:y:f:a:e:z:tn:w:q:c:s:p:h"; +static const char _short_opts[] = "d:x:y:f:a:e:z:tn:w:q:c:s:p:r:h"; static const struct option _long_opts[] = { {"device", required_argument, NULL, 'd'}, {"width", required_argument, NULL, 'x'}, @@ -62,6 +62,7 @@ static const struct option _long_opts[] = { {"host", required_argument, NULL, 's'}, {"port", required_argument, NULL, 'p'}, + {"drop-same-frames", required_argument, NULL, 'r'}, {"fake-width", required_argument, NULL, 2000}, {"fake-height", required_argument, NULL, 2001}, {"server-timeout", required_argument, NULL, 2002}, @@ -111,6 +112,10 @@ static void _help(struct device_t *dev, struct encoder_t *encoder, struct http_s printf("--------------------\n"); printf(" --host
-- Listen on Hostname or IP. Default: %s\n\n", server->host); printf(" --port -- Bind to this TCP port. Default: %d\n\n", server->port); + printf(" --drop-same-frames -- Don't send same frames to clients, but no more than specified number.\n"); + printf(" It can significantly reduce the outgoing traffic, but will increase\n"); + printf(" the CPU loading. Don't use this option with analog signal sources\n"); + printf(" or webcams, it's useless. Default: disabled.\n\n"); printf(" --fake-width -- Override image width for /ping. Default: disabled\n\n"); printf(" --fake-height -- Override image height for /ping. Default: disabled.\n\n"); printf(" --server-timeout -- Timeout for client connections. Default: %d\n\n", server->timeout); @@ -173,6 +178,7 @@ static int _parse_options(int argc, char *argv[], struct device_t *dev, struct e case 's': server->host = optarg; break; case 'p': OPT_UNSIGNED(server->port, "--port", 1, 65535); + case 'r': OPT_UNSIGNED(server->drop_same_frames, "--drop-same-frames", 0, 30); case 2000: OPT_UNSIGNED(server->fake_width, "--fake-width", 0, 1920); case 2001: OPT_UNSIGNED(server->fake_height, "--fake-height", 0, 1200); case 2002: OPT_UNSIGNED(server->timeout, "--server-timeout", 1, 60);