Files
One-KVM/docs/modules/video.md
mofeng-git 0c82d1a840 feat(rustdesk): 完整实现RustDesk协议和P2P连接
重大变更:
- 从prost切换到protobuf 3.4实现完整的RustDesk协议栈
- 新增P2P打洞模块(punch.rs)支持直连和中继回退
- 重构加密系统:临时Curve25519密钥对+Ed25519签名
- 完善HID适配器:支持CapsLock状态同步和修饰键映射
- 添加音频流支持:Opus编码+音频帧适配器
- 优化视频流:改进帧适配器和编码器协商
- 移除pacer.rs简化视频管道

扩展系统:
- 在设置向导中添加扩展步骤(ttyd/rustdesk切换)
- 扩展可用性检测和自动启动
- 新增WebConfig handler用于Web服务器配置

前端改进:
- SetupView增加第4步扩展配置
- 音频设备列表和配置界面
- 新增多语言支持(en-US/zh-CN)
- TypeScript类型生成更新

文档:
- 更新系统架构文档
- 完善config/hid/rustdesk/video/webrtc模块文档
2026-01-03 19:34:07 +08:00

38 KiB
Raw Blame History

Video 模块文档

1. 模块概述

Video 模块负责视频采集、编码和流传输,是 One-KVM 的核心功能模块。

1.1 主要功能

  • V4L2 视频设备采集
  • 多格式像素转换
  • 硬件/软件视频编码
  • MJPEG 和 WebRTC 流传输
  • 帧去重和质量控制

1.2 文件结构

src/video/
├── mod.rs                    # 模块导出
├── capture.rs                # V4L2 视频采集
├── device.rs                 # V4L2 设备枚举和能力查询
├── streamer.rs               # 视频流服务 (MJPEG)
├── stream_manager.rs         # 流管理器 (统一管理 MJPEG/WebRTC)
├── video_session.rs          # 视频会话管理 (多编码器会话)
├── shared_video_pipeline.rs  # 共享视频编码管道 (多编解码器)
├── h264_pipeline.rs          # H264 专用编码管道 (WebRTC)
├── format.rs                 # 像素格式定义
├── frame.rs                  # 视频帧结构 (零拷贝)
├── convert.rs                # 格式转换 (libyuv SIMD)
├── decoder/                  # 解码器
│   ├── mod.rs
│   └── mjpeg.rs              # MJPEG 解码 (TurboJPEG/VAAPI)
└── encoder/                  # 编码器
    ├── mod.rs
    ├── traits.rs             # Encoder trait + BitratePreset
    ├── codec.rs              # 编码器类型定义
    ├── h264.rs               # H264 编码
    ├── h265.rs               # H265 编码
    ├── vp8.rs                # VP8 编码
    ├── vp9.rs                # VP9 编码
    ├── jpeg.rs               # JPEG 编码
    └── registry.rs           # 编码器注册表 (硬件探测)

2. 架构设计

2.1 数据流

┌─────────────────────────────────────────────────────────────────────────────┐
│                          Video Data Flow                                     │
└─────────────────────────────────────────────────────────────────────────────┘

V4L2 Device (/dev/video0)
    │
    │ Raw frames (MJPEG/YUYV/NV12)
    ▼
┌───────────────────┐
│  VideoCapturer    │ ◄─── capture.rs
│  - open_device()  │
│  - read_frame()   │
│  - set_format()   │
└─────────┬─────────┘
          │ VideoFrame
          ▼
┌───────────────────┐
│    Streamer       │ ◄─── streamer.rs
│  - start()        │
│  - stop()         │
│  - get_info()     │
└─────────┬─────────┘
          │
    ┌─────┴─────┐
    │           │
    ▼           ▼
┌────────┐  ┌────────────────────────────┐
│ MJPEG  │  │  SharedVideoPipeline       │
│ Mode   │  │  - Decode (MJPEG→YUV)      │
│        │  │  - Convert (YUV→target)    │
│        │  │  - Encode (H264/H265/VP8)  │
└────────┘  └─────────────┬──────────────┘
    │                     │
    ▼                     ▼
┌────────┐          ┌────────┐
│ HTTP   │          │ WebRTC │
│ Stream │          │ RTP    │
└────────┘          └────────┘

2.2 组件关系

┌─────────────────────────────────────────────────────────────────────────────┐
│                       Component Relationships                                │
└─────────────────────────────────────────────────────────────────────────────┘

VideoStreamManager (单一入口)
    │
    ├── mode: StreamMode (当前激活的模式)
    │
    ├──► MJPEG Mode
    │    └──► Streamer ──► MjpegStreamHandler
    │         └──► VideoCapturer
    │
    └──► WebRTC Mode
         └──► WebRtcStreamer
              ├──► VideoSessionManager (多会话管理)
              │    └──► 多个 VideoSession (每个会话独立的编解码器)
              └──► SharedVideoPipeline (共享编码管道)
                   ├──► VideoCapturer
                   ├──► MjpegDecoder (MJPEG → YUV420P/NV12)
                   │    ├── MjpegTurboDecoder (软件)
                   │    └── MjpegVaapiDecoder (硬件)
                   ├──► PixelConverter (格式转换)
                   │    ├── Nv12Converter (YUYV/RGB → NV12)
                   │    └── Yuv420pConverter
                   └──► Encoders[] (通过 EncoderRegistry 选择)
                        ├── H264Encoder (VAAPI/RKMPP/V4L2/x264)
                        ├── H265Encoder (VAAPI/RKMPP)
                        ├── VP8Encoder (VAAPI)
                        └── VP9Encoder (VAAPI)

