feat(video): 事务化切换与前端统一编排,增强视频输入格式支持

- 后端:切换事务+transition_id,/stream/mode 返回 switching/transition_id 与实际 codec

- 事件:新增 mode_switching/mode_ready,config/webrtc_ready/mode_changed 关联事务

- 编码/格式:扩展 NV21/NV16/NV24/RGB/BGR 输入与转换链路,RKMPP direct input 优化

- 前端:useVideoSession 统一切换,失败回退真实切回 MJPEG,菜单格式同步修复

- 清理:useVideoStream 降级为 MJPEG-only
This commit is contained in:
mofeng-git
2026-01-11 10:41:57 +08:00
parent 9feb74b72c
commit 206594e292
110 changed files with 3955 additions and 2251 deletions

View File

@@ -102,8 +102,14 @@ impl HidController {
info!("Creating OTG HID backend from device paths");
Box::new(otg::OtgBackend::from_handles(handles)?)
}
HidBackendType::Ch9329 { ref port, baud_rate } => {
info!("Initializing CH9329 HID backend on {} @ {} baud", port, baud_rate);
HidBackendType::Ch9329 {
ref port,
baud_rate,
} => {
info!(
"Initializing CH9329 HID backend on {} @ {} baud",
port, baud_rate
);
Box::new(ch9329::Ch9329Backend::with_baud_rate(port, baud_rate)?)
}
HidBackendType::None => {
@@ -157,16 +163,25 @@ impl HidController {
// Report error to monitor, but skip temporary EAGAIN retries
// - "eagain_retry": within threshold, just temporary busy
// - "eagain": exceeded threshold, report as error
if let AppError::HidError { ref backend, ref reason, ref error_code } = e {
if let AppError::HidError {
ref backend,
ref reason,
ref error_code,
} = e
{
if error_code != "eagain_retry" {
self.monitor.report_error(backend, None, reason, error_code).await;
self.monitor
.report_error(backend, None, reason, error_code)
.await;
}
}
Err(e)
}
}
}
None => Err(AppError::BadRequest("HID backend not available".to_string())),
None => Err(AppError::BadRequest(
"HID backend not available".to_string(),
)),
}
}
@@ -188,16 +203,25 @@ impl HidController {
// Report error to monitor, but skip temporary EAGAIN retries
// - "eagain_retry": within threshold, just temporary busy
// - "eagain": exceeded threshold, report as error
if let AppError::HidError { ref backend, ref reason, ref error_code } = e {
if let AppError::HidError {
ref backend,
ref reason,
ref error_code,
} = e
{
if error_code != "eagain_retry" {
self.monitor.report_error(backend, None, reason, error_code).await;
self.monitor
.report_error(backend, None, reason, error_code)
.await;
}
}
Err(e)
}
}
}
None => Err(AppError::BadRequest("HID backend not available".to_string())),
None => Err(AppError::BadRequest(
"HID backend not available".to_string(),
)),
}
}
@@ -205,26 +229,33 @@ impl HidController {
pub async fn send_consumer(&self, event: ConsumerEvent) -> Result<()> {
let backend = self.backend.read().await;
match backend.as_ref() {
Some(b) => {
match b.send_consumer(event).await {
Ok(_) => {
if self.monitor.is_error().await {
let backend_type = self.backend_type.read().await;
self.monitor.report_recovered(backend_type.name_str()).await;
}
Ok(())
}
Err(e) => {
if let AppError::HidError { ref backend, ref reason, ref error_code } = e {
if error_code != "eagain_retry" {
self.monitor.report_error(backend, None, reason, error_code).await;
}
}
Err(e)
Some(b) => match b.send_consumer(event).await {
Ok(_) => {
if self.monitor.is_error().await {
let backend_type = self.backend_type.read().await;
self.monitor.report_recovered(backend_type.name_str()).await;
}
Ok(())
}
}
None => Err(AppError::BadRequest("HID backend not available".to_string())),
Err(e) => {
if let AppError::HidError {
ref backend,
ref reason,
ref error_code,
} = e
{
if error_code != "eagain_retry" {
self.monitor
.report_error(backend, None, reason, error_code)
.await;
}
}
Err(e)
}
},
None => Err(AppError::BadRequest(
"HID backend not available".to_string(),
)),
}
}
@@ -269,9 +300,9 @@ impl HidController {
// Include error information from monitor
let (error, error_code) = match self.monitor.status().await {
HidHealthStatus::Error { reason, error_code, .. } => {
(Some(reason), Some(error_code))
}
HidHealthStatus::Error {
reason, error_code, ..
} => (Some(reason), Some(error_code)),
_ => (None, None),
};
@@ -320,7 +351,7 @@ impl HidController {
None => {
warn!("OTG backend requires OtgService, but it's not available");
return Err(AppError::Config(
"OTG backend not available (OtgService missing)".to_string()
"OTG backend not available (OtgService missing)".to_string(),
));
}
};
@@ -341,7 +372,10 @@ impl HidController {
warn!("Failed to initialize OTG backend: {}", e);
// Cleanup: disable HID in OtgService
if let Err(e2) = otg_service.disable_hid().await {
warn!("Failed to cleanup HID after init failure: {}", e2);
warn!(
"Failed to cleanup HID after init failure: {}",
e2
);
}
None
}
@@ -363,8 +397,14 @@ impl HidController {
}
}
}
HidBackendType::Ch9329 { ref port, baud_rate } => {
info!("Initializing CH9329 HID backend on {} @ {} baud", port, baud_rate);
HidBackendType::Ch9329 {
ref port,
baud_rate,
} => {
info!(
"Initializing CH9329 HID backend on {} @ {} baud",
port, baud_rate
);
match ch9329::Ch9329Backend::with_baud_rate(port, baud_rate) {
Ok(b) => {
let boxed = Box::new(b);