add timeout argument for --open-on-demand

This commit is contained in:
Maxim Devaev
2026-11-04 18:34:36 +02:00
parent b8a224a5fa
commit 5e2542420f
4 changed files with 44 additions and 58 deletions

View File

@@ -112,8 +112,8 @@ It doesn't do anything. Still here for compatibility.
.BR \-l ", " \-\-slowdown
Slowdown capturing to 1 FPS or less when no stream or sink clients are connected. Useful to reduce CPU consumption. Default: disabled.
.TP
.BR \-\-open\-on\-demand
Don't open the capture device until at least one stream or sink client is connected, and close it again 3 seconds after the last one disconnects. Clients see a blank screen while the device is closed. Default: disabled.
.BR \-\-open\-on\-demand \fIsec
Don't open the capture device until at least one stream or sink client is connected, and close it again after a specified timeout since the last one disconnects. Clients see a blank screen while the device is closed. Default: disabled.
.TP
.BR \-\-device\-timeout\ \fIsec
Timeout for device querying. Default: 1.

View File

@@ -191,7 +191,7 @@ static const struct option _LONG_OPTS[] = {
{"blank", required_argument, NULL, _O_BLANK},
{"last-as-blank", required_argument, NULL, _O_LAST_AS_BLANK},
{"slowdown", no_argument, NULL, _O_SLOWDOWN},
{"open-on-demand", no_argument, NULL, _O_OPEN_ON_DEMAND},
{"open-on-demand", required_argument, NULL, _O_OPEN_ON_DEMAND},
{"device-timeout", required_argument, NULL, _O_DEVICE_TIMEOUT},
{"device-error-delay", required_argument, NULL, _O_DEVICE_ERROR_DELAY},
{"m2m-device", required_argument, NULL, _O_M2M_DEVICE},
@@ -455,7 +455,7 @@ int us_options_parse(
case _O_BLANK: break; // Deprecated
case _O_LAST_AS_BLANK: break; // Deprecated
case _O_SLOWDOWN: OPT_SET(stream->slowdown, true);
case _O_OPEN_ON_DEMAND: OPT_SET(stream->open_on_demand, true);
case _O_OPEN_ON_DEMAND: OPT_NUMBER("--open-on-demand", stream->open_on_demand, 1, 60, 0);
case _O_DEVICE_TIMEOUT: OPT_NUMBER("--device-timeout", cap->timeout, 1, 60, 0);
case _O_DEVICE_ERROR_DELAY: OPT_NUMBER("--device-error-delay", stream->error_delay, 1, 60, 0);
case _O_M2M_DEVICE: OPT_SET(enc->m2m_path, optarg);
@@ -752,10 +752,10 @@ static void _help(
SAY(" -K|--last-as-blank <sec> ──────────── It doesn't do anything. Still here for compatibility.\n");
SAY(" -l|--slowdown ─────────────────────── Slowdown capturing to 1 FPS or less when no stream or sink clients");
SAY(" are connected. Useful to reduce CPU consumption. Default: disabled.\n");
SAY(" --open-on-demand ──────────────────── Don't open the capture device until at least one stream");
SAY(" or sink client is connected, and close it again %u seconds", US_ON_DEMAND_CLOSE_DELAY);
SAY(" after the last one disconnects. Clients see a blank");
SAY(" screen while the device is closed. Default: disabled.\n");
SAY(" --open-on-demand <sec> ────────────── Don't open the capture device until at least one stream");
SAY(" or sink client is connected, and close it again");
SAY(" after a specified timeout since the last one disconnects.");
SAY(" Clients see a blank screen while the device is closed. Default: disabled.\n");
SAY(" --device-timeout <sec> ────────────── Timeout for device querying. Default: %u.\n", cap->timeout);
SAY(" --device-error-delay <sec> ────────── Delay before trying to connect to the device again");
SAY(" after an error (timeout for example). Default: %u.\n", stream->error_delay);

View File

@@ -91,7 +91,6 @@ static void _stream_update_captured_fpsi(us_stream_s *stream, const us_frame_s *
#ifdef WITH_V4P
static void _stream_drm_ensure_no_signal(us_stream_s *stream);
#endif
static void _stream_publish_blank(us_stream_s *stream, const char *reason);
static void _stream_expose_jpeg(us_stream_s *stream, const us_frame_s *frame);
static void _stream_expose_raw(us_stream_s *stream, const us_frame_s *frame);
static void _stream_encode_expose_h264(us_stream_s *stream, const us_frame_s *frame, bool force_key);
@@ -209,16 +208,16 @@ void us_stream_loop(us_stream_s *stream) {
int has_any_clients_prev = -1;
ldf no_clients_since_ts = 0;
while (!atomic_load(&run->stop) && !atomic_load(&threads_stop)) {
if (stream->open_on_demand) {
if (stream->open_on_demand > 0) {
if (_stream_has_any_clients_cached(stream)) {
no_clients_since_ts = 0;
} else {
const ldf now_ts = us_get_now_monotonic();
if (no_clients_since_ts == 0) {
no_clients_since_ts = now_ts;
} else if (no_clients_since_ts + US_ON_DEMAND_CLOSE_DELAY < now_ts) {
} else if (no_clients_since_ts + stream->open_on_demand < now_ts) {
US_LOG_INFO("No stream or sink clients found in last %u seconds, closing the device ...",
US_ON_DEMAND_CLOSE_DELAY);
stream->open_on_demand);
goto close;
}
}
@@ -595,23 +594,17 @@ static int _stream_init_loop(us_stream_s *stream) {
UPDATE_SINK(stream->h264_sink);
# undef UPDATE_SINK
_stream_check_suicide(stream, _stream_has_any_clients_cached(stream));
const bool has_any_clients = _stream_has_any_clients_cached(stream);
if (stream->open_on_demand && !_stream_has_any_clients_cached(stream)) {
_stream_check_suicide(stream, has_any_clients);
if (stream->open_on_demand > 0 && !has_any_clients) {
US_ONCE({ US_LOG_INFO("Waiting for the stream or sink clients to open the device ..."); });
_stream_publish_blank(stream, (
"< CAMERA IS IDLE >\n \n"
" The device will \n \n"
" be opened on the \n \n"
" first client "
));
for (uint i = 0; i < 10; ++i) {
if (atomic_load(&run->stop) || _stream_has_any_clients_cached(stream)) {
break;
}
usleep(100 * 1000);
}
continue;
blank_reason = (
"< WAITING FOR CLIENTS >\n \n"
" The camera is idle "
);
goto silent_error;
}
stream->cap->dma_export = (
@@ -632,7 +625,7 @@ static int _stream_init_loop(us_stream_s *stream) {
" - Bad config \n \n"
" - Malfunction "
);
goto silent_error;
goto silent_capture_error;
case US_ERROR_NO_CABLE:
blank_reason = (
"< NO VIDEO SOURCE >\n \n"
@@ -640,7 +633,7 @@ static int _stream_init_loop(us_stream_s *stream) {
" - Source is off \n \n"
" - Cable problems "
);
goto silent_error;
goto silent_capture_error;
case US_ERROR_NO_SIGNAL:
blank_reason = (
"< NO SIGNAL DETECTED >\n \n"
@@ -648,7 +641,7 @@ static int _stream_init_loop(us_stream_s *stream) {
" - Video suspended \n \n"
" - Cable problems "
);
goto silent_error;
goto silent_capture_error;
case US_ERROR_NO_SYNC:
blank_reason = (
"< NO SYNC WITH SIGNAL >\n \n"
@@ -656,7 +649,7 @@ static int _stream_init_loop(us_stream_s *stream) {
" - Source is crazy \n \n"
" - Cable problems "
);
goto silent_error;
goto silent_capture_error;
case US_ERROR_NO_LANES:
blank_reason = (
"< UNSUPPORTED SIGNAL TIMINGS >\n \n"
@@ -672,10 +665,12 @@ static int _stream_init_loop(us_stream_s *stream) {
us_encoder_open(stream->enc, stream->cap);
return 0;
silent_error:
silent_capture_error:
if (!stream->exit_on_device_error) {
US_ONCE({ US_LOG_INFO("Waiting for the capture device ..."); });
}
silent_error:
goto offline_and_retry;
verbose_error:
@@ -693,7 +688,22 @@ static int _stream_init_loop(us_stream_s *stream) {
}
if (i % 10 == 0) {
// Каждую секунду повторяем blank
_stream_publish_blank(stream, blank_reason);
uint width = stream->cap->run->width;
uint height = stream->cap->run->height;
if (width == 0 || height == 0) {
width = stream->cap->width;
height = stream->cap->height;
}
us_blank_draw(run->blank, blank_reason, width, height);
_stream_update_captured_fpsi(stream, run->blank->raw, false);
_stream_expose_jpeg(stream, run->blank->jpeg);
_stream_expose_raw(stream, run->blank->raw);
_stream_encode_expose_h264(stream, run->blank->raw, true);
# ifdef WITH_V4P
_stream_drm_ensure_no_signal(stream);
# endif
}
usleep(100 * 1000);
}
@@ -739,27 +749,6 @@ close:
}
#endif
static void _stream_publish_blank(us_stream_s *stream, const char *reason) {
us_stream_runtime_s *const run = stream->run;
uint width = stream->cap->run->width;
uint height = stream->cap->run->height;
if (width == 0 || height == 0) {
width = stream->cap->width;
height = stream->cap->height;
}
us_blank_draw(run->blank, reason, width, height);
_stream_update_captured_fpsi(stream, run->blank->raw, false);
_stream_expose_jpeg(stream, run->blank->jpeg);
_stream_expose_raw(stream, run->blank->raw);
_stream_encode_expose_h264(stream, run->blank->raw, true);
# ifdef WITH_V4P
_stream_drm_ensure_no_signal(stream);
# endif
}
static void _stream_expose_jpeg(us_stream_s *stream, const us_frame_s *frame) {
us_stream_runtime_s *const run = stream->run;

View File

@@ -44,9 +44,6 @@
#include "m2m.h"
#define US_ON_DEMAND_CLOSE_DELAY 3u
typedef struct {
# ifdef WITH_V4P
atomic_bool drm_live;
@@ -86,7 +83,7 @@ typedef struct {
uint desired_fps;
bool notify_parent;
bool slowdown;
bool open_on_demand;
uint open_on_demand;
uint error_delay;
bool exit_on_device_error;
uint exit_on_no_clients;