本文带你用 Go + HTML/WebSocket 从零实现一个 OpenHarmony 设备投屏 Demo:Go 侧用 hdckit-go 连接设备并抓取屏幕帧(UiDriver),通过 WebSocket 二进制实时推送到浏览器,前端用 Canvas 渲染,并根据设备分辨率自适应显示。

  • 源码:GitHub - airhandsome/hdckit-go-demo
  • 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)

依赖:

  • github.com/airhandsome/hdckit-go/hdc
  • github.com/gorilla/websocket

核心流程:

1) 初始化 HDC Client

cli := hdc.NewClient(hdc.Options{}) // 可配置 Bin/Host/Port
hdcClient = cli

2) 列出设备

targets, _ := hdcClient.ListTargets(ctx)
// 组装为 Device 列表返回给前端

3) UiDriver 投屏

drv := hdcClient.Target(deviceKey).CreateUiDriver()
_ = drv.Start(ctx)
_, _ = drv.StartCaptureScreen(ctx, func(frame []byte) {// 直接推送二进制帧(JPEG)broadcastBinaryFrame(frame)
}, 1)

4) WebSocket 广播二进制帧

func broadcastBinaryFrame(frame []byte) {for c := range clients {_ = c.WriteMessage(websocket.BinaryMessage, frame)}
}

5) 停止投屏

_ = drv.StopCaptureScreen(ctx)
drv.Stop() // 无参

常见坑:

  • drv.Stop() 不接受 context.Context,注意函数签名
  • 初次使用 UiDriver 需确保 uitest agent 可推送到设备(uitestkit_sdk/uitest_agent_v1.1.0.so)

四、前端实现要点(HTML/JS)

1) WebSocket 设置为二进制

ws = new WebSocket(`ws://${location.host}/ws`);
ws.binaryType = 'arraybuffer';

2) 消息处理:区分 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);}
};

3) 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 合批绘制,避免频繁同步 drawscheduleDraw(bitmap); // 内部 drawImage(bitmap, 0, 0)
}

4) 自适应缩放

  • Canvas 内部像素尺寸使用原始分辨率(canvas.width/height = naturalWidth/Height)
  • Canvas 样式使用缩放后的宽高(canvas.style.width/height),只在容器或源尺寸变化时更新

五、如何运行

1) 准备环境

  • 安装 Go 1.21+
  • 安装 HDC 并配置环境变量(可用 hdc version 验证)
  • 连接 OpenHarmony 设备(USB 或 Wi-Fi)
  • 确保 uitestkit_sdk/uitest_agent_v1.1.0.so 可用(hdckit-go 会自动推送)

2) 启动 Demo

go mod tidy
go run demo.go

3) 打开浏览器访问

http://localhost:8080
  • 左侧选择设备 → 点击“开始投屏” → 右侧 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)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/95974.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/95974.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/95974.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

运筹学——求解线性规划的单纯形法

单纯形法的原理 先来举个例子: 用单纯形法求解下面线性规划问题的最优解:注释:解的过程是反复迭代的过程,如果第一次迭代没有理解也没关系,再继续看第二次迭代,和第三次迭代,每次迭代的流程都是…

Python GUI 框架 -- DearPyGui 简易入门

DearPyGui 关于 DPG 是一个简单且功能强大的 Python 图形用户界面框架。 与其他Python图形用户界面库相比,DPG具有以下独特之处: GPU 渲染多线程高度可定制内置开发人员工具:主题检查、资源检查、运行时指标带有数百种小部件组合的 70 多…

gcloud cli 使用 impersonate模拟 服务帐号

什么是模拟服务帐号 众所周知, gcloud 登陆的方式有两种 使用个人帐号, 通常是1个邮箱地址使用一个service account 通常是1个 json key 文件 所谓模式服务帐号意思就是, 让操作人员用个人帐号登陆, 但是登陆后所有的操作都是基于…

idf--esp32的看门狗menuconfig

1.Interrupt Watchdog Timeout (ms):意思是中断看门狗,也就是专门监管中断响应时间的看门狗,如果某个中断服务程序超过了这个运行时间,就会导致程序重启。2.红框是任务看门狗的最大看门时间,超过时间就会警告&#xff…

git在Linux中的使用

git-Linux中的使用一、下载git二、https方式上传三、ssh秘钥方式上传一、下载git 版本信息 [rootrocky ~]# cat /etc/rocky-release Rocky Linux release 9.4 (Blue Onyx) [rootrocky ~]# cat /etc/rocky-release-upstream Derived from Red Hat Enterprise Linux 9.4 [rootro…

HMI(人机界面)

新晋码农一枚,小编定期整理一些写的比较好的代码,作为自己的学习笔记,会试着做一下批注和补充,转载或者参考他人文献会标明出处,非商用,如有侵权会删改!欢迎大家斧正和讨论!一、核心…

嵌入式解谜日志—多路I/O复用

