mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-02-18 02:55:46 +00:00
improved persistent logic
This commit is contained in:
parent
06eda04180
commit
b556dfb897
@ -85,13 +85,13 @@ Without arguments, ```ustreamer``` will try to open ```/dev/video0``` with 640x4
|
||||
|
||||
:exclamation: Please note that since µStreamer v2.0 cross-domain requests were disabled by default for [security reasons](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS). To enable the old behavior, use the option `--allow-origin=\*`.
|
||||
|
||||
The recommended way of running µStreamer with [Auvidea B101](https://www.raspberrypi.org/forums/viewtopic.php?f=38&t=120702&start=400#p1339178) on Raspberry Pi:
|
||||
The recommended way of running µStreamer with [TC358743-based capture device](https://www.raspberrypi.org/forums/viewtopic.php?f=38&t=120702&start=400#p1339178) on Raspberry Pi:
|
||||
```
|
||||
$ ./ustreamer \
|
||||
--format=uyvy \ # Device input format
|
||||
--encoder=m2m-image \ # Hardware encoding on V4L2 M2M driver
|
||||
--workers=3 \ # Workers number
|
||||
--persistent \ # Don't re-initialize device on timeout (for example when HDMI cable was disconnected)
|
||||
--persistent \ # Suppress repetitive signal source errors (for example when HDMI cable was disconnected)
|
||||
--dv-timings \ # Use DV-timings
|
||||
--drop-same-frames=30 # Save the traffic
|
||||
```
|
||||
|
||||
@ -17,7 +17,7 @@ Without arguments, \fBustreamer\fR will try to open \fB/dev/video0\fR with 640x4
|
||||
|
||||
Please note that since µStreamer v2\.0 cross\-domain requests were disabled by default for security reasons\. To enable the old behavior, use the option \fB\-\-allow\-origin=\e*\fR\.
|
||||
|
||||
For example, the recommended way of running µStreamer with Auvidea B101 on a Raspberry Pi is:
|
||||
For example, the recommended way of running µStreamer with TC358743-based capture device on a Raspberry Pi is:
|
||||
|
||||
\fBustreamer \e\fR
|
||||
.RS
|
||||
@ -27,7 +27,7 @@ For example, the recommended way of running µStreamer with Auvidea B101 on a Ra
|
||||
.nf
|
||||
\fB\-\-workers=3 \e\fR # Maximum workers for V4L2 encoder
|
||||
.nf
|
||||
\fB\-\-persistent \e\fR # Don\'t re\-initialize device on timeout (for example when HDMI cable was disconnected)
|
||||
\fB\-\-persistent \e\fR # Suppress repetitive signal source errors (for example when HDMI cable was disconnected)
|
||||
.nf
|
||||
\fB\-\-dv\-timings \e\fR # Use DV\-timings
|
||||
.nf
|
||||
@ -69,7 +69,7 @@ Desired FPS. Default: maximum possible.
|
||||
Drop frames smaller then this limit. Useful if the device produces small\-sized garbage frames. Default: 128 bytes.
|
||||
.TP
|
||||
.BR \-n ", " \-\-persistent
|
||||
Don't re\-initialize device on timeout. Default: disabled.
|
||||
Suppress repetitive signal source errors. Default: disabled.
|
||||
.TP
|
||||
.BR \-t ", " \-\-dv\-timings
|
||||
Enable DV-timings querying and events processing to automatic resolution change. Default: disabled.
|
||||
|
||||
@ -86,7 +86,7 @@ static int _device_consume_event(us_device_s *dev);
|
||||
static void _v4l2_buffer_copy(const struct v4l2_buffer *src, struct v4l2_buffer *dest);
|
||||
static bool _device_is_buffer_valid(us_device_s *dev, const struct v4l2_buffer *buf, const u8 *data);
|
||||
static int _device_open_check_cap(us_device_s *dev);
|
||||
static int _device_open_dv_timings(us_device_s *dev);
|
||||
static int _device_open_dv_timings(us_device_s *dev, bool apply);
|
||||
static int _device_open_format(us_device_s *dev, bool first);
|
||||
static void _device_open_hw_fps(us_device_s *dev);
|
||||
static void _device_open_jpeg_quality(us_device_s *dev);
|
||||
@ -174,20 +174,40 @@ int us_device_parse_io_method(const char *str) {
|
||||
int us_device_open(us_device_s *dev) {
|
||||
us_device_runtime_s *const run = dev->run;
|
||||
|
||||
if (access(dev->path, R_OK | W_OK) < 0) {
|
||||
if (run->open_error_reported != -errno) {
|
||||
run->open_error_reported = -errno; // Don't confuse it with __LINE__
|
||||
US_LOG_PERROR("No access to capture device");
|
||||
}
|
||||
goto tmp_error;
|
||||
}
|
||||
|
||||
_D_LOG_DEBUG("Opening capture device ...");
|
||||
if ((run->fd = open(dev->path, O_RDWR|O_NONBLOCK)) < 0) {
|
||||
if ((run->fd = open(dev->path, O_RDWR | O_NONBLOCK)) < 0) {
|
||||
_D_LOG_PERROR("Can't capture open device");
|
||||
goto error;
|
||||
}
|
||||
_D_LOG_DEBUG("Capture device fd=%d opened", run->fd);
|
||||
|
||||
if (dev->dv_timings && dev->persistent) {
|
||||
_D_LOG_DEBUG("Probing DV-timings or QuerySTD ...");
|
||||
if (_device_open_dv_timings(dev, false) < 0) {
|
||||
const int line = __LINE__;
|
||||
if (run->open_error_reported != line) {
|
||||
run->open_error_reported = line;
|
||||
_D_LOG_ERROR("No signal from source");
|
||||
}
|
||||
goto tmp_error;
|
||||
}
|
||||
}
|
||||
|
||||
if (_device_open_check_cap(dev) < 0) {
|
||||
goto error;
|
||||
}
|
||||
if (_device_apply_resolution(dev, dev->width, dev->height, dev->run->hz)) {
|
||||
goto error;
|
||||
}
|
||||
if (dev->dv_timings && _device_open_dv_timings(dev) < 0) {
|
||||
if (dev->dv_timings && _device_open_dv_timings(dev, true) < 0) {
|
||||
goto error;
|
||||
}
|
||||
if (_device_open_format(dev, true) < 0) {
|
||||
@ -216,10 +236,17 @@ int us_device_open(us_device_s *dev) {
|
||||
goto error;
|
||||
}
|
||||
run->streamon = true;
|
||||
|
||||
run->open_error_reported = 0;
|
||||
_D_LOG_INFO("Capturing started");
|
||||
return 0;
|
||||
|
||||
tmp_error:
|
||||
us_device_close(dev);
|
||||
return -2;
|
||||
|
||||
error:
|
||||
run->open_error_reported = 0;
|
||||
us_device_close(dev);
|
||||
return -1;
|
||||
}
|
||||
@ -268,7 +295,6 @@ void us_device_close(us_device_s *dev) {
|
||||
}
|
||||
|
||||
US_CLOSE_FD(run->fd);
|
||||
run->persistent_timeout_reported = false;
|
||||
|
||||
if (say) {
|
||||
_D_LOG_INFO("Capturing stopped");
|
||||
@ -279,17 +305,13 @@ int us_device_grab_buffer(us_device_s *dev, us_hw_buffer_s **hw) {
|
||||
// Это сложная функция, которая делает сразу много всего, чтобы получить новый фрейм.
|
||||
// - Вызывается _device_wait_buffer() с select() внутри, чтобы подождать новый фрейм
|
||||
// или эвент V4L2. Обработка эвентов более приоритетна, чем кадров.
|
||||
// - При таймауте select() в _device_wait_buffer() возвращаем -2 для персистентных
|
||||
// устройств типа TC358743, для остальных же возвращаем ошибку -1.
|
||||
// - Если есть новые фреймы, то пропустить их все, пока не закончатся и вернуть
|
||||
// самый-самый свежий, содержащий при этом валидные данные.
|
||||
// - Если таковых не нашлось, вернуть -3.
|
||||
// - Если таковых не нашлось, вернуть -2.
|
||||
// - Ошибка -1 возвращается при любых сбоях.
|
||||
|
||||
switch (_device_wait_buffer(dev)) {
|
||||
case 0: break; // New frame
|
||||
case -2: return -2; // Persistent timeout
|
||||
default: return -1; // Error
|
||||
if (_device_wait_buffer(dev) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
us_device_runtime_s *const run = dev->run;
|
||||
@ -372,7 +394,7 @@ int us_device_grab_buffer(us_device_s *dev, us_hw_buffer_s **hw) {
|
||||
if (buf_got) {
|
||||
break; // Process any latest valid frame
|
||||
} else if (broken) {
|
||||
return -3; // If we have only broken frames on this capture session
|
||||
return -2; // If we have only broken frames on this capture session
|
||||
}
|
||||
}
|
||||
_D_LOG_PERROR("Can't grab HW buffer");
|
||||
@ -452,18 +474,9 @@ int _device_wait_buffer(us_device_s *dev) {
|
||||
}
|
||||
return -1;
|
||||
} else if (selected == 0) {
|
||||
if (!dev->persistent) {
|
||||
// Если устройство не персистентное, то таймаут является ошибкой
|
||||
_D_LOG_ERROR("Device select() timeout");
|
||||
return -1;
|
||||
}
|
||||
if (!run->persistent_timeout_reported) {
|
||||
_D_LOG_ERROR("Persistent device timeout (unplugged)");
|
||||
run->persistent_timeout_reported = true;
|
||||
}
|
||||
return -2; // Таймаут, нет новых фреймов
|
||||
_D_LOG_ERROR("Device select() timeout");
|
||||
return -1;
|
||||
} else {
|
||||
run->persistent_timeout_reported = false;
|
||||
if (has_error && _device_consume_event(dev) < 0) {
|
||||
return -1; // Restart required
|
||||
}
|
||||
@ -586,13 +599,20 @@ static int _device_open_check_cap(us_device_s *dev) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _device_open_dv_timings(us_device_s *dev) {
|
||||
static int _device_open_dv_timings(us_device_s *dev, bool apply) {
|
||||
// Just probe only if @apply is false
|
||||
|
||||
const us_device_runtime_s *const run = dev->run;
|
||||
|
||||
int dv_errno = 0;
|
||||
|
||||
struct v4l2_dv_timings dv = {0};
|
||||
_D_LOG_DEBUG("Querying DV-timings ...");
|
||||
_D_LOG_DEBUG("Querying DV-timings (apply=%u) ...", apply);
|
||||
if (us_xioctl(run->fd, VIDIOC_QUERY_DV_TIMINGS, &dv) < 0) {
|
||||
dv_errno = errno; // ENOLINK if no signal
|
||||
goto querystd;
|
||||
} else if (!apply) {
|
||||
goto probe_only;
|
||||
}
|
||||
|
||||
float hz = 0;
|
||||
@ -602,11 +622,11 @@ static int _device_open_dv_timings(us_device_s *dev) {
|
||||
const uint vtot = V4L2_DV_BT_FRAME_HEIGHT(&dv.bt) / (dv.bt.interlaced ? 2 : 1);
|
||||
const uint fps = ((htot * vtot) > 0 ? ((100 * (u64)dv.bt.pixelclock)) / (htot * vtot) : 0);
|
||||
hz = (fps / 100) + (fps % 100) / 100.0;
|
||||
_D_LOG_INFO("Got new DV-timings: %ux%u%s%.02f, pixclk=%llu, vsync=%u, hsync=%u",
|
||||
_D_LOG_INFO("Detected DV-timings: %ux%u%s%.02f, pixclk=%llu, vsync=%u, hsync=%u",
|
||||
dv.bt.width, dv.bt.height, (dv.bt.interlaced ? "i" : "p"), hz,
|
||||
(ull)dv.bt.pixelclock, dv.bt.vsync, dv.bt.hsync); // See #11 about %llu
|
||||
} else {
|
||||
_D_LOG_INFO("Got new DV-timings: %ux%u, pixclk=%llu, vsync=%u, hsync=%u",
|
||||
_D_LOG_INFO("Detected DV-timings: %ux%u, pixclk=%llu, vsync=%u, hsync=%u",
|
||||
dv.bt.width, dv.bt.height,
|
||||
(ull)dv.bt.pixelclock, dv.bt.vsync, dv.bt.hsync);
|
||||
}
|
||||
@ -624,8 +644,16 @@ static int _device_open_dv_timings(us_device_s *dev) {
|
||||
querystd:
|
||||
_D_LOG_DEBUG("Failed to query DV-timings, trying QuerySTD ...");
|
||||
if (us_xioctl(run->fd, VIDIOC_QUERYSTD, &dev->standard) < 0) {
|
||||
_D_LOG_ERROR("Failed to query DV-timings and QuerySTD");
|
||||
if (apply) {
|
||||
char *std_error = us_errno_to_string(errno); // Read the errno first
|
||||
char *dv_error = us_errno_to_string(dv_errno);
|
||||
_D_LOG_ERROR("Failed to query DV-timings (%s) and QuerySTD (%s)", dv_error, std_error);
|
||||
free(dv_error);
|
||||
free(std_error);
|
||||
}
|
||||
return -1;
|
||||
} else if (!apply) {
|
||||
goto probe_only;
|
||||
}
|
||||
if (us_xioctl(run->fd, VIDIOC_S_STD, &dev->standard) < 0) {
|
||||
_D_LOG_PERROR("Can't set apply standard: %s", _standard_to_string(dev->standard));
|
||||
@ -640,6 +668,8 @@ subscribe:
|
||||
_D_LOG_PERROR("Can't subscribe to V4L2_EVENT_SOURCE_CHANGE");
|
||||
return -1;
|
||||
}
|
||||
|
||||
probe_only:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -67,7 +67,7 @@ typedef struct {
|
||||
enum v4l2_buf_type capture_type;
|
||||
bool capture_mplane;
|
||||
bool streamon;
|
||||
bool persistent_timeout_reported;
|
||||
int open_error_reported;
|
||||
} us_device_runtime_s;
|
||||
|
||||
typedef enum {
|
||||
|
||||
@ -195,8 +195,7 @@ void us_stream_loop(us_stream_s *stream) {
|
||||
us_hw_buffer_s *hw;
|
||||
const int buf_index = us_device_grab_buffer(dev, &hw);
|
||||
switch (buf_index) {
|
||||
case -3: continue; // Broken frame
|
||||
case -2: continue; // Persistent timeout
|
||||
case -2: continue; // Broken frame
|
||||
case -1: goto close; // Error
|
||||
}
|
||||
assert(buf_index >= 0);
|
||||
@ -248,6 +247,10 @@ void us_stream_loop(us_stream_s *stream) {
|
||||
|
||||
us_encoder_close(stream->enc);
|
||||
us_device_close(dev);
|
||||
|
||||
if (!atomic_load(&run->stop)) {
|
||||
US_SEP_INFO('=');
|
||||
}
|
||||
}
|
||||
|
||||
US_DELETE(run->h264, us_h264_stream_destroy);
|
||||
@ -421,7 +424,7 @@ static bool _stream_has_any_clients(us_stream_s *stream) {
|
||||
static int _stream_init_loop(us_stream_s *stream) {
|
||||
us_stream_runtime_s *const run = stream->run;
|
||||
|
||||
int access_errno = 0;
|
||||
bool waiting_reported = false;
|
||||
while (!atomic_load(&stream->run->stop)) {
|
||||
_stream_check_suicide(stream);
|
||||
|
||||
@ -445,32 +448,33 @@ static int _stream_init_loop(us_stream_s *stream) {
|
||||
|
||||
_SINK_PUT(raw_sink, run->blank->raw);
|
||||
|
||||
if (access(stream->dev->path, R_OK|W_OK) < 0) {
|
||||
if (access_errno != errno) {
|
||||
US_SEP_INFO('=');
|
||||
US_LOG_PERROR("Can't access device");
|
||||
US_LOG_INFO("Waiting for the device access ...");
|
||||
access_errno = errno;
|
||||
}
|
||||
goto sleep_and_retry;
|
||||
}
|
||||
|
||||
US_SEP_INFO('=');
|
||||
access_errno = 0;
|
||||
|
||||
stream->dev->dma_export = (
|
||||
stream->enc->type == US_ENCODER_TYPE_M2M_VIDEO
|
||||
|| stream->enc->type == US_ENCODER_TYPE_M2M_IMAGE
|
||||
|| run->h264 != NULL
|
||||
);
|
||||
if (us_device_open(stream->dev) == 0) {
|
||||
us_encoder_open(stream->enc, stream->dev);
|
||||
return 0;
|
||||
switch (us_device_open(stream->dev)) {
|
||||
case -2:
|
||||
if (!waiting_reported) {
|
||||
waiting_reported = true;
|
||||
US_LOG_INFO("Waiting for the capture device ...");
|
||||
}
|
||||
goto sleep_and_retry;
|
||||
case -1:
|
||||
waiting_reported = false;
|
||||
goto sleep_and_retry;
|
||||
default: break;
|
||||
}
|
||||
US_LOG_INFO("Sleeping %u seconds before new stream init ...", stream->error_delay);
|
||||
us_encoder_open(stream->enc, stream->dev);
|
||||
return 0;
|
||||
|
||||
sleep_and_retry:
|
||||
sleep(stream->error_delay);
|
||||
for (uint count = 0; count < stream->error_delay * 10; ++count) {
|
||||
if (atomic_load(&run->stop)) {
|
||||
break;
|
||||
}
|
||||
usleep(100 * 1000);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -203,6 +203,9 @@ static void _main_loop(void) {
|
||||
}
|
||||
|
||||
if (us_device_open(dev) < 0) {
|
||||
if (us_drm_wait_for_vsync(drm) == 0) {
|
||||
us_drm_expose(drm, US_DRM_EXPOSE_NO_SIGNAL, NULL, 0);
|
||||
}
|
||||
goto close;
|
||||
}
|
||||
|
||||
@ -219,12 +222,7 @@ static void _main_loop(void) {
|
||||
us_hw_buffer_s *hw;
|
||||
const int buf_index = us_device_grab_buffer(dev, &hw);
|
||||
switch (buf_index) {
|
||||
case -3: continue; // Broken frame
|
||||
case -2: // Persistent timeout
|
||||
if (us_drm_expose(drm, US_DRM_EXPOSE_NO_SIGNAL, NULL, 0) < 0) {
|
||||
_slowdown();
|
||||
continue;
|
||||
}
|
||||
case -2: continue; // Broken frame
|
||||
case -1: goto close; // Any error
|
||||
}
|
||||
assert(buf_index >= 0);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user