Files
One-KVM/docs/report/hwcodec/03-build-integration.md
mofeng-git a8a3b6c66b feat: 添加 RustDesk 协议支持和项目文档
- 新增 RustDesk 模块,支持与 RustDesk 客户端连接
  - 实现会合服务器协议和 P2P 连接
  - 支持 NaCl 加密和密钥交换
  - 添加视频帧和 HID 事件适配器
- 添加 Protobuf 协议定义 (message.proto, rendezvous.proto)
- 新增完整项目文档
  - 各功能模块文档 (video, hid, msd, otg, webrtc 等)
  - hwcodec 和 RustDesk 协议技术报告
  - 系统架构和技术栈文档
- 更新 Web 前端 RustDesk 配置界面和 API
2025-12-31 18:59:52 +08:00

13 KiB
Raw Blame History

hwcodec 构建系统与集成指南

1. 项目结构

libs/hwcodec/
├── Cargo.toml           # 包配置
├── Cargo.lock           # 依赖锁定
├── build.rs             # 构建脚本
├── src/                 # Rust 源码
│   ├── lib.rs           # 库入口
│   ├── common.rs        # 公共定义
│   ├── ffmpeg.rs        # FFmpeg 集成
│   ├── mux.rs           # 混流器
│   ├── android.rs       # Android 支持
│   ├── ffmpeg_ram/      # RAM 编解码
│   │   ├── mod.rs
│   │   ├── encode.rs
│   │   └── decode.rs
│   ├── vram/            # GPU 编解码 (Windows)
│   │   ├── mod.rs
│   │   ├── encode.rs
│   │   ├── decode.rs
│   │   └── ...
│   └── res/             # 测试资源
│       ├── 720p.h264
│       └── 720p.h265
├── cpp/                 # C++ 源码
│   ├── common/          # 公共代码
│   ├── ffmpeg_ram/      # FFmpeg RAM 实现
│   ├── ffmpeg_vram/     # FFmpeg VRAM 实现
│   ├── nv/              # NVIDIA 实现
│   ├── amf/             # AMD 实现
│   ├── mfx/             # Intel 实现
│   ├── mux/             # 混流实现
│   └── yuv/             # YUV 处理
├── externals/           # 外部 SDK (Git 子模块)
│   ├── nv-codec-headers_n12.1.14.0/
│   ├── Video_Codec_SDK_12.1.14/
│   ├── AMF_v1.4.35/
│   └── MediaSDK_22.5.4/
├── dev/                 # 开发工具
│   ├── capture/         # 捕获工具
│   ├── render/          # 渲染工具
│   └── tool/            # 通用工具
└── examples/            # 示例程序

2. Cargo 配置

2.1 Cargo.toml

[package]
name = "hwcodec"
version = "0.7.1"
edition = "2021"

[features]
default = []
vram = []  # GPU VRAM 直接编解码 (仅 Windows)

[dependencies]
log = "0.4"              # 日志
serde_derive = "1.0"     # 序列化派生宏
serde = "1.0"            # 序列化
serde_json = "1.0"       # JSON 序列化

[build-dependencies]
cc = "1.0"               # C++ 编译
bindgen = "0.59"         # FFI 绑定生成

[dev-dependencies]
env_logger = "0.10"      # 日志输出
rand = "0.8"             # 随机数

2.2 Feature 说明

Feature 说明 平台
default 基础功能 全平台
vram GPU VRAM 直接编解码 仅 Windows

2.3 使用方式

# 基础使用
[dependencies]
hwcodec = { path = "libs/hwcodec" }

# 启用 VRAM 功能 (Windows)
[dependencies]
hwcodec = { path = "libs/hwcodec", features = ["vram"] }

3. 构建脚本详解 (build.rs)

3.1 主入口

fn main() {
    let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    let mut builder = Build::new();

    // 1. 构建公共模块
    build_common(&mut builder);

    // 2. 构建 FFmpeg 相关模块
    ffmpeg::build_ffmpeg(&mut builder);

    // 3. 构建 SDK 模块 (Windows + vram feature)
    #[cfg(all(windows, feature = "vram"))]
    sdk::build_sdk(&mut builder);

    // 4. 编译生成静态库
    builder.static_crt(true).compile("hwcodec");
}

3.2 公共模块构建

fn build_common(builder: &mut Build) {
    let common_dir = manifest_dir.join("cpp").join("common");

    // 生成 FFI 绑定
    bindgen::builder()
        .header(common_dir.join("common.h"))
        .header(common_dir.join("callback.h"))
        .rustified_enum("*")
        .generate()
        .write_to_file(OUT_DIR.join("common_ffi.rs"));

    // 平台相关代码
    #[cfg(windows)]
    builder.file(common_dir.join("platform/win/win.cpp"));

    #[cfg(target_os = "linux")]
    builder.file(common_dir.join("platform/linux/linux.cpp"));

    #[cfg(target_os = "macos")]
    builder.file(common_dir.join("platform/mac/mac.mm"));

    // 工具代码
    builder.files([
        common_dir.join("log.cpp"),
        common_dir.join("util.cpp"),
    ]);
}

