From e7d8c93bff2d61ccf6c1c8a12ca8e3bab818071a Mon Sep 17 00:00:00 2001
From: mofeng
Date: Tue, 20 Jan 2026 19:53:15 +0800
Subject: [PATCH 1/6] =?UTF-8?q?docs:=20=E6=96=B0=E6=98=8E?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 305 ++++++++++++++++++++++++++++++++++++++++++--------
build/init.sh | 30 +++--
2 files changed, 278 insertions(+), 57 deletions(-)
diff --git a/README.md b/README.md
index 2ff2c63f..54ad79dd 100644
--- a/README.md
+++ b/README.md
@@ -1,81 +1,294 @@
-# One-KVM
+
+

+
One-KVM
+
Rust 编写的开放轻量 IP-KVM 解决方案,实现 BIOS 级远程管理
-
- 开放轻量的 IP-KVM 解决方案,实现 BIOS 级远程管理
-
+
简体中文
-
- 功能特性 •
- 快速开始
-
+ [](https://github.com/mofeng-git/One-KVM/stargazers)
+ [](https://github.com/mofeng-git/One-KVM/network/members)
+ [](https://github.com/mofeng-git/One-KVM/issues)
+
+
+ 📖 技术文档 •
+ ⚡ 快速开始 •
+ 📊 功能介绍 •
+ 🔁 迁移说明
+
+
---
-## 介绍
+## 📋 目录
-One-KVM 是一个用 Rust 编写的开放轻量的 IP-KVM(基于 IP 的键盘、视频、鼠标)解决方案,让你可以通过网络远程控制计算机,包括 BIOS 级别的操作。
+- [项目概述](#项目概述)
+- [迁移说明](#迁移说明)
+- [功能介绍](#功能介绍)
+- [快速开始](#快速开始)
+- [贡献与反馈](#贡献与反馈)
+- [致谢](#致谢)
+- [许可证](#许可证)
-**当前软件处于开发早期阶段,各种功能和细节还有待完善,欢迎体验,但请勿应用于生产环境。**
+## 📖 项目概述
-## 功能特性
+**One-KVM Rust** 是一个用 Rust 编写的轻量级 IP-KVM 解决方案,可通过网络远程管理服务器和工作站,实现 BIOS 级远程控制。
+
+项目目标:
+
+- **开放**:不绑定特定硬件配置,尽量适配常见 Linux 设备
+- **轻量**:单二进制分发,部署过程更简单
+- **易用**:网页界面完成设备与参数配置,尽量减少手动改配置文件
+
+> **注意:** One-KVM Rust 目前仍处于开发早期阶段,功能与细节会快速迭代,欢迎体验与反馈。
+
+## 🔁 迁移说明
+
+开发重心正在从 **One-KVM Python** 逐步转向 **One-KVM Rust**。
+
+- 如果你在使用 **One-KVM Python(基于 PiKVM)**,请查看 [One-KVM Python 文档](https://docs.one-kvm.cn/python/)
+- One-KVM Rust 相较于 One-KVM Python:**尚未适配 CSI HDMI 采集卡**、**不支持 VNC 访问**,仍处于开发早期阶段
+
+## 📊 功能介绍
### 核心功能
| 功能 | 说明 |
|------|------|
-| 视频采集 | HDMI USB 采集卡支持,提供 MJPEG/H264/H265/VP8/VP9 视频流 |
-| 键鼠控制 | USB OTG HID 或 CH340 + CH39329 HID,支持绝对/相对鼠标模式 |
-| 虚拟U盘 | USB Mass Storage,支持 ISO/IMG 镜像挂载和 Ventoy 虚拟U盘模式 |
+| 视频采集 | HDMI USB 采集卡支持,提供 MJPEG / WebRTC(H.264/H.265/VP8/VP9) |
+| 键鼠控制 | USB OTG HID 或 CH340 + CH9329 HID,支持绝对/相对鼠标模式 |
+| 虚拟媒体 | USB Mass Storage,支持 ISO/IMG 镜像挂载和 Ventoy 虚拟U盘模式 |
| ATX 电源控制 | GPIO 控制电源/重启按钮 |
| 音频传输 | ALSA 采集 + Opus 编码(HTTP/WebRTC) |
### 硬件编码
支持自动检测和选择硬件加速:
-- **VAAPI** - Intel/AMD GPU
-- **RKMPP** - Rockchip SoC (**尚未实现**)
-- **V4L2 M2M** - 通用硬件编码器 (**尚未实现**)
-- **软件编码** - CPU 编码
-### 其他特性
+- **VAAPI**:Intel/AMD GPU
+- **RKMPP**:Rockchip SoC
+- **V4L2 M2M**:通用硬件编码器(尚未实现)
+- **软件编码**:CPU 编码
-- 单二进制部署,依赖更轻量
-- Web UI 配置,无需编辑配置文件,多语言支持 (中文/英文)
-- 内置 Web 终端 (ttyd),内网穿透支持 (gostc),P2P 组网支持 (EasyTier)
+### 扩展能力
-## 快速开始
+- Web UI 配置,多语言支持(中文/英文)
+- 内置 Web 终端(ttyd)内网穿透支持(gostc)、P2P 组网支持(EasyTier)、RustDesk 协议集成(用于跨平台远程访问能力扩展)
-### Docker 运行
+## ⚡ 快速开始
+
+安装方式:Docker / DEB 软件包 / 飞牛 NAS(FPK)。
+
+### 方式一:Docker 安装(推荐)
+
+前提条件:
+
+- Linux 主机已安装 Docker
+- 插好 USB HDMI 采集卡
+- 启用 USB OTG 或插好 CH340+CH9329 HID 线(用于 HID 模拟)
+
+启动容器:
```bash
-docker run -d --privileged \
- --name one-kvm \
- -v /dev:/dev \
- -v /sys/kernel/config:/sys/kernel/config \
- --net=host \
- silentwind0/one-kvm
+docker run --name one-kvm -itd --privileged=true \
+ -v /dev:/dev -v /sys/:/sys \
+ --net=host \
+ silentwind0/one-kvm
```
-访问 http://IP:8080
+访问 Web 界面:`http://<设备IP>:8080`(首次访问会引导创建管理员账户)。默认端口:HTTP `8080`;启用 HTTPS 后为 `8443`。
-### 环境变量
+#### 常用环境变量(Docker)
-| 变量 | 说明 | 默认值 |
-|------|------|--------|
-| `ENABLE_HTTPS` | 启用 HTTPS | `false` |
-| `HTTP_PORT` | HTTP 端口 | `8080` |
-| `VERBOSE` | 日志级别 (1/2/3) | - |
+| 变量名 | 默认值 | 说明 |
+|------|------|------|
+| `ENABLE_HTTPS` | `false` | 是否启用 HTTPS(`true/false`) |
+| `HTTP_PORT` | `8080` | HTTP 端口(`ENABLE_HTTPS=false` 时生效) |
+| `HTTPS_PORT` | `8443` | HTTPS 端口(`ENABLE_HTTPS=true` 时生效) |
+| `BIND_ADDRESS` | - | 监听地址(如 `0.0.0.0`) |
+| `VERBOSE` | `0` | 日志详细程度:`1`(-v)、`2`(-vv)、`3`(-vvv) |
+| `DATA_DIR` | `/etc/one-kvm` | 数据目录(等价于 `one-kvm -d `,优先级高于 `ONE_KVM_DATA_DIR`) |
+> 说明:`--privileged=true` 和挂载 `/dev`、`/sys` 是硬件访问所需配置,当前版本不可省略。
+>
+> 兼容性:同时支持旧变量名 `ONE_KVM_DATA_DIR`。
+>
+> HTTPS:未提供证书时会自动生成默认自签名证书。
+>
+> Ventoy:若修改 `DATA_DIR`,请确保 Ventoy 资源文件位于 `${DATA_DIR}/ventoy`(`boot.img`、`core.img`、`ventoy.disk.img`)。
-## 致谢
+### 方式二:DEB 软件包安装
-感谢以下项目:
+前提条件:
-- [PiKVM](https://github.com/pikvm/pikvm) - 原始 Python 版 IP-KVM
-- [RustDesk](https://github.com/rustdesk/rustdesk) - hwcodec 硬件编码库
-- [ttyd](https://github.com/tsl0922/ttyd) - Web 终端
-- [EasyTier](https://github.com/EasyTier/EasyTier) - P2P 组网
+- Debian 11+ / Ubuntu 22+
+- 插好 USB HDMI 采集卡、HID 线(OTG 或 CH340+CH9329)
-## 许可证
+安装步骤:
-待定
+1. 从 GitHub Releases 下载适合架构的 `one-kvm_*.deb`:[Releases](https://github.com/mofeng-git/One-KVM/releases)
+2. 安装:
+
+```bash
+sudo apt update
+sudo apt install ./one-kvm_*_*.deb
+```
+
+访问 Web 界面:`http://<设备IP>:8080`。
+
+### 方式三:飞牛 NAS(FPK)安装
+
+前提条件:
+
+- 飞牛 NAS 系统(目前仅支持 x86_64 架构)
+- 插好 USB HDMI 采集卡、CH340+CH9329 HID 线
+
+安装步骤:
+
+1. 从 GitHub Releases 下载 `*.fpk` 软件包:[Releases](https://github.com/mofeng-git/One-KVM/releases)
+2. 在飞牛应用商店选择“手动安装”,导入 `*.fpk`
+
+访问 Web 界面:`http://<设备IP>:8420`。
+
+## 报告问题
+
+如果您发现了问题,请:
+1. 使用 [GitHub Issues](https://github.com/mofeng-git/One-KVM/issues) 报告
+2. 提供详细的错误信息和复现步骤
+3. 包含您的硬件配置和系统信息
+
+## 赞助支持
+
+本项目基于多个优秀开源项目进行二次开发,作者投入了大量时间进行测试和维护。如果您觉得这个项目有价值,欢迎通过 **[为爱发电](https://afdian.com/a/silentwind)** 支持项目发展。
+
+### 感谢名单
+
+
+点击查看感谢名单
+
+- 浩龙的电子嵌入式之路
+
+- Tsuki
+
+- H_xiaoming
+
+- 0蓝蓝0
+
+- fairybl
+
+- Will
+
+- 浩龙的电子嵌入式之路
+
+- 自.知
+
+- 观棋不语٩ ི۶
+
+- 爱发电用户_a57a4
+
+- 爱发电用户_2c769
+
+- 霜序
+
+- 远方(闲鱼用户名:小远技术店铺)
+
+- 爱发电用户_399fc
+
+- 斐斐の
+
+- 爱发电用户_09451
+
+- 超高校级的錆鱼
+
+- 爱发电用户_08cff
+
+- guoke
+
+- mgt
+
+- 姜沢掵
+
+- ui_beam
+
+- 爱发电用户_c0dd7
+
+- 爱发电用户_dnjK
+
+- 忍者胖猪
+
+- 永遠の願い
+
+- 爱发电用户_GBrF
+
+- 爱发电用户_fd65c
+
+- 爱发电用户_vhNa
+
+- 爱发电用户_Xu6S
+
+- moss
+
+- woshididi
+
+- 爱发电用户_a0fd1
+
+- 爱发电用户_f6bH
+
+- 码农
+
+- 爱发电用户_6639f
+
+- jeron
+
+- 爱发电用户_CN7y
+
+- 爱发电用户_Up6w
+
+- 爱发电用户_e3202
+
+- 一语念白
+
+- 云边
+
+- 爱发电用户_5a711
+
+- 爱发电用户_9a706
+
+- T0m9ir1SUKI
+
+- 爱发电用户_56d52
+
+- 爱发电用户_3N6F
+
+- DUSK
+
+- 飘零
+
+- .
+
+- 饭太稀
+
+- 葱
+
+- ......
+
+
+
+### 赞助商
+
+本项目得到以下赞助商的支持:
+
+**CDN 加速及安全防护:**
+- **[Tencent EdgeOne](https://edgeone.ai/zh?from=github)** - 提供 CDN 加速及安全防护服务
+
+
+
+**文件存储服务:**
+- **[Huang1111公益计划](https://pan.huang1111.cn/s/mxkx3T1)** - 提供免登录下载服务
+
+**云服务商**
+
+- **[林枫云](https://www.dkdun.cn)** - 赞助了本项目宁波大带宽服务器
+
+
+
+林枫云主营国内外地域的精品线路业务服务器、高主频游戏服务器和大带宽服务器。
\ No newline at end of file
diff --git a/build/init.sh b/build/init.sh
index 6929f8ae..70089554 100644
--- a/build/init.sh
+++ b/build/init.sh
@@ -4,36 +4,44 @@
set -e
-# Start one-kvm with default options
-# Additional options can be passed via environment variables
-EXTRA_ARGS="-d /etc/one-kvm"
+# Start one-kvm with default options.
+# Additional options can be passed via environment variables.
+
+# Data directory (prefer DATA_DIR, keep ONE_KVM_DATA_DIR for backward compatibility)
+DATA_DIR="${DATA_DIR:-${ONE_KVM_DATA_DIR:-/etc/one-kvm}}"
+ARGS=(-d "$DATA_DIR")
# Enable HTTPS if requested
if [ "${ENABLE_HTTPS:-false}" = "true" ]; then
- EXTRA_ARGS="$EXTRA_ARGS --enable-https"
+ ARGS+=(--enable-https)
fi
# Custom bind address
if [ -n "$BIND_ADDRESS" ]; then
- EXTRA_ARGS="$EXTRA_ARGS -a $BIND_ADDRESS"
+ ARGS+=(-a "$BIND_ADDRESS")
fi
# Custom port
if [ -n "$HTTP_PORT" ]; then
- EXTRA_ARGS="$EXTRA_ARGS -p $HTTP_PORT"
+ ARGS+=(-p "$HTTP_PORT")
+fi
+
+# Custom HTTPS port
+if [ -n "$HTTPS_PORT" ]; then
+ ARGS+=(--https-port "$HTTPS_PORT")
fi
# Verbosity level
if [ -n "$VERBOSE" ]; then
case "$VERBOSE" in
- 1) EXTRA_ARGS="$EXTRA_ARGS -v" ;;
- 2) EXTRA_ARGS="$EXTRA_ARGS -vv" ;;
- 3) EXTRA_ARGS="$EXTRA_ARGS -vvv" ;;
+ 1) ARGS+=(-v) ;;
+ 2) ARGS+=(-vv) ;;
+ 3) ARGS+=(-vvv) ;;
esac
fi
echo "[INFO] Starting one-kvm..."
-echo "[INFO] Extra arguments: $EXTRA_ARGS"
+echo "[INFO] Arguments: ${ARGS[*]}"
# Execute one-kvm
-exec /usr/bin/one-kvm $EXTRA_ARGS
+exec /usr/bin/one-kvm "${ARGS[@]}"
From 89072ad58de91009d0e802490d41a6025a76fb6a Mon Sep 17 00:00:00 2001
From: a15355447898a
Date: Fri, 23 Jan 2026 17:11:19 +0800
Subject: [PATCH 2/6] =?UTF-8?q?=E6=94=AF=E6=8C=81v4l2=E7=BC=96=E7=A0=81,ar?=
=?UTF-8?q?m=E6=9C=BA=E5=99=A8=E5=8E=9F=E7=94=9F=E6=9E=84=E5=BB=BA,docker?=
=?UTF-8?q?=E9=95=9C=E5=83=8F=E6=8D=A2archlinux,=E5=85=81=E8=AE=B8?=
=?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E6=97=B6=E7=A6=81=E7=94=A8HID?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
build/Dockerfile.runtime | 31 ++++++------
build/build-images.sh | 18 ++++++-
libs/hwcodec/build.rs | 7 +--
.../cpp/common/platform/linux/linux.cpp | 15 ++++--
libs/hwcodec/cpp/common/util.cpp | 16 +++++--
.../cpp/ffmpeg_ram/ffmpeg_ram_decode.cpp | 4 ++
libs/hwcodec/src/ffmpeg_ram/encode.rs | 47 ++++++++++---------
web/src/views/SettingsView.vue | 1 +
web/src/views/SetupView.vue | 18 +++++++
9 files changed, 107 insertions(+), 50 deletions(-)
diff --git a/build/Dockerfile.runtime b/build/Dockerfile.runtime
index 44617adf..36a0e7ef 100644
--- a/build/Dockerfile.runtime
+++ b/build/Dockerfile.runtime
@@ -1,38 +1,37 @@
# One-KVM Runtime Image
# This Dockerfile only packages pre-compiled binaries (no compilation)
# Used after cross-compiling with `cross build`
-# Using Debian 11 for maximum compatibility (GLIBC 2.31)
+# Using Arch Linux base to match rolling glibc on build hosts.
ARG TARGETPLATFORM=linux/amd64
-FROM debian:11-slim
+FROM lsiobase/arch
ARG TARGETPLATFORM
# Install runtime dependencies in a single layer
# All codec libraries (libx264, libx265, libopus) are now statically linked
# Only hardware acceleration drivers and core system libraries remain dynamic
-RUN apt-get update && \
- apt-get install -y --no-install-recommends \
- # Core runtime (all platforms) - no codec libs needed
+RUN pacman -Syu --noconfirm --needed \
ca-certificates \
- libudev1 \
- libasound2 \
- # v4l2 is handled by kernel, minimal userspace needed
- libv4l-0 \
+ systemd-libs \
+ alsa-lib \
+ libv4l \
+ libyuv \
+ ffmpeg \
&& \
# Platform-specific hardware acceleration
if [ "$TARGETPLATFORM" = "linux/amd64" ]; then \
- apt-get install -y --no-install-recommends \
- libva2 libva-drm2 libva-x11-2 libx11-6 libxcb1 libxau6 libxdmcp6 libmfx1; \
+ pacman -S --noconfirm --needed \
+ libva libx11 libxcb libxau libxdmcp libmfx; \
elif [ "$TARGETPLATFORM" = "linux/arm64" ]; then \
- apt-get install -y --no-install-recommends \
- libdrm2 libva2; \
+ pacman -S --noconfirm --needed \
+ libdrm libva; \
elif [ "$TARGETPLATFORM" = "linux/arm/v7" ]; then \
- apt-get install -y --no-install-recommends \
- libdrm2 libva2; \
+ pacman -S --noconfirm --needed \
+ libdrm libva; \
fi && \
- rm -rf /var/lib/apt/lists/* && \
+ pacman -Scc --noconfirm && \
mkdir -p /etc/one-kvm/ventoy
# Copy init script
diff --git a/build/build-images.sh b/build/build-images.sh
index 106a9b6e..a5bf06bb 100755
--- a/build/build-images.sh
+++ b/build/build-images.sh
@@ -19,8 +19,22 @@ ARCH_MAP=(
build_arch() {
local rust_target="$1"
- echo "=== Building: $rust_target (via cross with custom Dockerfile) ==="
- cross build --release --target "$rust_target"
+ # Build frontend first
+ if [ ! -d "$PROJECT_DIR/web/dist" ]; then
+ echo "=== Building Frontend ==="
+ cd "$PROJECT_DIR/web" && npm install && npm run build
+ cd "$PROJECT_DIR"
+ fi
+
+ local host_arch=$(rustc -vV | grep host | cut -d ' ' -f 2)
+
+ if [ "$rust_target" == "$host_arch" ]; then
+ echo "=== Building: $rust_target (NATIVE build, skipping cross) ==="
+ cargo build --release --target "$rust_target"
+ else
+ echo "=== Building: $rust_target (via cross) ==="
+ cross build --release --target "$rust_target"
+ fi
}
# Main
diff --git a/libs/hwcodec/build.rs b/libs/hwcodec/build.rs
index 791316eb..bdae4450 100644
--- a/libs/hwcodec/build.rs
+++ b/libs/hwcodec/build.rs
@@ -361,10 +361,11 @@ mod ffmpeg {
// RKMPP decode only exists on ARM builds where FFmpeg is compiled with RKMPP support.
// Avoid compiling this file on x86/x64 where `AV_HWDEVICE_TYPE_RKMPP` doesn't exist.
+ // Also check if RKMPP is available in the current FFmpeg environment to avoid compilation errors on non-Rockchip ARM (e.g. RPi).
let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default();
- let enable_rkmpp = matches!(target_arch.as_str(), "aarch64" | "arm")
- || std::env::var_os("CARGO_FEATURE_RKMPP").is_some();
- if enable_rkmpp {
+ let is_arm = matches!(target_arch.as_str(), "aarch64" | "arm");
+
+ if is_arm {
builder.file(ffmpeg_ram_dir.join("ffmpeg_ram_decode.cpp"));
} else {
println!(
diff --git a/libs/hwcodec/cpp/common/platform/linux/linux.cpp b/libs/hwcodec/cpp/common/platform/linux/linux.cpp
index 2d60c9ce..c9036d20 100644
--- a/libs/hwcodec/cpp/common/platform/linux/linux.cpp
+++ b/libs/hwcodec/cpp/common/platform/linux/linux.cpp
@@ -107,10 +107,19 @@ int linux_support_rkmpp() {
// Returns 0 if a M2M capable device is found, -1 otherwise
int linux_support_v4l2m2m() {
// Check common V4L2 M2M device paths used by various ARM SoCs
+ // /dev/video10 - Standard on many SoCs
+ // /dev/video11 - Standard on many SoCs (often decoder)
+ // /dev/video0 - Some platforms (like RPi) might use this
+ // /dev/video1 - Alternate RPi path
+ // /dev/video2 - Alternate path
+ // /dev/video32 - Some Allwinner/Rockchip legacy
const char *m2m_devices[] = {
- "/dev/video10", // Common M2M encoder device
- "/dev/video11", // Common M2M decoder device
- "/dev/video0", // Some SoCs use video0 for M2M
+ "/dev/video10",
+ "/dev/video11",
+ "/dev/video0",
+ "/dev/video1",
+ "/dev/video2",
+ "/dev/video32",
};
for (size_t i = 0; i < sizeof(m2m_devices) / sizeof(m2m_devices[0]); i++) {
diff --git a/libs/hwcodec/cpp/common/util.cpp b/libs/hwcodec/cpp/common/util.cpp
index a65d5a7f..0418e337 100644
--- a/libs/hwcodec/cpp/common/util.cpp
+++ b/libs/hwcodec/cpp/common/util.cpp
@@ -10,11 +10,19 @@ extern "C" {
#include "common.h"
-#include "common.h"
-
#define LOG_MODULE "UTIL"
#include "log.h"
+#ifndef FF_PROFILE_H264_BASELINE
+#define FF_PROFILE_H264_BASELINE 66
+#endif
+#ifndef FF_PROFILE_H264_HIGH
+#define FF_PROFILE_H264_HIGH 100
+#endif
+#ifndef FF_PROFILE_HEVC_MAIN
+#define FF_PROFILE_HEVC_MAIN 1
+#endif
+
namespace {
// Helper function: check if encoder is software H264 (libx264)
@@ -147,11 +155,11 @@ bool set_lantency_free(void *priv_data, const std::string &name) {
// V4L2 M2M hardware encoder - minimize buffer latency
if (name.find("v4l2m2m") != std::string::npos) {
// Minimize number of output buffers for lower latency
- if ((ret = av_opt_set_int(priv_data, "num_output_buffers", 2, 0)) < 0) {
+ if ((ret = av_opt_set_int(priv_data, "num_output_buffers", 4, 0)) < 0) {
LOG_WARN(std::string("v4l2m2m set num_output_buffers failed, ret = ") + av_err2str(ret));
// Not fatal
}
- if ((ret = av_opt_set_int(priv_data, "num_capture_buffers", 2, 0)) < 0) {
+ if ((ret = av_opt_set_int(priv_data, "num_capture_buffers", 4, 0)) < 0) {
LOG_WARN(std::string("v4l2m2m set num_capture_buffers failed, ret = ") + av_err2str(ret));
// Not fatal
}
diff --git a/libs/hwcodec/cpp/ffmpeg_ram/ffmpeg_ram_decode.cpp b/libs/hwcodec/cpp/ffmpeg_ram/ffmpeg_ram_decode.cpp
index 52a6e2a6..b4387686 100644
--- a/libs/hwcodec/cpp/ffmpeg_ram/ffmpeg_ram_decode.cpp
+++ b/libs/hwcodec/cpp/ffmpeg_ram/ffmpeg_ram_decode.cpp
@@ -55,7 +55,11 @@ public:
callback_ = callback;
if (name_.find("rkmpp") != std::string::npos) {
+#ifdef AV_HWDEVICE_TYPE_RKMPP
hw_device_type_ = AV_HWDEVICE_TYPE_RKMPP;
+#else
+ set_last_error("RKMPP support not compiled in FFmpeg");
+#endif
}
}
diff --git a/libs/hwcodec/src/ffmpeg_ram/encode.rs b/libs/hwcodec/src/ffmpeg_ram/encode.rs
index dff0a135..17c34442 100644
--- a/libs/hwcodec/src/ffmpeg_ram/encode.rs
+++ b/libs/hwcodec/src/ffmpeg_ram/encode.rs
@@ -353,40 +353,43 @@ impl Encoder {
let mut passed = false;
let mut last_err: Option = None;
- let max_attempts = 1;
+ let max_attempts = if codec.name.contains("v4l2m2m") {
+ 5
+ } else {
+ 1
+ };
for attempt in 0..max_attempts {
+ encoder.request_keyframe();
let pts = (attempt as i64) * 33; // 33ms is an approximation for 30 FPS (1000 / 30)
let start = std::time::Instant::now();
match encoder.encode(&yuv, pts) {
Ok(frames) => {
let elapsed = start.elapsed().as_millis();
- if frames.len() == 1 {
- if frames[0].key == 1 && elapsed < TEST_TIMEOUT_MS as _ {
- debug!(
- "Encoder {} test passed on attempt {}",
- codec.name,
- attempt + 1
- );
- res.push(codec.clone());
- passed = true;
- break;
- } else {
- debug!(
- "Encoder {} test failed on attempt {} - key: {}, timeout: {}ms",
- codec.name,
- attempt + 1,
- frames[0].key,
- elapsed
- );
- }
- } else {
+ if frames.len() >= 1 && elapsed < TEST_TIMEOUT_MS as _ {
debug!(
- "Encoder {} test failed on attempt {} - wrong frame count: {}",
+ "Encoder {} test passed on attempt {} (frames: {})",
codec.name,
attempt + 1,
frames.len()
);
+ res.push(codec.clone());
+ passed = true;
+ break;
+ } else if frames.is_empty() {
+ debug!(
+ "Encoder {} test produced no output on attempt {}",
+ codec.name,
+ attempt + 1
+ );
+ } else {
+ debug!(
+ "Encoder {} test failed on attempt {} - frames: {}, timeout: {}ms",
+ codec.name,
+ attempt + 1,
+ frames.len(),
+ elapsed
+ );
}
}
Err(err) => {
diff --git a/web/src/views/SettingsView.vue b/web/src/views/SettingsView.vue
index 98e6fa40..7fca4b7f 100644
--- a/web/src/views/SettingsView.vue
+++ b/web/src/views/SettingsView.vue
@@ -1323,6 +1323,7 @@ onMounted(async () => {
diff --git a/web/src/views/SetupView.vue b/web/src/views/SetupView.vue
index 90df3085..c177a3a7 100644
--- a/web/src/views/SetupView.vue
+++ b/web/src/views/SetupView.vue
@@ -319,6 +319,10 @@ watch(hidBackend, (newBackend) => {
if (newBackend === 'otg' && !otgUdc.value && devices.value.udc.length > 0) {
otgUdc.value = devices.value.udc[0]?.name || ''
}
+ if (newBackend === 'none') {
+ ch9329Port.value = ''
+ otgUdc.value = ''
+ }
})
onMounted(async () => {
@@ -341,6 +345,11 @@ onMounted(async () => {
otgUdc.value = result.udc[0].name
}
+ // If no HID devices exist, default to disabled to avoid blocking setup
+ if (result.serial.length === 0 && result.udc.length === 0) {
+ hidBackend.value = 'none'
+ }
+
// Auto-select audio device if available (and no video device to trigger watch)
if (result.audio.length > 0 && !audioDevice.value) {
// Prefer HDMI audio device
@@ -357,6 +366,10 @@ onMounted(async () => {
// Use defaults
}
+ if (devices.value.serial.length === 0 && devices.value.udc.length === 0) {
+ hidBackend.value = 'none'
+ }
+
// Load encoder backends
try {
const codecsResult = await streamApi.getCodecs()
@@ -864,6 +877,7 @@ const stepIcons = [User, Video, Keyboard, Puzzle]
CH9329 ({{ t('setup.serialHid') }})
USB OTG
+ {{ t('setup.disableHid') }}
@@ -931,6 +945,10 @@ const stepIcons = [User, Video, Keyboard, Puzzle]
+
+
+ {{ t('setup.hidDisabledHint') }}
+
From d78c6ed0472c7a1f23fae919dd9a44d0209c6fca Mon Sep 17 00:00:00 2001
From: a15355447898a
Date: Fri, 23 Jan 2026 21:20:48 +0800
Subject: [PATCH 3/6] =?UTF-8?q?=E5=9B=9E=E6=BB=9Adocker=E6=9E=84=E5=BB=BA?=
=?UTF-8?q?=E8=84=9A=E6=9C=AC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
build/Dockerfile.runtime | 31 ++++++++++++++++---------------
1 file changed, 16 insertions(+), 15 deletions(-)
diff --git a/build/Dockerfile.runtime b/build/Dockerfile.runtime
index 36a0e7ef..44617adf 100644
--- a/build/Dockerfile.runtime
+++ b/build/Dockerfile.runtime
@@ -1,37 +1,38 @@
# One-KVM Runtime Image
# This Dockerfile only packages pre-compiled binaries (no compilation)
# Used after cross-compiling with `cross build`
-# Using Arch Linux base to match rolling glibc on build hosts.
+# Using Debian 11 for maximum compatibility (GLIBC 2.31)
ARG TARGETPLATFORM=linux/amd64
-FROM lsiobase/arch
+FROM debian:11-slim
ARG TARGETPLATFORM
# Install runtime dependencies in a single layer
# All codec libraries (libx264, libx265, libopus) are now statically linked
# Only hardware acceleration drivers and core system libraries remain dynamic
-RUN pacman -Syu --noconfirm --needed \
+RUN apt-get update && \
+ apt-get install -y --no-install-recommends \
+ # Core runtime (all platforms) - no codec libs needed
ca-certificates \
- systemd-libs \
- alsa-lib \
- libv4l \
- libyuv \
- ffmpeg \
+ libudev1 \
+ libasound2 \
+ # v4l2 is handled by kernel, minimal userspace needed
+ libv4l-0 \
&& \
# Platform-specific hardware acceleration
if [ "$TARGETPLATFORM" = "linux/amd64" ]; then \
- pacman -S --noconfirm --needed \
- libva libx11 libxcb libxau libxdmcp libmfx; \
+ apt-get install -y --no-install-recommends \
+ libva2 libva-drm2 libva-x11-2 libx11-6 libxcb1 libxau6 libxdmcp6 libmfx1; \
elif [ "$TARGETPLATFORM" = "linux/arm64" ]; then \
- pacman -S --noconfirm --needed \
- libdrm libva; \
+ apt-get install -y --no-install-recommends \
+ libdrm2 libva2; \
elif [ "$TARGETPLATFORM" = "linux/arm/v7" ]; then \
- pacman -S --noconfirm --needed \
- libdrm libva; \
+ apt-get install -y --no-install-recommends \
+ libdrm2 libva2; \
fi && \
- pacman -Scc --noconfirm && \
+ rm -rf /var/lib/apt/lists/* && \
mkdir -p /etc/one-kvm/ventoy
# Copy init script
From 176e71c79e2d42655b9ce85f14b089184a962eb7 Mon Sep 17 00:00:00 2001
From: ayaya <62456287+a15355447898a@users.noreply.github.com>
Date: Fri, 23 Jan 2026 21:24:22 +0800
Subject: [PATCH 4/6] =?UTF-8?q?=E4=BF=AE=E6=94=B9readme?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 54ad79dd..8ead0e9c 100644
--- a/README.md
+++ b/README.md
@@ -66,7 +66,7 @@
- **VAAPI**:Intel/AMD GPU
- **RKMPP**:Rockchip SoC
-- **V4L2 M2M**:通用硬件编码器(尚未实现)
+- **V4L2 M2M**:RaspberryPi
- **软件编码**:CPU 编码
### 扩展能力
@@ -291,4 +291,4 @@ sudo apt install ./one-kvm_*_*.deb

-林枫云主营国内外地域的精品线路业务服务器、高主频游戏服务器和大带宽服务器。
\ No newline at end of file
+林枫云主营国内外地域的精品线路业务服务器、高主频游戏服务器和大带宽服务器。
From 21bea797e46593904050a7905cc649e9cba425a4 Mon Sep 17 00:00:00 2001
From: Fridayssheep <18313899771@163.com>
Date: Wed, 11 Feb 2026 13:06:05 +0800
Subject: [PATCH 5/6] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E4=BA=86?=
=?UTF-8?q?=E5=AF=B9=E4=B8=B2=E5=8F=A3=E5=8D=8F=E8=AE=AE=E7=9A=84=E7=BB=A7?=
=?UTF-8?q?=E7=94=B5=E5=99=A8=E7=9A=84=E6=94=AF=E6=8C=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/atx/executor.rs | 99 ++++++++++++++++++++++++++++++++++
src/atx/mod.rs | 4 ++
src/atx/types.rs | 8 +++
web/src/types/generated.ts | 7 +++
web/src/views/SettingsView.vue | 35 ++++++++++--
5 files changed, 150 insertions(+), 3 deletions(-)
diff --git a/src/atx/executor.rs b/src/atx/executor.rs
index ffe85973..80ddab3d 100644
--- a/src/atx/executor.rs
+++ b/src/atx/executor.rs
@@ -4,6 +4,7 @@
//! Each executor handles one button (power or reset) with its own hardware binding.
use gpio_cdev::{Chip, LineHandle, LineRequestFlags};
+use serialport::SerialPort;
use std::fs::{File, OpenOptions};
use std::io::Write;
use std::sync::atomic::{AtomicBool, Ordering};
@@ -38,6 +39,8 @@ pub struct AtxKeyExecutor {
gpio_handle: Mutex