From 620a0ec847def56138d1cbd1b3fafe1ae139617a Mon Sep 17 00:00:00 2001 From: Maxim Devaev Date: Mon, 26 May 2025 22:51:17 +0300 Subject: [PATCH] Fixed #290: improved blank diagnostics --- README.md | 2 +- src/libs/capture.c | 36 ++++++++++++++++++++---- src/libs/drm/drm.c | 2 +- src/libs/errors.h | 5 +++- src/libs/tc358743.c | 11 -------- src/libs/tc358743.h | 19 +++++++++++++ src/ustreamer/blank.c | 2 +- src/ustreamer/http/server.c | 2 +- src/ustreamer/stream.c | 56 +++++++++++++++++++++++++++++-------- 9 files changed, 102 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index c3d7d41..883dfcc 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ |----------|---------------|-------------------| | Multithreaded JPEG encoding | ✔ | ✘ | | Hardware image encoding
on Raspberry Pi | ✔ | ✘ | -| Behavior when the device
is disconnected while streaming | ✔ Shows a black screen
with ```NO SIGNAL``` on it
until reconnected | ✘ Stops the streaming 1 | +| Behavior when the device
is disconnected while streaming | ✔ Shows a black screen
with ```NO LIVE VIDEO``` on it
until reconnected | ✘ Stops the streaming 1 | | [DV-timings](https://linuxtv.org/downloads/v4l-dvb-apis-new/userspace-api/v4l/dv-timings.html) support -
the ability to change resolution
on the fly by source signal | ✔ | ☹ Partially yes 1 | | Option to skip frames when streaming
static images by HTTP to save the traffic | ✔ 2 | ✘ | | Streaming via UNIX domain socket | ✔ | ✘ | diff --git a/src/libs/capture.c b/src/libs/capture.c index 52eb76d..4496aef 100644 --- a/src/libs/capture.c +++ b/src/libs/capture.c @@ -48,6 +48,7 @@ #include "threading.h" #include "frame.h" #include "xioctl.h" +#include "tc358743.h" static const struct { @@ -200,11 +201,11 @@ int us_capture_open(us_capture_s *cap) { } } _LOG_DEBUG("Probing DV-timings or QuerySTD ..."); - if (_capture_open_dv_timings(cap, false) < 0) { - US_ONCE_FOR(run->open_error_once, __LINE__, { - _LOG_ERROR("No signal from source"); - }); - goto error_no_signal; + switch (_capture_open_dv_timings(cap, false)) { + case 0: break; + case US_ERROR_NO_SIGNAL: goto error_no_signal; + case US_ERROR_NO_SYNC: goto error_no_sync; + default: goto error; } } @@ -222,6 +223,15 @@ int us_capture_open(us_capture_s *cap) { if (_capture_open_format(cap, true) < 0) { goto error; } + if (cap->dv_timings && cap->persistent) { + struct v4l2_control ctl = {.id = TC358743_CID_LANES_ENOUGH}; + if (!us_xioctl(run->fd, VIDIOC_G_CTRL, &ctl)) { + if (!ctl.value) { + _LOG_ERROR("Not enough lanes, hardware can't handle this signal"); + goto error_no_lanes; + } + } + } _capture_open_hw_fps(cap); _capture_open_jpeg_quality(cap); if (_capture_open_io_method(cap) < 0) { @@ -259,8 +269,18 @@ error_no_cable: return US_ERROR_NO_CABLE; error_no_signal: + US_ONCE_FOR(run->open_error_once, __LINE__, { _LOG_ERROR("No signal from source"); }); us_capture_close(cap); - return US_ERROR_NO_DATA; + return US_ERROR_NO_SIGNAL; + +error_no_sync: + US_ONCE_FOR(run->open_error_once, __LINE__, { _LOG_ERROR("No sync on signal"); }); + us_capture_close(cap); + return US_ERROR_NO_SYNC; + +error_no_lanes: + us_capture_close(cap); + return US_ERROR_NO_LANES; error: run->open_error_once = 0; @@ -630,6 +650,10 @@ static int _capture_open_dv_timings(us_capture_s *cap, bool apply) { // TC358743 errors here (see in the kernel: drivers/media/i2c/tc358743.c): // - ENOLINK: No valid signal (SYS_STATUS & MASK_S_TMDS) // - ENOLCK: No sync on signal (SYS_STATUS & MASK_S_SYNC) + switch (errno) { + case ENOLINK: return US_ERROR_NO_SIGNAL; + case ENOLCK: return US_ERROR_NO_SYNC; + } dv_errno = errno; goto querystd; } else if (!apply) { diff --git a/src/libs/drm/drm.c b/src/libs/drm/drm.c index 3f52415..ea05ee8 100644 --- a/src/libs/drm/drm.c +++ b/src/libs/drm/drm.c @@ -378,7 +378,7 @@ int us_drm_expose_stub(us_drm_s *drm, us_drm_stub_e stub, const us_capture_s *ca DRAW_MSG("=== PiKVM ===\n \n< UNSUPPORTED CAPTURE FORMAT >"); break; case US_DRM_STUB_NO_SIGNAL: - DRAW_MSG("=== PiKVM ===\n \n< NO SIGNAL >"); + DRAW_MSG("=== PiKVM ===\n \n< NO LIVE VIDEO >"); break; case US_DRM_STUB_BUSY: DRAW_MSG("=== PiKVM ===\n \n< ONLINE IS ACTIVE >"); diff --git a/src/libs/errors.h b/src/libs/errors.h index 6ba405f..f393e6c 100644 --- a/src/libs/errors.h +++ b/src/libs/errors.h @@ -25,4 +25,7 @@ #define US_ERROR_COMMON -1 #define US_ERROR_NO_DEVICE -2 #define US_ERROR_NO_CABLE -3 -#define US_ERROR_NO_DATA -4 +#define US_ERROR_NO_SIGNAL -4 +#define US_ERROR_NO_SYNC -5 +#define US_ERROR_NO_LANES -6 +#define US_ERROR_NO_DATA -7 diff --git a/src/libs/tc358743.c b/src/libs/tc358743.c index 3adccb8..5098a99 100644 --- a/src/libs/tc358743.c +++ b/src/libs/tc358743.c @@ -33,17 +33,6 @@ #include "xioctl.h" -#ifndef V4L2_CID_USER_TC358743_BASE -# define V4L2_CID_USER_TC358743_BASE (V4L2_CID_USER_BASE + 0x1080) -#endif -#ifndef TC358743_CID_AUDIO_PRESENT -# define TC358743_CID_AUDIO_PRESENT (V4L2_CID_USER_TC358743_BASE + 1) -#endif -#ifndef TC358743_CID_AUDIO_SAMPLING_RATE -# define TC358743_CID_AUDIO_SAMPLING_RATE (V4L2_CID_USER_TC358743_BASE + 0) -#endif - - int us_tc358743_xioctl_get_audio_hz(int fd, uint *audio_hz) { *audio_hz = 0; diff --git a/src/libs/tc358743.h b/src/libs/tc358743.h index 49f3efd..590544b 100644 --- a/src/libs/tc358743.h +++ b/src/libs/tc358743.h @@ -22,7 +22,26 @@ #pragma once +#include + #include "types.h" +#ifndef V4L2_CID_USER_TC358743_BASE +# define V4L2_CID_USER_TC358743_BASE (V4L2_CID_USER_BASE + 0x1080) +#endif + +#ifndef TC358743_CID_AUDIO_SAMPLING_RATE +# define TC358743_CID_AUDIO_SAMPLING_RATE (V4L2_CID_USER_TC358743_BASE + 0) +#endif + +#ifndef TC358743_CID_AUDIO_PRESENT +# define TC358743_CID_AUDIO_PRESENT (V4L2_CID_USER_TC358743_BASE + 1) +#endif + +#ifndef TC358743_CID_LANES_ENOUGH +# define TC358743_CID_LANES_ENOUGH (V4L2_CID_USER_TC358743_BASE + 2) +#endif + + int us_tc358743_xioctl_get_audio_hz(int fd, uint *audio_hz); diff --git a/src/ustreamer/blank.c b/src/ustreamer/blank.c index c1ec758..09b1b00 100644 --- a/src/ustreamer/blank.c +++ b/src/ustreamer/blank.c @@ -36,7 +36,7 @@ us_blank_s *us_blank_init(void) { blank->ft = us_frametext_init(); blank->raw = blank->ft->frame; blank->jpeg = us_frame_init(); - us_blank_draw(blank, "< NO SIGNAL >", 640, 480); + us_blank_draw(blank, "< NO LIVE VIDEO >", 640, 480); return blank; } diff --git a/src/ustreamer/http/server.c b/src/ustreamer/http/server.c index 229969e..d26deed 100644 --- a/src/ustreamer/http/server.c +++ b/src/ustreamer/http/server.c @@ -880,7 +880,7 @@ static void _http_send_snapshot(us_server_s *server) { if (!captured_meta.online) { if (blank == NULL) { blank = us_blank_init(); - us_blank_draw(blank, "< NO SIGNAL >", captured_meta.width, captured_meta.height); + us_blank_draw(blank, "< NO LIVE VIDEO >", captured_meta.width, captured_meta.height); } frame = blank->jpeg; } diff --git a/src/ustreamer/stream.c b/src/ustreamer/stream.c index 704ef8b..700939e 100644 --- a/src/ustreamer/stream.c +++ b/src/ustreamer/stream.c @@ -129,7 +129,7 @@ us_stream_s *us_stream_init(us_capture_s *cap, us_encoder_s *enc) { void us_stream_update_blank(us_stream_s *stream, const us_capture_s *cap) { us_stream_runtime_s *const run = stream->run; - us_blank_draw(run->blank, "< NO SIGNAL >", cap->width, cap->height); + us_blank_draw(run->blank, "< NO LIVE VIDEO >", cap->width, cap->height); us_fpsi_frame_to_meta(run->blank->raw, &run->notify_meta); // Initial "unchanged" meta _stream_update_captured_fpsi(stream, run->blank->raw, false); } @@ -529,7 +529,7 @@ static int _stream_init_loop(us_stream_s *stream) { int once = 0; while (!atomic_load(&stream->run->stop)) { - char *blank_reason = "< NO SIGNAL >"; + const char *blank_reason = "< NO LIVE VIDEO >"; # ifdef WITH_GPIO us_gpio_set_stream_online(false); @@ -556,24 +556,58 @@ static int _stream_init_loop(us_stream_s *stream) { switch (us_capture_open(stream->cap)) { case 0: break; case US_ERROR_NO_DEVICE: - blank_reason = "< NO CAPTURE DEVICE >"; - goto known_error; + blank_reason = ( + "< NO CAPTURE DEVICE >\n \n" + " Possible reasons: \n \n" + " - Device unplugged \n \n" + " - Bad config \n \n" + " - Malfunction " + ); + goto silent_error; case US_ERROR_NO_CABLE: - blank_reason = "< NO VIDEO SOURCE >"; - goto known_error; - case US_ERROR_NO_DATA: - goto known_error; + blank_reason = ( + "< NO VIDEO SOURCE >\n \n" + " Possible reasons: \n \n" + " - Source is off \n \n" + " - Cable problems " + ); + goto silent_error; + case US_ERROR_NO_SIGNAL: + blank_reason = ( + "< NO SIGNAL DETECTED >\n \n" + " Possible reasons: \n \n" + " - Video suspended \n \n" + " - Cable problems " + ); + goto silent_error; + case US_ERROR_NO_SYNC: + blank_reason = ( + "< NO SYNC WITH SIGNAL >\n \n" + " Possible reasons: \n \n" + " - Source is crazy \n \n" + " - Cable problems " + ); + goto silent_error; + case US_ERROR_NO_LANES: + blank_reason = ( + "< UNSUPPORTED SIGNAL TIMINGS >\n \n" + " Possible reasons: \n \n" + " - Too high frequency \n \n" + " - Source ignores EDID \n \n" + " - Invalid EDID " + ); + goto verbose_error; default: - goto unknown_error; + goto verbose_error; } us_encoder_open(stream->enc, stream->cap); return 0; - known_error: + silent_error: US_ONCE({ US_LOG_INFO("Waiting for the capture device ..."); }); goto offline_and_retry; - unknown_error: + verbose_error: once = 0; goto offline_and_retry;