3.3 FFmpeg 模块构建

mod ffmpeg {
    pub fn build_ffmpeg(builder: &mut Build) {
        // 生成 FFmpeg FFI 绑定
        ffmpeg_ffi();

        // 链接 FFmpeg 库
        if let Ok(vcpkg_root) = std::env::var("VCPKG_ROOT") {
            link_vcpkg(builder, vcpkg_root.into());
        } else {
            link_system_ffmpeg(builder);  // pkg-config
        }

        // 链接系统库
        link_os();

        // 构建子模块
        build_ffmpeg_ram(builder);
        #[cfg(feature = "vram")]
        build_ffmpeg_vram(builder);
        build_mux(builder);
    }
}

3.4 FFmpeg 链接方式

VCPKG (跨平台静态链接)

fn link_vcpkg(builder: &mut Build, path: PathBuf) -> PathBuf {
    // 目标平台识别
    let target = match (target_os, target_arch) {
        ("windows", "x86_64") => "x64-windows-static",
        ("macos", "x86_64") => "x64-osx",
        ("macos", "aarch64") => "arm64-osx",
        ("linux", arch) => format!("{}-linux", arch),
        _ => panic!("unsupported platform"),
    };

    let lib_path = path.join("installed").join(target).join("lib");

    // 链接 FFmpeg 静态库
    println!("cargo:rustc-link-search=native={}", lib_path);
    ["avcodec", "avutil", "avformat"].iter()
        .for_each(|lib| println!("cargo:rustc-link-lib=static={}", lib));
}

pkg-config (Linux 动态链接)

fn link_system_ffmpeg(builder: &mut Build) {
    let libs = ["libavcodec", "libavutil", "libavformat", "libswscale"];

    for lib in &libs {
        // 获取编译标志
        let cflags = Command::new("pkg-config")
            .args(["--cflags", lib])
            .output()?;

        // 获取链接标志
        let libs = Command::new("pkg-config")
            .args(["--libs", lib])
            .output()?;

        // 解析并应用
        for flag in libs.split_whitespace() {
            if flag.starts_with("-L") {
                println!("cargo:rustc-link-search=native={}", &flag[2..]);
            } else if flag.starts_with("-l") {
                println!("cargo:rustc-link-lib={}", &flag[2..]);
            }
        }
    }
}

3.5 系统库链接

fn link_os() {
    let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();

    let libs: Vec<&str> = match target_os.as_str() {
        "windows" => vec!["User32", "bcrypt", "ole32", "advapi32"],
        "linux" => vec!["drm", "X11", "stdc++", "z"],
        "macos" | "ios" => vec!["c++", "m"],
        "android" => vec!["z", "m", "android", "atomic", "mediandk"],
        _ => panic!("unsupported os"),
    };

    for lib in libs {
        println!("cargo:rustc-link-lib={}", lib);
    }

    // macOS 框架
    if target_os == "macos" || target_os == "ios" {
        for framework in ["CoreFoundation", "CoreVideo", "CoreMedia",
                         "VideoToolbox", "AVFoundation"] {
            println!("cargo:rustc-link-lib=framework={}", framework);
        }
    }
}

3.6 SDK 模块构建 (Windows)

#[cfg(all(windows, feature = "vram"))]
mod sdk {
    pub fn build_sdk(builder: &mut Build) {
        build_amf(builder);  // AMD AMF
        build_nv(builder);   // NVIDIA
        build_mfx(builder);  // Intel MFX
    }

    fn build_nv(builder: &mut Build) {
        let sdk_path = externals_dir.join("Video_Codec_SDK_12.1.14");

        // 包含 SDK 头文件
        builder.includes([
            sdk_path.join("Interface"),
            sdk_path.join("Samples/Utils"),
            sdk_path.join("Samples/NvCodec"),
        ]);

        // 编译 SDK 源文件
        builder.file(sdk_path.join("Samples/NvCodec/NvEncoder/NvEncoder.cpp"));
        builder.file(sdk_path.join("Samples/NvCodec/NvEncoder/NvEncoderD3D11.cpp"));
        builder.file(sdk_path.join("Samples/NvCodec/NvDecoder/NvDecoder.cpp"));

        // 编译封装代码
        builder.files([
            nv_dir.join("nv_encode.cpp"),
            nv_dir.join("nv_decode.cpp"),
        ]);
    }
}

4. FFI 绑定生成

4.1 bindgen 配置

bindgen::builder()
    .header("path/to/header.h")
    .rustified_enum("*")           // 生成 Rust 枚举
    .parse_callbacks(Box::new(Callbacks))  // 自定义回调
    .generate()
    .write_to_file(OUT_DIR.join("ffi.rs"));

4.2 自定义派生

#[derive(Debug)]
struct CommonCallbacks;

impl bindgen::callbacks::ParseCallbacks for CommonCallbacks {
    fn add_derives(&self, name: &str) -> Vec<String> {
        // 为特定类型添加序列化支持
        match name {
            "DataFormat" | "SurfaceFormat" | "API" => {
                vec!["Serialize".to_string(), "Deserialize".to_string()]
            }
            _ => vec![],
        }
    }
}

