From 616a3eb6a6c27228361a76ea899b84648923ae9e Mon Sep 17 00:00:00 2001 From: Maxim Devaev Date: Mon, 14 Feb 2022 02:44:58 +0300 Subject: [PATCH] new option --exit-on-no-clients --- man/ustreamer.1 | 3 +++ src/libs/process.h | 8 ++++++++ src/ustreamer/http/server.c | 31 ++++++++++++++++++++++++++++++- src/ustreamer/http/server.h | 4 ++++ src/ustreamer/options.c | 5 +++++ src/ustreamer/stream.c | 22 ++++++++++++---------- src/ustreamer/stream.h | 2 ++ 7 files changed, 64 insertions(+), 11 deletions(-) diff --git a/man/ustreamer.1 b/man/ustreamer.1 index 7e70197..f2f44c0 100644 --- a/man/ustreamer.1 +++ b/man/ustreamer.1 @@ -249,6 +249,9 @@ Intarval between keyframes. Default: 30. .BR \-\-exit\-on\-parent\-death Exit the program if the parent process is dead. Required \fBHAS_PDEATHSIG\fR feature. Default: disabled. .TP +.BR \-\-exit\-on\-no\-clients \fIsec +Exit the program if there have been no stream or sink clients or any HTTP requests in the last N seconds. Default: 0 (disabled). +.TP .BR \-\-process\-name\-prefix\ \fIstr Set process name prefix which will be displayed in the process list like '\fIstr: ustreamer \-\-blah\-blah\-blah'\fR. Required \fBWITH_SETPROCTITLE\fR feature. Default: disabled. .TP diff --git a/src/libs/process.h b/src/libs/process.h index 8dc13f0..73078f5 100644 --- a/src/libs/process.h +++ b/src/libs/process.h @@ -137,3 +137,11 @@ INLINE void process_notify_parent(void) { LOG_PERROR("Can't send SIGUSR2 to the parent process %d", parent); } } + +INLINE void process_suicide(void) { + pid_t pid = getpid(); + + if (kill(pid, SIGTERM) < 0) { + LOG_PERROR("Can't send SIGTERM to own pid %d", pid); + } +} diff --git a/src/ustreamer/http/server.c b/src/ustreamer/http/server.c index 382dbc5..32f0b43 100644 --- a/src/ustreamer/http/server.c +++ b/src/ustreamer/http/server.c @@ -36,6 +36,7 @@ static void _http_callback_stream(struct evhttp_request *request, void *v_server static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_ctx); static void _http_callback_stream_error(struct bufferevent *buf_event, short what, void *v_ctx); +static void _http_request_watcher(int fd, short event, void *v_server); static void _http_refresher(int fd, short event, void *v_server); static void _http_queue_send_stream(server_s *server, bool stream_updated, bool frame_updated); @@ -85,6 +86,11 @@ void server_destroy(server_s *server) { event_free(RUN(refresher)); } + if (RUN(request_watcher)) { + event_del(RUN(request_watcher)); + event_free(RUN(request_watcher)); + } + evhttp_free(RUN(http)); if (RUN(ext_fd)) { close(RUN(ext_fd)); @@ -128,6 +134,14 @@ int server_listen(server_s *server) { EX(notify_last_width) = EX(frame->width); EX(notify_last_height) = EX(frame->height); + if (server->exit_on_no_clients > 0) { + RUN(last_request_ts) = get_now_monotonic(); + struct timeval interval = {0}; + interval.tv_usec = 100000; + assert((RUN(request_watcher) = event_new(RUN(base), -1, EV_PERSIST, _http_request_watcher, server))); + assert(!event_add(RUN(request_watcher), &interval)); + } + { struct timeval interval = {0}; if (STREAM(dev->desired_fps) > 0) { @@ -135,7 +149,6 @@ int server_listen(server_s *server) { } else { interval.tv_usec = 16000; // ~60fps } - assert((RUN(refresher) = event_new(RUN(base), -1, EV_PERSIST, _http_refresher, server))); assert(!event_add(RUN(refresher), &interval)); } @@ -203,6 +216,8 @@ void server_loop_break(server_s *server) { assert(!evhttp_add_header(evhttp_request_get_output_headers(request), _key, _value)) static int _http_preprocess_request(struct evhttp_request *request, server_s *server) { + RUN(last_request_ts) = get_now_monotonic(); + if (RUN(auth_token)) { const char *token = evhttp_find_header(evhttp_request_get_input_headers(request), "Authorization"); @@ -779,6 +794,20 @@ static void _http_queue_send_stream(server_s *server, bool stream_updated, bool } } +static void _http_request_watcher(UNUSED int fd, UNUSED short what, void *v_server) { + server_s *server = (server_s *)v_server; + const long double now = get_now_monotonic(); + + if (stream_has_clients(RUN(stream))) { + RUN(last_request_ts) = now; + } else if (RUN(last_request_ts) + server->exit_on_no_clients < now) { + LOG_INFO("HTTP: No requests or HTTP/sink clients found in last %u seconds, exiting ...", + server->exit_on_no_clients); + process_suicide(); + RUN(last_request_ts) = now; + } +} + static void _http_refresher(UNUSED int fd, UNUSED short what, void *v_server) { server_s *server = (server_s *)v_server; bool stream_updated = false; diff --git a/src/ustreamer/http/server.h b/src/ustreamer/http/server.h index f86b12c..ff37abc 100644 --- a/src/ustreamer/http/server.h +++ b/src/ustreamer/http/server.h @@ -119,6 +119,9 @@ typedef struct { char *auth_token; + struct event *request_watcher; + long double last_request_ts; + struct event *refresher; stream_s *stream; exposed_s *exposed; @@ -152,6 +155,7 @@ typedef struct server_sx { unsigned fake_height; bool notify_parent; + unsigned exit_on_no_clients; server_runtime_s *run; } server_s; diff --git a/src/ustreamer/options.c b/src/ustreamer/options.c index 2749c57..7df0079 100644 --- a/src/ustreamer/options.c +++ b/src/ustreamer/options.c @@ -112,6 +112,7 @@ enum _OPT_VALUES { # ifdef HAS_PDEATHSIG _O_EXIT_ON_PARENT_DEATH, # endif + _O_EXIT_ON_NO_CLIENTS, # ifdef WITH_SETPROCTITLE _O_PROCESS_NAME_PREFIX, # endif @@ -209,6 +210,7 @@ static const struct option _LONG_OPTS[] = { # ifdef HAS_PDEATHSIG {"exit-on-parent-death", no_argument, NULL, _O_EXIT_ON_PARENT_DEATH}, # endif + {"exit-on-no-clients", required_argument, NULL, _O_EXIT_ON_NO_CLIENTS}, # ifdef WITH_SETPROCTITLE {"process-name-prefix", required_argument, NULL, _O_PROCESS_NAME_PREFIX}, # endif @@ -467,6 +469,7 @@ int options_parse(options_s *options, device_s *dev, encoder_s *enc, stream_s *s }; break; # endif + case _O_EXIT_ON_NO_CLIENTS: OPT_NUMBER("--exit-on-no-clients", server->exit_on_no_clients, 0, 86400, 0); # ifdef WITH_SETPROCTITLE case _O_PROCESS_NAME_PREFIX: OPT_SET(process_name_prefix, optarg); # endif @@ -715,6 +718,8 @@ static void _help(FILE *fp, device_s *dev, encoder_s *enc, stream_s *stream, ser # ifdef HAS_PDEATHSIG SAY(" --exit-on-parent-death ─────── Exit the program if the parent process is dead. Default: disabled.\n"); # endif + SAY(" --exit-on-no-clients ──── Exit the program if there have been no stream or sink clients"); + SAY(" or any HTTP requests in the last N seconds. Default: 0 (disabled)\n"); # ifdef WITH_SETPROCTITLE SAY(" --process-name-prefix ── Set process name prefix which will be displayed in the process list"); SAY(" like ': ustreamer --blah-blah-blah'. Default: disabled.\n"); diff --git a/src/ustreamer/stream.c b/src/ustreamer/stream.c index 6a9c4ab..2a0136b 100644 --- a/src/ustreamer/stream.c +++ b/src/ustreamer/stream.c @@ -131,16 +131,7 @@ void stream_loop(stream_s *stream) { # endif if (stream->slowdown) { unsigned slc = 0; - while ( - slc < 10 - && !atomic_load(&RUN(stop)) - && !atomic_load(&RUN(video->has_clients)) - // has_clients синков НЕ обновляются в реальном времени - && (stream->sink == NULL || !atomic_load(&stream->sink->has_clients)) -# ifdef WITH_OMX - && (RUN(h264) == NULL || /*RUN(h264->sink) == NULL ||*/ !atomic_load(&RUN(h264->sink->has_clients))) -# endif - ) { + for (; slc < 10 && !atomic_load(&RUN(stop)) && !stream_has_clients(stream); ++slc) { usleep(100000); ++slc; } @@ -252,6 +243,17 @@ void stream_loop_break(stream_s *stream) { atomic_store(&RUN(stop), true); } +bool stream_has_clients(stream_s *stream) { + return ( + atomic_load(&RUN(video->has_clients)) + // has_clients синков НЕ обновляются в реальном времени + || (stream->sink != NULL && atomic_load(&stream->sink->has_clients)) +# ifdef WITH_OMX + || (RUN(h264) != NULL && /*RUN(h264->sink) == NULL ||*/ atomic_load(&RUN(h264->sink->has_clients))) +# endif + ); +} + static workers_pool_s *_stream_init_loop(stream_s *stream) { workers_pool_s *pool = NULL; diff --git a/src/ustreamer/stream.h b/src/ustreamer/stream.h index a09841b..58ec503 100644 --- a/src/ustreamer/stream.h +++ b/src/ustreamer/stream.h @@ -97,3 +97,5 @@ void stream_destroy(stream_s *stream); void stream_loop(stream_s *stream); void stream_loop_break(stream_s *stream); + +bool stream_has_clients(stream_s *stream);