feat: 完善架构优化性能

- 调整音视频架构,提升 RKMPP 编码 MJPEG-->H264 性能,同时解决丢帧马赛克问题;
- 删除多用户逻辑,只保留单用户,支持设置 web 单会话;
- 修复删除体验不好的的回退逻辑,前端页面菜单位置微调;
- 增加 OTG USB 设备动态调整功能;
- 修复 mdns 问题,webrtc 视频切换更顺畅。
This commit is contained in:
mofeng
2026-01-25 16:04:29 +08:00
parent 01e01430da
commit 1786b7689d
66 changed files with 4225 additions and 2936 deletions

View File

@@ -109,13 +109,13 @@ impl LedState {
/// reopened on the next operation attempt.
pub struct OtgBackend {
/// Keyboard device path (/dev/hidg0)
keyboard_path: PathBuf,
keyboard_path: Option<PathBuf>,
/// Relative mouse device path (/dev/hidg1)
mouse_rel_path: PathBuf,
mouse_rel_path: Option<PathBuf>,
/// Absolute mouse device path (/dev/hidg2)
mouse_abs_path: PathBuf,
mouse_abs_path: Option<PathBuf>,
/// Consumer control device path (/dev/hidg3)
consumer_path: PathBuf,
consumer_path: Option<PathBuf>,
/// Keyboard device file
keyboard_dev: Mutex<Option<File>>,
/// Relative mouse device file
@@ -157,9 +157,7 @@ impl OtgBackend {
keyboard_path: paths.keyboard,
mouse_rel_path: paths.mouse_relative,
mouse_abs_path: paths.mouse_absolute,
consumer_path: paths
.consumer
.unwrap_or_else(|| PathBuf::from("/dev/hidg3")),
consumer_path: paths.consumer,
keyboard_dev: Mutex::new(None),
mouse_rel_dev: Mutex::new(None),
mouse_abs_dev: Mutex::new(None),
@@ -300,13 +298,25 @@ impl OtgBackend {
/// 2. If handle is None but path exists, reopen the device
/// 3. Return whether the device is ready for I/O
fn ensure_device(&self, device_type: DeviceType) -> Result<()> {
let (path, dev_mutex) = match device_type {
let (path_opt, dev_mutex) = match device_type {
DeviceType::Keyboard => (&self.keyboard_path, &self.keyboard_dev),
DeviceType::MouseRelative => (&self.mouse_rel_path, &self.mouse_rel_dev),
DeviceType::MouseAbsolute => (&self.mouse_abs_path, &self.mouse_abs_dev),
DeviceType::ConsumerControl => (&self.consumer_path, &self.consumer_dev),
};
let path = match path_opt {
Some(p) => p,
None => {
self.online.store(false, Ordering::Relaxed);
return Err(AppError::HidError {
backend: "otg".to_string(),
reason: "Device disabled".to_string(),
error_code: "disabled".to_string(),
});
}
};
// Check if device path exists
if !path.exists() {
// Close the device if open (device was removed)
@@ -383,20 +393,40 @@ impl OtgBackend {
/// Check if all HID device files exist
pub fn check_devices_exist(&self) -> bool {
self.keyboard_path.exists() && self.mouse_rel_path.exists() && self.mouse_abs_path.exists()
self.keyboard_path
.as_ref()
.map_or(true, |p| p.exists())
&& self
.mouse_rel_path
.as_ref()
.map_or(true, |p| p.exists())
&& self
.mouse_abs_path
.as_ref()
.map_or(true, |p| p.exists())
&& self
.consumer_path
.as_ref()
.map_or(true, |p| p.exists())
}
/// Get list of missing device paths
pub fn get_missing_devices(&self) -> Vec<String> {
let mut missing = Vec::new();
if !self.keyboard_path.exists() {
missing.push(self.keyboard_path.display().to_string());
if let Some(ref path) = self.keyboard_path {
if !path.exists() {
missing.push(path.display().to_string());
}
}
if !self.mouse_rel_path.exists() {
missing.push(self.mouse_rel_path.display().to_string());
if let Some(ref path) = self.mouse_rel_path {
if !path.exists() {
missing.push(path.display().to_string());
}
}
if !self.mouse_abs_path.exists() {
missing.push(self.mouse_abs_path.display().to_string());
if let Some(ref path) = self.mouse_abs_path {
if !path.exists() {
missing.push(path.display().to_string());
}
}
missing
}
@@ -407,6 +437,10 @@ impl OtgBackend {
/// ESHUTDOWN errors by closing the device handle for later reconnection.
/// Uses write_with_timeout to avoid blocking on busy devices.
fn send_keyboard_report(&self, report: &KeyboardReport) -> Result<()> {
if self.keyboard_path.is_none() {
return Ok(());
}
// Ensure device is ready
self.ensure_device(DeviceType::Keyboard)?;
@@ -472,6 +506,10 @@ impl OtgBackend {
/// ESHUTDOWN errors by closing the device handle for later reconnection.
/// Uses write_with_timeout to avoid blocking on busy devices.
fn send_mouse_report_relative(&self, buttons: u8, dx: i8, dy: i8, wheel: i8) -> Result<()> {
if self.mouse_rel_path.is_none() {
return Ok(());
}
// Ensure device is ready
self.ensure_device(DeviceType::MouseRelative)?;
@@ -534,6 +572,10 @@ impl OtgBackend {
/// ESHUTDOWN errors by closing the device handle for later reconnection.
/// Uses write_with_timeout to avoid blocking on busy devices.
fn send_mouse_report_absolute(&self, buttons: u8, x: u16, y: u16, wheel: i8) -> Result<()> {
if self.mouse_abs_path.is_none() {
return Ok(());
}
// Ensure device is ready
self.ensure_device(DeviceType::MouseAbsolute)?;
@@ -600,6 +642,10 @@ impl OtgBackend {
///
/// Sends a consumer control usage code and then releases it (sends 0x0000).
fn send_consumer_report(&self, usage: u16) -> Result<()> {
if self.consumer_path.is_none() {
return Ok(());
}
// Ensure device is ready
self.ensure_device(DeviceType::ConsumerControl)?;
@@ -708,71 +754,72 @@ impl HidBackend for OtgBackend {
}
// Wait for devices to appear (they should already exist from OtgService)
let device_paths = vec![
self.keyboard_path.clone(),
self.mouse_rel_path.clone(),
self.mouse_abs_path.clone(),
];
let mut device_paths = Vec::new();
if let Some(ref path) = self.keyboard_path {
device_paths.push(path.clone());
}
if let Some(ref path) = self.mouse_rel_path {
device_paths.push(path.clone());
}
if let Some(ref path) = self.mouse_abs_path {
device_paths.push(path.clone());
}
if let Some(ref path) = self.consumer_path {
device_paths.push(path.clone());
}
if device_paths.is_empty() {
return Err(AppError::Internal(
"No HID devices configured for OTG backend".into(),
));
}
if !wait_for_hid_devices(&device_paths, 2000).await {
return Err(AppError::Internal("HID devices did not appear".into()));
}
// Open keyboard device
if self.keyboard_path.exists() {
let file = Self::open_device(&self.keyboard_path)?;
*self.keyboard_dev.lock() = Some(file);
info!("Keyboard device opened: {}", self.keyboard_path.display());
} else {
warn!(
"Keyboard device not found: {}",
self.keyboard_path.display()
);
if let Some(ref path) = self.keyboard_path {
if path.exists() {
let file = Self::open_device(path)?;
*self.keyboard_dev.lock() = Some(file);
info!("Keyboard device opened: {}", path.display());
} else {
warn!("Keyboard device not found: {}", path.display());
}
}
// Open relative mouse device
if self.mouse_rel_path.exists() {
let file = Self::open_device(&self.mouse_rel_path)?;
*self.mouse_rel_dev.lock() = Some(file);
info!(
"Relative mouse device opened: {}",
self.mouse_rel_path.display()
);
} else {
warn!(
"Relative mouse device not found: {}",
self.mouse_rel_path.display()
);
if let Some(ref path) = self.mouse_rel_path {
if path.exists() {
let file = Self::open_device(path)?;
*self.mouse_rel_dev.lock() = Some(file);
info!("Relative mouse device opened: {}", path.display());
} else {
warn!("Relative mouse device not found: {}", path.display());
}
}
// Open absolute mouse device
if self.mouse_abs_path.exists() {
let file = Self::open_device(&self.mouse_abs_path)?;
*self.mouse_abs_dev.lock() = Some(file);
info!(
"Absolute mouse device opened: {}",
self.mouse_abs_path.display()
);
} else {
warn!(
"Absolute mouse device not found: {}",
self.mouse_abs_path.display()
);
if let Some(ref path) = self.mouse_abs_path {
if path.exists() {
let file = Self::open_device(path)?;
*self.mouse_abs_dev.lock() = Some(file);
info!("Absolute mouse device opened: {}", path.display());
} else {
warn!("Absolute mouse device not found: {}", path.display());
}
}
// Open consumer control device (optional, may not exist on older setups)
if self.consumer_path.exists() {
let file = Self::open_device(&self.consumer_path)?;
*self.consumer_dev.lock() = Some(file);
info!(
"Consumer control device opened: {}",
self.consumer_path.display()
);
} else {
debug!(
"Consumer control device not found: {}",
self.consumer_path.display()
);
if let Some(ref path) = self.consumer_path {
if path.exists() {
let file = Self::open_device(path)?;
*self.consumer_dev.lock() = Some(file);
info!("Consumer control device opened: {}", path.display());
} else {
debug!("Consumer control device not found: {}", path.display());
}
}
// Mark as online if all devices opened successfully
@@ -905,7 +952,9 @@ impl HidBackend for OtgBackend {
}
fn supports_absolute_mouse(&self) -> bool {
self.mouse_abs_path.exists()
self.mouse_abs_path
.as_ref()
.map_or(false, |p| p.exists())
}
async fn send_consumer(&self, event: ConsumerEvent) -> Result<()> {
@@ -928,7 +977,7 @@ pub fn is_otg_available() -> bool {
let mouse_rel = PathBuf::from("/dev/hidg1");
let mouse_abs = PathBuf::from("/dev/hidg2");
kb.exists() && mouse_rel.exists() && mouse_abs.exists()
kb.exists() || mouse_rel.exists() || mouse_abs.exists()
}
/// Implement Drop for OtgBackend to close device files