3. 核心组件

3.1 VideoCapturer (capture.rs)

异步 V4L2 视频采集器,使用 memory-mapped 缓冲区进行高性能视频采集。

主要接口

pub struct VideoCapturer {
    /// V4L2 设备句柄
    device: Arc<Mutex<Device>>,
    /// 采集任务句柄
    capture_task: Option<JoinHandle<()>>,
    /// 帧广播通道
    frame_tx: broadcast::Sender<VideoFrame>,
    /// 采集状态
    state: Arc<RwLock<CaptureState>>,
    /// 统计信息
    stats: Arc<RwLock<CaptureStats>>,
}

impl VideoCapturer {
    /// 创建采集器 (不立即打开设备)
    pub fn new() -> Arc<Self>;

    /// 启动采集
    pub async fn start(&self, config: CaptureConfig) -> Result<()>;

    /// 停止采集
    pub async fn stop(&self) -> Result<()>;

    /// 订阅帧流 (广播模式)
    pub fn subscribe(&self) -> broadcast::Receiver<VideoFrame>;

    /// 获取当前状态
    pub fn state(&self) -> CaptureState;

    /// 获取统计信息
    pub fn stats(&self) -> CaptureStats;
}

采集配置

pub struct CaptureConfig {
    /// 设备路径
    pub device_path: PathBuf,        // /dev/video0
    /// 分辨率
    pub resolution: Resolution,      // 1920x1080
    /// 像素格式
    pub format: PixelFormat,         // MJPEG/YUYV/NV12
    /// 帧率 (0 = 最大)
    pub fps: u32,                    // 30
    /// 缓冲区数量 (默认 2)
    pub buffer_count: u32,
    /// 超时时间
    pub timeout: Duration,
    /// JPEG 质量 (1-100)
    pub jpeg_quality: u8,
}

采集状态

#[derive(Clone, Copy)]
pub enum CaptureState {
    Idle,           // 未初始化
    Starting,       // 正在启动
    Running,        // 正在采集
    Stopping,       // 正在停止
    NoSignal,       // 无信号
    DeviceLost,     // 设备丢失
    Error,          // 错误状态
}

使用示例

// 创建采集器
let capturer = VideoCapturer::new();

// 启动采集
let config = CaptureConfig {
    device_path: PathBuf::from("/dev/video0"),
    resolution: Resolution::HD1080,
    format: PixelFormat::Mjpeg,
    fps: 30,
    buffer_count: 2,
    timeout: Duration::from_secs(2),
    jpeg_quality: 80,
};
capturer.start(config).await?;

// 订阅帧流
let mut frame_rx = capturer.subscribe();
while let Ok(frame) = frame_rx.recv().await {
    // 处理帧
    process_frame(frame).await;
}

3.2 VideoDevice (device.rs)

V4L2 设备枚举和能力查询工具。

/// 视频设备信息
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VideoDeviceInfo {
    /// 设备路径 (/dev/video0)
    pub path: PathBuf,
    /// 设备名称
    pub name: String,
    /// 驱动名称
    pub driver: String,
    /// 总线信息
    pub bus_info: String,
    /// 卡片名称
    pub card: String,
    /// 支持的像素格式列表
    pub formats: Vec<FormatInfo>,
    /// 设备能力
    pub capabilities: DeviceCapabilities,
    /// 是否为采集卡 (自动识别)
    pub is_capture_card: bool,
    /// 优先级分数 (用于自动选择设备)
    pub priority: u32,
}

/// 枚举所有视频设备
pub fn enumerate_devices() -> Result<Vec<VideoDeviceInfo>>;

/// 自动选择最佳设备 (优先级最高的采集卡)
pub fn find_best_device() -> Result<VideoDeviceInfo>;

3.3 VideoFrame (frame.rs)

视频帧数据结构,支持零拷贝和帧去重。

pub struct VideoFrame {
    /// 帧数据 (引用计数)
    data: Arc<Bytes>,

    /// xxHash64 缓存 (用于去重)
    hash: Arc<OnceLock<u64>>,

    /// 分辨率
    resolution: Resolution,

    /// 像素格式
    format: PixelFormat,

    /// 行步长
    stride: u32,

    /// 是否关键帧
    key_frame: bool,

    /// 帧序号
    sequence: u64,

    /// 采集时间戳
    capture_ts: Instant,

    /// 是否有信号
    online: bool,
}

impl VideoFrame {
    /// 创建新帧
    pub fn new(data: Bytes, resolution: Resolution, format: PixelFormat) -> Self;

    /// 获取帧数据
    pub fn data(&self) -> &[u8];

    /// 计算帧哈希 (懒加载)
    pub fn hash(&self) -> u64;

    /// 检查帧是否相同 (用于去重)
    pub fn is_same_as(&self, other: &Self) -> bool;

    /// 克隆帧 (零拷贝)
    pub fn clone_ref(&self) -> Self;
}

3.3 PixelFormat (format.rs)

