TinyPilot:不到 100 美元自制 KVM over IP
原文由 Michael Lynch 于 发布,订阅该博客
TinyPilot 是我打造的一款价格低廉的开源远程控制设备。即使在操作系统尚未启动时它也能正常工作,因此我常用它来为裸机家庭实验室服务器安装新系统、排查启动故障。
本文将详细介绍我打造 TinyPilot 的经历,并告诉你如何用树莓派以不到 100 美元的成本自己动手做一个。

在 Microsoft Surface 的 Chrome 浏览器中通过 TinyPilot 控制我的 Ubuntu 笔记本电脑
别讲故事了,直接告诉我怎么做
如果你没兴趣看我在开发 TinyPilot 过程中那些跌宕起伏的精彩故事,请直接跳到“如何自制 TinyPilot”一节。
演示
为什么要做 TinyPilot?
几年前,我为了测试软件自己组装了一台家用服务器。这笔投入非常值得,我每天都在用它。

2017 年我为运行虚拟机而搭建的家庭实验室服务器
这台服务器没有连接键盘和显示器,我平时通过 SSH 或网页界面访问它。这种方式很方便,但一旦出了小问题,就会变得非常麻烦。
每隔几个月,我总会把什么东西搞砸,导致服务器无法启动或联网,彻底把自己锁在门外。为了让它重新跑起来,我不得不拔掉所有线缆,把服务器拖到桌边,再手忙脚乱地接上桌面的键盘和显示器。
市面上的商用方案
朋友们向我极力推荐 iDRAC 的使用体验。这是戴尔服务器上的一块芯片,能在系统通电后立刻提供虚拟控制台。我曾考虑给下一台家用服务器配一个 iDRAC,但高昂的价格很快让我打消了念头。光是授权费就要 300 美元,还需要昂贵的专用硬件。

戴尔 iDRAC 技术的授权费为每台机器 300 美元,还不包括硬件成本
接着,我又看了商用的 KVM over IP 方案。它们的功能与戴尔的 iDRAC 类似,但属于外置设备,需要连接到电脑的键盘、显示和鼠标接口(KVM 也因此得名)。遗憾的是,它们更贵,单台设备的价格在 500 到 1000 美元之间。

商用 KVM over IP 设备的价格在 500 至 1000 美元之间。
虽然我很懒得搬服务器,但为了每年少插拔几次线就花 500 美元,还是说不过去。
于是,我做了任何一个足够“较真”的程序员都会做的事:花上几百个小时自己做一个 KVM over IP。
用树莓派打造 KVM over IP
树莓派是一款小巧、廉价的单板计算机。它的性能足以运行完整的桌面操作系统,30 到 60 美元的售价让它在创客和程序员中非常受欢迎。

树莓派是一台功能完整的计算机,集成在一块单板上,售价仅 30 至 60 美元。
新版树莓派支持 USB OTG(USB On-The-Go),可以让树莓派模拟键盘、U 盘、麦克风等 USB 设备。
为了验证用树莓派做 KVM 的想法,我做了一个名为 Key Mime Pi 的简单网页应用作为概念验证。

Key Mime Pi,我早期开发的 TinyPilot 前身,仅支持键盘转发。
Key Mime Pi 通过 USB 连接到另一台电脑,并注册为一个 USB 键盘。它同时提供一个网页,监听 JavaScript 键盘事件。当用户输入时,Key Mime Pi 会捕获这些按键事件,并通过虚拟的 USB 键盘将其转换为按键信号,从而在目标电脑上呈现输入。我在上一篇博文中详细介绍了这一原理。
视频采集的挑战
如果看不到屏幕画面,光有键盘转发也没什么用。下一步很自然就是想办法让树莓派采集服务器的显示输出,并在浏览器中呈现出来。
我第一次尝试视频采集用的是 Lenkeng LKV373A HDMI 延长器。Daniel Kučera(又名 danman)对这款设备做了非常出色的逆向分析。它在 eBay 上的中国卖家那里售价约 40 美元,看起来是最好的选择。

Lenkeng LKV373A HDMI 延长器是我第一次尝试 HDMI 视频采集时使用的设备。
视频采集并不顺利,因为 LKV373A 的发送端并不是视频采集设备。它的设计用途是与 LKV373A 接收端配对,将网络流再转换回 HDMI 输出。在 danman 的研究中,他发现了一种拦截并捕获视频流的方法,但 LKV373A 使用的是 RTP 协议的非标准变体,很少有视频工具能识别。
好在 danman 向 ffmpeg 提交了一个补丁来处理 LKV373A 的怪异行为,因此我得以用 ffmpeg 的播放器来渲染视频流:
ffplay -i udp://239.255.42.42:5004
使用 ffplay 渲染来自 LKV373A 的视频流
就在这里,我第一次遇到了困扰整个项目的问题:延迟。目标电脑的画面到我桌面上的视频播放之间有将近一秒的延迟。

