mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-03-16 20:43:42 +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
|
.BR \-\-exit\-on\-parent\-death
|
||||||
Exit the program if the parent process is dead. Required \fBHAS_PDEATHSIG\fR feature. Default: disabled.
|
Exit the program if the parent process is dead. Required \fBHAS_PDEATHSIG\fR feature. Default: disabled.
|
||||||
.TP
|
.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
|
.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.
|
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
|
.TP
|
||||||
|
|||||||
@@ -137,3 +137,11 @@ INLINE void process_notify_parent(void) {
|
|||||||
LOG_PERROR("Can't send SIGUSR2 to the parent process %d", parent);
|
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_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_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_refresher(int fd, short event, void *v_server);
|
||||||
static void _http_queue_send_stream(server_s *server, bool stream_updated, bool frame_updated);
|
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));
|
event_free(RUN(refresher));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (RUN(request_watcher)) {
|
||||||
|
event_del(RUN(request_watcher));
|
||||||
|
event_free(RUN(request_watcher));
|
||||||
|
}
|
||||||
|
|
||||||
evhttp_free(RUN(http));
|
evhttp_free(RUN(http));
|
||||||
if (RUN(ext_fd)) {
|
if (RUN(ext_fd)) {
|
||||||
close(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_width) = EX(frame->width);
|
||||||
EX(notify_last_height) = EX(frame->height);
|
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};
|
struct timeval interval = {0};
|
||||||
if (STREAM(dev->desired_fps) > 0) {
|
if (STREAM(dev->desired_fps) > 0) {
|
||||||
@@ -135,7 +149,6 @@ int server_listen(server_s *server) {
|
|||||||
} else {
|
} else {
|
||||||
interval.tv_usec = 16000; // ~60fps
|
interval.tv_usec = 16000; // ~60fps
|
||||||
}
|
}
|
||||||
|
|
||||||
assert((RUN(refresher) = event_new(RUN(base), -1, EV_PERSIST, _http_refresher, server)));
|
assert((RUN(refresher) = event_new(RUN(base), -1, EV_PERSIST, _http_refresher, server)));
|
||||||
assert(!event_add(RUN(refresher), &interval));
|
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))
|
assert(!evhttp_add_header(evhttp_request_get_output_headers(request), _key, _value))
|
||||||
|
|
||||||
static int _http_preprocess_request(struct evhttp_request *request, server_s *server) {
|
static int _http_preprocess_request(struct evhttp_request *request, server_s *server) {
|
||||||
|
RUN(last_request_ts) = get_now_monotonic();
|
||||||
|
|
||||||
if (RUN(auth_token)) {
|
if (RUN(auth_token)) {
|
||||||
const char *token = evhttp_find_header(evhttp_request_get_input_headers(request), "Authorization");
|
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) {
|
static void _http_refresher(UNUSED int fd, UNUSED short what, void *v_server) {
|
||||||
server_s *server = (server_s *)v_server;
|
server_s *server = (server_s *)v_server;
|
||||||
bool stream_updated = false;
|
bool stream_updated = false;
|
||||||
|
|||||||
@@ -119,6 +119,9 @@ typedef struct {
|
|||||||
|
|
||||||
char *auth_token;
|
char *auth_token;
|
||||||
|
|
||||||
|
struct event *request_watcher;
|
||||||
|
long double last_request_ts;
|
||||||
|
|
||||||
struct event *refresher;
|
struct event *refresher;
|
||||||
stream_s *stream;
|
stream_s *stream;
|
||||||
exposed_s *exposed;
|
exposed_s *exposed;
|
||||||
@@ -152,6 +155,7 @@ typedef struct server_sx {
|
|||||||
unsigned fake_height;
|
unsigned fake_height;
|
||||||
|
|
||||||
bool notify_parent;
|
bool notify_parent;
|
||||||
|
unsigned exit_on_no_clients;
|
||||||
|
|
||||||
server_runtime_s *run;
|
server_runtime_s *run;
|
||||||
} server_s;
|
} server_s;
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ enum _OPT_VALUES {
|
|||||||
# ifdef HAS_PDEATHSIG
|
# ifdef HAS_PDEATHSIG
|
||||||
_O_EXIT_ON_PARENT_DEATH,
|
_O_EXIT_ON_PARENT_DEATH,
|
||||||
# endif
|
# endif
|
||||||
|
_O_EXIT_ON_NO_CLIENTS,
|
||||||
# ifdef WITH_SETPROCTITLE
|
# ifdef WITH_SETPROCTITLE
|
||||||
_O_PROCESS_NAME_PREFIX,
|
_O_PROCESS_NAME_PREFIX,
|
||||||
# endif
|
# endif
|
||||||
@@ -209,6 +210,7 @@ static const struct option _LONG_OPTS[] = {
|
|||||||
# ifdef HAS_PDEATHSIG
|
# ifdef HAS_PDEATHSIG
|
||||||
{"exit-on-parent-death", no_argument, NULL, _O_EXIT_ON_PARENT_DEATH},
|
{"exit-on-parent-death", no_argument, NULL, _O_EXIT_ON_PARENT_DEATH},
|
||||||
# endif
|
# endif
|
||||||
|
{"exit-on-no-clients", required_argument, NULL, _O_EXIT_ON_NO_CLIENTS},
|
||||||
# ifdef WITH_SETPROCTITLE
|
# ifdef WITH_SETPROCTITLE
|
||||||
{"process-name-prefix", required_argument, NULL, _O_PROCESS_NAME_PREFIX},
|
{"process-name-prefix", required_argument, NULL, _O_PROCESS_NAME_PREFIX},
|
||||||
# endif
|
# endif
|
||||||
@@ -467,6 +469,7 @@ int options_parse(options_s *options, device_s *dev, encoder_s *enc, stream_s *s
|
|||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
# endif
|
# endif
|
||||||
|
case _O_EXIT_ON_NO_CLIENTS: OPT_NUMBER("--exit-on-no-clients", server->exit_on_no_clients, 0, 86400, 0);
|
||||||
# ifdef WITH_SETPROCTITLE
|
# ifdef WITH_SETPROCTITLE
|
||||||
case _O_PROCESS_NAME_PREFIX: OPT_SET(process_name_prefix, optarg);
|
case _O_PROCESS_NAME_PREFIX: OPT_SET(process_name_prefix, optarg);
|
||||||
# endif
|
# endif
|
||||||
@@ -715,6 +718,8 @@ static void _help(FILE *fp, device_s *dev, encoder_s *enc, stream_s *stream, ser
|
|||||||
# ifdef HAS_PDEATHSIG
|
# ifdef HAS_PDEATHSIG
|
||||||
SAY(" --exit-on-parent-death ─────── Exit the program if the parent process is dead. Default: disabled.\n");
|
SAY(" --exit-on-parent-death ─────── Exit the program if the parent process is dead. Default: disabled.\n");
|
||||||
# endif
|
# 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
|
# ifdef WITH_SETPROCTITLE
|
||||||
SAY(" --process-name-prefix <str> ── Set process name prefix which will be displayed in the process list");
|
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");
|
SAY(" like '<str>: ustreamer --blah-blah-blah'. Default: disabled.\n");
|
||||||
|
|||||||
@@ -131,16 +131,7 @@ void stream_loop(stream_s *stream) {
|
|||||||
# endif
|
# endif
|
||||||
if (stream->slowdown) {
|
if (stream->slowdown) {
|
||||||
unsigned slc = 0;
|
unsigned slc = 0;
|
||||||
while (
|
for (; slc < 10 && !atomic_load(&RUN(stop)) && !stream_has_clients(stream); ++slc) {
|
||||||
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
|
|
||||||
) {
|
|
||||||
usleep(100000);
|
usleep(100000);
|
||||||
++slc;
|
++slc;
|
||||||
}
|
}
|
||||||
@@ -252,6 +243,17 @@ void stream_loop_break(stream_s *stream) {
|
|||||||
atomic_store(&RUN(stop), true);
|
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) {
|
static workers_pool_s *_stream_init_loop(stream_s *stream) {
|
||||||
|
|
||||||
workers_pool_s *pool = NULL;
|
workers_pool_s *pool = NULL;
|
||||||
|
|||||||
@@ -97,3 +97,5 @@ void stream_destroy(stream_s *stream);
|
|||||||
|
|
||||||
void stream_loop(stream_s *stream);
|
void stream_loop(stream_s *stream);
|
||||||
void stream_loop_break(stream_s *stream);
|
void stream_loop_break(stream_s *stream);
|
||||||
|
|
||||||
|
bool stream_has_clients(stream_s *stream);
|
||||||
|
|||||||
Reference in New Issue
Block a user