支持的像素格式定义。

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PixelFormat {
    // 压缩格式
    Mjpeg,      // Motion JPEG (优先级: 100)
    Jpeg,       // Static JPEG (优先级: 99)

    // YUV 4:2:2 打包格式
    Yuyv,       // YUYV/YUY2 (优先级: 80)
    Yvyu,       // YVYU (优先级: 64)
    Uyvy,       // UYVY (优先级: 65)

    // YUV 半平面格式
    Nv12,       // NV12 (优先级: 75)
    Nv16,       // NV16 (优先级: 60)
    Nv24,       // NV24 (优先级: 55)

    // YUV 平面格式
    Yuv420,     // I420/YU12 (优先级: 70)
    Yvu420,     // YV12 (优先级: 63)

    // RGB 格式
    Rgb565,     // RGB565 (优先级: 40)
    Rgb24,      // RGB24 (优先级: 50)
    Bgr24,      // BGR24 (优先级: 49)

    // 灰度
    Grey,       // 8-bit grayscale (优先级: 10)
}

impl PixelFormat {
    /// 获取格式优先级 (越高越好)
    pub fn priority(&self) -> u32;

    /// 计算帧大小
    pub fn frame_size(&self, width: u32, height: u32) -> usize;

    /// 转换为 V4L2 FourCC
    pub fn to_fourcc(&self) -> u32;

    /// 从 V4L2 FourCC 转换
    pub fn from_fourcc(fourcc: u32) -> Option<Self>;

    /// 是否压缩格式
    pub fn is_compressed(&self) -> bool;
}

3.4 PixelFormat (format.rs)

支持的像素格式定义 (与实际代码一致)。

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum PixelFormat {
    // 压缩格式
    Mjpeg,      // Motion JPEG
    Jpeg,       // Static JPEG

    // YUV 4:2:2 打包格式
    Yuyv,       // YUYV/YUY2
    Yvyu,       // YVYU
    Uyvy,       // UYVY

    // YUV 半平面格式
    Nv12,       // NV12 (Y + interleaved UV)
    Nv16,       // NV16
    Nv24,       // NV24

    // YUV 平面格式
    Yuv420,     // I420/YU12
    Yvu420,     // YV12

    // RGB 格式
    Rgb565,     // RGB565
    Rgb24,      // RGB24
    Bgr24,      // BGR24

    // 灰度
    Grey,       // 8-bit grayscale
}

impl PixelFormat {
    /// 转换为 V4L2 FourCC
    pub fn to_fourcc(&self) -> FourCC;

    /// 从 V4L2 FourCC 转换
    pub fn from_fourcc(fourcc: FourCC) -> Option<Self>;

    /// 是否压缩格式
    pub fn is_compressed(&self) -> bool;

    /// 获取每像素字节数 (未压缩格式)
    pub fn bytes_per_pixel(&self) -> Option<usize>;

    /// 计算帧大小
    pub fn frame_size(&self, resolution: Resolution) -> Option<usize>;
}

3.5 SharedVideoPipeline (shared_video_pipeline.rs)

多会话共享的视频编码管道。

pub struct SharedVideoPipeline {
    /// 视频采集器
    capturer: Arc<Mutex<VideoCapturer>>,

    /// MJPEG 解码器
    decoder: MjpegDecoder,

    /// YUV 转换器
    converter: YuvConverter,

    /// 编码器实例
    encoders: HashMap<VideoCodec, Box<dyn Encoder>>,

    /// 活跃会话
    sessions: Arc<RwLock<Vec<SessionSender>>>,

    /// 配置
    config: PipelineConfig,
}

impl SharedVideoPipeline {
    /// 创建管道
    pub async fn new(config: PipelineConfig) -> Result<Self>;

    /// 启动管道
    pub async fn start(&self) -> Result<()>;

    /// 停止管道
    pub async fn stop(&self) -> Result<()>;

    /// 添加会话订阅
    pub fn subscribe(&self, codec: VideoCodec) -> Receiver<EncodedFrame>;

    /// 移除会话订阅
    pub fn unsubscribe(&self, session_id: &str);

    /// 编码单帧 (多编码器)
    async fn encode_frame(&self, frame: VideoFrame) -> Result<()>;
}

编码流程

Input: VideoFrame (MJPEG)
    │
    ▼
┌───────────────────┐
│   MJPEG Decode    │  turbojpeg / VAAPI
│   MJPEG → YUV420  │
└─────────┬─────────┘
          │
          ▼
┌───────────────────┐
│   YUV Convert     │  libyuv (SIMD)
│  YUV420 → target  │
└─────────┬─────────┘
          │
    ┌─────┴─────┬─────────┬─────────┐
    │           │         │         │
    ▼           ▼         ▼         ▼
┌───────┐  ┌───────┐  ┌───────┐  ┌───────┐
│ H264  │  │ H265  │  │  VP8  │  │  VP9  │
│Encoder│  │Encoder│  │Encoder│  │Encoder│
└───┬───┘  └───┬───┘  └───┬───┘  └───┬───┘
    │          │          │          │
    └──────────┴──────────┴──────────┘
                    │
                    ▼
            EncodedFrame[]
            (distribute to sessions)

3.5 SharedVideoPipeline (shared_video_pipeline.rs)

通用共享视频编码管道,支持 H264/H265/VP8/VP9 多种编码器。

pub struct SharedVideoPipeline {
    /// 配置
    config: SharedVideoPipelineConfig,
    /// 编码器实例
    encoder: Arc<Mutex<Box<dyn VideoEncoder>>>,
    /// 像素转换器
    converter: Arc<Mutex<Option<Nv12Converter>>>,
    /// MJPEG 解码器
    mjpeg_decoder: Arc<Mutex<Option<Box<dyn MjpegDecoder>>>>,
    /// 编码帧广播通道
    encoded_tx: broadcast::Sender<EncodedVideoFrame>,
    /// 统计信息
    stats: Arc<RwLock<SharedVideoPipelineStats>>,
    /// 运行状态
    running: AtomicBool,
}

