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

@@ -35,10 +35,12 @@ impl JpegEncoder {
// I420: Y = width*height, U = width*height/4, V = width*height/4
let i420_size = width * height * 3 / 2;
let mut compressor = turbojpeg::Compressor::new()
.map_err(|e| AppError::VideoError(format!("Failed to create turbojpeg compressor: {}", e)))?;
let mut compressor = turbojpeg::Compressor::new().map_err(|e| {
AppError::VideoError(format!("Failed to create turbojpeg compressor: {}", e))
})?;
compressor.set_quality(config.quality.min(100) as i32)
compressor
.set_quality(config.quality.min(100) as i32)
.map_err(|e| AppError::VideoError(format!("Failed to set JPEG quality: {}", e)))?;
Ok(Self {
@@ -56,7 +58,8 @@ impl JpegEncoder {
/// Set JPEG quality (1-100)
pub fn set_quality(&mut self, quality: u32) -> Result<()> {
self.compressor.set_quality(quality.min(100) as i32)
self.compressor
.set_quality(quality.min(100) as i32)
.map_err(|e| AppError::VideoError(format!("Failed to set JPEG quality: {}", e)))?;
self.config.quality = quality;
Ok(())
@@ -73,12 +76,14 @@ impl JpegEncoder {
pixels: self.i420_buffer.as_slice(),
width,
height,
align: 1, // No padding between rows
align: 1, // No padding between rows
subsamp: turbojpeg::Subsamp::Sub2x2, // YUV 4:2:0
};
// Compress YUV directly to JPEG (skips color space conversion!)
let jpeg_data = self.compressor.compress_yuv_to_vec(yuv_image)
let jpeg_data = self
.compressor
.compress_yuv_to_vec(yuv_image)
.map_err(|e| AppError::VideoError(format!("JPEG compression failed: {}", e)))?;
Ok(EncodedFrame::jpeg(