refactor(hwcodec): 简化 hwcodec 库以适配 IP-KVM 场景

移除 IP-KVM 场景不需要的功能模块:
- 移除 VRAM 模块 (GPU 显存直接编解码)
- 移除 Mux 模块 (视频混流)
- 移除 macOS/Android 平台支持
- 移除外部 SDK 依赖 (~9MB)
- 移除开发工具和示例程序

简化解码器为仅支持 MJPEG (采集卡输出格式)
简化 NVIDIA 检测代码 (使用 dlopen 替代 SDK)
更新版本号至 0.8.0
更新相关技术文档
This commit is contained in:
mofeng-git
2025-12-31 19:47:08 +08:00
parent a8a3b6c66b
commit d0e2e13b35
441 changed files with 467 additions and 143421 deletions

View File

@@ -2,26 +2,24 @@
## 1. 项目概述
hwcodec 是一个基于 FFmpeg 的硬件视频编解码库,来源于 RustDesk 项目并针对 One-KVM 进行了定制优化。该库提供跨平台的 GPU 加速视频编码能力,支持多个 GPU 厂商和多种编码标准
hwcodec 是一个基于 FFmpeg 的硬件视频编解码库,来源于 RustDesk 项目并针对 One-KVM 进行了深度定制优化。该库专注于 IP-KVM 场景,提供 Windows 和 Linux 平台的 GPU 加速视频编码能力。
### 1.1 项目位置
```
libs/hwcodec/
├── src/ # Rust 源代码
── cpp/ # C++ 源代码
├── externals/ # 外部依赖 (SDK)
├── dev/ # 开发工具
└── examples/ # 示例程序
── cpp/ # C++ 源代码
```
### 1.2 核心特性
- **多编解码格式支持**: H.264, H.265 (HEVC), VP8, VP9, AV1, MJPEG
- **硬件加速**: NVENC/NVDEC, AMF, Intel QSV/MFX, VAAPI, RKMPP, V4L2 M2M, VideoToolbox
- **跨平台**: Windows, Linux, macOS, Android, iOS
- **多编解码格式支持**: H.264, H.265 (HEVC), VP8, VP9, MJPEG
- **硬件加速**: NVENC, AMF, Intel QSV (Windows), VAAPI, RKMPP, V4L2 M2M (Linux)
- **跨平台**: Windows, Linux (x86_64, ARM64, ARMv7)
- **低延迟优化**: 专为实时流媒体场景设计
- **Rust/C++ 混合架构**: Rust 提供安全的上层 APIC++ 实现底层编解码逻辑
- **IP-KVM 专用**: 解码仅支持 MJPEG采集卡输出格式编码支持多种硬件加速
## 2. 架构设计
@@ -30,35 +28,31 @@ libs/hwcodec/
```
┌─────────────────────────────────────────────────────────────┐
│ Rust API Layer │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐
│ │ ffmpeg_ram vram │ │ mux
│ │ module module │ │ module
│ └────────────┘ └──────┬──────┘ └────────────────────┘
├─────────────────────────┼───────────────────┼──────────────┤
FFI Bindings (bindgen)
│ ┌─────────────────────────────────────────────────────────┐│
│ │ ffmpeg_ram module ││
│ │ (encode.rs + decode.rs) ││
│ └─────────────────────────┬──────────────────────────────┘│
├─────────────────────────────┼───────────────────────────────┤
│ │
│ FFI Bindings (bindgen)
▼ │
├─────────────────────────────────────────────────────────────┤
│ C++ Core Layer │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐
│ │ ffmpeg_ram ffmpeg_vram │ │ mux.cpp
│ encode/ │ │ encode/ │ │ │
│ │ decode │ │ decode │ │ │ │
└──────┬──────┘ └──────┬──────┘ └──────────┬──────────┘
├─────────┼────────────────┼───────────────────┼──────────────┤
│ │ │ │ │
│ └────────────────┴───────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐│
│ │ ffmpeg_ram (encode/decode) ││
└──────────────────────────┬──────────────────────────────┘
├─────────────────────────────┼───────────────────────────────┤
│ ▼ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ FFmpeg Libraries │ │
│ │ libavcodec │ libavutil │ libavformat │ libswscale │ │
│ └──────────────────────────────────────────────────────┘ │
├────────────────────────────────────────────────────────────┤
├────────────────────────────────────────────────────────────┤
│ Hardware Acceleration Backends │
│ ┌────────┐ ┌─────┐ ┌─────┐ ┌───────┐ ┌───────┐ ┌───────┐ │
│ │ NVENC │ │ AMF │ │ MFX │ │ VAAPI │ │ RKMPP │ │V4L2M2M│ │
│ │ NVENC │ │ AMF │ │ QSV │ │ VAAPI │ │ RKMPP │ │V4L2M2M│ │
│ └────────┘ └─────┘ └─────┘ └───────┘ └───────┘ └───────┘ │
└─────────────────────────────────────────────────────────────┘
```
@@ -68,8 +62,6 @@ libs/hwcodec/
| 模块 | 职责 | 关键文件 |
|------|------|----------|
| `ffmpeg_ram` | 基于 RAM 的软件/硬件编解码 | `src/ffmpeg_ram/` |
| `vram` | GPU 显存直接编解码 (Windows) | `src/vram/` |
| `mux` | 视频混流 (MP4/MKV) | `src/mux.rs` |
| `common` | 公共定义和 GPU 检测 | `src/common.rs` |
| `ffmpeg` | FFmpeg 日志和初始化 | `src/ffmpeg.rs` |
@@ -82,17 +74,11 @@ libs/hwcodec/
pub mod common;
pub mod ffmpeg;
pub mod ffmpeg_ram;
pub mod mux;
#[cfg(all(windows, feature = "vram"))]
pub mod vram;
#[cfg(target_os = "android")]
pub mod android;
```
**功能**:
- 导出所有子模块
- 提供 C 日志回调函数 `hwcodec_log`
- 条件编译: `vram` 模块仅在 Windows + vram feature 启用时编译
- 提供 C 日志回调函数
### 3.2 公共模块 (common.rs)
@@ -111,13 +97,11 @@ pub enum Driver {
| 平台 | 检测函数 | 检测方式 |
|------|----------|----------|
| Linux | `linux_support_nv()` | 加载 CUDA/NVENC 动态库 |
| Linux | `linux_support_nv()` | 加载 libcuda.so + libnvidia-encode.so |
| Linux | `linux_support_amd()` | 检查 `libamfrt64.so.1` |
| Linux | `linux_support_intel()` | 检查 `libvpl.so`/`libmfx.so` |
| Linux | `linux_support_rkmpp()` | 检查 `/dev/mpp_service` |
| Linux | `linux_support_v4l2m2m()` | 检查 `/dev/video*` 设备 |
| macOS | `get_video_toolbox_codec_support()` | 调用 VideoToolbox API |
| Windows | 通过 VRAM 模块检测 | 查询 D3D11 设备 |
### 3.3 FFmpeg RAM 编码模块
@@ -129,7 +113,7 @@ pub enum Driver {
pub struct CodecInfo {
pub name: String, // 编码器名称如 "h264_nvenc"
pub mc_name: Option<String>, // MediaCodec 名称 (Android)
pub format: DataFormat, // H264/H265/VP8/VP9/AV1/MJPEG
pub format: DataFormat, // H264/H265/VP8/VP9/MJPEG
pub priority: i32, // 优先级 (Best=0, Good=1, Normal=2, Soft=3, Bad=4)
pub hwdevice: AVHWDeviceType, // 硬件设备类型
}
@@ -179,7 +163,7 @@ pub struct Encoder {
#### 3.3.2 C++ 层 (cpp/ffmpeg_ram/)
**FFmpegRamEncoder 类** (ffmpeg_ram_encode.cpp:97-420):
**FFmpegRamEncoder 类** (ffmpeg_ram_encode.cpp):
```cpp
class FFmpegRamEncoder {
@@ -225,6 +209,8 @@ fill_frame() - 填充 AVFrame 数据指针
### 3.4 FFmpeg RAM 解码模块
**IP-KVM 专用设计**: 解码器仅支持 MJPEG 软件解码,因为 IP-KVM 场景中视频采集卡输出的是 MJPEG 格式。
**Decoder 类**:
```rust
@@ -244,16 +230,27 @@ pub struct DecodeFrame {
}
```
**available_decoders()**: 仅返回 MJPEG 软件解码器
```rust
pub fn available_decoders() -> Vec<CodecInfo> {
vec![CodecInfo {
name: "mjpeg".to_owned(),
format: MJPEG,
hwdevice: AV_HWDEVICE_TYPE_NONE,
priority: Priority::Best as _,
..Default::default()
}]
}
```
**C++ 实现** (ffmpeg_ram_decode.cpp):
```cpp
class FFmpegRamDecoder {
AVCodecContext *c_ = NULL;
AVBufferRef *hw_device_ctx_ = NULL;
AVFrame *sw_frame_ = NULL; // 软件帧 (用于硬件→软件转换)
AVFrame *frame_ = NULL; // 解码输出帧
AVPacket *pkt_ = NULL;
bool hwaccel_ = true;
int do_decode(const void *obj);
};
@@ -262,23 +259,16 @@ class FFmpegRamDecoder {
**解码流程**:
```
输入编码数据
输入 MJPEG 数据
avcodec_send_packet() - 发送数据到解码器
avcodec_receive_frame() - 获取解码帧
avcodec_receive_frame() - 获取解码帧 (YUV420P)
├──▶ (软件解码) 直接使用 frame_
└──▶ (硬件解码) av_hwframe_transfer_data()
sw_frame_ (GPU → CPU)
callback() - 回调输出
callback() - 回调输出
```
## 4. 硬件加速支持
@@ -293,27 +283,32 @@ avcodec_receive_frame() - 获取解码帧
| VAAPI | 通用 | Linux | h264_vaapi, hevc_vaapi, vp8_vaapi, vp9_vaapi |
| RKMPP | Rockchip | Linux | h264_rkmpp, hevc_rkmpp |
| V4L2 M2M | ARM SoC | Linux | h264_v4l2m2m, hevc_v4l2m2m |
| VideoToolbox | Apple | macOS/iOS | hevc_videotoolbox |
| MediaCodec | Google | Android | h264_mediacodec, hevc_mediacodec |
### 4.2 硬件检测逻辑 (Linux)
```cpp
// libs/hwcodec/cpp/common/platform/linux/linux.cpp
// NVIDIA 检测 - 加载 CUDA 和 NVENC 动态库
// NVIDIA 检测 - 简化的动态库检测
int linux_support_nv() {
CudaFunctions *cuda_dl = NULL;
NvencFunctions *nvenc_dl = NULL;
CuvidFunctions *cvdl = NULL;
load_driver(&cuda_dl, &nvenc_dl, &cvdl);
// 成功加载则返回 0
void *handle = dlopen("libcuda.so.1", RTLD_LAZY);
if (!handle) handle = dlopen("libcuda.so", RTLD_LAZY);
if (!handle) return -1;
dlclose(handle);
handle = dlopen("libnvidia-encode.so.1", RTLD_LAZY);
if (!handle) handle = dlopen("libnvidia-encode.so", RTLD_LAZY);
if (!handle) return -1;
dlclose(handle);
return 0;
}
// AMD 检测 - 检查 AMF 运行时库
int linux_support_amd() {
void *handle = dlopen("libamfrt64.so.1", RTLD_LAZY);
// 成功加载则返回 0
if (!handle) return -1;
dlclose(handle);
return 0;
}
// Intel 检测 - 检查 VPL/MFX 库
@@ -379,11 +374,6 @@ bool set_lantency_free(void *priv_data, const std::string &name) {
name.find("vaapi") != std::string::npos) {
av_opt_set(priv_data, "async_depth", "1", 0);
}
// VideoToolbox: 实时模式
if (name.find("videotoolbox") != std::string::npos) {
av_opt_set_int(priv_data, "realtime", 1, 0);
av_opt_set_int(priv_data, "prio_speed", 1, 0);
}
// libvpx: 实时模式
if (name.find("libvpx") != std::string::npos) {
av_opt_set(priv_data, "deadline", "realtime", 0);
@@ -394,86 +384,19 @@ bool set_lantency_free(void *priv_data, const std::string &name) {
}
```
## 5. 混流模块 (Mux)
## 5. 构建系统
### 5.1 功能概述
混流模块提供将编码后的视频流写入容器格式 (MP4/MKV) 的功能。
### 5.2 Rust API
```rust
// libs/hwcodec/src/mux.rs
pub struct MuxContext {
pub filename: String, // 输出文件名
pub width: usize, // 视频宽度
pub height: usize, // 视频高度
pub is265: bool, // 是否为 H.265
pub framerate: usize, // 帧率
}
pub struct Muxer {
inner: *mut c_void, // C++ Muxer 指针
pub ctx: MuxContext,
start: Instant, // 开始时间
}
impl Muxer {
pub fn new(ctx: MuxContext) -> Result<Self, ()>;
pub fn write_video(&mut self, data: &[u8], key: bool) -> Result<(), i32>;
pub fn write_tail(&mut self) -> Result<(), i32>;
}
```
### 5.3 C++ 实现
```cpp
// libs/hwcodec/cpp/mux/mux.cpp
class Muxer {
OutputStream video_st; // 视频流
AVFormatContext *oc = NULL; // 格式上下文
int framerate;
int64_t start_ms; // 起始时间戳
int64_t last_pts; // 上一帧 PTS
int got_first; // 是否收到第一帧
bool init(const char *filename, int width, int height,
int is265, int framerate);
int write_video_frame(const uint8_t *data, int len,
int64_t pts_ms, int key);
};
```
**写入流程**:
```
write_video_frame()
├── 检查是否为关键帧 (第一帧必须是关键帧)
├── 计算 PTS (相对于 start_ms)
├── 填充 AVPacket
├── av_packet_rescale_ts() (ms → stream timebase)
└── av_write_frame() → 写入文件
```
## 6. 构建系统
### 6.1 Cargo.toml 配置
### 5.1 Cargo.toml 配置
```toml
[package]
name = "hwcodec"
version = "0.7.1"
version = "0.8.0"
edition = "2021"
description = "Hardware video codec for IP-KVM (Windows/Linux)"
[features]
default = []
vram = [] # GPU VRAM 直接编解码 (Windows only)
[dependencies]
log = "0.4"
@@ -486,7 +409,7 @@ cc = "1.0" # C++ 编译
bindgen = "0.59" # FFI 绑定生成
```
### 6.2 构建流程 (build.rs)
### 5.2 构建流程 (build.rs)
```
build.rs
@@ -494,57 +417,61 @@ build.rs
├── build_common()
│ ├── 生成 common_ffi.rs (bindgen)
│ ├── 编译平台相关 C++ 代码
│ └── 链接系统库 (d3d11, dxgi, stdc++)
│ └── 链接系统库 (stdc++)
── ffmpeg::build_ffmpeg()
├── 生成 ffmpeg_ffi.rs
├── 链接 FFmpeg 库 (VCPKG 或 pkg-config)
── build_ffmpeg_ram()
└── 编译 ffmpeg_ram_encode.cpp, ffmpeg_ram_decode.cpp
│ ├── build_ffmpeg_vram() [vram feature]
│ │ └── 编译 ffmpeg_vram_encode.cpp, ffmpeg_vram_decode.cpp
│ └── build_mux()
│ └── 编译 mux.cpp
└── sdk::build_sdk() [Windows + vram feature]
├── build_nv() - NVIDIA SDK
├── build_amf() - AMD AMF
└── build_mfx() - Intel MFX
── ffmpeg::build_ffmpeg()
├── 生成 ffmpeg_ffi.rs
├── 链接 FFmpeg 库 (VCPKG 或 pkg-config)
── build_ffmpeg_ram()
└── 编译 ffmpeg_ram_encode.cpp, ffmpeg_ram_decode.cpp
```
### 6.3 FFmpeg 链接方式
### 5.3 FFmpeg 链接方式
| 方式 | 平台 | 条件 |
|------|------|------|
| VCPKG 静态链接 | 跨平台 | 设置 `VCPKG_ROOT` 环境变量 |
| pkg-config 动态链接 | Linux | 默认方式 |
## 7. 外部依赖
## 6. 与原版 hwcodec 的区别
### 7.1 SDK 版本
针对 One-KVM IP-KVM 场景,对原版 RustDesk hwcodec 进行了以下简化:
| SDK | 版本 | 用途 |
|-----|------|------|
| nv-codec-headers | n12.1.14.0 | NVIDIA 编码头文件 |
| Video_Codec_SDK | 12.1.14 | NVIDIA 编解码 SDK |
| AMF | v1.4.35 | AMD Advanced Media Framework |
| MediaSDK | 22.5.4 | Intel Media SDK |
### 6.1 移除的功能
### 7.2 FFmpeg 依赖库
| 移除项 | 原因 |
|--------|------|
| VRAM 模块 | IP-KVM 不需要 GPU 显存直接编解码 |
| Mux 模块 | IP-KVM 不需要录制到文件 |
| macOS 支持 | IP-KVM 目标平台不包含 macOS |
| Android 支持 | IP-KVM 目标平台不包含 Android |
| 外部 SDK | 简化构建,减少依赖 |
| 多格式解码 | IP-KVM 仅需 MJPEG 解码 |
```
libavcodec - 编解码核心
libavutil - 工具函数
libavformat - 容器格式
libswscale - 图像缩放转换
```
### 6.2 保留的功能
## 8. 总结
| 保留项 | 用途 |
|--------|------|
| FFmpeg RAM 编码 | WebRTC 视频编码 |
| FFmpeg RAM 解码 | MJPEG 采集卡解码 |
| 硬件加速编码 | 低延迟高效编码 |
| 软件编码后备 | 无硬件加速时的兜底方案 |
hwcodec 库通过 Rust/C++ 混合架构,在保证内存安全的同时实现了高性能的视频编解码。其核心设计特点包括:
### 6.3 代码量对比
1. **统一的编解码器 API**: 无论使用硬件还是软件编解码,上层 API 保持一致
| 指标 | 原版 | 简化版 | 减少 |
|------|------|--------|------|
| 外部 SDK | ~9MB | 0 | 100% |
| C++ 文件 | ~30 | ~10 | ~67% |
| Rust 模块 | 6 | 3 | 50% |
## 7. 总结
hwcodec 库通过 Rust/C++ 混合架构,在保证内存安全的同时实现了高性能的视频编解码。针对 One-KVM IP-KVM 场景的优化设计特点包括:
1. **精简的编解码器 API**: 解码仅支持 MJPEG编码支持多种硬件加速
2. **自动硬件检测**: 运行时自动检测并选择最优的硬件加速后端
3. **优先级系统**: 基于质量和性能为不同编码器分配优先级
4. **低延迟优化**: 针对实时流媒体场景进行了专门优化
5. **跨平台支持**: 覆盖主流操作系统和 GPU 厂商
5. **简化的构建系统**: 无需外部 SDK仅依赖系统 FFmpeg
6. **Windows/Linux 跨平台**: 支持 x86_64、ARM64、ARMv7 架构

View File

@@ -9,7 +9,7 @@
```rust
pub struct EncodeContext {
pub name: String, // 编码器名称
pub mc_name: Option<String>, // MediaCodec 名称 (Android)
pub mc_name: Option<String>, // MediaCodec 名称 (保留字段)
pub width: i32, // 视频宽度 (必须为偶数)
pub height: i32, // 视频高度 (必须为偶数)
pub pixfmt: AVPixelFormat, // 像素格式
@@ -58,7 +58,6 @@ pub struct EncodeContext {
| `hevc_rkmpp` | H.265 | Rockchip MPP | Linux |
| `h264_v4l2m2m` | H.264 | V4L2 M2M | Linux |
| `hevc_v4l2m2m` | H.265 | V4L2 M2M | Linux |
| `hevc_videotoolbox` | H.265 | VideoToolbox | macOS |
| `h264` | H.264 | 软件 (x264) | 全平台 |
| `hevc` | H.265 | 软件 (x265) | 全平台 |
| `libvpx` | VP8 | 软件 | 全平台 |
@@ -161,51 +160,44 @@ for encoder in available_encoders {
## 2. 解码器 API
### 2.1 解码器初始化
### 2.1 IP-KVM 专用设计
在 One-KVM IP-KVM 场景中,解码器仅支持 MJPEG 软件解码。这是因为视频采集卡输出的格式是 MJPEG不需要其他格式的硬件解码支持。
### 2.2 解码器初始化
#### DecodeContext 参数
```rust
pub struct DecodeContext {
pub name: String, // 解码器名称
pub device_type: AVHWDeviceType, // 硬件设备类型
pub name: String, // 解码器名称 ("mjpeg")
pub device_type: AVHWDeviceType, // 硬件设备类型 (NONE)
pub thread_count: i32, // 解码线程数
}
```
#### 硬件设备类型
| AVHWDeviceType | 说明 |
|----------------|------|
| `AV_HWDEVICE_TYPE_NONE` | 软件解码 |
| `AV_HWDEVICE_TYPE_CUDA` | NVIDIA CUDA |
| `AV_HWDEVICE_TYPE_VAAPI` | Linux VAAPI |
| `AV_HWDEVICE_TYPE_D3D11VA` | Windows D3D11 |
| `AV_HWDEVICE_TYPE_VIDEOTOOLBOX` | macOS VideoToolbox |
| `AV_HWDEVICE_TYPE_MEDIACODEC` | Android MediaCodec |
### 2.2 创建解码器
### 2.3 创建解码器
```rust
use hwcodec::ffmpeg_ram::decode::{Decoder, DecodeContext};
use hwcodec::ffmpeg::AVHWDeviceType;
let ctx = DecodeContext {
name: "h264".to_string(),
device_type: AVHWDeviceType::AV_HWDEVICE_TYPE_VAAPI,
name: "mjpeg".to_string(),
device_type: AVHWDeviceType::AV_HWDEVICE_TYPE_NONE,
thread_count: 4,
};
let decoder = Decoder::new(ctx)?;
```
### 2.3 解码帧
### 2.4 解码帧
```rust
// 输入编码数据
let encoded_packet: Vec<u8> = receive_encoded_data();
// 输入 MJPEG 编码数据
let mjpeg_data: Vec<u8> = receive_mjpeg_frame();
match decoder.decode(&encoded_packet) {
match decoder.decode(&mjpeg_data) {
Ok(frames) => {
for frame in frames.iter() {
println!("Decoded: {}x{}, format={:?}, key={}",
@@ -214,7 +206,7 @@ match decoder.decode(&encoded_packet) {
// 访问 YUV 数据
let y_plane = &frame.data[0];
let u_plane = &frame.data[1];
let v_plane = &frame.data[2]; // 仅 YUV420P
let v_plane = &frame.data[2];
}
}
Err(code) => {
@@ -223,7 +215,7 @@ match decoder.decode(&encoded_packet) {
}
```
### 2.4 DecodeFrame 结构体
### 2.5 DecodeFrame 结构体
```rust
pub struct DecodeFrame {
@@ -246,7 +238,7 @@ pub struct DecodeFrame {
| `NV12` | 2 | Y | UV (交错) | - |
| `NV21` | 2 | Y | VU (交错) | - |
### 2.5 检测可用解码器
### 2.6 获取可用解码器
```rust
use hwcodec::ffmpeg_ram::decode::Decoder;
@@ -256,6 +248,9 @@ for decoder in available_decoders {
println!("Available: {} (format: {:?}, hwdevice: {:?})",
decoder.name, decoder.format, decoder.hwdevice);
}
// 输出:
// Available: mjpeg (format: MJPEG, hwdevice: AV_HWDEVICE_TYPE_NONE)
```
## 3. 码率控制模式
@@ -287,7 +282,6 @@ pub enum RateControl {
| amf | ✓ | ✓ (低延迟) | ✗ |
| qsv | ✓ | ✓ | ✗ |
| vaapi | ✓ | ✓ | ✗ |
| mediacodec | ✓ | ✓ | ✓ |
## 4. 质量等级
@@ -310,45 +304,9 @@ pub enum Quality {
| Medium | p4 | balanced | medium |
| Low | p1 | speed | veryfast |
## 5. 混流器 API
## 5. 错误处理
### 5.1 创建混流器
```rust
use hwcodec::mux::{Muxer, MuxContext};
let ctx = MuxContext {
filename: "/tmp/output.mp4".to_string(),
width: 1920,
height: 1080,
is265: false, // H.264
framerate: 30,
};
let muxer = Muxer::new(ctx)?;
```
### 5.2 写入视频帧
```rust
// 编码后的帧数据
let encoded_data: Vec<u8> = encoder.encode(...)?;
let is_keyframe = true;
muxer.write_video(&encoded_data, is_keyframe)?;
```
### 5.3 完成写入
```rust
// 写入文件尾
muxer.write_tail()?;
// muxer 被 drop 时自动释放资源
```
## 6. 错误处理
### 6.1 错误码
### 5.1 错误码
| 错误码 | 常量 | 说明 |
|--------|------|------|
@@ -356,7 +314,7 @@ muxer.write_tail()?;
| -1 | `HWCODEC_ERR_COMMON` | 通用错误 |
| -2 | `HWCODEC_ERR_HEVC_COULD_NOT_FIND_POC` | HEVC 解码参考帧丢失 |
### 6.2 常见错误处理
### 5.2 常见错误处理
```rust
match encoder.encode(&yuv_data, pts) {
@@ -372,9 +330,9 @@ match encoder.encode(&yuv_data, pts) {
}
```
## 7. 最佳实践
## 6. 最佳实践
### 7.1 编码器选择策略
### 6.1 编码器选择策略
```rust
fn select_best_encoder(
@@ -399,7 +357,7 @@ fn select_best_encoder(
}
```
### 7.2 帧内存布局
### 6.2 帧内存布局
```rust
// 获取 NV12 帧布局信息
@@ -417,7 +375,7 @@ let mut buffer = vec![0u8; length as usize];
// 填充 UV 平面: buffer[offset[0]..length]
```
### 7.3 关键帧控制
### 6.3 关键帧控制
```rust
let mut frame_count = 0;
@@ -433,7 +391,7 @@ loop {
}
```
### 7.4 线程安全
### 6.4 线程安全
```rust
// Decoder 实现了 Send + Sync
@@ -443,3 +401,81 @@ unsafe impl Sync for Decoder {}
// 可以安全地在多线程间传递
let decoder = Arc::new(Mutex::new(Decoder::new(ctx)?));
```
## 7. IP-KVM 典型使用场景
### 7.1 视频采集和转码流程
```
USB 采集卡 (MJPEG)
┌─────────────────┐
│ MJPEG Decoder │ ◄── Decoder::new("mjpeg")
│ (软件解码) │
└────────┬────────┘
│ YUV420P
┌─────────────────┐
│ H264 Encoder │ ◄── Encoder::new("h264_vaapi")
│ (硬件加速) │
└────────┬────────┘
│ H264 NAL
WebRTC 传输
```
### 7.2 完整示例
```rust
use hwcodec::ffmpeg_ram::decode::{Decoder, DecodeContext};
use hwcodec::ffmpeg_ram::encode::{Encoder, EncodeContext};
use hwcodec::ffmpeg::AVHWDeviceType;
// 创建 MJPEG 解码器
let decode_ctx = DecodeContext {
name: "mjpeg".to_string(),
device_type: AVHWDeviceType::AV_HWDEVICE_TYPE_NONE,
thread_count: 4,
};
let mut decoder = Decoder::new(decode_ctx)?;
// 检测并选择最佳编码器
let encode_ctx = EncodeContext {
name: String::new(),
width: 1920,
height: 1080,
// ...
};
let available = Encoder::available_encoders(encode_ctx.clone(), None);
let best_h264 = available.iter()
.filter(|e| e.format == DataFormat::H264)
.min_by_key(|e| e.priority)
.expect("No H264 encoder available");
// 使用最佳编码器创建实例
let encode_ctx = EncodeContext {
name: best_h264.name.clone(),
..encode_ctx
};
let mut encoder = Encoder::new(encode_ctx)?;
// 处理循环
loop {
let mjpeg_frame = capture_frame();
// 解码 MJPEG -> YUV
let decoded = decoder.decode(&mjpeg_frame)?;
// 编码 YUV -> H264
for frame in decoded {
let yuv_data = frame.data.concat();
let encoded = encoder.encode(&yuv_data, pts)?;
// 发送编码数据
for packet in encoded {
send_to_webrtc(packet.data);
}
}
}
```

View File

@@ -35,7 +35,7 @@
每个检测到的硬件编码器都会进行实际编码测试:
```rust
// libs/hwcodec/src/ffmpeg_ram/encode.rs:358-450
// libs/hwcodec/src/ffmpeg_ram/encode.rs
// 生成测试用 YUV 数据
let yuv = Encoder::dummy_yuv(ctx.clone())?;
@@ -47,7 +47,7 @@ match Encoder::new(c) {
match encoder.encode(&yuv, 0) {
Ok(frames) => {
let elapsed = start.elapsed().as_millis();
// 验证: 必须产生 1 帧且为关键帧,且在 1 秒内完成
// 验证: 必须产生 1 帧且为关键帧,且在超时时间内完成
if frames.len() == 1 && frames[0].key == 1
&& elapsed < TEST_TIMEOUT_MS {
res.push(codec);
@@ -64,27 +64,35 @@ match Encoder::new(c) {
### 2.1 检测机制 (Linux)
使用简化的动态库检测方法,无需 CUDA SDK 依赖:
```cpp
// libs/hwcodec/cpp/common/platform/linux/linux.cpp:57-73
// libs/hwcodec/cpp/common/platform/linux/linux.cpp
int linux_support_nv() {
CudaFunctions *cuda_dl = NULL;
NvencFunctions *nvenc_dl = NULL;
CuvidFunctions *cvdl = NULL;
// 检测 CUDA 运行时库
void *handle = dlopen("libcuda.so.1", RTLD_LAZY);
if (!handle) {
handle = dlopen("libcuda.so", RTLD_LAZY);
}
if (!handle) {
LOG_TRACE("NVIDIA: libcuda.so not found");
return -1;
}
dlclose(handle);
// 加载 CUDA 动态
if (cuda_load_functions(&cuda_dl, NULL) < 0)
throw "cuda_load_functions failed";
// 检测 NVENC 编码
handle = dlopen("libnvidia-encode.so.1", RTLD_LAZY);
if (!handle) {
handle = dlopen("libnvidia-encode.so", RTLD_LAZY);
}
if (!handle) {
LOG_TRACE("NVIDIA: libnvidia-encode.so not found");
return -1;
}
dlclose(handle);
// 加载 NVENC 动态库
if (nvenc_load_functions(&nvenc_dl, NULL) < 0)
throw "nvenc_load_functions failed";
// 加载 CUVID (解码) 动态库
if (cuvid_load_functions(&cvdl, NULL) < 0)
throw "cuvid_load_functions failed";
// 全部成功则支持 NVIDIA 硬件加速
LOG_TRACE("NVIDIA: driver support detected");
return 0;
}
```
@@ -127,16 +135,15 @@ av_opt_set(priv_data, "rc", "cbr", 0); // 或 "vbr"
### 2.4 依赖库
- `libcuda.so` - CUDA 运行时
- `libnvidia-encode.so` - NVENC 编码器
- `libnvcuvid.so` - NVDEC 解码器
- `libcuda.so` / `libcuda.so.1` - CUDA 运行时
- `libnvidia-encode.so` / `libnvidia-encode.so.1` - NVENC 编码器
## 3. AMD AMF
### 3.1 检测机制 (Linux)
```cpp
// libs/hwcodec/cpp/common/platform/linux/linux.cpp:75-91
// libs/hwcodec/cpp/common/platform/linux/linux.cpp
int linux_support_amd() {
#if defined(__x86_64__) || defined(__aarch64__)
@@ -186,26 +193,12 @@ av_opt_set(priv_data, "rc", "vbr_latency", 0); // 低延迟 VBR
- `libamfrt64.so.1` (64位) 或 `libamfrt32.so.1` (32位)
### 3.4 外部 SDK
```
externals/AMF_v1.4.35/
├── amf/
│ ├── public/common/ # 公共代码
│ │ ├── AMFFactory.cpp
│ │ ├── Thread.cpp
│ │ └── TraceAdapter.cpp
│ └── public/include/ # 头文件
│ ├── components/ # 组件定义
│ └── core/ # 核心定义
```
## 4. Intel QSV/MFX
### 4.1 检测机制 (Linux)
```cpp
// libs/hwcodec/cpp/common/platform/linux/linux.cpp:93-107
// libs/hwcodec/cpp/common/platform/linux/linux.cpp
int linux_support_intel() {
const char *libs[] = {
@@ -262,18 +255,7 @@ c->strict_std_compliance = FF_COMPLIANCE_UNOFFICIAL;
### 4.3 限制
- QSV 不支持 `YUV420P` 像素格式,必须使用 `NV12`
- 在 Windows 平台完全支持
### 4.4 外部 SDK
```
externals/MediaSDK_22.5.4/
├── api/
│ ├── include/ # MFX 头文件
│ ├── mfx_dispatch/ # MFX 调度器
│ └── mediasdk_structures/ # 数据结构
└── samples/sample_common/ # 示例代码
```
- One-KVM 简化版中仅 Windows 平台完全支持
## 5. VAAPI (Linux)
@@ -362,17 +344,20 @@ avcodec_receive_packet(c_, pkt_) // 获取编码数据
### 6.1 检测机制
```cpp
// libs/hwcodec/cpp/common/platform/linux/linux.cpp:122-137
// libs/hwcodec/cpp/common/platform/linux/linux.cpp
int linux_support_rkmpp() {
// 检测 MPP 服务设备
if (access("/dev/mpp_service", F_OK) == 0) {
LOG_TRACE("RKMPP: Found /dev/mpp_service");
return 0; // MPP 可用
}
// 备用: 检测 RGA 设备
if (access("/dev/rga", F_OK) == 0) {
LOG_TRACE("RKMPP: Found /dev/rga");
return 0; // MPP 可能可用
}
LOG_TRACE("RKMPP: No Rockchip MPP device found");
return -1; // MPP 不可用
}
```
@@ -395,7 +380,7 @@ int linux_support_rkmpp() {
### 7.1 检测机制
```cpp
// libs/hwcodec/cpp/common/platform/linux/linux.cpp:139-163
// libs/hwcodec/cpp/common/platform/linux/linux.cpp
int linux_support_v4l2m2m() {
const char *m2m_devices[] = {
@@ -409,10 +394,12 @@ int linux_support_v4l2m2m() {
int fd = open(m2m_devices[i], O_RDWR | O_NONBLOCK);
if (fd >= 0) {
close(fd);
LOG_TRACE("V4L2 M2M: Found device " + m2m_devices[i]);
return 0; // V4L2 M2M 可用
}
}
}
LOG_TRACE("V4L2 M2M: No M2M device found");
return -1;
}
```
@@ -429,75 +416,9 @@ int linux_support_v4l2m2m() {
- 通用 ARM SoC (Allwinner, Amlogic 等)
- 支持 V4L2 M2M API 的设备
## 8. Apple VideoToolbox
## 8. 硬件加速优先级
### 8.1 检测机制 (macOS)
```rust
// libs/hwcodec/src/common.rs:57-87
#[cfg(target_os = "macos")]
pub(crate) fn get_video_toolbox_codec_support() -> (bool, bool, bool, bool) {
extern "C" {
fn checkVideoToolboxSupport(
h264_encode: *mut i32,
h265_encode: *mut i32,
h264_decode: *mut i32,
h265_decode: *mut i32,
) -> c_void;
}
let mut h264_encode = 0;
let mut h265_encode = 0;
let mut h264_decode = 0;
let mut h265_decode = 0;
unsafe {
checkVideoToolboxSupport(&mut h264_encode, &mut h265_encode,
&mut h264_decode, &mut h265_decode);
}
(h264_encode == 1, h265_encode == 1,
h264_decode == 1, h265_decode == 1)
}
```
### 8.2 编码配置
```cpp
// libs/hwcodec/cpp/common/util.cpp
// VideoToolbox 低延迟配置
if (name.find("videotoolbox") != std::string::npos) {
av_opt_set_int(priv_data, "realtime", 1, 0);
av_opt_set_int(priv_data, "prio_speed", 1, 0);
}
// 强制硬件编码
if (name.find("videotoolbox") != std::string::npos) {
av_opt_set_int(priv_data, "allow_sw", 0, 0);
}
```
### 8.3 限制
- H.264 编码不稳定,已禁用
- 仅支持 H.265 编码
- 完全支持 H.264/H.265 解码
### 8.4 依赖框架
```
CoreFoundation
CoreVideo
CoreMedia
VideoToolbox
AVFoundation
```
## 9. 硬件加速优先级
### 9.1 优先级定义
### 8.1 优先级定义
```rust
pub enum Priority {
@@ -509,7 +430,7 @@ pub enum Priority {
}
```
### 9.2 各编码器优先级
### 8.2 各编码器优先级
| 优先级 | 编码器 |
|--------|--------|
@@ -517,10 +438,10 @@ pub enum Priority {
| Good (1) | vaapi, v4l2m2m |
| Soft (3) | x264, x265, libvpx |
### 9.3 选择策略
### 8.3 选择策略
```rust
// libs/hwcodec/src/ffmpeg_ram/mod.rs:49-117
// libs/hwcodec/src/ffmpeg_ram/mod.rs
pub fn prioritized(coders: Vec<CodecInfo>) -> CodecInfos {
// 对于每种格式,选择优先级最高的编码器
@@ -537,9 +458,9 @@ pub fn prioritized(coders: Vec<CodecInfo>) -> CodecInfos {
}
```
## 10. 故障排除
## 9. 故障排除
### 10.1 NVIDIA
### 9.1 NVIDIA
```bash
# 检查 NVIDIA 驱动
@@ -553,7 +474,7 @@ ldconfig -p | grep cuda
ldconfig -p | grep nvidia-encode
```
### 10.2 AMD
### 9.2 AMD
```bash
# 检查 AMD 驱动
@@ -563,7 +484,7 @@ lspci | grep AMD
ldconfig -p | grep amf
```
### 10.3 Intel
### 9.3 Intel
```bash
# 检查 Intel 驱动
@@ -574,7 +495,7 @@ ldconfig -p | grep mfx
ldconfig -p | grep vpl
```
### 10.4 VAAPI
### 9.4 VAAPI
```bash
# 安装 vainfo
@@ -593,7 +514,7 @@ vainfo
# ...
```
### 10.5 Rockchip MPP
### 9.5 Rockchip MPP
```bash
# 检查 MPP 设备
@@ -604,7 +525,7 @@ ls -la /dev/rga
ldconfig -p | grep rockchip_mpp
```
### 10.6 V4L2 M2M
### 9.6 V4L2 M2M
```bash
# 列出 V4L2 设备
@@ -613,3 +534,28 @@ v4l2-ctl --list-devices
# 检查设备能力
v4l2-ctl -d /dev/video10 --all
```
## 10. 性能优化建议
### 10.1 编码器选择
1. **优先使用硬件编码**: NVENC > AMF > QSV > VAAPI > V4L2 M2M > 软件
2. **ARM 设备**: 优先检测 RKMPP其次 V4L2 M2M
3. **x86 设备**: 根据 GPU 厂商自动选择
### 10.2 低延迟配置
所有硬件编码器都启用了低延迟优化:
| 编码器 | 配置 |
|--------|------|
| NVENC | `delay=0` |
| AMF | `query_timeout=1000` |
| QSV | `async_depth=1` |
| VAAPI | `async_depth=1` |
| libvpx | `deadline=realtime`, `cpu-used=6` |
### 10.3 码率控制
- **实时流**: 推荐 CBR 模式,保证稳定码率
- **GOP 大小**: 建议 30-60 帧 (1-2秒),平衡延迟和压缩效率

View File

@@ -7,43 +7,35 @@ 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/ # 示例程序
── src/ # Rust 源码
├── lib.rs # 库入口
├── common.rs # 公共定义
├── ffmpeg.rs # FFmpeg 集成
── ffmpeg_ram/ # RAM 编解码
├── mod.rs
├── encode.rs
└── decode.rs
└── cpp/ # C++ 源码
├── common/ # 公共代码
│ ├── log.cpp
│ ├── log.h
│ ├── util.cpp
│ ├── util.h
── callback.h
│ ├── common.h
── platform/
── linux/
├── linux.cpp
│ │ └── linux.h
│ └── win/
│ ├── win.cpp
└── win.h
├── ffmpeg_ram/ # FFmpeg RAM 实现
│ ├── ffmpeg_ram_encode.cpp
│ ├── ffmpeg_ram_decode.cpp
│ └── ffmpeg_ram_ffi.h
└── yuv/ # YUV 处理
└── yuv.cpp
```
## 2. Cargo 配置
@@ -53,12 +45,12 @@ libs/hwcodec/
```toml
[package]
name = "hwcodec"
version = "0.7.1"
version = "0.8.0"
edition = "2021"
description = "Hardware video codec for IP-KVM (Windows/Linux)"
[features]
default = []
vram = [] # GPU VRAM 直接编解码 (仅 Windows)
[dependencies]
log = "0.4" # 日志
@@ -72,26 +64,23 @@ bindgen = "0.59" # FFI 绑定生成
[dev-dependencies]
env_logger = "0.10" # 日志输出
rand = "0.8" # 随机数
```
### 2.2 Feature 说明
### 2.2 与原版的区别
| Feature | 说明 | 平台 |
|---------|------|------|
| `default` | 基础功能 | 全平台 |
| `vram` | GPU VRAM 直接编解码 | 仅 Windows |
| 特性 | 原版 (RustDesk) | 简化版 (One-KVM) |
|------|-----------------|------------------|
| `vram` feature | ✓ | ✗ (已移除) |
| 外部 SDK | 需要 | 不需要 |
| 版本号 | 0.7.1 | 0.8.0 |
| 目标平台 | Windows/Linux/macOS/Android | Windows/Linux |
### 2.3 使用方式
```toml
# 基础使用
# 在 One-KVM 项目中使用
[dependencies]
hwcodec = { path = "libs/hwcodec" }
# 启用 VRAM 功能 (Windows)
[dependencies]
hwcodec = { path = "libs/hwcodec", features = ["vram"] }
```
## 3. 构建脚本详解 (build.rs)
@@ -109,11 +98,7 @@ fn main() {
// 2. 构建 FFmpeg 相关模块
ffmpeg::build_ffmpeg(&mut builder);
// 3. 构建 SDK 模块 (Windows + vram feature)
#[cfg(all(windows, feature = "vram"))]
sdk::build_sdk(&mut builder);
// 4. 编译生成静态库
// 3. 编译生成静态库
builder.static_crt(true).compile("hwcodec");
}
```
@@ -139,9 +124,6 @@ fn build_common(builder: &mut Build) {
#[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"),
@@ -168,11 +150,8 @@ mod ffmpeg {
// 链接系统库
link_os();
// 构建模块
// 构建 FFmpeg RAM 模块
build_ffmpeg_ram(builder);
#[cfg(feature = "vram")]
build_ffmpeg_vram(builder);
build_mux(builder);
}
}
```
@@ -186,8 +165,6 @@ 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"),
};
@@ -239,57 +216,12 @@ fn link_os() {
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)
```rust
#[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"),
]);
}
}
```
@@ -332,40 +264,10 @@ impl bindgen::callbacks::ParseCallbacks for CommonCallbacks {
| `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. 平台构建指南
### 5.1 Git 子模块
```bash
# 初始化子模块
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 构建
### 5.1 Linux 构建
```bash
# 安装 FFmpeg 开发库
@@ -374,11 +276,14 @@ sudo apt install libavcodec-dev libavformat-dev libavutil-dev libswscale-dev
# 安装其他依赖
sudo apt install libdrm-dev libx11-dev pkg-config
# 安装 clang (bindgen 需要)
sudo apt install clang libclang-dev
# 构建
cargo build --release -p hwcodec
```
### 6.2 Windows 构建 (VCPKG)
### 5.2 Windows 构建 (VCPKG)
```powershell
# 安装 VCPKG
@@ -392,26 +297,11 @@ cd vcpkg
# 设置环境变量
$env:VCPKG_ROOT = "C:\path\to\vcpkg"
# 构建
cargo build --release -p hwcodec --features vram
```
### 6.3 macOS 构建
```bash
# 安装 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 交叉编译
### 5.3 交叉编译
```bash
# 安装 cross
@@ -424,9 +314,9 @@ cross build --release -p hwcodec --target aarch64-unknown-linux-gnu
cross build --release -p hwcodec --target armv7-unknown-linux-gnueabihf
```
## 7. 集成到 One-KVM
## 6. 集成到 One-KVM
### 7.1 依赖配置
### 6.1 依赖配置
```toml
# Cargo.toml
@@ -434,12 +324,12 @@ cross build --release -p hwcodec --target armv7-unknown-linux-gnueabihf
hwcodec = { path = "libs/hwcodec" }
```
### 7.2 使用示例
### 6.2 使用示例
```rust
use hwcodec::ffmpeg_ram::encode::{Encoder, EncodeContext};
use hwcodec::ffmpeg_ram::decode::{Decoder, DecodeContext};
use hwcodec::ffmpeg::AVPixelFormat;
use hwcodec::ffmpeg::{AVPixelFormat, AVHWDeviceType};
// 检测可用编码器
let encoders = Encoder::available_encoders(ctx, None);
@@ -458,31 +348,41 @@ let encoder = Encoder::new(EncodeContext {
// 编码
let frames = encoder.encode(&yuv_data, pts_ms)?;
// 创建 MJPEG 解码器 (IP-KVM 专用)
let decoder = Decoder::new(DecodeContext {
name: "mjpeg".to_string(),
device_type: AVHWDeviceType::AV_HWDEVICE_TYPE_NONE,
thread_count: 4,
})?;
// 解码
let frames = decoder.decode(&mjpeg_data)?;
```
### 7.3 日志集成
### 6.3 日志集成
```rust
// hwcodec 使用 log crate与 One-KVM 日志系统兼容
use log::{debug, info, warn, error};
// C++ 层日志通过回调传递
// C++ 层日志通过回调传递到 Rust
#[no_mangle]
pub extern "C" fn hwcodec_log(level: i32, message: *const c_char) {
pub extern "C" fn hwcodec_av_log_callback(level: i32, message: *const c_char) {
// 转发到 Rust log 系统
match level {
0 => error!("{}", message),
1 => warn!("{}", message),
2 => info!("{}", message),
3 => debug!("{}", message),
4 => trace!("{}", message),
AV_LOG_ERROR => error!("{}", message),
AV_LOG_WARNING => warn!("{}", message),
AV_LOG_INFO => info!("{}", message),
AV_LOG_DEBUG => debug!("{}", message),
_ => {}
}
}
```
## 8. 故障排除
## 7. 故障排除
### 8.1 编译错误
### 7.1 编译错误
**FFmpeg 未找到**:
```
@@ -502,7 +402,7 @@ error: failed to run custom build command for `hwcodec`
sudo apt install clang libclang-dev
```
### 8.2 链接错误
### 7.2 链接错误
**符号未定义**:
```
@@ -521,7 +421,7 @@ sudo ldconfig
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
```
### 8.3 运行时错误
### 7.3 运行时错误
**硬件编码器不可用**:
```
@@ -537,3 +437,41 @@ Encoder h264_vaapi test failed
avcodec_receive_frame failed, ret = -11
```
解决: 这通常表示需要更多输入数据 (EAGAIN),是正常行为
## 8. 与原版 RustDesk hwcodec 的构建差异
### 8.1 移除的构建步骤
| 步骤 | 原因 |
|------|------|
| `build_mux()` | 移除了 Mux 模块 |
| `build_ffmpeg_vram()` | 移除了 VRAM 模块 |
| `sdk::build_sdk()` | 移除了外部 SDK 依赖 |
| macOS 框架链接 | 移除了 macOS 支持 |
| Android NDK 链接 | 移除了 Android 支持 |
### 8.2 简化的构建流程
```
原版构建流程:
build.rs
├── build_common()
├── ffmpeg::build_ffmpeg()
│ ├── build_ffmpeg_ram()
│ ├── build_ffmpeg_vram() [已移除]
│ └── build_mux() [已移除]
└── sdk::build_sdk() [已移除]
简化版构建流程:
build.rs
├── build_common()
└── ffmpeg::build_ffmpeg()
└── build_ffmpeg_ram()
```
### 8.3 优势
1. **更快的编译**: 无需编译外部 SDK 代码
2. **更少的依赖**: 无需下载 ~9MB 的外部 SDK
3. **更简单的维护**: 代码量减少约 67%
4. **更小的二进制**: 不包含未使用的功能