impl SharedVideoPipeline {
    /// 创建管道
    pub async fn new(config: SharedVideoPipelineConfig) -> Result<Arc<Self>>;

    /// 启动管道
    pub async fn start(&self, frame_rx: broadcast::Receiver<VideoFrame>) -> Result<()>;

    /// 停止管道
    pub async fn stop(&self) -> Result<()>;

    /// 订阅编码帧
    pub fn subscribe(&self) -> broadcast::Receiver<EncodedVideoFrame>;

    /// 获取统计信息
    pub fn stats(&self) -> SharedVideoPipelineStats;

    /// 编码单帧 (内部方法)
    async fn encode_frame(&self, frame: VideoFrame) -> Result<EncodedVideoFrame>;
}

管道配置

#[derive(Debug, Clone)]
pub struct SharedVideoPipelineConfig {
    /// 输入分辨率
    pub resolution: Resolution,
    /// 输入像素格式
    pub input_format: PixelFormat,
    /// 输出编码器类型
    pub output_codec: VideoEncoderType,
    /// 码率预设 (替代原始 bitrate_kbps)
    pub bitrate_preset: BitratePreset,
    /// 目标帧率
    pub fps: u32,
    /// 编码器后端 (None = 自动选择)
    pub encoder_backend: Option<EncoderBackend>,
}

impl SharedVideoPipelineConfig {
    /// 创建 H264 配置
    pub fn h264(resolution: Resolution, preset: BitratePreset) -> Self;

    /// 创建 H265 配置
    pub fn h265(resolution: Resolution, preset: BitratePreset) -> Self;

    /// 创建 VP8 配置
    pub fn vp8(resolution: Resolution, preset: BitratePreset) -> Self;

    /// 创建 VP9 配置
    pub fn vp9(resolution: Resolution, preset: BitratePreset) -> Self;
}

3.6 VideoSessionManager (video_session.rs)

管理多个 WebRTC 视频会话,每个会话可使用不同的编解码器。

pub struct VideoSessionManager {
    /// 会话映射 (session_id -> VideoSession)
    sessions: Arc<RwLock<HashMap<String, VideoSession>>>,
    /// 管道映射 (codec -> SharedVideoPipeline)
    pipelines: Arc<RwLock<HashMap<VideoEncoderType, Arc<SharedVideoPipeline>>>>,
    /// 配置
    config: VideoSessionManagerConfig,
}

impl VideoSessionManager {
    /// 创建会话管理器
    pub fn new(config: VideoSessionManagerConfig) -> Arc<Self>;

    /// 创建新会话
    pub async fn create_session(
        &self,
        session_id: String,
        codec: VideoEncoderType,
    ) -> Result<broadcast::Receiver<EncodedVideoFrame>>;

    /// 关闭会话
    pub async fn close_session(&self, session_id: &str) -> Result<()>;

    /// 获取会话信息
    pub fn get_session_info(&self, session_id: &str) -> Option<VideoSessionInfo>;

    /// 列出所有会话
    pub fn list_sessions(&self) -> Vec<VideoSessionInfo>;

    /// 获取或创建编码管道
    async fn get_or_create_pipeline(
        &self,
        codec: VideoEncoderType,
    ) -> Result<Arc<SharedVideoPipeline>>;
}

3.7 Streamer (streamer.rs)

高层视频流服务,管理采集和分发。

pub struct Streamer {
    /// 采集器
    capturer: Option<Arc<Mutex<VideoCapturer>>>,

    /// 采集任务句柄
    capture_task: Option<JoinHandle<()>>,

    /// 帧广播通道
    frame_tx: broadcast::Sender<VideoFrame>,

    /// 状态
    state: Arc<RwLock<StreamerState>>,

    /// 配置
    config: StreamerConfig,

    /// 事件总线
    events: Arc<EventBus>,
}

impl Streamer {
    /// 创建流服务
    pub fn new(events: Arc<EventBus>) -> Self;

    /// 启动流
    pub async fn start(&self, config: StreamerConfig) -> Result<()>;

    /// 停止流
    pub async fn stop(&self) -> Result<()>;

    /// 订阅帧
    pub fn subscribe(&self) -> broadcast::Receiver<VideoFrame>;

    /// 获取状态
    pub fn state(&self) -> StreamerState;

    /// 获取信息
    pub fn get_info(&self) -> StreamerInfo;

    /// 应用配置
    pub async fn apply_config(&self, config: StreamerConfig) -> Result<()>;
}

pub struct StreamerState {
    pub status: StreamStatus,
    pub device: Option<String>,
    pub resolution: Option<Resolution>,
    pub format: Option<PixelFormat>,
    pub fps: f32,
    pub frame_count: u64,
    pub error: Option<String>,
}

pub enum StreamStatus {
    Idle,
    Starting,
    Streaming,
    Stopping,
    Error,
}

3.7 Streamer (streamer.rs)

高层 MJPEG 视频流服务,集成采集、设备管理和状态监控。

