Fixed #290: improved blank diagnostics

This commit is contained in:
Maxim Devaev
2025-05-26 22:51:17 +03:00
parent 7a1d4816ed
commit 620a0ec847
9 changed files with 102 additions and 33 deletions

View File

@@ -11,7 +11,7 @@
|----------|---------------|-------------------| |----------|---------------|-------------------|
| Multithreaded JPEG encoding | ✔ | ✘ | | Multithreaded JPEG encoding | ✔ | ✘ |
| Hardware image encoding<br>on Raspberry Pi | ✔ | ✘ | | Hardware image encoding<br>on Raspberry Pi | ✔ | ✘ |
| Behavior when the device<br>is disconnected while streaming | ✔ Shows a black screen<br>with ```NO SIGNAL``` on it<br>until reconnected | ✘ Stops the streaming <sup>1</sup> | | Behavior when the device<br>is disconnected while streaming | ✔ Shows a black screen<br>with ```NO LIVE VIDEO``` on it<br>until reconnected | ✘ Stops the streaming <sup>1</sup> |
| [DV-timings](https://linuxtv.org/downloads/v4l-dvb-apis-new/userspace-api/v4l/dv-timings.html) support -<br>the ability to change resolution<br>on the fly by source signal | ✔ | ☹ Partially yes <sup>1</sup> | | [DV-timings](https://linuxtv.org/downloads/v4l-dvb-apis-new/userspace-api/v4l/dv-timings.html) support -<br>the ability to change resolution<br>on the fly by source signal | ✔ | ☹ Partially yes <sup>1</sup> |
| Option to skip frames when streaming<br>static images by HTTP to save the traffic | ✔ <sup>2</sup> | ✘ | | Option to skip frames when streaming<br>static images by HTTP to save the traffic | ✔ <sup>2</sup> | ✘ |
| Streaming via UNIX domain socket | ✔ | ✘ | | Streaming via UNIX domain socket | ✔ | ✘ |

View File

@@ -48,6 +48,7 @@
#include "threading.h" #include "threading.h"
#include "frame.h" #include "frame.h"
#include "xioctl.h" #include "xioctl.h"
#include "tc358743.h"
static const struct { static const struct {
@@ -200,11 +201,11 @@ int us_capture_open(us_capture_s *cap) {
} }
} }
_LOG_DEBUG("Probing DV-timings or QuerySTD ..."); _LOG_DEBUG("Probing DV-timings or QuerySTD ...");
if (_capture_open_dv_timings(cap, false) < 0) { switch (_capture_open_dv_timings(cap, false)) {
US_ONCE_FOR(run->open_error_once, __LINE__, { case 0: break;
_LOG_ERROR("No signal from source"); case US_ERROR_NO_SIGNAL: goto error_no_signal;
}); case US_ERROR_NO_SYNC: goto error_no_sync;
goto error_no_signal; default: goto error;
} }
} }
@@ -222,6 +223,15 @@ int us_capture_open(us_capture_s *cap) {
if (_capture_open_format(cap, true) < 0) { if (_capture_open_format(cap, true) < 0) {
goto error; 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_hw_fps(cap);
_capture_open_jpeg_quality(cap); _capture_open_jpeg_quality(cap);
if (_capture_open_io_method(cap) < 0) { if (_capture_open_io_method(cap) < 0) {
@@ -259,8 +269,18 @@ error_no_cable:
return US_ERROR_NO_CABLE; return US_ERROR_NO_CABLE;
error_no_signal: error_no_signal:
US_ONCE_FOR(run->open_error_once, __LINE__, { _LOG_ERROR("No signal from source"); });
us_capture_close(cap); 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: error:
run->open_error_once = 0; 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): // TC358743 errors here (see in the kernel: drivers/media/i2c/tc358743.c):
// - ENOLINK: No valid signal (SYS_STATUS & MASK_S_TMDS) // - ENOLINK: No valid signal (SYS_STATUS & MASK_S_TMDS)
// - ENOLCK: No sync on signal (SYS_STATUS & MASK_S_SYNC) // - 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; dv_errno = errno;
goto querystd; goto querystd;
} else if (!apply) { } else if (!apply) {

View File

@@ -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 >"); DRAW_MSG("=== PiKVM ===\n \n< UNSUPPORTED CAPTURE FORMAT >");
break; break;
case US_DRM_STUB_NO_SIGNAL: case US_DRM_STUB_NO_SIGNAL:
DRAW_MSG("=== PiKVM ===\n \n< NO SIGNAL >"); DRAW_MSG("=== PiKVM ===\n \n< NO LIVE VIDEO >");
break; break;
case US_DRM_STUB_BUSY: case US_DRM_STUB_BUSY:
DRAW_MSG("=== PiKVM ===\n \n< ONLINE IS ACTIVE >"); DRAW_MSG("=== PiKVM ===\n \n< ONLINE IS ACTIVE >");

View File

@@ -25,4 +25,7 @@
#define US_ERROR_COMMON -1 #define US_ERROR_COMMON -1
#define US_ERROR_NO_DEVICE -2 #define US_ERROR_NO_DEVICE -2
#define US_ERROR_NO_CABLE -3 #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

View File

@@ -33,17 +33,6 @@
#include "xioctl.h" #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) { int us_tc358743_xioctl_get_audio_hz(int fd, uint *audio_hz) {
*audio_hz = 0; *audio_hz = 0;

View File

@@ -22,7 +22,26 @@
#pragma once #pragma once
#include <linux/v4l2-controls.h>
#include "types.h" #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); int us_tc358743_xioctl_get_audio_hz(int fd, uint *audio_hz);

View File

@@ -36,7 +36,7 @@ us_blank_s *us_blank_init(void) {
blank->ft = us_frametext_init(); blank->ft = us_frametext_init();
blank->raw = blank->ft->frame; blank->raw = blank->ft->frame;
blank->jpeg = us_frame_init(); blank->jpeg = us_frame_init();
us_blank_draw(blank, "< NO SIGNAL >", 640, 480); us_blank_draw(blank, "< NO LIVE VIDEO >", 640, 480);
return blank; return blank;
} }

View File

@@ -880,7 +880,7 @@ static void _http_send_snapshot(us_server_s *server) {
if (!captured_meta.online) { if (!captured_meta.online) {
if (blank == NULL) { if (blank == NULL) {
blank = us_blank_init(); 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; frame = blank->jpeg;
} }

View File

@@ -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) { void us_stream_update_blank(us_stream_s *stream, const us_capture_s *cap) {
us_stream_runtime_s *const run = stream->run; 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 us_fpsi_frame_to_meta(run->blank->raw, &run->notify_meta); // Initial "unchanged" meta
_stream_update_captured_fpsi(stream, run->blank->raw, false); _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; int once = 0;
while (!atomic_load(&stream->run->stop)) { while (!atomic_load(&stream->run->stop)) {
char *blank_reason = "< NO SIGNAL >"; const char *blank_reason = "< NO LIVE VIDEO >";
# ifdef WITH_GPIO # ifdef WITH_GPIO
us_gpio_set_stream_online(false); 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)) { switch (us_capture_open(stream->cap)) {
case 0: break; case 0: break;
case US_ERROR_NO_DEVICE: case US_ERROR_NO_DEVICE:
blank_reason = "< NO CAPTURE DEVICE >"; blank_reason = (
goto known_error; "< 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: case US_ERROR_NO_CABLE:
blank_reason = "< NO VIDEO SOURCE >"; blank_reason = (
goto known_error; "< NO VIDEO SOURCE >\n \n"
case US_ERROR_NO_DATA: " Possible reasons: \n \n"
goto known_error; " - 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: default:
goto unknown_error; goto verbose_error;
} }
us_encoder_open(stream->enc, stream->cap); us_encoder_open(stream->enc, stream->cap);
return 0; return 0;
known_error: silent_error:
US_ONCE({ US_LOG_INFO("Waiting for the capture device ..."); }); US_ONCE({ US_LOG_INFO("Waiting for the capture device ..."); });
goto offline_and_retry; goto offline_and_retry;
unknown_error: verbose_error:
once = 0; once = 0;
goto offline_and_retry; goto offline_and_retry;