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

@@ -42,9 +42,8 @@ impl ImageManager {
/// Ensure images directory exists
pub fn ensure_dir(&self) -> Result<()> {
fs::create_dir_all(&self.images_path).map_err(|e| {
AppError::Internal(format!("Failed to create images directory: {}", e))
})?;
fs::create_dir_all(&self.images_path)
.map_err(|e| AppError::Internal(format!("Failed to create images directory: {}", e)))?;
Ok(())
}
@@ -54,9 +53,9 @@ impl ImageManager {
let mut images = Vec::new();
for entry in fs::read_dir(&self.images_path).map_err(|e| {
AppError::Internal(format!("Failed to read images directory: {}", e))
})? {
for entry in fs::read_dir(&self.images_path)
.map_err(|e| AppError::Internal(format!("Failed to read images directory: {}", e)))?
{
let entry = entry.map_err(|e| {
AppError::Internal(format!("Failed to read directory entry: {}", e))
})?;
@@ -146,9 +145,8 @@ impl ImageManager {
)));
}
let mut file = File::create(&path).map_err(|e| {
AppError::Internal(format!("Failed to create image file: {}", e))
})?;
let mut file = File::create(&path)
.map_err(|e| AppError::Internal(format!("Failed to create image file: {}", e)))?;
file.write_all(data).map_err(|e| {
// Try to clean up on error
@@ -193,9 +191,8 @@ impl ImageManager {
}
// Create file and copy data
let mut file = File::create(&path).map_err(|e| {
AppError::Internal(format!("Failed to create image file: {}", e))
})?;
let mut file = File::create(&path)
.map_err(|e| AppError::Internal(format!("Failed to create image file: {}", e)))?;
let bytes_written = io::copy(reader, &mut file).map_err(|e| {
let _ = fs::remove_file(&path);
@@ -244,9 +241,11 @@ impl ImageManager {
let mut bytes_written: u64 = 0;
// Stream chunks directly to disk
while let Some(chunk) = field.chunk().await.map_err(|e| {
AppError::Internal(format!("Failed to read upload chunk: {}", e))
})? {
while let Some(chunk) = field
.chunk()
.await
.map_err(|e| AppError::Internal(format!("Failed to read upload chunk: {}", e)))?
{
// Check size limit
bytes_written += chunk.len() as u64;
if bytes_written > MAX_IMAGE_SIZE {
@@ -260,15 +259,15 @@ impl ImageManager {
}
// Write chunk to file
file.write_all(&chunk).await.map_err(|e| {
AppError::Internal(format!("Failed to write chunk: {}", e))
})?;
file.write_all(&chunk)
.await
.map_err(|e| AppError::Internal(format!("Failed to write chunk: {}", e)))?;
}
// Flush and close file
file.flush().await.map_err(|e| {
AppError::Internal(format!("Failed to flush file: {}", e))
})?;
file.flush()
.await
.map_err(|e| AppError::Internal(format!("Failed to flush file: {}", e)))?;
drop(file);
// Move temp file to final location
@@ -279,7 +278,10 @@ impl ImageManager {
AppError::Internal(format!("Failed to rename temp file: {}", e))
})?;
info!("Created image (streaming): {} ({} bytes)", name, bytes_written);
info!(
"Created image (streaming): {} ({} bytes)",
name, bytes_written
);
self.get_by_name(&name)
}
@@ -288,9 +290,8 @@ impl ImageManager {
pub fn delete(&self, id: &str) -> Result<()> {
let image = self.get(id)?;
fs::remove_file(&image.path).map_err(|e| {
AppError::Internal(format!("Failed to delete image: {}", e))
})?;
fs::remove_file(&image.path)
.map_err(|e| AppError::Internal(format!("Failed to delete image: {}", e)))?;
info!("Deleted image: {}", image.name);
Ok(())
@@ -304,9 +305,8 @@ impl ImageManager {
return Err(AppError::NotFound(format!("Image not found: {}", name)));
}
fs::remove_file(&path).map_err(|e| {
AppError::Internal(format!("Failed to delete image: {}", e))
})?;
fs::remove_file(&path)
.map_err(|e| AppError::Internal(format!("Failed to delete image: {}", e)))?;
info!("Deleted image: {}", name);
Ok(())
@@ -414,7 +414,9 @@ impl ImageManager {
};
if final_filename.is_empty() {
return Err(AppError::BadRequest("Could not determine filename".to_string()));
return Err(AppError::BadRequest(
"Could not determine filename".to_string(),
));
}
// Check if file already exists
@@ -468,16 +470,14 @@ impl ImageManager {
progress_callback(0, content_length);
while let Some(chunk_result) = stream.next().await {
let chunk = chunk_result
.map_err(|e| AppError::Internal(format!("Download error: {}", e)))?;
let chunk =
chunk_result.map_err(|e| AppError::Internal(format!("Download error: {}", e)))?;
file.write_all(&chunk)
.await
.map_err(|e| {
// Cleanup on error
let _ = std::fs::remove_file(&temp_path);
AppError::Internal(format!("Failed to write data: {}", e))
})?;
file.write_all(&chunk).await.map_err(|e| {
// Cleanup on error
let _ = std::fs::remove_file(&temp_path);
AppError::Internal(format!("Failed to write data: {}", e))
})?;
downloaded += chunk.len() as u64;