mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-11-06 04:54:13 +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
|
# These are supported funding model platforms
|
||||||
|
|
||||||
patreon: pikvm
|
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
|
.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.
|
Slowdown capturing to 1 FPS or less when no stream or sink clients are connected. Useful to reduce CPU consumption. Default: disabled.
|
||||||
.TP
|
.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
|
.BR \-\-device\-timeout\ \fIsec
|
||||||
Timeout for device querying. Default: 1.
|
Timeout for device querying. Default: 1.
|
||||||
.TP
|
.TP
|
||||||
|
|||||||
@@ -120,8 +120,8 @@ char *us_simplify_request_path(const char *str) {
|
|||||||
#ifdef TEST_HTTP_PATH
|
#ifdef TEST_HTTP_PATH
|
||||||
|
|
||||||
int test_simplify_request_path(const char *sample, const char *expected) {
|
int test_simplify_request_path(const char *sample, const char *expected) {
|
||||||
char *result = us_simplify_request_path(sample);
|
const char *result = us_simplify_request_path(sample);
|
||||||
int retval = -!!strcmp(result, expected);
|
const int retval = -(!!strcmp(result, expected));
|
||||||
|
|
||||||
printf("Testing '%s' -> '%s' ... ", sample, expected);
|
printf("Testing '%s' -> '%s' ... ", sample, expected);
|
||||||
if (retval == 0) {
|
if (retval == 0) {
|
||||||
|
|||||||
@@ -89,7 +89,8 @@ enum _US_OPT_VALUES {
|
|||||||
|
|
||||||
// Longs only
|
// Longs only
|
||||||
|
|
||||||
_O_DEVICE_TIMEOUT = 10000,
|
_O_OPEN_ON_DEMAND = 10000,
|
||||||
|
_O_DEVICE_TIMEOUT,
|
||||||
_O_DEVICE_ERROR_DELAY,
|
_O_DEVICE_ERROR_DELAY,
|
||||||
_O_FORMAT_SWAP_RGB,
|
_O_FORMAT_SWAP_RGB,
|
||||||
_O_M2M_DEVICE,
|
_O_M2M_DEVICE,
|
||||||
@@ -190,6 +191,7 @@ static const struct option _LONG_OPTS[] = {
|
|||||||
{"blank", required_argument, NULL, _O_BLANK},
|
{"blank", required_argument, NULL, _O_BLANK},
|
||||||
{"last-as-blank", required_argument, NULL, _O_LAST_AS_BLANK},
|
{"last-as-blank", required_argument, NULL, _O_LAST_AS_BLANK},
|
||||||
{"slowdown", no_argument, NULL, _O_SLOWDOWN},
|
{"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-timeout", required_argument, NULL, _O_DEVICE_TIMEOUT},
|
||||||
{"device-error-delay", required_argument, NULL, _O_DEVICE_ERROR_DELAY},
|
{"device-error-delay", required_argument, NULL, _O_DEVICE_ERROR_DELAY},
|
||||||
{"m2m-device", required_argument, NULL, _O_M2M_DEVICE},
|
{"m2m-device", required_argument, NULL, _O_M2M_DEVICE},
|
||||||
@@ -453,6 +455,7 @@ int us_options_parse(
|
|||||||
case _O_BLANK: break; // Deprecated
|
case _O_BLANK: break; // Deprecated
|
||||||
case _O_LAST_AS_BLANK: break; // Deprecated
|
case _O_LAST_AS_BLANK: break; // Deprecated
|
||||||
case _O_SLOWDOWN: OPT_SET(stream->slowdown, true);
|
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_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_DEVICE_ERROR_DELAY: OPT_NUMBER("--device-error-delay", stream->error_delay, 1, 60, 0);
|
||||||
case _O_M2M_DEVICE: OPT_SET(enc->m2m_path, optarg);
|
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(" -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(" -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(" 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-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(" --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);
|
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 ...");
|
US_LOG_INFO("Capturing ...");
|
||||||
|
|
||||||
int has_any_clients_prev = -1;
|
int has_any_clients_prev = -1;
|
||||||
|
ldf no_clients_since_ts = 0;
|
||||||
while (!atomic_load(&run->stop) && !atomic_load(&threads_stop)) {
|
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;
|
us_capture_hwbuf_s *hw;
|
||||||
switch (us_capture_hwbuf_grab(cap, &hw)) {
|
switch (us_capture_hwbuf_grab(cap, &hw)) {
|
||||||
case 0 ... INT_MAX: break; // Grabbed buffer number
|
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);
|
UPDATE_SINK(stream->h264_sink);
|
||||||
# undef UPDATE_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->cap->dma_export = (
|
||||||
stream->enc->type == US_ENCODER_TYPE_M2M_VIDEO
|
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"
|
" - Bad config \n \n"
|
||||||
" - Malfunction "
|
" - Malfunction "
|
||||||
);
|
);
|
||||||
goto silent_error;
|
goto silent_capture_error;
|
||||||
case US_ERROR_NO_CABLE:
|
case US_ERROR_NO_CABLE:
|
||||||
blank_reason = (
|
blank_reason = (
|
||||||
"< NO VIDEO SOURCE >\n \n"
|
"< NO VIDEO SOURCE >\n \n"
|
||||||
@@ -606,7 +633,7 @@ static int _stream_init_loop(us_stream_s *stream) {
|
|||||||
" - Source is off \n \n"
|
" - Source is off \n \n"
|
||||||
" - Cable problems "
|
" - Cable problems "
|
||||||
);
|
);
|
||||||
goto silent_error;
|
goto silent_capture_error;
|
||||||
case US_ERROR_NO_SIGNAL:
|
case US_ERROR_NO_SIGNAL:
|
||||||
blank_reason = (
|
blank_reason = (
|
||||||
"< NO SIGNAL DETECTED >\n \n"
|
"< NO SIGNAL DETECTED >\n \n"
|
||||||
@@ -614,7 +641,7 @@ static int _stream_init_loop(us_stream_s *stream) {
|
|||||||
" - Video suspended \n \n"
|
" - Video suspended \n \n"
|
||||||
" - Cable problems "
|
" - Cable problems "
|
||||||
);
|
);
|
||||||
goto silent_error;
|
goto silent_capture_error;
|
||||||
case US_ERROR_NO_SYNC:
|
case US_ERROR_NO_SYNC:
|
||||||
blank_reason = (
|
blank_reason = (
|
||||||
"< NO SYNC WITH SIGNAL >\n \n"
|
"< NO SYNC WITH SIGNAL >\n \n"
|
||||||
@@ -622,7 +649,7 @@ static int _stream_init_loop(us_stream_s *stream) {
|
|||||||
" - Source is crazy \n \n"
|
" - Source is crazy \n \n"
|
||||||
" - Cable problems "
|
" - Cable problems "
|
||||||
);
|
);
|
||||||
goto silent_error;
|
goto silent_capture_error;
|
||||||
case US_ERROR_NO_LANES:
|
case US_ERROR_NO_LANES:
|
||||||
blank_reason = (
|
blank_reason = (
|
||||||
"< UNSUPPORTED SIGNAL TIMINGS >\n \n"
|
"< 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);
|
us_encoder_open(stream->enc, stream->cap);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
silent_error:
|
silent_capture_error:
|
||||||
if (!stream->exit_on_device_error) {
|
if (!stream->exit_on_device_error) {
|
||||||
US_ONCE({ US_LOG_INFO("Waiting for the capture device ..."); });
|
US_ONCE({ US_LOG_INFO("Waiting for the capture device ..."); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
silent_error:
|
||||||
goto offline_and_retry;
|
goto offline_and_retry;
|
||||||
|
|
||||||
verbose_error:
|
verbose_error:
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ typedef struct {
|
|||||||
uint desired_fps;
|
uint desired_fps;
|
||||||
bool notify_parent;
|
bool notify_parent;
|
||||||
bool slowdown;
|
bool slowdown;
|
||||||
|
uint open_on_demand;
|
||||||
uint error_delay;
|
uint error_delay;
|
||||||
bool exit_on_device_error;
|
bool exit_on_device_error;
|
||||||
uint exit_on_no_clients;
|
uint exit_on_no_clients;
|
||||||
|
|||||||
Reference in New Issue
Block a user