多路 I/O复用(Multiplexed I/O):1.定义:系统提供的I/O事件通知机制2.应用:是一种 I/O 编程模型,用于在单线程中同时处理多个(阻塞) I/O 操作,避免因等待某个 I/O 操作完成…

关于嵌入式学习——单片机4

ds18b20温度传感器的使用一、传感器分类:数字温度传感器,实现简单,不需要额外转换电路,采集过来的就是数字温度值模拟温度传感器->热敏电阻->AD转换电路->数字值二、传感器接口:GPIO接口:&#xf…

Kali搭建sqli-labs靶场

1.输入apt-get install docker.io即可下载靶场镜像。 下载好后,我们输入docker search sqli-labs搜索sqli-labs靶场。2.我们选择第一个,输入docker pull acgpiano/sqli-labs,将该靶场装到本地。此时输入docker images,发现本地有s…

电脑外接显示屏字体和图标过大

当外接显示屏的分辨率过高时,可以调整显示器设置来解决字体和图标过大的问题。具体操作包括在桌面右击选择显示设置,切换到外接显示器,将分辨率调至推荐的1920x1080,或根据个人偏好进行适当调节,然后保存更改。 原因&a…

Linux 网络流量监控 Shell 脚本详解(支持邮件告警)

前言 一、脚本功能 二、实现原理 三、Shell 脚本实现 四、关键知识点解析 1. Bash 关联数组 2. 命令组 { } 与子 Shell ( ) 3. 字符串拼接换行 4. 流量计算逻辑 五、测试方法 六、优化建议 七、总结 前言 在生产环境中,监控服务器的 网络流量 非常重要…

【牛客刷题-剑指Offer】BM18 二维数组中的查找:一题四解,从暴力到最优

文章目录 一、题目介绍 1.1 描述 1.2 示例1 1.3 示例2 1.4 给的部分代码 二、题解 方法一:暴力遍历 方法二:二分查找(逐行) 方法三:Z字形查找(最优解) 方法四:递归分治(拓展思路) 三、总结 心得体会 一、题目介绍 原题链接:https://www.nowcoder.com/practice/abc3…

使用pyspark对上百亿行的hive表生成稀疏向量

背景:一张上百亿行的hive表,只有id和app两列,其中app的去重量是8w多个(原app有上百万枚举值,此处已经用id数量进行过筛选,只留下有一定规模的app),id的去重量大概有八九亿&#xff0…

【设计模式】关于学习《重学Java设计模式》的一些成长笔记

【设计模式】关于学习《重学Java设计模式》的一些成长笔记 没有几个人是一说就会的,掌握一些技能,不仅要用心,而且还需要从温故中知新。 为此,好记性不如烂笔头,我干脆一步一脚印地系统学习一遍设计模式! (关注不迷路哈!!!) 文章目录 【设计模式】关于学习《重学Jav…

【基础-判断】@Entry装饰的自定义组件将作为页面的入口。在单个页面中可以使用多个@Entry装饰不同自定义组件。

@Entry装饰的自定义组件将作为页面的入口。在单个页面中可以使用多个@Entry装饰不同自定义组件。 解释: @Entry 的核心作用与唯一性:@Entry 装饰器用于明确声明该组件是一个页面的入口组件,即整个页面的“根”和“起点”。当UIAbility实例加载并显示页面时,系统需要明确知道…

医学影像AI应用-实践:使用MONAI实现肺部CT图像分割的原理与实践

🧑 博主简介:CSDN博客专家、CSDN平台优质创作者,高级开发工程师,数学专业,10年以上C/C++, C#,Java等多种编程语言开发经验,拥有高级工程师证书;擅长C/C++、C#等开发语言,熟悉Java常用开发技术,能熟练应用常用数据库SQL server,Oracle,mysql,postgresql等进行开发应用…

如何训练一个简单的Transformer模型(附源码)李宏毅2025大模型-作业4

摘要:一、作业目标:使用只有2层transformer的GPT-2,生成完整宝可梦图像。二、源码&解析:使用提供的Transformer模型(GPT-2)进行训练,FID Score: 96.3425一、作业目标1)目标使用T…

leetcode211.添加与搜索单词-数据结构设计

与208.前缀树的设计是一样的,关键点在于word中存在通配符“.",所以针对该特殊情况,在search时针对这里进行全子节点的深度搜索class WordDictionary {TrieNode root;private class TrieNode {char val;// 当前节点的值,冗余了…

项目中的一些比较实用的自定义控件

本文是记录项目开发中一些相对复杂但都比较实用的控件,这些控件都是基于自定义的方式去实现,如果有需要的朋友,这个可以作为一个参考,同时也做一个自我总结。 (1)子项大小不一致的RecyclerView(…

[iOS] 折叠 cell

目录 前言 1.原理 2.折叠 cell 的点击选中 3.折叠 cell 高度的变化 4.实现效果 5.总结 前言 折叠 cell 是在 3GShare 中写过的一个小控件,这篇博客是一个小小的总结。 1.原理 在这里的核心就是我们可以通过改变按钮的 tag 值来判断我们是否应该展开还是回收…