从零到开源:用 AI 驱动构建 macOS 鸿蒙投屏工具的踩坑全记录
缘起:鸿蒙生态缺失的那块拼图
做 Android 开发的人都知道 scrcpy——USB 一插,手机画面就出现在电脑屏幕上,还能直接操控。iOS 有 QuickTime 投屏。但 HarmonyOS 呢?只能在 DevEco Studio 那个笨重的 IDE 里开一个小窗口,延迟高、不可交互、体验糟心。
于是就有了 HarmonyMirror:一个 macOS 原生的鸿蒙设备投屏与控制工具。本文记录这个项目从零到开源的全过程——包括那些翻车时刻和 AI 编程的真实体验。
技术选型:为什么是 Swift + SwiftUI
第一反应是用 Electron。跨平台、生态好、开发快。
但投屏这个场景对延迟极度敏感——视频解码、触摸注入、窗口渲染,每一层都是性能瓶颈。Electron 的 Chromium 渲染层天然多了一层开销。而且鸿蒙开发工具链(hdc、DevEco Studio)都在 macOS 上运行得最好。
最终选了 Swift + SwiftUI 构建原生 macOS 应用:
AVSampleBufferDisplayLayer做硬件解码,直接走 VideoToolboxNWConnection(Network.framework) 处理 TCP 通信- SwiftUI 构建 UI,原生性能和动画
这是一个用纯 AI(Claude Code)驱动的项目,从第一天起就没写过一行代码,全靠 prompt。
架构设计:七层协议的”翻译官”
整个系统的核心是把手机画面搬到 Mac 屏幕上,同时把 Mac 的鼠标键盘操作送回手机。听起来简单,实际上经历了七层协议转换:
1 | 手机屏幕 |
每一层都有坑,下面按时间线记录踩坑过程。
第一坑:视频流——从截图到实时流的蜕变
最初方案(已废弃):通过 hdc shell snapshot_display 定时截图,WebSocket 传到 Mac 端显示。
结果呢?每 500ms 一张截图,肉眼可见的幻灯片效果。CPU 占用爆表,截图命令本身就要 300ms+。
推翻重来:研究华为投屏扩展 libscreen_casting.z.so——这是 DevEco Testing 内置的投屏库,通过 gRPC 输出 H.264 实时编码流。
最大的挑战是 gRPC 协议桥接。Python bridge(deveco_cast_bridge.py)负责把 gRPC 数据中转成纯 TCP 帧,期间踩了无数坑:
- Annex-B vs AVCC:H.264 两种封装格式,设备发出来的和 macOS 期望的可能不一样。写了个自适应检测器,先扫描起始码
0x00000001,没有就走 AVCC 长度前缀路径。 - SPS/PPS 提取:不提取序列参数集和图像参数集,
CMSampleBuffer创建就失败,直接黑屏。 - 关键帧等待:I 帧间隔 2 秒,连接时刚好错过就得等 2 秒。做了 bridge 60 秒延迟复用机制,重连同一设备时秒连。
第二坑:触摸延迟——从 100ms 到 2ms
触摸输入一开始直接用 hdc shell uinput -T -c x y。每次点击都要启动一个完整的 shell 进程——100ms 延迟。
这什么概念?你手指在触控板上划一下,100ms 后手机才开始响应。拖拽窗口时肉眼可见的跳跃感。
HarmonyAgent 方案:在手机上部署一个 C 语言写的常驻进程,通过 TCP 接收 Mac 端发来的 8 字节二进制触摸帧:
1 | struct TouchFrame { |
Agent 收到帧后直接 write() 到 /dev/uinput,延迟从 100ms 降到 <2ms。
这套方案的开发过程也踩了不少坑:
- 交叉编译:ARM aarch64 二进制用什么工具链?最后用 HarmonyOS NDK 交叉编译。
- 自动部署:
hdc file send推送到/data/local/tmp/,chmod +x,后台启动。每次投屏自动检测 Agent 是否已部署。 - 健康检查:ping/pong 心跳,15 秒无响应自动重连。
- 多点触控:
ABS_MT_PRESSURE、ABS_MT_TOUCH_MAJOR、独立 tracking ID,完整的 10 指多点触控协议。
最妙的是 fallback 策略:
1 | Agent TCP → hdc shell uinput → hdc shell uitest |
不管什么场景,总有一条路走通。
第三坑:锁屏/安全屏幕——安卓和鸿蒙共同的”叹息之墙”
这是最让人头疼的问题。HarmonyOS(和 Android 一样)对锁屏、密码输入、支付等敏感界面有 FLAG_SECURE 保护:
- 录屏被屏蔽——投屏画面黑屏
- 触摸注入被拒绝——
uinput事件直接丢弃
华为自家的”远程协同”为什么可以?因为它跑在 DSoftBus 系统服务通道上,经过安全芯片认证,权限完全不同。
我们尝试了三条突破路径:
层级 1:在 Agent 上设置 INPUT_PROP_DIRECT 设备属性,模拟直接输入设备。
层级 2:绕过 uinput,直接往 /dev/input/eventX 写 input_event 结构体。
层级 3(理论可行但未落地):研究 OpenHarmony 开源的 DSoftBus 代码,逆向分析 libnstackx.so。
是的,我们甚至用 Ghidra 逆向分析了鸿蒙系统库。在 DSoftBusResearch/ 目录下有完整的 Ghidra 分析指南和 CoAP 消息格式的研究笔记。虽然最终因为 DSoftBus 需要设备信任认证 + 加密通道,macOS 端实现工作量太大而暂停,但这个过程本身就是一次非常硬核的技术探索。
目前方案:建议用户用”滑动解锁”,Agent 可以通过唤醒按钮点亮屏幕后,用户在物理设备上完成解锁,画面自动恢复。
第四坑:稳定性的”死亡一千刀”
功能跑通只是开始,稳定性才是真正考验。以下是几个差点让人崩溃的 bug:
黑屏 Bug
现象:TCP 连接成功,数据在传输,屏幕就是黑的。
排查了整整一个晚上,最后发现是 H264VideoLayer 里一行看似无害的判断:
1 | // Bug: 在 layer 还没挂到视图层级时就丢弃了帧 |
AVSampleBufferDisplayLayer 在未添加到视图层级时依然可以接收帧并缓存——它会在挂载后自动渲染。提前丢弃导致前几帧全丢了,而由于关键帧间隔 2 秒,画面就一直黑着。
崩溃 Bug
HDCCommand.swift 里进程超时被 terminate() 后,后续代码尝试读取已关闭的 pipe,readDataToEndOfFile() 直接抛异常崩溃。
修复简单但教训深刻:异步 IO 的边界条件处理是 Bug 高发区。
并发 Bug
15+ 个线程同时扫描局域网端口,CPU 飙到 100%。修复是引入 Actor 隔离和并发限制。
窗口 Bug
多显示器 + Spaces + 全屏状态切换后,窗口经常”消失”——实际是落在了不可见的坐标区域。修复是在启动时清理旧 frame 并把窗口强制拉回主屏可见区域。
AI 编程的真实体验
这个项目 100% 由 Claude Code 完成。说几个真实感受:
好的一面:
- 速度惊人:从”鸿蒙能不能投屏到 Mac”这个想法到第一个能用的原型,只用了几天。AI 直接输出可运行的 Swift 代码、C 代码、Python 脚本。
- 跨语言无压力:Swift 写 Mac 端、C 写设备端 Agent、Python 写协议桥接、Shell 写自动化脚本——AI 在四种语言间无缝切换,不需要我切换脑子。
- 架构建议有价值:当我说”触摸延迟太高”,AI 直接提出了设备端常驻进程 + TCP 二进制协议的一整套方案,包括交叉编译、部署策略和 fallback 机制。这种从需求 → 架构设计 → 实现细节的端到端推理能力,是 AI 编程最让我惊喜的地方。
真实痛点:
- 幻觉问题:AI 偶尔会”发明”不存在的 API。比如它用过
AVPlayer.play() async这种不存在的异步方法。需要每段代码都实际编译验证。 - 上下文衰减:长对话中 AI 会”忘记”之前的设计决策。超过 100 轮对话后,需要频繁提醒它”Agent 的端口转发已经在 MirrorService 里做了”。
- 调试靠人:AI 能写出 90% 正确率的代码,但剩下的 10%(通常是并发问题、内存管理、硬件兼容性)需要人去排查。黑屏 Bug 排查了一晚上,AI 给出的所有”直接原因”都不对,最后是自己逐行 review 代码找到的。
- 需要领域知识:如果我不懂 H.264 的 Annex-B vs AVCC 格式差异,AI 说”格式不兼容”时我完全不知道它是不是在胡扯。VibeCode 的前提是你对所做领域有基本判断力。
开源发布:双仓库架构
考虑到开发过程中大量实验性代码和真实测试环境信息(IP、设备序列号等),最终采用双仓库架构:
- 私有仓库 (
HarmonyMirror):完整开发历史 + 所有实验代码 - 公开仓库 (
HarmonyMirror-macOS):脱敏处理后的干净版本,main分支含完整功能,no-agent分支是纯 hdc 方案(不含 Agent)
脱敏处理包括:替换真实 IP、移除设备特定信息、清理 Git 历史中的敏感 commit。
同目录下的其他项目
在 harmony/ 目录下还躺着几个”兄弟项目”:
- Kimi_Agent:早期用 Kimi 对接鸿蒙投屏的实验项目,主要探索不同的 AI Agent 接入方式
- Kimi_Agent_HarmonyMirror:Kimi Agent + HarmonyMirror 的融合尝试,包含 macOS 原生方案和 TypeScript 方案两个方向
- DevEcoCastMac / DevEcoCastMac_2:早期的 DevEco Cast 协议分析项目,为后续的投屏实现奠定了基础
- 多个 backup 目录:每次大重构前都会留一份备份——事实证明这个习惯救了好几次命
这些项目之间不是隔离的——DSoftBus 的研究在 HarmonyMirror 中复用,Kimi Agent 的协议设计思路影响了 HarmonyAgent,早期 DevEcoCastMac 的反编译分析笔记直接变成了 DESIGN.md 中”安全屏幕专题”的素材。
总结
开源地址:github.com/lxiol-star/HarmonyMirror-macOS
这个项目让我对 AI 编程有了更清醒的认识:AI 是放大器,不是魔法棒。它能 10x 加速”已知路径”上的开发,但遇到真正的未知问题时(安全屏幕突破、DSoftBus 逆向),AI 提供的是探索方向而不是答案。
如果你也在用 AI 做类似的项目,几点建议:
- 每个阶段都留备份——AI 的一次错误重构可能毁掉几个小时的成果
- 先编译,再信任——不要假设 AI 写的代码能跑
- 保留设计文档——DESIGN.md 是 AI 最大的”记忆外挂”
- 不要跳过 debug 环节——AI 写的 Bug 往往比人写的更难找,因为你会下意识信任它
- 标题: 从零到开源:用 AI 驱动构建 macOS 鸿蒙投屏工具的踩坑全记录
- 作者: lxiol
- 创建于 : 2026-04-28 23:00:00
- 更新于 : 2026-05-12 16:47:34
- 链接: https://blog.lxiol.cn/2026/04/28/harmonymirror-ai-driven-development/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。