pub struct Streamer {
    /// 配置
    config: RwLock<StreamerConfig>,
    /// 视频采集器
    capturer: RwLock<Option<Arc<VideoCapturer>>>,
    /// MJPEG 流处理器
    mjpeg_handler: Arc<MjpegStreamHandler>,
    /// 当前设备信息
    current_device: RwLock<Option<VideoDeviceInfo>>,
    /// 流状态
    state: RwLock<StreamerState>,
    /// 事件总线 (可选)
    events: RwLock<Option<Arc<EventBus>>>,
}

impl Streamer {
    /// 创建流服务
    pub fn new() -> Arc<Self>;

    /// 启动流
    pub async fn start(&self, config: StreamerConfig) -> Result<()>;

    /// 停止流
    pub async fn stop(&self) -> Result<()>;

    /// 设置事件总线
    pub async fn set_event_bus(&self, events: Arc<EventBus>);

    /// 获取状态
    pub fn state(&self) -> StreamerState;

    /// 获取 MJPEG 处理器
    pub fn mjpeg_handler(&self) -> Arc<MjpegStreamHandler>;

    /// 应用配置 (热更新)
    pub async fn apply_config(&self, config: StreamerConfig) -> Result<()>;
}

#[derive(Clone, Copy, PartialEq, Eq)]
pub enum StreamerState {
    Uninitialized,  // 未初始化
    Ready,          // 就绪但未流式传输
    Streaming,      // 正在流式传输
    NoSignal,       // 无视频信号
    Error,          // 错误
    DeviceLost,     // 设备丢失
    Recovering,     // 设备恢复中
}

3.8 VideoStreamManager (stream_manager.rs)

统一视频流管理器,作为唯一入口协调 MJPEG 和 WebRTC 两种流模式。

pub struct VideoStreamManager {
    /// 当前流模式
    mode: RwLock<StreamMode>,
    /// MJPEG 流服务
    streamer: Arc<Streamer>,
    /// WebRTC 流服务
    webrtc_streamer: Arc<WebRtcStreamer>,
    /// 事件总线
    events: RwLock<Option<Arc<EventBus>>>,
    /// 配置存储
    config_store: RwLock<Option<ConfigStore>>,
    /// 模式切换锁
    switching: AtomicBool,
}

impl VideoStreamManager {
    /// 创建管理器 (指定 WebRtcStreamer)
    pub fn with_webrtc_streamer(
        streamer: Arc<Streamer>,
        webrtc_streamer: Arc<WebRtcStreamer>,
    ) -> Arc<Self>;

    /// 启动流 (启动当前模式)
    pub async fn start(&self) -> Result<()>;

    /// 停止流
    pub async fn stop(&self) -> Result<()>;

    /// 切换流模式 (MJPEG ↔ WebRTC)
    pub async fn set_mode(&self, mode: StreamMode) -> Result<()>;

    /// 获取当前模式
    pub fn mode(&self) -> StreamMode;

    /// 获取 Streamer (MJPEG)
    pub fn streamer(&self) -> Arc<Streamer>;

    /// 获取 WebRtcStreamer
    pub fn webrtc_streamer(&self) -> Arc<WebRtcStreamer>;

    /// 设置事件总线
    pub async fn set_event_bus(&self, events: Arc<EventBus>);

    /// 设置配置存储
    pub async fn set_config_store(&self, config_store: ConfigStore);
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StreamMode {
    Mjpeg,   // MJPEG over HTTP
    Webrtc,  // H264/H265/VP8/VP9 over WebRTC
}

4. 编码器系统

4.1 BitratePreset (encoder/traits.rs)

码率预设简化配置,提供三个常用档位和自定义选项。

#[typeshare]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum BitratePreset {
    /// 速度优先: 1 Mbps, 最低延迟, 更小的 GOP
    /// 适用于: 慢速网络, 远程管理, 低带宽场景
    Speed,

    /// 平衡: 4 Mbps, 质量/延迟均衡 (推荐默认)
    /// 适用于: 常规使用
    Balanced,

    /// 质量优先: 8 Mbps, 最佳视觉质量
    /// 适用于: 本地网络, 高带宽场景, 详细工作
    Quality,

    /// 自定义码率 (kbps, 高级用户)
    Custom(u32),
}

impl BitratePreset {
    /// 获取码率值 (kbps)
    pub fn bitrate_kbps(&self) -> u32;

    /// 获取推荐 GOP 大小 (基于帧率)
    pub fn gop_size(&self, fps: u32) -> u32;

    /// 获取质量级别 ("low" | "medium" | "high")
    pub fn quality_level(&self) -> &'static str;

    /// 从 kbps 值创建 (自动映射到最近预设或 Custom)
    pub fn from_kbps(kbps: u32) -> Self;
}

4.2 VideoEncoder Trait (encoder/traits.rs)

所有编码器的通用接口 (hwcodec 编码器的封装)。

pub trait VideoEncoder: Send + Sync {
    /// 编码一帧 (输入 NV12, 输出压缩数据)
    fn encode(&mut self, yuv: &[u8], ms: i64) -> Result<EncodedVideoFrame>;

    /// 获取编码器类型
    fn encoder_type(&self) -> VideoEncoderType;

    /// 设置码率 (kbps)
    fn set_bitrate(&mut self, bitrate_kbps: u32) -> Result<()>;

    /// 请求关键帧
    fn request_keyframe(&mut self);

    /// 获取编码器信息
    fn info(&self) -> EncoderInfo;
}

