Qt编程:开源工具QtScrcpy

    QtScrcpy 是一款开源的 Android 屏幕镜像和控制工具,基于 Scrcpy(由 Genymobile 开发)并增加了 Qt 编写的图形界面。它允许用户通过电脑实时显示和控制 Android 设备,支持 USB 连接和无线连接(需先通过 USB 配置)。

主要功能

  1. 屏幕镜像

    • 实时显示 Android 设备屏幕,延迟低(取决于设备性能)。

    • 支持高分辨率(可调整画质和帧率)。

  2. 设备控制

    • 通过鼠标和键盘直接操作 Android 设备。

    • 支持触控模拟、文本输入、按键映射(如游戏手柄或键盘映射到手机操作)。

  3. 无线连接

    • 通过 ADB 配置后,可无线连接设备,无需一直插着 USB 线。

  4. 多设备支持

    • 同时连接并控制多台 Android 设备。

  5. 录屏与截图

    • 录制设备屏幕并保存为视频文件。

    • 一键截图保存。

  6. 剪贴板共享

    • 在电脑和 Android 设备之间同步剪贴板内容。

  7. 自定义设置

    • 调整镜像参数(如分辨率、比特率、帧率等)。

    • 支持窗口置顶、全屏模式等。

支持的平台

  • 电脑端:Windows、Linux、macOS(依赖 Qt 环境)。

  • Android 设备:需开启 USB 调试模式(开发者选项),Android 5.0 及以上版本。

使用方法

  1. 下载与安装

    • 从 GitHub 发布页下载对应系统的版本:QtScrcpy Releases

    • Windows 用户可直接使用预编译版本,Linux/macOS 可能需要自行编译。

  2. 连接设备

    • 通过 USB 线连接 Android 设备,并启用 USB 调试模式

    • 运行 QtScrcpy,工具会自动检测设备。

  3. 无线连接(可选)

    • 先用 USB 连接,点击 “无线连接” 按钮,然后拔掉 USB 线即可切换至无线模式。

  4. 调整设置

    • 在界面中修改分辨率、帧率等参数,或设置按键映射。

  5. 开始控制

    • 点击 “启动” 按钮,即可在电脑上操作手机。

与原生 Scrcpy 的区别

特性QtScrcpy原生 Scrcpy
图形界面✅ Qt 编写的 GUI❌ 仅命令行
多设备支持✅ 同时管理多设备❌ 需手动命令
无线连接✅ 一键切换✅ 需手动命令
按键映射✅ 图形化配置❌ 需手动编辑文件
跨平台✅ Windows/Linux/macOS✅ 全平台支持

常见问题

  1. 无法检测设备?

    • 检查 USB 调试是否开启,电脑是否安装 ADB 驱动(Windows 用户可能需要手动安装)。

  2. 无线连接失败?

    • 确保设备和电脑在同一局域网,且先用 USB 完成初始配置。

  3. 高延迟?

    • 降低分辨率或比特率(在设置中调整)。

  4. Linux/macOS 兼容性

    • 可能需要手动编译,确保安装 Qt 和 ADB。

下载地址

  • GitHub 项目页:barry-ran/QtScrcpy

  • 官方文档(中文):提供详细配置指南。

如果你需要更轻量级的解决方案,可以尝试原生 Scrcpy(命令行工具)。QtScrcpy 适合需要图形界面和多设备管理的用户。

 

Scrcpy 开发指南

概述

Scrcpy 由两部分组成:

  1. 服务端(scrcpy-server):在 Android 设备上运行。

  2. 客户端(scrcpy 可执行文件):在主机(电脑)上运行。

    客户端负责将服务端推送到设备并启动它。

    客户端和服务端建立连接后,服务端首先发送设备信息(名称和初始屏幕尺寸),然后开始传输设备屏幕的原始 H.264 视频流。客户端解码视频帧并尽快显示,不进行缓冲,以最小化延迟。客户端不感知设备旋转(由服务端处理),仅知道视频帧的尺寸。

    客户端捕获键盘和鼠标事件,并将其传输到服务端,服务端再注入到设备中。

服务端

权限

  • 捕获屏幕需要 shell 权限。

  • 服务端是一个 Java 应用(包含 public static void main(String... args) 方法),编译时依赖 Android 框架,并在设备上以 shell 身份运行。

运行方式

     Java 类需编译为 classes.dex。例如,主类 my.package.MainClass 编译为 classes.dex 并推送到 /data/local/tmp 后,可通过以下命令运行:

adb shell CLASSPATH=/data/local/tmp/classes.dex \
    app_process / my.package.MainClass

  /data/local/tmp 是一个合适的路径,因为 shell 可读写,但不可全局写入,防止恶意应用替换服务端。Scrcpy 服务端被构建为一个未签名的 APK(重命名为 scrcpy-server),以便利用 Gradle 构建系统。

隐藏方法

