本文带你用 Go + HTML/WebSocket 从零实现一个 OpenHarmony 设备投屏 Demo:Go 侧用 hdckit-go 连接设备并抓取屏幕帧(UiDriver),通过 WebSocket 二进制实时推送到浏览器,前端用 Canvas 渲染,并根据设备分辨率自适应显示。
- hdckit-go 项目地址:GitHub - airhandsome/hdckit-go
一、功能概览
- 设备发现:通过 hdckit-go 列出 HDC 可连接设备
- 屏幕捕获:UiDriver 启动后回调获取 JPEG 帧
- 实时传输:WebSocket 二进制帧推送
- 前端渲染:Canvas 使用 createImageBitmap + requestAnimationFrame 平滑绘制
- 自适应缩放:根据设备原始分辨率动态调整 Canvas 样式尺寸
- 可扩展:可继续加按键/触控映射、录屏、帧率调节等
二、整体架构
- Go 后端
- hdc.NewClient → Target → CreateUiDriver → Start → StartCaptureScreen
- WebSocket 端点 /ws 推送二进制 JPEG 帧;REST 接口 /api/devices 获取设备列表,/api/devices/{key}/start|stop 控制投屏
- 前端
- WebSocket 连接服务器(binaryType = 'arraybuffer')
- 收到 ArrayBuffer(二进制帧)→ createImageBitmap → requestAnimationFrame → Canvas 绘制
- 按容器大小与原始分辨率等比缩放显示
三、后端实现要点(Go)
依赖:
核心流程:
-
初始化 HDC Client
cli := hdc.NewClient(hdc.Options{}) // 可配置 Bin/Host/Port
hdcClient = cli -
列出设备
targets, _ := hdcClient.ListTargets(ctx)
// 组装为 Device 列表返回给前端 -
UiDriver 投屏
drv := hdcClient.Target(deviceKey).CreateUiDriver()
_ = drv.Start(ctx)
_, _ = drv.StartCaptureScreen(ctx, func(frame []byte) {
// 直接推送二进制帧(JPEG)
broadcastBinaryFrame(frame)
}, 1) -
WebSocket 广播二进制帧
func broadcastBinaryFrame(frame []byte) {
for c := range clients {
_ = c.WriteMessage(websocket.BinaryMessage, frame)
}
} -
停止投屏
_ = drv.StopCaptureScreen(ctx)
drv.Stop() // 无参
常见坑:
- drv.Stop() 不接受 context.Context,注意函数签名
- 初次使用 UiDriver 需确保 uitest agent 可推送到设备(uitestkit_sdk/uitest_agent_v1.1.0.so)
四、前端实现要点(HTML/JS)
-
WebSocket 设置为二进制
ws = new WebSocket(
ws://${location.host}/ws
);
ws.binaryType = 'arraybuffer'; -
消息处理:区分 JSON(设备列表)与二进制帧
ws.onmessage = (event) => {
const data = event.data;
if (typeof data === 'string') {
// 设备列表等 JSON 文本
const msg = JSON.parse(data);
handleMessage(msg);
} else if (data instanceof ArrayBuffer) {
renderFrame(data);
}
}; -
Canvas 渲染(平滑且防闪)
async function renderFrame(buffer) {
if (!isCapturing) return;
const blob = new Blob([buffer], { type: 'image/jpeg' });
const bitmap = await createImageBitmap(blob);// 源尺寸
imgNaturalWidth = bitmap.width;
imgNaturalHeight = bitmap.height;// 仅在尺寸变化时调整样式尺寸
ensureCanvasDisplaySize();// rAF 合批绘制,避免频繁同步 draw
scheduleDraw(bitmap); // 内部 drawImage(bitmap, 0, 0)
} -
自适应缩放
- Canvas 内部像素尺寸使用原始分辨率(canvas.width/height = naturalWidth/Height)
- Canvas 样式使用缩放后的宽高(canvas.style.width/height),只在容器或源尺寸变化时更新
五、如何运行
- 准备环境
- 安装 Go 1.21+
- 安装 HDC 并配置环境变量(可用 hdc version 验证)
- 连接 OpenHarmony 设备(USB 或 Wi-Fi)
- 确保 uitestkit_sdk/uitest_agent_v1.1.0.so 可用(hdckit-go 会自动推送)
-
启动 Demo
go mod tidy
go run demo.go -
打开浏览器访问
- 左侧选择设备 → 点击"开始投屏" → 右侧 Canvas 显示实时画面
六、常见问题与排查
- 页面黑屏/无画面
- 是否收到了二进制帧(开发者工具 Network 或在 renderFrame 里 console.log(buffer.byteLength))
- UiDriver 是否成功 Start/StartCaptureScreen(后端日志)
- 设备端是否推送/运行 uitest agent
- 画面闪烁
- 已通过 createImageBitmap + requestAnimationFrame + 尺寸惰性更新 处理,多数场景可消除抖动
- 帧率过高 + 分辨率大 → 建议后端降低帧率或做节流(可以丢弃落后的帧)
- 画面比例不对
- 确保 ensureCanvasDisplaySize() 使用的是容器尺寸与原始分辨率做等比缩放
- 触控映射要用显示尺寸反算到原始像素坐标
七、可扩展方向
- 触控/按键映射:后端用 hdc shell input 或 UiDriver 输入能力实现
- 录屏/录像:将帧写入视频编码器(FFmpeg / WebCodecs)
- OSD/标记:Canvas 上叠加调试信息/热点区域
- 帧传输压缩与安全:考虑使用 WebSocket over TLS + GOP/帧内压缩策略
八、结语
本文提供了一个"能跑、可扩展、易维护"的投屏基础方案:Go 侧 hdckit-go 负责连接与采集、WebSocket 推二进制帧;前端 Canvas 以高效方式渲染。你可以在此基础上继续拓展全链路控制能力(UI 自动化、按键/触控、录屏等),构建自己的 OpenHarmony 设备调试控制台。
参考项目:
- hdckit-go(airhandsome)