new option --exit-on-no-clients

This commit is contained in:
Maxim Devaev
2022-02-14 02:44:58 +03:00
parent 3a8f035014
commit 616a3eb6a6
7 changed files with 64 additions and 11 deletions

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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 <sec> ──── 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 <str> ── Set process name prefix which will be displayed in the process list");
SAY(" like '<str>: ustreamer --blah-blah-blah'. Default: disabled.\n");

View File

@@ -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;

View File

@@ -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);