/// 编码后的视频帧
#[derive(Debug, Clone)]
pub struct EncodedVideoFrame {
    /// 编码数据 (Bytes 引用计数,零拷贝)
    pub data: Bytes,
    /// 呈现时间戳 (毫秒)
    pub pts_ms: i64,
    /// 是否关键帧
    pub is_keyframe: bool,
    /// 帧序号
    pub sequence: u64,
    /// 帧时长
    pub duration: Duration,
    /// 编码类型
    pub codec: VideoEncoderType,
}

4.3 VideoEncoderType & EncoderBackend (encoder/registry.rs)

编码器类型和硬件后端定义。

/// 视频编码器类型
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum VideoEncoderType {
    H264,   // H.264/AVC
    H265,   // H.265/HEVC
    VP8,    // VP8
    VP9,    // VP9
}

impl VideoEncoderType {
    /// 是否仅支持硬件编码 (无软件回退)
    pub fn hardware_only(&self) -> bool {
        match self {
            VideoEncoderType::H264 => false,  // x264 软件回退
            VideoEncoderType::H265 => true,   // 仅硬件
            VideoEncoderType::VP8 => true,    // 仅硬件
            VideoEncoderType::VP9 => true,    // 仅硬件
        }
    }
}

/// 编码器硬件后端
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum EncoderBackend {
    Vaapi,      // Intel/AMD VAAPI (Linux)
    Nvenc,      // NVIDIA NVENC
    Qsv,        // Intel Quick Sync
    Amf,        // AMD AMF
    Rkmpp,      // Rockchip MPP
    V4l2M2m,    // V4L2 Memory-to-Memory
    Software,   // x264/x265/libvpx (软件)
}

4.4 EncoderRegistry (encoder/registry.rs)

全局编码器注册表,自动检测硬件并选择最佳编码器。

/// 编码器注册表 (全局单例)
pub struct EncoderRegistry {
    /// 可用编码器映射
    available_encoders: HashMap<VideoEncoderType, Vec<EncoderBackend>>,
}

impl EncoderRegistry {
    /// 获取全局实例
    pub fn global() -> &'static EncoderRegistry;

    /// 列出可用编码器
    pub fn list_available(&self, codec: VideoEncoderType) -> &[EncoderBackend];

    /// 检查编码器是否可用
    pub fn is_available(&self, codec: VideoEncoderType, backend: EncoderBackend) -> bool;

    /// 获取最佳编码器后端 (自动选择)
    pub fn get_best_backend(&self, codec: VideoEncoderType) -> Option<EncoderBackend>;

    /// 创建编码器实例
    pub fn create_encoder(
        &self,
        codec: VideoEncoderType,
        config: EncoderConfig,
        backend: Option<EncoderBackend>,
    ) -> Result<Box<dyn VideoEncoder>>;
}

4.5 编码器优先级

实际的硬件检测顺序 (基于 hwcodec 库)

H264 编码器选择顺序:
1. VAAPI (Intel/AMD GPU - 优先)
2. Rkmpp (Rockchip 平台)
3. V4L2 M2M (通用 Linux)
4. x264 (软件回退)

H265 编码器选择顺序:
1. VAAPI
2. Rkmpp
(无软件回退)

VP8/VP9 编码器:
1. VAAPI only
(无软件回退)

5. 格式转换与解码

5.1 MJPEG 解码器 (decoder/mjpeg.rs)

支持硬件和软件两种 MJPEG 解码方式。

/// MJPEG 解码器 trait
pub trait MjpegDecoder: Send + Sync {
    /// 解码 MJPEG 到 YUV420P
    fn decode(&mut self, jpeg_data: &[u8]) -> Result<DecodedYuv420pFrame>;

    /// 获取解码器类型
    fn decoder_type(&self) -> &str;
}

/// MJPEG TurboJPEG 软件解码器
pub struct MjpegTurboDecoder {
    decompressor: Decompressor,
    output_buffer: Vec<u8>,
}

/// MJPEG VAAPI 硬件解码器 (输出 NV12)
pub struct MjpegVaapiDecoder {
    decoder: VaapiDecoder,
    config: MjpegVaapiDecoderConfig,
}

impl MjpegVaapiDecoder {
    /// 创建 VAAPI 解码器
    pub fn new(config: MjpegVaapiDecoderConfig) -> Result<Self>;

    /// 解码 MJPEG 到 NV12 (硬件加速)
    pub fn decode_to_nv12(&mut self, jpeg_data: &[u8]) -> Result<Vec<u8>>;
}

5.2 像素转换器 (convert.rs)

使用 libyuv SIMD 加速的格式转换。

/// NV12 转换器 (YUYV/RGB → NV12)
pub struct Nv12Converter {
    input_format: PixelFormat,
    resolution: Resolution,
    nv12_buffer: Nv12Buffer,
}

impl Nv12Converter {
    /// 创建转换器
    pub fn new(input_format: PixelFormat, resolution: Resolution) -> Self;

    /// 转换到 NV12
    pub fn convert(&mut self, input: &[u8]) -> Result<&[u8]>;
}

/// YUV420P 缓冲区
pub struct Yuv420pBuffer {
    data: Vec<u8>,
    width: u32,
    height: u32,
    y_offset: usize,
    u_offset: usize,
    v_offset: usize,
}

impl Yuv420pBuffer {
    /// 获取 Y 平面
    pub fn y_plane(&self) -> &[u8];

    /// 获取 U 平面
    pub fn u_plane(&self) -> &[u8];

