mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-11-04 20:14:24 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11fd828ab0 | ||
|
|
5e2542420f | ||
|
|
b8a224a5fa | ||
|
|
296639a2ac | ||
|
|
7cde878f02 |
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
@@ -1,4 +1,4 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
patreon: pikvm
|
||||
custom: https://paypal.me/pikvm
|
||||
ko_fi: pikvm
|
||||
|
||||
@@ -112,6 +112,9 @@ 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 \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.
|
||||
.TP
|
||||
|
||||
@@ -120,8 +120,8 @@ char *us_simplify_request_path(const char *str) {
|
||||
#ifdef TEST_HTTP_PATH
|
||||
|
||||
int test_simplify_request_path(const char *sample, const char *expected) {
|
||||
char *result = us_simplify_request_path(sample);
|
||||
int retval = -!!strcmp(result, expected);
|
||||
const char *result = us_simplify_request_path(sample);
|
||||
const int retval = -(!!strcmp(result, expected));
|
||||
|
||||
printf("Testing '%s' -> '%s' ... ", sample, expected);
|
||||
if (retval == 0) {
|
||||
|
||||
@@ -89,7 +89,8 @@ enum _US_OPT_VALUES {
|
||||
|
||||
// Longs only
|
||||
|
||||
_O_DEVICE_TIMEOUT = 10000,
|
||||
_O_OPEN_ON_DEMAND = 10000,
|
||||
_O_DEVICE_TIMEOUT,
|
||||
_O_DEVICE_ERROR_DELAY,
|
||||
_O_FORMAT_SWAP_RGB,
|
||||
_O_M2M_DEVICE,
|
||||
@@ -190,6 +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", 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},
|
||||
@@ -453,6 +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_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);
|
||||
@@ -749,6 +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 <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);
|
||||
|
||||
@@ -206,7 +206,23 @@ void us_stream_loop(us_stream_s *stream) {
|
||||
US_LOG_INFO("Capturing ...");
|
||||
|
||||
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 > 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 + stream->open_on_demand < now_ts) {
|
||||
US_LOG_INFO("No stream or sink clients found in last %u seconds, closing the device ...",
|
||||
stream->open_on_demand);
|
||||
goto close;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
us_capture_hwbuf_s *hw;
|
||||
switch (us_capture_hwbuf_grab(cap, &hw)) {
|
||||
case 0 ... INT_MAX: break; // Grabbed buffer number
|
||||
@@ -578,7 +594,18 @@ 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);
|
||||
|
||||
_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 ..."); });
|
||||
blank_reason = (
|
||||
"< WAITING FOR CLIENTS >\n \n"
|
||||
" The camera is idle "
|
||||
);
|
||||
goto silent_error;
|
||||
}
|
||||
|
||||
stream->cap->dma_export = (
|
||||
stream->enc->type == US_ENCODER_TYPE_M2M_VIDEO
|
||||
@@ -598,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"
|
||||
@@ -606,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"
|
||||
@@ -614,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"
|
||||
@@ -622,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"
|
||||
@@ -638,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:
|
||||
|
||||
@@ -83,6 +83,7 @@ typedef struct {
|
||||
uint desired_fps;
|
||||
bool notify_parent;
|
||||
bool slowdown;
|
||||
uint open_on_demand;
|
||||
uint error_delay;
|
||||
bool exit_on_device_error;
|
||||
uint exit_on_no_clients;
|
||||
|
||||
Reference in New Issue
Block a user