mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-01-31 18:11:54 +08:00
fix: 修复 /dev/videoX 设备非视频设备 v4l2 不返回内容,导致主程序挂起的问题
This commit is contained in:
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::sync::mpsc;
|
||||||
|
use std::time::Duration;
|
||||||
use tracing::{debug, info, warn};
|
use tracing::{debug, info, warn};
|
||||||
use v4l::capability::Flags;
|
use v4l::capability::Flags;
|
||||||
use v4l::prelude::*;
|
use v4l::prelude::*;
|
||||||
@@ -12,6 +14,8 @@ use v4l::FourCC;
|
|||||||
use super::format::{PixelFormat, Resolution};
|
use super::format::{PixelFormat, Resolution};
|
||||||
use crate::error::{AppError, Result};
|
use crate::error::{AppError, Result};
|
||||||
|
|
||||||
|
const DEVICE_PROBE_TIMEOUT_MS: u64 = 400;
|
||||||
|
|
||||||
/// Information about a video device
|
/// Information about a video device
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct VideoDeviceInfo {
|
pub struct VideoDeviceInfo {
|
||||||
@@ -401,32 +405,29 @@ pub fn enumerate_devices() -> Result<Vec<VideoDeviceInfo>> {
|
|||||||
|
|
||||||
debug!("Found video device: {:?}", path);
|
debug!("Found video device: {:?}", path);
|
||||||
|
|
||||||
// Try to open and query the device
|
if !sysfs_maybe_capture(&path) {
|
||||||
match VideoDevice::open(&path) {
|
debug!("Skipping non-capture candidate (sysfs): {:?}", path);
|
||||||
Ok(device) => {
|
continue;
|
||||||
match device.info() {
|
}
|
||||||
Ok(info) => {
|
|
||||||
// Only include devices with video capture capability
|
// Try to open and query the device (with timeout)
|
||||||
if info.capabilities.video_capture || info.capabilities.video_capture_mplane
|
match probe_device_with_timeout(&path, Duration::from_millis(DEVICE_PROBE_TIMEOUT_MS)) {
|
||||||
{
|
Some(info) => {
|
||||||
info!(
|
// Only include devices with video capture capability
|
||||||
"Found capture device: {} ({}) - {} formats",
|
if info.capabilities.video_capture || info.capabilities.video_capture_mplane {
|
||||||
info.name,
|
info!(
|
||||||
info.driver,
|
"Found capture device: {} ({}) - {} formats",
|
||||||
info.formats.len()
|
info.name,
|
||||||
);
|
info.driver,
|
||||||
devices.push(info);
|
info.formats.len()
|
||||||
} else {
|
);
|
||||||
debug!("Skipping non-capture device: {:?}", path);
|
devices.push(info);
|
||||||
}
|
} else {
|
||||||
}
|
debug!("Skipping non-capture device: {:?}", path);
|
||||||
Err(e) => {
|
|
||||||
debug!("Failed to get info for {:?}: {}", path, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
None => {
|
||||||
debug!("Failed to open {:?}: {}", path, e);
|
debug!("Failed to probe {:?}", path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -438,6 +439,104 @@ pub fn enumerate_devices() -> Result<Vec<VideoDeviceInfo>> {
|
|||||||
Ok(devices)
|
Ok(devices)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn probe_device_with_timeout(path: &Path, timeout: Duration) -> Option<VideoDeviceInfo> {
|
||||||
|
let path = path.to_path_buf();
|
||||||
|
let path_for_thread = path.clone();
|
||||||
|
let (tx, rx) = mpsc::channel();
|
||||||
|
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
let result = (|| -> Result<VideoDeviceInfo> {
|
||||||
|
let device = VideoDevice::open(&path_for_thread)?;
|
||||||
|
device.info()
|
||||||
|
})();
|
||||||
|
let _ = tx.send(result);
|
||||||
|
});
|
||||||
|
|
||||||
|
match rx.recv_timeout(timeout) {
|
||||||
|
Ok(Ok(info)) => Some(info),
|
||||||
|
Ok(Err(e)) => {
|
||||||
|
debug!("Failed to get info for {:?}: {}", path, e);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
Err(mpsc::RecvTimeoutError::Timeout) => {
|
||||||
|
warn!("Timed out probing video device: {:?}", path);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
Err(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sysfs_maybe_capture(path: &Path) -> bool {
|
||||||
|
let name = match path.file_name().and_then(|n| n.to_str()) {
|
||||||
|
Some(name) => name,
|
||||||
|
None => return true,
|
||||||
|
};
|
||||||
|
let sysfs_base = Path::new("/sys/class/video4linux").join(name);
|
||||||
|
|
||||||
|
let sysfs_name = read_sysfs_string(&sysfs_base.join("name"))
|
||||||
|
.unwrap_or_default()
|
||||||
|
.to_lowercase();
|
||||||
|
let uevent = read_sysfs_string(&sysfs_base.join("device/uevent"))
|
||||||
|
.unwrap_or_default()
|
||||||
|
.to_lowercase();
|
||||||
|
let driver = extract_uevent_value(&uevent, "driver");
|
||||||
|
|
||||||
|
let mut maybe_capture = false;
|
||||||
|
let capture_hints = [
|
||||||
|
"capture",
|
||||||
|
"hdmi",
|
||||||
|
"usb",
|
||||||
|
"uvc",
|
||||||
|
"ms2109",
|
||||||
|
"ms2130",
|
||||||
|
"macrosilicon",
|
||||||
|
"tc358743",
|
||||||
|
"grabber",
|
||||||
|
];
|
||||||
|
if capture_hints.iter().any(|hint| sysfs_name.contains(hint)) {
|
||||||
|
maybe_capture = true;
|
||||||
|
}
|
||||||
|
if let Some(driver) = driver {
|
||||||
|
if driver.contains("uvcvideo") || driver.contains("tc358743") {
|
||||||
|
maybe_capture = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let skip_hints = [
|
||||||
|
"codec",
|
||||||
|
"decoder",
|
||||||
|
"encoder",
|
||||||
|
"isp",
|
||||||
|
"mem2mem",
|
||||||
|
"m2m",
|
||||||
|
"vbi",
|
||||||
|
"radio",
|
||||||
|
"metadata",
|
||||||
|
"output",
|
||||||
|
];
|
||||||
|
if skip_hints.iter().any(|hint| sysfs_name.contains(hint)) && !maybe_capture {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_sysfs_string(path: &Path) -> Option<String> {
|
||||||
|
std::fs::read_to_string(path)
|
||||||
|
.ok()
|
||||||
|
.map(|value| value.trim().to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_uevent_value(content: &str, key: &str) -> Option<String> {
|
||||||
|
let key_upper = key.to_ascii_uppercase();
|
||||||
|
for line in content.lines() {
|
||||||
|
if let Some(value) = line.strip_prefix(&format!("{}=", key_upper)) {
|
||||||
|
return Some(value.to_lowercase());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
/// Find the best video device for KVM use
|
/// Find the best video device for KVM use
|
||||||
pub fn find_best_device() -> Result<VideoDeviceInfo> {
|
pub fn find_best_device() -> Result<VideoDeviceInfo> {
|
||||||
let devices = enumerate_devices()?;
|
let devices = enumerate_devices()?;
|
||||||
|
|||||||
Reference in New Issue
Block a user