4.3 生成的文件

文件 来源 内容
common_ffi.rs common.h, callback.h 枚举、常量、回调类型
ffmpeg_ffi.rs ffmpeg_ffi.h FFmpeg 日志级别、函数
ffmpeg_ram_ffi.rs ffmpeg_ram_ffi.h 编解码器函数
mux_ffi.rs mux_ffi.h 混流器函数

5. 外部依赖管理

5.1 Git 子模块

# 初始化子模块
git submodule update --init --recursive

# 更新子模块
git submodule update --remote externals

5.2 子模块配置 (.gitmodules)

[submodule "externals"]
    path = libs/hwcodec/externals
    url = https://github.com/rustdesk-org/externals.git

5.3 依赖版本

依赖 版本 用途
nv-codec-headers n12.1.14.0 NVIDIA FFmpeg 编码头
Video_Codec_SDK 12.1.14 NVIDIA 编解码 SDK
AMF v1.4.35 AMD Advanced Media Framework
MediaSDK 22.5.4 Intel Media SDK

6. 平台构建指南

6.1 Linux 构建

# 安装 FFmpeg 开发库
sudo apt install libavcodec-dev libavformat-dev libavutil-dev libswscale-dev

# 安装其他依赖
sudo apt install libdrm-dev libx11-dev pkg-config

# 构建
cargo build --release -p hwcodec

6.2 Windows 构建 (VCPKG)

# 安装 VCPKG
git clone https://github.com/microsoft/vcpkg
cd vcpkg
./bootstrap-vcpkg.bat

# 安装 FFmpeg
./vcpkg install ffmpeg:x64-windows-static

# 设置环境变量
$env:VCPKG_ROOT = "C:\path\to\vcpkg"

# 构建
cargo build --release -p hwcodec --features vram

6.3 macOS 构建

# 安装 FFmpeg (Homebrew)
brew install ffmpeg pkg-config

# 或使用 VCPKG
export VCPKG_ROOT=/path/to/vcpkg
vcpkg install ffmpeg:arm64-osx  # Apple Silicon
vcpkg install ffmpeg:x64-osx    # Intel

# 构建
cargo build --release -p hwcodec

6.4 交叉编译

# 安装 cross
cargo install cross --git https://github.com/cross-rs/cross

# ARM64 Linux
cross build --release -p hwcodec --target aarch64-unknown-linux-gnu

# ARMv7 Linux
cross build --release -p hwcodec --target armv7-unknown-linux-gnueabihf

7. 集成到 One-KVM

7.1 依赖配置

# Cargo.toml
[dependencies]
hwcodec = { path = "libs/hwcodec" }

7.2 使用示例

use hwcodec::ffmpeg_ram::encode::{Encoder, EncodeContext};
use hwcodec::ffmpeg_ram::decode::{Decoder, DecodeContext};
use hwcodec::ffmpeg::AVPixelFormat;

// 检测可用编码器
let encoders = Encoder::available_encoders(ctx, None);

// 创建编码器
let encoder = Encoder::new(EncodeContext {
    name: "h264_vaapi".to_string(),
    width: 1920,
    height: 1080,
    pixfmt: AVPixelFormat::AV_PIX_FMT_NV12,
    fps: 30,
    gop: 30,
    kbs: 4000,
    // ...
})?;

// 编码
let frames = encoder.encode(&yuv_data, pts_ms)?;

7.3 日志集成

// hwcodec 使用 log crate与 One-KVM 日志系统兼容
use log::{debug, info, warn, error};

// C++ 层日志通过回调传递
#[no_mangle]
pub extern "C" fn hwcodec_log(level: i32, message: *const c_char) {
    match level {
        0 => error!("{}", message),
        1 => warn!("{}", message),
        2 => info!("{}", message),
        3 => debug!("{}", message),
        4 => trace!("{}", message),
        _ => {}
    }
}

8. 故障排除

8.1 编译错误

FFmpeg 未找到:

error: pkg-config failed for libavcodec

解决: 安装 FFmpeg 开发库

sudo apt install libavcodec-dev libavformat-dev libavutil-dev libswscale-dev

bindgen 错误:

error: failed to run custom build command for `hwcodec`

解决: 安装 clang

sudo apt install clang libclang-dev

8.2 链接错误

符号未定义:

undefined reference to `av_log_set_level'

解决: 检查 FFmpeg 库链接顺序,确保 pkg-config 正确配置

动态库未找到:

error while loading shared libraries: libavcodec.so.59

解决:

sudo ldconfig
# 或设置 LD_LIBRARY_PATH
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH

8.3 运行时错误

硬件编码器不可用:

Encoder h264_vaapi test failed

检查:

  1. 驱动是否正确安装: vainfo
  2. 权限是否足够: ls -la /dev/dri/
  3. 用户是否在 video 组: groups

解码失败:

avcodec_receive_frame failed, ret = -11

解决: 这通常表示需要更多输入数据 (EAGAIN),是正常行为