mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-02-28 12:46:32 +00:00
new option --exit-on-no-clients
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user