在未经过任何重编码的情况下,LKV373A 就引入了 838 毫秒的延迟。
我尝试调整 ffplay 众多的命令行参数来加速视频流,但延迟始终没能压到 800 毫秒以下。这还是在我配备高端显卡和 CPU 的台式机上的结果。对于性能孱弱的小树莓派来说,前景显然不妙。
幸运的是,我完全出于偶然找到了更好的解决方案。
HDMI 转 USB 采集棒
在漫无目的地刷 Twitter 时,我偶然看到一条来自 Arsenio Dev 的推文,提到他刚买了一个廉价的 HDMI 转 USB 采集棒:

一条来自 Arsenio Dev 的推文让我发现了更好的视频采集方案。
能以 1080p 分辨率、30 帧每秒采集视频,听起来有点好得不像真的,于是我在 eBay 上下单买了一个,含运费只要 11 美元。我甚至不知道它叫什么——它没有品牌名,我就叫它“HDMI 采集棒”吧。虽然外观有好几种,但其实都是同一颗 MacroSilicon MS2109 芯片,只是外壳不同。

eBay 上售价 11.20 美元包邮的 HDMI 转 USB 采集棒
几天后设备到货,效果让我惊艳。无需任何折腾,一插到树莓派上,它就被识别为 UVC 视频采集设备。
$ sudo v4l2-ctl --list-devices
bcm2835-codec-decode (platform:bcm2835-codec):
/dev/video10
/dev/video11
/dev/video12
UVC Camera (534d:2109): USB Vid (usb-0000:01:00.0-1.4): <<< HDMI capture dongle
/dev/video0
/dev/video1几分钟内,我就成功采集并转发了 HDMI 视频:
# On the Pi
ffmpeg \
-re \
-f v4l2 \
-i /dev/video0 \
-vcodec libx264 \
-f mpegts udp://10.0.0.100:1234/stream
# On my Windows desktop
ffplay.exe -i udp://@10.0.0.100:1234/stream而且它非常方便。LKV373A 几乎有砖头那么大,还需要独立供电和网线。而这个 HDMI 采集棒只有 U 盘大小,只需要一个 USB 接口就行。

Lenkeng LKV373A HDMI 延长器(左)体积更大、需要的连线也比 HDMI 采集棒(右)多。
唯一的问题,还是延迟。树莓派转发的视频流比源电脑慢了 7 到 10 秒。

使用 ffmpeg 从树莓派推流时,视频延迟高达 10 秒。
我不确定延迟是来自采集棒本身、树莓派上的 ffmpeg,还是桌面上的 ffplay。Arsenio Dev 报告的延迟只有 20 毫秒,看来只要深入研究 ffmpeg 那些晦涩神秘的命令行参数,还是有可能获得更快的速度。
又一次幸运让我免于去啃这块硬骨头。
借鉴类似项目
在我发布上一篇关于 Key Mime Pi 的博文后,收到了 Max Devaev 的留言,他建议我去看看他的项目 Pi-KVM。

Max Devaev 向我推荐了他已有的 Pi-KVM 项目。

我之前使用面包板的经历是以不小心把它熔化告终。
我曾简单看过 Pi-KVM,但它对面包板和焊接的要求把我吓退了。
在 Max 的建议下,我又仔细研究了一遍 Pi-KVM,尤其关注他是如何解决视频延迟问题的。我注意到他是通过一个叫 uStreamer 的工具来采集视频的。
注:与 Max 进一步交流后,我了解到 Pi-KVM 其实也支持无需焊接和面包板的搭建方式。
uStreamer:超快的视频推流工具
你有没有遇到过一款好用到连你还没想到的问题都顺带解决了的工具?
uStreamer 开箱即用,直接把我的延迟从 8 秒降到了 500 至 600 毫秒。而且它还省去了一整串额外的工作。

