用 Go + HTML 實現 OpenHarmony 投屏(hdckit-go + WebSocket + Canvas 實戰)

本文帶你用 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 值來判斷我們是否應該展開還是回收…