鸿蒙投屏实现 原创

surunzi
发布于 2025-4-21 11:49
浏览
1收藏

在做鸿蒙 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 的执行代码。

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.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

总结下投屏大概的流程:

  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 的长度果然对得上。

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

{
  "module": "com.ohos.devicetest.hypiumApiHelper",
  "method": "Captures",
  "params": {
    "api": "startCaptureScreen",
    "args": {
      "options": {
        "scale": 0.5
      }
    }
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

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

具体实现

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

;(async () => {
    const target = clinet.getTarget(connectKey)
    const uiDriver = await target.createUiDriver()
    await uiDriver.stopCaptureScreen()
    uiDriver.startCaptureScreen(function (image) {
        // 拿到图片数据后做处理
    })
})()
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

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

鸿蒙投屏实现-鸿蒙开发者社区

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
标签
已于2025-4-21 11:49:55修改
1
收藏 1
回复
举报
1
1


回复
    相关推荐