mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-03-12 18:43:42 +00:00
--drop-same-frames to reduce the outgoing traffic
This commit is contained in:
77
src/http.c
77
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_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_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 bool _expose_new_picture(struct http_server_t *server);
|
||||||
static void _expose_blank_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) {
|
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->exposed = exposed;
|
||||||
run->refresh_interval.tv_sec = 0;
|
run->refresh_interval.tv_sec = 0;
|
||||||
run->refresh_interval.tv_usec = 30000; // ~30 refreshes per second
|
run->refresh_interval.tv_usec = 30000; // ~30 refreshes per second
|
||||||
|
run->drop_same_frames_blank = 10;
|
||||||
|
|
||||||
A_CALLOC(server, 1);
|
A_CALLOC(server, 1);
|
||||||
server->host = "localhost";
|
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) {
|
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);
|
LOG_DEBUG("Binding HTTP to [%s]:%d ...", server->host, server->port);
|
||||||
evhttp_set_timeout(server->run->http, server->timeout);
|
evhttp_set_timeout(server->run->http, server->timeout);
|
||||||
|
|
||||||
if (evhttp_bind_socket(server->run->http, server->host, server->port) != 0) {
|
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)
|
LOG_PERROR("Can't listen HTTP on [%s]:%d", server->host, server->port)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_INFO("Listening HTTP on [%s]:%d", server->host, server->port);
|
LOG_INFO("Listening HTTP on [%s]:%d", server->host, server->port);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -229,6 +234,7 @@ static void _http_callback_stream(struct evhttp_request *request, void *v_server
|
|||||||
client->server = server;
|
client->server = server;
|
||||||
client->request = request;
|
client->request = request;
|
||||||
client->need_initial = true;
|
client->need_initial = true;
|
||||||
|
client->need_first_frame = true;
|
||||||
|
|
||||||
if (server->run->stream_clients == NULL) {
|
if (server->run->stream_clients == NULL) {
|
||||||
server->run->stream_clients = client;
|
server->run->stream_clients = client;
|
||||||
@@ -321,23 +327,26 @@ static void _http_callback_stream_error(UNUSED struct bufferevent *buf_event, UN
|
|||||||
free(client);
|
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 stream_client_t *client;
|
||||||
struct evhttp_connection *conn;
|
struct evhttp_connection *conn;
|
||||||
struct bufferevent *buf_event;
|
struct bufferevent *buf_event;
|
||||||
|
|
||||||
for (client = server->run->stream_clients; client != NULL; client = client->next) {
|
for (client = server->run->stream_clients; client != NULL; client = client->next) {
|
||||||
conn = evhttp_request_get_connection(client->request);
|
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);
|
buf_event = evhttp_connection_get_bufferevent(conn);
|
||||||
bufferevent_setcb(buf_event, NULL, _http_callback_stream_write, _http_callback_stream_error, (void *)client);
|
bufferevent_setcb(buf_event, NULL, _http_callback_stream_write, _http_callback_stream_error, (void *)client);
|
||||||
bufferevent_enable(buf_event, EV_READ|EV_WRITE);
|
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) {
|
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;
|
struct http_server_t *server = (struct http_server_t *)v_server;
|
||||||
|
bool updated = false;
|
||||||
|
bool queue_send = false;
|
||||||
|
|
||||||
#define LOCK_STREAM \
|
#define LOCK_STREAM \
|
||||||
A_PTHREAD_M_LOCK(&server->run->stream->mutex);
|
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 ...");
|
LOG_DEBUG("Refreshing HTTP exposed ...");
|
||||||
LOCK_STREAM;
|
LOCK_STREAM;
|
||||||
if (server->run->stream->picture.size > 0) { // If online
|
if (server->run->stream->picture.size > 0) { // If online
|
||||||
_expose_new_picture(server);
|
updated = _expose_new_picture(server);
|
||||||
UNLOCK_STREAM;
|
UNLOCK_STREAM;
|
||||||
} else {
|
} else {
|
||||||
UNLOCK_STREAM;
|
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) {
|
} else if (!server->run->exposed->online) {
|
||||||
LOG_DEBUG("Refreshing HTTP exposed (BLANK) ...");
|
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 LOCK_STREAM
|
||||||
# undef UNLOCK_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) {
|
if (server->run->exposed->picture.allocated < server->run->stream->picture.allocated) {
|
||||||
A_REALLOC(server->run->exposed->picture.data, 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;
|
server->run->exposed->picture.allocated = server->run->stream->picture.allocated;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(
|
memcpy(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)
|
# undef MEM_STREAM_TO_EXPOSED
|
||||||
);
|
|
||||||
|
|
||||||
server->run->exposed->picture.size = server->run->stream->picture.size;
|
server->run->exposed->picture.size = server->run->stream->picture.size;
|
||||||
server->run->exposed->width = server->run->stream->width;
|
server->run->exposed->width = server->run->stream->width;
|
||||||
server->run->exposed->height = server->run->stream->height;
|
server->run->exposed->height = server->run->stream->height;
|
||||||
server->run->exposed->fps = server->run->stream->fps;
|
|
||||||
server->run->exposed->online = true;
|
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->online || server->run->exposed->picture.size == 0) {
|
||||||
if (server->run->exposed->picture.allocated < BLANK_JPG_SIZE) {
|
if (server->run->exposed->picture.allocated < BLANK_JPG_SIZE) {
|
||||||
A_REALLOC(server->run->exposed->picture.data, 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->height = BLANK_JPG_HEIGHT;
|
||||||
server->run->exposed->fps = 0;
|
server->run->exposed->fps = 0;
|
||||||
server->run->exposed->online = false;
|
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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ struct stream_client_t {
|
|||||||
struct http_server_t *server;
|
struct http_server_t *server;
|
||||||
struct evhttp_request *request;
|
struct evhttp_request *request;
|
||||||
bool need_initial;
|
bool need_initial;
|
||||||
|
bool need_first_frame;
|
||||||
|
|
||||||
struct stream_client_t *prev;
|
struct stream_client_t *prev;
|
||||||
struct stream_client_t *next;
|
struct stream_client_t *next;
|
||||||
@@ -44,6 +45,7 @@ struct exposed_t {
|
|||||||
unsigned height;
|
unsigned height;
|
||||||
unsigned fps;
|
unsigned fps;
|
||||||
bool online;
|
bool online;
|
||||||
|
unsigned dropped;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct http_server_runtime_t {
|
struct http_server_runtime_t {
|
||||||
@@ -55,11 +57,13 @@ struct http_server_runtime_t {
|
|||||||
struct timeval refresh_interval;
|
struct timeval refresh_interval;
|
||||||
|
|
||||||
struct stream_client_t *stream_clients;
|
struct stream_client_t *stream_clients;
|
||||||
|
unsigned drop_same_frames_blank;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct http_server_t {
|
struct http_server_t {
|
||||||
char *host;
|
char *host;
|
||||||
unsigned port;
|
unsigned port;
|
||||||
|
unsigned drop_same_frames;
|
||||||
unsigned fake_width;
|
unsigned fake_width;
|
||||||
unsigned fake_height;
|
unsigned fake_height;
|
||||||
unsigned timeout;
|
unsigned timeout;
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
#include "http.h"
|
#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[] = {
|
static const struct option _long_opts[] = {
|
||||||
{"device", required_argument, NULL, 'd'},
|
{"device", required_argument, NULL, 'd'},
|
||||||
{"width", required_argument, NULL, 'x'},
|
{"width", required_argument, NULL, 'x'},
|
||||||
@@ -62,6 +62,7 @@ static const struct option _long_opts[] = {
|
|||||||
|
|
||||||
{"host", required_argument, NULL, 's'},
|
{"host", required_argument, NULL, 's'},
|
||||||
{"port", required_argument, NULL, 'p'},
|
{"port", required_argument, NULL, 'p'},
|
||||||
|
{"drop-same-frames", required_argument, NULL, 'r'},
|
||||||
{"fake-width", required_argument, NULL, 2000},
|
{"fake-width", required_argument, NULL, 2000},
|
||||||
{"fake-height", required_argument, NULL, 2001},
|
{"fake-height", required_argument, NULL, 2001},
|
||||||
{"server-timeout", required_argument, NULL, 2002},
|
{"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("--------------------\n");
|
||||||
printf(" --host <address> -- Listen on Hostname or IP. Default: %s\n\n", server->host);
|
printf(" --host <address> -- Listen on Hostname or IP. Default: %s\n\n", server->host);
|
||||||
printf(" --port <N> -- Bind to this TCP port. Default: %d\n\n", server->port);
|
printf(" --port <N> -- Bind to this TCP port. Default: %d\n\n", server->port);
|
||||||
|
printf(" --drop-same-frames <N> -- 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 <N> -- Override image width for /ping. Default: disabled\n\n");
|
printf(" --fake-width <N> -- Override image width for /ping. Default: disabled\n\n");
|
||||||
printf(" --fake-height <N> -- Override image height for /ping. Default: disabled.\n\n");
|
printf(" --fake-height <N> -- Override image height for /ping. Default: disabled.\n\n");
|
||||||
printf(" --server-timeout <seconds> -- Timeout for client connections. Default: %d\n\n", server->timeout);
|
printf(" --server-timeout <seconds> -- 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 's': server->host = optarg; break;
|
||||||
case 'p': OPT_UNSIGNED(server->port, "--port", 1, 65535);
|
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 2000: OPT_UNSIGNED(server->fake_width, "--fake-width", 0, 1920);
|
||||||
case 2001: OPT_UNSIGNED(server->fake_height, "--fake-height", 0, 1200);
|
case 2001: OPT_UNSIGNED(server->fake_height, "--fake-height", 0, 1200);
|
||||||
case 2002: OPT_UNSIGNED(server->timeout, "--server-timeout", 1, 60);
|
case 2002: OPT_UNSIGNED(server->timeout, "--server-timeout", 1, 60);
|
||||||
|
|||||||
Reference in New Issue
Block a user