鸿蒙投屏实现

在做鸿蒙 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 上即可,最终的效果如下图:

相关推荐
resh_people14 小时前
开源鸿蒙平台 KMP_CMP 三方库「Kermit」适配全流程
华为·开源·harmonyos
李游Leo16 小时前
HarmonyOS 7 EasyGo + ArkUI Navigation:平行视界主副页筛选事务与跨栏撤销一致性【鸿蒙心迹】
华为·harmonyos
HwJack2017 小时前
【共创稿事节】HarmonyOS 7空间应用的可用性测试和体验评估小方法分享
华为·harmonyos·可用性测试
m0_7381858217 小时前
Flutter 鸿蒙化实战:flutter_scankit 适配 OpenHarmony,华为 ScanKit 扫码
flutter·华为·harmonyos·鸿蒙
用户1179104883319 小时前
别让AI随便启动你的LSP:hmharness给语言服务器加SHA256门禁 GitHub:swsgbl/hmharness
ai编程·harmonyos
Helix25021 小时前
Electron、WebView 框架与 HTA 对比:桌面应用选型、安全性与现代替代方案
前端·javascript·electron·webview·vbscript·hta·本地html应用
不可能片场1 天前
AI视频首尾帧控制 用参考图定住镜头
前端·electron
李游Leo1 天前
HarmonyOS 7 + Share Kit-Window Kit:精准碰一碰屏幕坐标的窗口换算与落点拒绝【鸿蒙心迹】
华为·harmonyos
翼辉cto1 天前
Compose Multiplatform KV 存储库 multiplatform-settings 的 OpenHarmony 鸿蒙化适配实战
华为·harmonyos
事圆则缓1 天前
Flutter 开发鸿蒙实战:从 OpenHarmony 适配版到 HAP 构建与插件接入
flutter·华为·harmonyos