uStreamer 将我的延迟降低了 15 倍。
在使用 uStreamer 之前,我还不确定该如何把 ffmpeg 的视频送到用户浏览器里,但我知道总有办法实现。我试过一篇基本靠谱的教程,用 HLS 把视频从 ffmpeg 推到 nginx,但这反而增加了更多延迟。而且还有一些问题没解决,比如如何在 HDMI 线缆插拔时自动启停推流,以及如何把视频转换为浏览器友好的格式。
uStreamer 一下子解决了所有这些问题。它自带一个极简的 HTTP 服务器,以 Motion JPEG 格式提供视频,而这种格式浏览器可以直接播放。我再也不用操心 HLS 推流,或是让 ffmpeg 和 nginx 协同工作了。
这个工具功能如此完善,我一度以为 Max 只是从某个更成熟的项目 fork 来的,结果完全想错了。这个“狠人”用 C 语言自己写了一个视频编码器,就为了把树莓派硬件的性能压榨到极致。我立刻给 Max 捐了款,也邀请所有使用他软件的人都这么做。
进一步降低视频延迟
uStreamer 把我的延迟从 10 秒降到了约 600 毫秒。这已经是巨大的进步,但延迟依然能感觉到。我告诉 Max,如果他能找到进一步提升性能的方法,我愿意为 uStreamer 提供资金支持,于是我们聊了起来。
Max 对我用的这款 HDMI 采集棒很感兴趣,因为他以前没见过这个型号。他邀请我用 tmate 创建一个共享终端会话,以便远程访问我的树莓派。

Max 说要么帮我优化延迟,要么栽赃让我背上联邦重罪。好在,他最后选择了前者。
在我的硬件上测试了几分钟 uStreamer 的运行情况后,Max 运行了v4l2-ctl 工具,看到了一行让他眼前一亮、却让我完全摸不着头脑的信息:
$ sudo v4l2-ctl --all
Driver Info:
Driver name : uvcvideo
Card type : UVC Camera (534d:2109): USB Vid
...
Format Video Capture:
Width/Height : 1280/720
Pixel Format : 'MJPG' (Motion-JPEG)
...
Streaming Parameters Video Capture:
Capabilities : timeperframe
Frames per second: 30.000 (30/1)HDMI 采集棒输出的视频流本身就是 Motion JPEG 格式!uStreamer 的硬件加速编码虽然很快,但其实完全多余,因为现代浏览器本身就能直接播放 Motion JPEG。
我们把 uStreamer 配置为跳过重编码,直接透传原始视频流。

在树莓派上跳过额外的重编码步骤后,延迟从 600 毫秒降至 200 毫秒。
延迟从 600 毫秒一路降到了 200 毫秒。虽然不是完全无延迟,但已经低到用上几分钟后就会忽略它的存在。
TinyPilot 实战
还记得本文开头我说想要 TinyPilot 来在无头虚拟机服务器启动前就能访问它吗?它真的做到了,我也确实在这么用!
我在 Key Mime Pi 的基础上迭代,打造了一个集成视频采集功能的全新网页界面:

今年我搭建了一台新的无头虚拟机服务器,并用 TinyPilot 安装了 Proxmox——一款用于管理虚拟机的开源虚拟化平台和网页界面。
通过 TinyPilot,我可以在浏览器中完成整个安装过程。这比以前拖着主机来回搬、反复插拔线缆的体验要好太多了。
如何自制 TinyPilot
零件清单
- 树莓派 4(所有型号均可)
- USB-C 转 USB-A 数据线(公对公)
- HDMI 转 USB 采集棒
- 奇怪的是,这些采集棒没有品牌名,但可以通过外观辨认。
- 在 eBay 上一般售价为 11 至 15 美元。
- microSD 卡(Class 10,8GB 或以上)
- HDMI 转 HDMI 线
- 或其他接口转 HDMI,取决于目标机器的显示输出接口。
- (可选)USB-C OTG 一分二转接头
- 需要额外两根 USB-A 转 microUSB 数据线和一个 3 安培电源适配器。
- (可选)散热外壳、散热片或风扇
- 请选择能露出树莓派 GPIO 引脚的外壳。
- 我使用的是这款极简被动散热外壳。
安装 Raspberry Pi OS Lite
首先,在 microSD 卡上安装 Raspberry Pi OS Lite(原名 Raspbian)。

我使用 Rufus 来烧录树莓派的 microSD 卡,任何整盘镜像工具都可以。
在 microSD 卡的 boot 分区放置一个名为 ssh 的空文件即可启用 SSH 访问。如果通过无线连接,还需要一个wpa_supplicant.conf 文件。
准备好 microSD 卡后,将其插入树莓派。
安装外壳(可选)
树莓派 4 以发热量大而闻名。不加散热也能运行,但时间一长很可能会出现稳定性问题。
我喜欢这款极简外壳,因为它价格便宜,而且无需风扇就能为树莓派被动散热:

这款极简铝制外壳无需风扇就能为树莓派提供良好的散热。
通过 USB 连接到目标机器
要让 TinyPilot 作为虚拟键盘工作,请将树莓派的 USB-C 接口连接到目标机器的 USB-A 接口:


使用 USB-C 转 USB-A 数据线,将 USB-C 一端接到树莓派的 USB-C 接口,USB-A 一端接到目标电脑。
注:优先使用 USB 3.0 接口,因为它能为树莓派提供更多电力。
连接 HDMI 采集棒
要完成硬件组装,将 HDMI 采集棒插入树莓派的任意一个 USB 接口。然后将 HDMI 线的一端接到采集棒,另一端接到目标电脑的显示输出接口。


将目标电脑的显示输出连接到 HDMI 采集棒,并将其插入树莓派的 USB 接口。
注:如果要连接的电脑没有 HDMI 输出,理论上可以使用 DisplayPort 转 HDMI 线或 DVI 转 HDMI 线,不过我本人没有亲自测试过。
连接网线
如果通过有线局域网连接树莓派,请将网线接到树莓派的以太网口:

将网线连接到树莓派。
注:如果已按上文所述添加 wpa_supplicant.conf 文件配置了无线连接,可跳过此步。
安装 TinyPilot 软件
通过 SSH 登录树莓派(Raspberry Pi OS 的默认用户名/密码为 pi / raspberry),然后运行以下命令:
curl -sS https://raw.githubusercontent.com/tiny-pilot/tinypilot/master/quick-install \
| bash -
sudo reboot如果你对直接将网上脚本管道到 shell 执行的做法心存疑虑,我建议你先查看一下源码。
该脚本会引导一个自包含的 Ansible 环境,并使用我的 TinyPilot Ansible 角色。它会安装四个开机自启的服务:
- nginx:一款流行的开源网页服务器
- ustreamer:轻量级 HTTP 视频推流服务器
- usb-gadget:用于启用树莓派“USB gadget 模式”的脚本,使树莓派能够模拟 USB 设备
- tinypilot:我为 TinyPilot 开发的网页界面
使用 TinyPilot
运行安装脚本后,即可通过以下地址访问 TinyPilot:

安装完成后,你可以在本地网络通过 http://raspberrypi/ 访问 TinyPilot 的网页界面。
供电问题
这套方案最大的限制是供电。依赖目标电脑供电意味着一旦目标关机,树莓派就会遭遇意外断电。
此外,树莓派 4 需要 3 安培电流才能稳定运行,虽然在更低电流下也能工作。电脑的 USB 3.0 接口仅提供 0.9 安培,USB 2.0 更是只有 0.5 安培,因此你可能会在树莓派的系统日志中看到如下警告:
$ sudo journalctl -xe | grep "Under-voltage"
Jun 28 06:23:15 tinypilot kernel: Under-voltage detected! (0x00050005)为了解决这个问题,我与一家工程公司合作定制了一块电路板,将树莓派的 USB-C 接口一分为二。第一个接口用于接入 USB 电源,从而可以为树莓派提供完整的 3 安培供电;第二个接口用于输出 USB 数据,使树莓派仍能模拟 USB 键盘。


TinyPilot 电源转接器可让树莓派通过 USB-C 接口获得 3 安培供电,同时不丧失 USB OTG 功能。
重要的是,该电源转接器的数据端口去掉了 USB 供电线路。这样可以确保电脑电源与树莓派电源之间的电压差不会导致不必要的电流回灌。
注:如果没有使用合适的转接器,在树莓派已连接到电脑的情况下再用外部电源供电,有可能损坏硬件。更多详情请参见 TinyPilot wiki。
源代码
TinyPilot 的软件基于宽松的 MIT 许可证开源:
- tinypilot:TinyPilot 的网页界面和后端。
- ansible-role-tinypilot:用于将 TinyPilot 及其依赖作为 systemd 服务安装的 Ansible 角色。
成品 TinyPilot 设备
成品 TinyPilot 设备可在 TinyPilot 官网购买。我是 TinyPilot 公司的最初创始人,但在 2024 年 4 月已将公司出售,现在除了作为一名热情用户外,与该公司已无任何关联。
特别感谢 Max Devaev 在 uStreamer 上的出色工作以及他对 TinyPilot 的贡献。
随机一篇博客
评论
登录后参与讨论