Files
One-KVM/docs/report/hwcodec/02-hardware-acceleration.md
mofeng-git d0e2e13b35 refactor(hwcodec): 简化 hwcodec 库以适配 IP-KVM 场景
移除 IP-KVM 场景不需要的功能模块:
- 移除 VRAM 模块 (GPU 显存直接编解码)
- 移除 Mux 模块 (视频混流)
- 移除 macOS/Android 平台支持
- 移除外部 SDK 依赖 (~9MB)
- 移除开发工具和示例程序

简化解码器为仅支持 MJPEG (采集卡输出格式)
简化 NVIDIA 检测代码 (使用 dlopen 替代 SDK)
更新版本号至 0.8.0
更新相关技术文档
2025-12-31 19:47:08 +08:00

562 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# hwcodec 硬件加速详解
## 1. 硬件加速架构
### 1.1 整体流程
```
┌─────────────────────────────────────────────────────────────┐
│ 应用层 (Rust) │
│ ┌─────────────────────────────────────────────────────────┐│
│ │ Encoder::available_encoders() → 自动检测可用硬件编码器 ││
│ └─────────────────────────────────────────────────────────┘│
└────────────────────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 硬件检测层 (C++) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────────┐│
│ │linux_ │ │linux_ │ │linux_ │ │linux_support_ ││
│ │support_nv│ │support_ │ │support_ │ │rkmpp/v4l2m2m ││
│ └────┬─────┘ │amd │ │intel │ └─────────┬────────┘│
│ │ └────┬─────┘ └────┬─────┘ │ │
└───────┼────────────┼────────────┼─────────────────┼─────────┘
│ │ │ │
▼ ▼ ▼ ▼
┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────────────┐
│ CUDA/ │ │ AMF │ │ VPL/MFX │ │ 设备节点检测 │
│ NVENC │ │ Runtime │ │ Library │ │ /dev/mpp_service │
│ 动态库 │ │ 动态库 │ │ 动态库 │ │ /dev/video* │
└───────────┘ └───────────┘ └───────────┘ └───────────────────┘
```
### 1.2 编码器测试验证
每个检测到的硬件编码器都会进行实际编码测试:
```rust
// libs/hwcodec/src/ffmpeg_ram/encode.rs
// 生成测试用 YUV 数据
let yuv = Encoder::dummy_yuv(ctx.clone())?;
// 尝试创建编码器并编码测试帧
match Encoder::new(c) {
Ok(mut encoder) => {
let start = std::time::Instant::now();
match encoder.encode(&yuv, 0) {
Ok(frames) => {
let elapsed = start.elapsed().as_millis();
// 验证: 必须产生 1 帧且为关键帧,且在超时时间内完成
if frames.len() == 1 && frames[0].key == 1
&& elapsed < TEST_TIMEOUT_MS {
res.push(codec);
}
}
Err(_) => { /* 编码失败,跳过 */ }
}
}
Err(_) => { /* 创建失败,跳过 */ }
}
```
## 2. NVIDIA NVENC/NVDEC
### 2.1 检测机制 (Linux)
使用简化的动态库检测方法,无需 CUDA SDK 依赖:
```cpp
// libs/hwcodec/cpp/common/platform/linux/linux.cpp
int linux_support_nv() {
// 检测 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);
// 检测 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);
LOG_TRACE("NVIDIA: driver support detected");
return 0;
}
```
### 2.2 编码配置
```cpp
// libs/hwcodec/cpp/common/util.cpp
// NVENC 低延迟配置
if (name.find("nvenc") != std::string::npos) {
// 禁用编码延迟
av_opt_set(priv_data, "delay", "0", 0);
}
// GPU 选择
if (name.find("nvenc") != std::string::npos) {
av_opt_set_int(priv_data, "gpu", gpu_index, 0);
}
// 质量预设
switch (quality) {
case Quality_Medium:
av_opt_set(priv_data, "preset", "p4", 0);
break;
case Quality_Low:
av_opt_set(priv_data, "preset", "p1", 0);
break;
}
// 码率控制
av_opt_set(priv_data, "rc", "cbr", 0); // 或 "vbr"
```
### 2.3 环境变量
| 变量 | 说明 |
|------|------|
| `RUSTDESK_HWCODEC_NVENC_GPU` | 指定使用的 GPU 索引 (-1 = 自动) |
### 2.4 依赖库
- `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
int linux_support_amd() {
#if defined(__x86_64__) || defined(__aarch64__)
#define AMF_DLL_NAMEA "libamfrt64.so.1"
#else
#define AMF_DLL_NAMEA "libamfrt32.so.1"
#endif
void *handle = dlopen(AMF_DLL_NAMEA, RTLD_LAZY);
if (!handle) {
return -1; // AMF 不可用
}
dlclose(handle);
return 0; // AMF 可用
}
```
### 3.2 编码配置
```cpp
// libs/hwcodec/cpp/common/util.cpp
// AMF 低延迟配置
if (name.find("amf") != std::string::npos) {
av_opt_set(priv_data, "query_timeout", "1000", 0);
}
// 质量预设
switch (quality) {
case Quality_High:
av_opt_set(priv_data, "quality", "quality", 0);
break;
case Quality_Medium:
av_opt_set(priv_data, "quality", "balanced", 0);
break;
case Quality_Low:
av_opt_set(priv_data, "quality", "speed", 0);
break;
}
// 码率控制
av_opt_set(priv_data, "rc", "cbr", 0); // 恒定码率
av_opt_set(priv_data, "rc", "vbr_latency", 0); // 低延迟 VBR
```
### 3.3 依赖库
- `libamfrt64.so.1` (64位) 或 `libamfrt32.so.1` (32位)
## 4. Intel QSV/MFX
### 4.1 检测机制 (Linux)
```cpp
// libs/hwcodec/cpp/common/platform/linux/linux.cpp
int linux_support_intel() {
const char *libs[] = {
"libvpl.so", // oneVPL (新版)
"libmfx.so", // Media SDK
"libmfx-gen.so.1.2", // 新驱动
"libmfxhw64.so.1" // 旧版驱动
};
for (size_t i = 0; i < sizeof(libs) / sizeof(libs[0]); i++) {
void *handle = dlopen(libs[i], RTLD_LAZY);
if (handle) {
dlclose(handle);
return 0; // 找到可用库
}
}
return -1; // Intel MFX 不可用
}
```
### 4.2 编码配置
```cpp
// libs/hwcodec/cpp/common/util.cpp
// QSV 低延迟配置
if (name.find("qsv") != std::string::npos) {
av_opt_set(priv_data, "async_depth", "1", 0);
}
// QSV 特殊码率配置
if (name.find("qsv") != std::string::npos) {
c->rc_max_rate = c->bit_rate;
c->bit_rate--; // 实现 CBR 效果
}
// 质量预设
switch (quality) {
case Quality_High:
av_opt_set(priv_data, "preset", "veryslow", 0);
break;
case Quality_Medium:
av_opt_set(priv_data, "preset", "medium", 0);
break;
case Quality_Low:
av_opt_set(priv_data, "preset", "veryfast", 0);
break;
}
// 严格标准兼容性 (用于某些特殊设置)
c->strict_std_compliance = FF_COMPLIANCE_UNOFFICIAL;
```
### 4.3 限制
- QSV 不支持 `YUV420P` 像素格式,必须使用 `NV12`
- 在 One-KVM 简化版中仅 Windows 平台完全支持
## 5. VAAPI (Linux)
### 5.1 工作原理
VAAPI (Video Acceleration API) 是 Linux 上的通用硬件视频加速接口:
```
┌─────────────────────────────────────────────────────────────┐
│ Application │
├─────────────────────────────────────────────────────────────┤
│ FFmpeg libavcodec │
├─────────────────────────────────────────────────────────────┤
│ VAAPI (libva) │
├──────────────┬──────────────┬──────────────┬────────────────┤
│ Intel i965 │ Intel iHD │ AMD radeonsi │ NVIDIA VDPAU │
│ (Gen8-) │ (Gen9+) │ │ (via wrapper) │
├──────────────┴──────────────┴──────────────┴────────────────┤
│ Kernel DRM Driver │
├──────────────┬──────────────┬──────────────┬────────────────┤
│ i915 │ amdgpu │ nvidia │ ... │
└──────────────┴──────────────┴──────────────┴────────────────┘
```
### 5.2 编码配置
```cpp
// libs/hwcodec/cpp/common/util.cpp
// VAAPI 低延迟配置
if (name.find("vaapi") != std::string::npos) {
av_opt_set(priv_data, "async_depth", "1", 0);
}
```
### 5.3 硬件上下文初始化
```cpp
// libs/hwcodec/cpp/ffmpeg_ram/ffmpeg_ram_encode.cpp
// 检测 VAAPI 编码器
if (name_.find("vaapi") != std::string::npos) {
hw_device_type_ = AV_HWDEVICE_TYPE_VAAPI;
hw_pixfmt_ = AV_PIX_FMT_VAAPI;
}
// 创建硬件设备上下文
ret = av_hwdevice_ctx_create(&hw_device_ctx_, hw_device_type_,
NULL, // 使用默认设备
NULL, 0);
// 设置硬件帧上下文
set_hwframe_ctx();
// 分配硬件帧
hw_frame_ = av_frame_alloc();
av_hwframe_get_buffer(c_->hw_frames_ctx, hw_frame_, 0);
```
### 5.4 编码流程
```
输入 YUV (CPU 内存)
av_hwframe_transfer_data(hw_frame_, frame_, 0) // CPU → GPU
avcodec_send_frame(c_, hw_frame_) // 发送 GPU 帧
avcodec_receive_packet(c_, pkt_) // 获取编码数据
编码数据 (CPU 内存)
```
### 5.5 依赖库
- `libva.so` - VAAPI 核心库
- `libva-drm.so` - DRM 后端
- `libva-x11.so` - X11 后端 (可选)
## 6. Rockchip MPP
### 6.1 检测机制
```cpp
// 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 不可用
}
```
### 6.2 支持的编码器
| 编码器 | 优先级 | 说明 |
|--------|--------|------|
| `h264_rkmpp` | Best (0) | H.264 硬件编码 |
| `hevc_rkmpp` | Best (0) | H.265 硬件编码 |
### 6.3 适用设备
- Rockchip RK3328 (Onecloud, Chainedbox)
- Rockchip RK3399/RK3588 系列
- 其他 Rockchip SoC
## 7. V4L2 M2M
### 7.1 检测机制
```cpp
// libs/hwcodec/cpp/common/platform/linux/linux.cpp
int linux_support_v4l2m2m() {
const char *m2m_devices[] = {
"/dev/video10", // 常见 M2M 编码设备
"/dev/video11", // 常见 M2M 解码设备
"/dev/video0", // 某些 SoC 使用
};
for (size_t i = 0; i < sizeof(m2m_devices) / sizeof(m2m_devices[0]); i++) {
if (access(m2m_devices[i], F_OK) == 0) {
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;
}
```
### 7.2 支持的编码器
| 编码器 | 优先级 | 说明 |
|--------|--------|------|
| `h264_v4l2m2m` | Good (1) | H.264 V4L2 编码 |
| `hevc_v4l2m2m` | Good (1) | H.265 V4L2 编码 |
### 7.3 适用设备
- 通用 ARM SoC (Allwinner, Amlogic 等)
- 支持 V4L2 M2M API 的设备
## 8. 硬件加速优先级
### 8.1 优先级定义
```rust
pub enum Priority {
Best = 0, // 专用硬件编码器
Good = 1, // 通用硬件加速
Normal = 2, // 基本硬件支持
Soft = 3, // 软件编码
Bad = 4, // 最低优先级
}
```
### 8.2 各编码器优先级
| 优先级 | 编码器 |
|--------|--------|
| Best (0) | nvenc, amf, qsv, rkmpp |
| Good (1) | vaapi, v4l2m2m |
| Soft (3) | x264, x265, libvpx |
### 8.3 选择策略
```rust
// libs/hwcodec/src/ffmpeg_ram/mod.rs
pub fn prioritized(coders: Vec<CodecInfo>) -> CodecInfos {
// 对于每种格式,选择优先级最高的编码器
for coder in coders {
match coder.format {
DataFormat::H264 => {
if h264.is_none() || h264.priority > coder.priority {
h264 = Some(coder);
}
}
// ... 其他格式类似
}
}
}
```
## 9. 故障排除
### 9.1 NVIDIA
```bash
# 检查 NVIDIA 驱动
nvidia-smi
# 检查 NVENC 支持
ls /dev/nvidia*
# 检查 CUDA 库
ldconfig -p | grep cuda
ldconfig -p | grep nvidia-encode
```
### 9.2 AMD
```bash
# 检查 AMD 驱动
lspci | grep AMD
# 检查 AMF 库
ldconfig -p | grep amf
```
### 9.3 Intel
```bash
# 检查 Intel 驱动
vainfo
# 检查 MFX 库
ldconfig -p | grep mfx
ldconfig -p | grep vpl
```
### 9.4 VAAPI
```bash
# 安装 vainfo
sudo apt install vainfo
# 检查 VAAPI 支持
vainfo
# 输出示例:
# libva info: VA-API version 1.14.0
# libva info: Trying to open /usr/lib/x86_64-linux-gnu/dri/iHD_drv_video.so
# vainfo: Driver version: Intel iHD driver for Intel(R) Gen Graphics
# vainfo: Supported profile and entrypoints
# VAProfileH264Main : VAEntrypointVLD
# VAProfileH264Main : VAEntrypointEncSlice
# ...
```
### 9.5 Rockchip MPP
```bash
# 检查 MPP 设备
ls -la /dev/mpp_service
ls -la /dev/rga
# 检查 MPP 库
ldconfig -p | grep rockchip_mpp
```
### 9.6 V4L2 M2M
```bash
# 列出 V4L2 设备
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秒),平衡延迟和压缩效率