mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-11-07 21:44:15 +00:00
Merge pull request #343 from lagnat/open-on-demand
Add --open-on-demand: open the capture device only when someone is watching
This commit is contained in:
@@ -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
|
||||
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.
|
||||
.TP
|
||||
.BR \-\-device\-timeout\ \fIsec
|
||||
Timeout for device querying. Default: 1.
|
||||
.TP
|
||||
|
||||
@@ -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", no_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_SET(stream->open_on_demand, true);
|
||||
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 ──────────────────── 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(" --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);
|
||||
|
||||
@@ -91,6 +91,7 @@ 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);
|
||||
@@ -206,7 +207,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) {
|
||||
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) {
|
||||
US_LOG_INFO("No stream or sink clients found in last %u seconds, closing the device ...",
|
||||
US_ON_DEMAND_CLOSE_DELAY);
|
||||
goto close;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
us_capture_hwbuf_s *hw;
|
||||
switch (us_capture_hwbuf_grab(cap, &hw)) {
|
||||
case 0 ... INT_MAX: break; // Grabbed buffer number
|
||||
@@ -580,6 +597,23 @@ static int _stream_init_loop(us_stream_s *stream) {
|
||||
|
||||
_stream_check_suicide(stream, _stream_has_any_clients_cached(stream));
|
||||
|
||||
if (stream->open_on_demand && !_stream_has_any_clients_cached(stream)) {
|
||||
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;
|
||||
}
|
||||
|
||||
stream->cap->dma_export = (
|
||||
stream->enc->type == US_ENCODER_TYPE_M2M_VIDEO
|
||||
|| stream->enc->type == US_ENCODER_TYPE_M2M_IMAGE
|
||||
@@ -659,22 +693,7 @@ static int _stream_init_loop(us_stream_s *stream) {
|
||||
}
|
||||
if (i % 10 == 0) {
|
||||
// Каждую секунду повторяем blank
|
||||
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
|
||||
_stream_publish_blank(stream, blank_reason);
|
||||
}
|
||||
usleep(100 * 1000);
|
||||
}
|
||||
@@ -720,6 +739,27 @@ 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;
|
||||
|
||||
|
||||
@@ -44,6 +44,9 @@
|
||||
#include "m2m.h"
|
||||
|
||||
|
||||
#define US_ON_DEMAND_CLOSE_DELAY 3u
|
||||
|
||||
|
||||
typedef struct {
|
||||
# ifdef WITH_V4P
|
||||
atomic_bool drm_live;
|
||||
@@ -83,6 +86,7 @@ typedef struct {
|
||||
uint desired_fps;
|
||||
bool notify_parent;
|
||||
bool slowdown;
|
||||
bool open_on_demand;
|
||||
uint error_delay;
|
||||
bool exit_on_device_error;
|
||||
uint exit_on_no_clients;
|
||||
|
||||
Reference in New Issue
Block a user