    /// 获取 V 平面
    pub fn v_plane(&self) -> &[u8];
}

/// 像素转换器 (通用接口)
pub trait PixelConverter: Send + Sync {
    /// YUYV → YUV420P
    fn yuyv_to_yuv420p(src: &[u8], width: u32, height: u32) -> Yuv420pBuffer;

    /// NV12 → YUV420P
    fn nv12_to_yuv420p(src: &[u8], width: u32, height: u32) -> Yuv420pBuffer;

    /// RGB24 → YUV420P
    fn rgb24_to_yuv420p(src: &[u8], width: u32, height: u32) -> Yuv420pBuffer;
}

6. 配置说明

6.1 视频配置

#[derive(Serialize, Deserialize)]
#[typeshare]
pub struct VideoConfig {
    /// 设备路径 (None = 自动检测)
    pub device: Option<String>,

    /// 像素格式 (None = 自动选择: MJPEG > YUYV > NV12)
    pub format: Option<String>,

    /// 宽度
    pub width: u32,

    /// 高度
    pub height: u32,

    /// 帧率
    pub fps: u32,

    /// JPEG 质量 (1-100, 仅 MJPEG)
    pub quality: u32,
}

impl Default for VideoConfig {
    fn default() -> Self {
        Self {
            device: None,
            format: None,
            width: 1920,
            height: 1080,
            fps: 30,
            quality: 80,
        }
    }
}

6.2 WebRTC 配置

#[derive(Serialize, Deserialize)]
#[typeshare]
pub struct WebRtcConfig {
    /// 码率预设
    pub bitrate_preset: BitratePreset,

    /// 首选编码器 (H264/H265/VP8/VP9)
    pub preferred_codec: String,

    /// STUN 服务器
    pub stun_server: Option<String>,

    /// TURN 服务器
    pub turn_server: Option<String>,

    /// TURN 用户名
    pub turn_username: Option<String>,

    /// TURN 密码
    pub turn_password: Option<String>,
}

impl Default for WebRtcConfig {
    fn default() -> Self {
        Self {
            bitrate_preset: BitratePreset::Balanced,
            preferred_codec: "H264".to_string(),
            stun_server: Some("stun:stun.l.google.com:19302".to_string()),
            turn_server: None,
            turn_username: None,
            turn_password: None,
        }
    }
}

7. API 端点

7.1 视频流控制 (用户权限)

端点 方法 描述
/stream/status GET 获取流状态
/stream/start POST 启动流
/stream/stop POST 停止流
/stream/mode GET 获取流模式 (MJPEG/WebRTC)
/stream/mode POST 设置流模式
/stream/bitrate POST 设置码率 (WebRTC)
/stream/codecs GET 列出可用编码器

7.2 WebRTC 端点 (用户权限)

端点 方法 描述
/webrtc/session POST 创建 WebRTC 会话
/webrtc/offer POST 发送 SDP offer
/webrtc/ice POST 发送 ICE candidate
/webrtc/ice-servers GET 获取 STUN/TURN 配置
/webrtc/status GET 获取 WebRTC 状态
/webrtc/close POST 关闭会话

7.3 设备管理 (用户权限)

端点 方法 描述
/devices GET 列出所有视频设备

7.4 配置管理 (管理员权限)

端点 方法 描述
/config/video GET 获取视频配置
/config/video PATCH 更新视频配置
/config/stream GET 获取流配置
/config/stream PATCH 更新流配置

7.5 响应格式

// GET /stream/status
{
    "state": "streaming",
    "device": "/dev/video0",
    "resolution": { "width": 1920, "height": 1080 },
    "format": "MJPEG",
    "fps": 30.0,
    "mode": "mjpeg"
}

// GET /devices
{
    "devices": [
        {
            "path": "/dev/video0",
            "name": "USB Capture HDMI",
            "driver": "uvcvideo",
            "bus_info": "usb-0000:00:14.0-1",
            "formats": ["MJPEG", "YUYV"],
            "is_capture_card": true,
            "priority": 100
        }
    ]
}

// GET /stream/codecs
{
    "codecs": [
        {
            "codec": "H264",
            "backends": ["VAAPI", "x264"]
        },
        {
            "codec": "H265",
            "backends": ["VAAPI"]
        }
    ]
}

8. 事件系统

视频模块通过 EventBus 发布的实时事件 (通过 WebSocket /ws 推送到前端)

pub enum SystemEvent {
    /// 流状态变化
    StreamStateChanged {
        state: String,      // "uninitialized" | "ready" | "streaming" | "no_signal" | "error" | "device_lost" | "recovering"
        device: Option<String>,
        resolution: Option<Resolution>,
        fps: Option<f32>,
        mode: String,       // "mjpeg" | "webrtc"
    },

    /// 视频设备插拔事件
    VideoDeviceChanged {
        added: Vec<String>,
        removed: Vec<String>,
    },

    /// WebRTC 会话状态变化
    WebRtcSessionChanged {
        session_id: String,
        state: String,      // "created" | "active" | "paused" | "closing" | "closed"
        codec: String,
    },

    /// 编码器变化 (硬件/软件切换)
    EncoderChanged {
        codec: String,
        backend: String,    // "VAAPI" | "RKMPP" | "x264" | ...
        hardware: bool,
    },
}

前端订阅示例:

const ws = new WebSocket('ws://localhost:8080/ws');
ws.onmessage = (event) => {
    const systemEvent = JSON.parse(event.data);
    if (systemEvent.type === 'StreamStateChanged') {
        console.log('Stream state:', systemEvent.state);
    }
};

9. 错误处理

#[derive(Debug, thiserror::Error)]
pub enum VideoError {
    #[error("Device not found: {0}")]
    DeviceNotFound(String),

    #[error("Device busy: {0}")]
    DeviceBusy(String),

    #[error("Format not supported: {0:?}")]
    FormatNotSupported(PixelFormat),

    #[error("Resolution not supported: {0}x{1}")]
    ResolutionNotSupported(u32, u32),

    #[error("Capture error: {0}")]
    CaptureError(String),

    #[error("Encoder error: {0}")]
    EncoderError(String),

    #[error("No signal")]
    NoSignal,

    #[error("Device lost")]
    DeviceLost,
}

10. 性能优化

10.1 零拷贝架构

  • Arc<Bytes> 共享帧数据,避免内存拷贝
  • 引用计数多播,单次采集多个消费者
  • broadcast::Sender 高效分发帧到多个订阅者

10.2 帧去重 (Frame Deduplication)

  • xxHash64 快速哈希计算 (懒加载)
  • 相同帧跳过编码,降低 CPU 使用
  • 适用于静态画面场景

10.3 硬件加速优先

编码器自动选择优先级:

  1. VAAPI (Intel/AMD GPU) - 最优先
  2. Rkmpp (Rockchip 平台)
  3. V4L2 M2M (通用 Linux)
  4. Software (x264) - 仅 H264 有软件回退

解码器优先级:

  1. VAAPI (硬件 MJPEG 解码 → NV12)
  2. TurboJPEG (软件 MJPEG 解码 → YUV420P)

10.4 SIMD 加速

  • libyuv 库提供 NEON/SSE 优化的像素转换
  • 自动检测 CPU 指令集并使用最快路径
  • YUYV → NV12 转换性能提升 3-4 倍

10.5 低延迟优化

  • 缓冲区数量减少至 2 (降低采集延迟)
  • WebRTC 模式下直接 RTP 封装,无额外缓冲
  • GOP 大小可调 (Speed 预设: 0.5s, Balanced: 1s, Quality: 2s)

11. 常见问题

Q: 如何添加新的视频格式支持?

  1. format.rs 添加 PixelFormat 枚举值
  2. 实现 to_fourcc()from_fourcc() 映射
  3. convert.rs 添加转换函数 (如果需要转为 NV12/YUV420P)
  4. 更新 Nv12ConverterPixelConverter

Q: 如何添加新的编码器后端?

  1. encoder/registry.rs 添加 EncoderBackend 枚举值
  2. 在对应编码器 (如 h264.rs) 中实现新后端
  3. 更新 EncoderRegistry::create_encoder() 的后端选择逻辑
  4. 添加硬件探测代码

Q: 帧率不稳定或丢帧怎么办?

诊断步骤:

  1. 检查 /stream/status API查看实际 FPS
  2. 检查 USB 带宽是否充足 (使用 lsusb -t)
  3. 检查 CPU 使用率,确认编码器负载

解决方案:

  • 降低分辨率: 1080p → 720p
  • 使用 MJPEG 格式: 减少主机侧解码负担
  • 启用硬件编码: 检查 /stream/codecs 确认有 VAAPI/Rkmpp
  • 降低码率预设: Quality → Balanced → Speed
  • 关闭帧去重: 如果画面高度动态

Q: WebRTC 无法连接?

  1. 检查 STUN/TURN 服务器配置 (/webrtc/ice-servers)
  2. 确认防火墙允许 UDP 流量
  3. 检查浏览器控制台 ICE 连接状态
  4. 尝试使用公共 STUN 服务器: stun:stun.l.google.com:19302

Q: 如何在 MJPEG 和 WebRTC 模式之间切换?

# 切换到 MJPEG 模式 (高兼容性)
curl -X POST http://localhost:8080/stream/mode \
  -H "Content-Type: application/json" \
  -d '{"mode": "mjpeg"}'

# 切换到 WebRTC 模式 (低延迟)
curl -X POST http://localhost:8080/stream/mode \
  -H "Content-Type: application/json" \
  -d '{"mode": "webrtc"}'

Q: 支持同时多个 WebRTC 连接吗?

是的,VideoSessionManager 支持最多 8 个并发 WebRTC 会话。每个会话共享同一个视频采集源,但可以使用不同的编码器 (H264/H265/VP8/VP9)。

Q: 如何查看当前使用的编码器后端?

监听 EncoderChanged 事件 (通过 WebSocket),或查看日志中的编码器初始化信息。


12. 架构亮点

12.1 单一入口设计

VideoStreamManager 是所有视频操作的唯一入口,封装了 MJPEG 和 WebRTC 两种模式的复杂性。

12.2 模式隔离

MJPEG 和 WebRTC 模式完全分离,避免相互干扰。切换模式时会完全停止旧模式再启动新模式。

12.3 硬件自适应

通过 EncoderRegistry 自动检测硬件能力,优先使用硬件加速,无需手动配置。

12.4 多编解码器支持

WebRTC 模式支持 H264/H265/VP8/VP9 四种编码器,可根据客户端能力协商最佳编码器。

12.5 零配置设备发现

自动扫描 /dev/video*,识别 HDMI 采集卡并计算优先级,优先选择最佳设备。