虽然编译时依赖 Android 框架,但隐藏方法和类无法直接访问(不同 Android 版本可能不同)。可通过反射调用,封装类和 AIDL 提供与隐藏组件的通信。

线程模型

服务端使用 3 个线程:

  1. 主线程:编码视频并流式传输到客户端。

  2. 控制线程:监听客户端的控制消息(如键盘和鼠标事件)。

  3. 接收线程(由控制线程管理):向客户端发送设备消息(如剪贴板内容)。

由于视频编码通常由硬件加速,单独线程编码和传输并无优势。

屏幕视频编码

编码由 ScreenEncoder 管理,使用 MediaCodec API

  • 输入来自与显示关联的 Surface。

  • 输出 H.264 流写入到客户端连接的 Socket。

  • 设备旋转时,重新初始化编解码器、Surface 和显示,并生成新的视频流。

帧仅在 Surface 变化时生成,以减少不必要的数据传输。但会导致:

  1. 如果设备屏幕无变化,启动时不发送任何帧。

  2. 快速运动后,最后一帧可能质量较差。
    这两个问题通过 KEY_REPEAT_PREVIOUS_FRAME_AFTER 标志解决。

输入事件注入

控制消息由 Controller(运行在单独线程)接收,包括:

  • 按键码(KeyEvent)。

  • 文本(特殊字符可能无法直接通过按键码处理)。

  • 鼠标移动/点击。

  • 鼠标滚轮。

  • 其他命令(如开关屏幕或复制剪贴板)。

     部分事件需注入到系统,使用隐藏方法 InputManager.injectInputEvent(通过封装类 InputManager 暴露)。

客户端

客户端依赖 SDL(跨平台 UI、输入事件、线程等 API),视频流由 libav(FFmpeg)解码。

初始化

启动时,除了初始化 libavSDL,客户端还需:

  1. 推送并启动服务端。

  2. 打开两个 Socket(视频流和控制)。

应用层角色

  • 服务端提供视频流并处理客户端请求。

  • 客户端通过服务端控制设备。

网络层角色

  • 客户端先监听端口,再启动服务端。

  • 服务端连接客户端。

这种角色反转避免竞争条件和轮询。

(注:TCP/IP 模式下角色不反转,因 adb reverse 的 Bug,见 commit 1038bad 和 issue #5。)

连接后,服务端发送设备信息(名称和初始屏幕尺寸),客户端据此初始化窗口和渲染器。

为减少启动时间,SDL 初始化在等待服务端连接时并行执行(见 commit 90a46b4)。

线程模型

客户端使用 4 个线程:

  1. 主线程:执行 SDL 事件循环。

  2. 流线程:接收视频,用于解码和录制。

  3. 控制线程:向服务端发送控制消息。

  4. 接收线程(由控制线程管理):接收服务端的设备消息。

额外线程可能用于处理 APK 安装、文件推送(拖放到主窗口)或控制台帧率打印。

视频流处理

视频流在单独线程中从 Socket 接收:

  • 如果有解码器(未设置 --no-display),则使用 libav 解码 H.264 流,并在新帧可用时通知主线程。

  • 内存中同时存在两帧:

    • 解码帧:由解码线程写入。

    • 渲染帧:由主线程渲染到纹理。

当新帧解码完成,解码器交换两帧(带同步机制),立即开始解码下一帧,同时主线程渲染上一帧。

如果启用录制(--record),则原始 H.264 数据被混流到输出文件。

                                   +----------+      +----------+
                              ---> | decoder  | ---> |  screen  |
             +---------+     /     +----------+      +----------+
 socket ---> | stream  | ----
             +---------+     \     +----------+
                              ---> | recorder |
                                   +----------+

控制器

控制器负责向设备发送控制消息,运行在单独线程以避免主线程 I/O 阻塞。

主线程的 SDL 事件由输入管理器转换为 Android 事件(convert),控制消息被推送到控制器的队列。控制器线程从队列取消息,序列化并发送到服务端。

UI 和事件循环

初始化、输入事件和渲染均在主线程处理。事件循环更新屏幕或委托输入管理器处理。

调试服务端

服务端由客户端在启动时推送到设备。

启用调试器

配置时启用服务端调试器:

meson x -Dserver_debugger=true
# 或(如果 x 已配置)
meson configure x -Dserver_debugger=true

如果设备运行 Android 8 或更低版本,需额外设置:

meson x -Dserver_debugger=true -Dserver_debugger_method=old
# 或
meson configure x -Dserver_debugger=true -Dserver_debugger_method=old

重新编译后,启动 scrcpy 时会在设备端口 5005 启动调试器。将端口转发到电脑:

adb forward tcp:5005 tcp:5005

在 Android Studio 中:

  1. Run > Debug > Edit configurations...

  2. 点击 + > Remote,填写:

    • Host: localhost

    • Port: 5005

  3. 点击 Debug

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值