鸿蒙投屏实现

在做鸿蒙 HDC 桌面应用 ECHO 时,希望像 ADB 版一样提供投屏功能。研究了下,目前在鸿蒙上并没有像 Android Scrcpy 一样的开源实现,无法直接拿来使用。想着官方是不是有提供类似功能,查找了下的确存在一个 IDE 测试插件 Hypium 封装了投屏功能。果断下载安装上,试用了下,虽然没有跟 Scrcpy 一样的流畅度,效果也还过得去,便想着能不能将这个功能在 ECHO 上也复刻下。

Hypium 插件投屏原理

首先找了下这个 Hypium 插件有没有开源,没有找到相关仓库,那就只能通过其它方法来挖掘下了。

不管投屏是什么方法实现的,它总要跟手机通信,这个大概率是通过 HDC 工具来实现的。然而,在启动投屏时,通过抓包(HDC 默认端口 8710)却没有看到大量类似视频流或图片的数据。没有通过 HDC Server 传递数据,那有可能是像 Android Scrcpy 那样先在手机上启动了某个服务端,再通过 HDC 的端口映射功能直接与服务端进行通信。执行下 hdc fport ls,果然看到了一个映射到 8012 端口的项。那么问题来了,这个监听 8012 的服务端是怎么来的?

联想到 Android Scrcpy 会往手机上推文件然后启动的情况,执行 hdc shell "ls /data/local/tmp" 查看目录下确实有个叫 agent.so 的文件。为了验证下这个文件是否确实是投屏功能推过来的文件,我将这个文件给删除掉同时重启了手机,然后再次重新启用投屏功能。通过抓包 HDC 可以发现投屏时确实推送了 agent.so 的文件,同时还能看到执行了一次 uitest start-daemon singleness,这应该就是启动这个 so 的命令了。uitest 工具是有开源的,翻看了下,在其源码中也确实能够找到有关 agent.so 的执行代码。

cpp 复制代码
bool ExecuteExtension(string_view version, int32_t argc, char *argv[])
{
    // ... 
    const char *name = "agent.so";
    if (argc > 1 && string_view(argv[0]) == "--extension-name") {
        name = argv[1];
        used_argc = TWO; // argv0,1 is consumed, donot pass down
    }
    string extensionPath = string("/data/local/tmp/") + name;
    // ...
}

总结下投屏大概的流程:

  1. 推送 agent.so 到 /data/local/tmp/ 目录下
  2. 执行 uitest start-daemon singleness 加载 agent.so 启动服务
  3. 使用 HDC 端口映射到 8012
  4. 跟 8012 端口建立连接开始互传数据

投屏协议解析

这个就比较简单直接了,直接抓包映射到 8012 的端口,分析下传递的数据包。

首先是数据包的格式,抓包后直接就能发现每条消息的开头跟结尾都有明确的标识符:_uitestkit_rpc_message_head_ 和 _uitestkit_rpc_message_tail_。中间除了实际的消息数据外前边还有八个字节,前四个字节通过发送跟回传的消息一样可以推测出是 id 之类用于标识响应哪条命令,后四个字节则应该是数据的长度,转十六进制到十进制,计算到 tail 的长度果然对得上。

再分析下投屏时发送的具体内容,可以发现投屏前会先发送开始截屏的指令,大概格式如下:

json 复制代码
{
  "module": "com.ohos.devicetest.hypiumApiHelper",
  "method": "Captures",
  "params": {
    "api": "startCaptureScreen",
    "args": {
      "options": {
        "scale": 0.5
      }
    }
  }
}

之后客户端画面发生变动时会回传一系列的 jepg 格式截图,把这个截图不断绘制出来就可以了。

具体实现

由于 ECHO 是 electron 应用,因此需要在 Node.js 上实现这整个流程。为了复用和方便维护,我将这个封装在了 hdckit 中,只要安装这个包后直接调用相关接口即可。

javascript 复制代码
;(async () => {
    const target = clinet.getTarget(connectKey)
    const uiDriver = await target.createUiDriver()
    await uiDriver.stopCaptureScreen()
    uiDriver.startCaptureScreen(function (image) {
        // 拿到图片数据后做处理
    })
})()

最后只要将图片数据绘制到 canvas 上即可,最终的效果如下图:

相关推荐
特立独行的猫A30 分钟前
10 MB 的桌面 AI 客户端:用仓颉 + 系统 WebView 替代 Electron
前端·harmonyos
SZYSK4 小时前
鸿蒙人脸识别算法放在终端里:和外挂授权 SDK 差在排障路径
物联网·算法·华为·harmonyos
贾伟康4 小时前
【健康菜谱助手|06】HarmonyOS ArkTS 烹饪计时器实战:处理倒计时、暂停、恢复和到期反馈
软件测试·移动开发·harmonyos·arkts·arkui
贾伟康8 小时前
【健康菜谱助手|05】HarmonyOS ArkTS 购物清单实战:去重食材并同步勾选、数量与清空动作
软件测试·移动开发·harmonyos·arkts·arkui
特立独行的猫a9 小时前
仓颉语言cj-tauri 跨端开发框架实战:给鸿蒙侧加一个「读设备电量」的原生能力
ui·harmonyos·tauri·1024程序员节·仓颉·鸿蒙pc
贾伟康19 小时前
【健康菜谱助手|01】HarmonyOS ArkTS 菜谱详情页实战:组织食材、营养、步骤和收藏状态
软件测试·移动开发·harmonyos·arkts·arkui
一目Leizi1 天前
折叠屏适配踩坑实录:从“一展开就变形“到一套代码适配多种形态【鸿蒙心迹】
华为·harmonyos
马剑威(威哥爱编程)1 天前
【HarmonyOS 7 小艺智能体实战】03:第一次建智能体——在小艺开放平台把骨架立起来
人工智能·华为·harmonyos
KevinChaos1 天前
HarmonyOS 7.0 端侧3DGS实验⑤:空洞、漂浮伪影与视角稳定
harmonyos