Qt编程:开源工具QtScrcpy
QtScrcpy 是一款开源的 Android 屏幕镜像和控制工具,基于 Scrcpy(由 Genymobile 开发)并增加了 Qt 编写的图形界面。它允许用户通过电脑实时显示和控制 Android 设备,支持 USB 连接和无线连接(需先通过 USB 配置)。
QtScrcpy 是一款开源的 Android 屏幕镜像和控制工具,基于 Scrcpy(由 Genymobile 开发)并增加了 Qt 编写的图形界面。它允许用户通过电脑实时显示和控制 Android 设备,支持 USB 连接和无线连接(需先通过 USB 配置)。
主要功能
-
屏幕镜像
-
实时显示 Android 设备屏幕,延迟低(取决于设备性能)。
-
支持高分辨率(可调整画质和帧率)。
-
-
设备控制
-
通过鼠标和键盘直接操作 Android 设备。
-
支持触控模拟、文本输入、按键映射(如游戏手柄或键盘映射到手机操作)。
-
-
无线连接
-
通过 ADB 配置后,可无线连接设备,无需一直插着 USB 线。
-
-
多设备支持
-
同时连接并控制多台 Android 设备。
-
-
录屏与截图
-
录制设备屏幕并保存为视频文件。
-
一键截图保存。
-
-
剪贴板共享
-
在电脑和 Android 设备之间同步剪贴板内容。
-
-
自定义设置
-
调整镜像参数(如分辨率、比特率、帧率等)。
-
支持窗口置顶、全屏模式等。
-
支持的平台
-
电脑端:Windows、Linux、macOS(依赖 Qt 环境)。
-
Android 设备:需开启 USB 调试模式(开发者选项),Android 5.0 及以上版本。
使用方法
-
下载与安装
-
从 GitHub 发布页下载对应系统的版本:QtScrcpy Releases。
-
Windows 用户可直接使用预编译版本,Linux/macOS 可能需要自行编译。
-
-
连接设备
-
通过 USB 线连接 Android 设备,并启用 USB 调试模式。
-
运行 QtScrcpy,工具会自动检测设备。
-
-
无线连接(可选)
-
先用 USB 连接,点击 “无线连接” 按钮,然后拔掉 USB 线即可切换至无线模式。
-
-
调整设置
-
在界面中修改分辨率、帧率等参数,或设置按键映射。
-
-
开始控制
-
点击 “启动” 按钮,即可在电脑上操作手机。
-
与原生 Scrcpy 的区别
特性 | QtScrcpy | 原生 Scrcpy |
---|---|---|
图形界面 | ✅ Qt 编写的 GUI | ❌ 仅命令行 |
多设备支持 | ✅ 同时管理多设备 | ❌ 需手动命令 |
无线连接 | ✅ 一键切换 | ✅ 需手动命令 |
按键映射 | ✅ 图形化配置 | ❌ 需手动编辑文件 |
跨平台 | ✅ Windows/Linux/macOS | ✅ 全平台支持 |
常见问题
-
无法检测设备?
-
检查 USB 调试是否开启,电脑是否安装 ADB 驱动(Windows 用户可能需要手动安装)。
-
-
无线连接失败?
-
确保设备和电脑在同一局域网,且先用 USB 完成初始配置。
-
-
高延迟?
-
降低分辨率或比特率(在设置中调整)。
-
-
Linux/macOS 兼容性
-
可能需要手动编译,确保安装 Qt 和 ADB。
-
下载地址
-
GitHub 项目页:barry-ran/QtScrcpy
-
官方文档(中文):提供详细配置指南。
如果你需要更轻量级的解决方案,可以尝试原生 Scrcpy(命令行工具)。QtScrcpy 适合需要图形界面和多设备管理的用户。
Scrcpy 开发指南
概述
Scrcpy 由两部分组成:
-
服务端(scrcpy-server):在 Android 设备上运行。
-
客户端(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 个线程:
-
主线程:编码视频并流式传输到客户端。
-
控制线程:监听客户端的控制消息(如键盘和鼠标事件)。
-
接收线程(由控制线程管理):向客户端发送设备消息(如剪贴板内容)。
由于视频编码通常由硬件加速,单独线程编码和传输并无优势。
屏幕视频编码
编码由 ScreenEncoder
管理,使用 MediaCodec API
:
-
输入来自与显示关联的 Surface。
-
输出 H.264 流写入到客户端连接的 Socket。
-
设备旋转时,重新初始化编解码器、Surface 和显示,并生成新的视频流。
帧仅在 Surface 变化时生成,以减少不必要的数据传输。但会导致:
-
如果设备屏幕无变化,启动时不发送任何帧。
-
快速运动后,最后一帧可能质量较差。
这两个问题通过KEY_REPEAT_PREVIOUS_FRAME_AFTER
标志解决。
输入事件注入
控制消息由 Controller
(运行在单独线程)接收,包括:
-
按键码(
KeyEvent
)。 -
文本(特殊字符可能无法直接通过按键码处理)。
-
鼠标移动/点击。
-
鼠标滚轮。
-
其他命令(如开关屏幕或复制剪贴板)。
部分事件需注入到系统,使用隐藏方法 InputManager.injectInputEvent
(通过封装类 InputManager
暴露)。
客户端
客户端依赖 SDL(跨平台 UI、输入事件、线程等 API),视频流由 libav
(FFmpeg)解码。
初始化
启动时,除了初始化 libav
和 SDL
,客户端还需:
-
推送并启动服务端。
-
打开两个 Socket(视频流和控制)。
应用层角色:
-
服务端提供视频流并处理客户端请求。
-
客户端通过服务端控制设备。
网络层角色:
-
客户端先监听端口,再启动服务端。
-
服务端连接客户端。
这种角色反转避免竞争条件和轮询。
(注:TCP/IP 模式下角色不反转,因 adb reverse
的 Bug,见 commit 1038bad 和 issue #5。)
连接后,服务端发送设备信息(名称和初始屏幕尺寸),客户端据此初始化窗口和渲染器。
为减少启动时间,SDL 初始化在等待服务端连接时并行执行(见 commit 90a46b4)。
线程模型
客户端使用 4 个线程:
-
主线程:执行 SDL 事件循环。
-
流线程:接收视频,用于解码和录制。
-
控制线程:向服务端发送控制消息。
-
接收线程(由控制线程管理):接收服务端的设备消息。
额外线程可能用于处理 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 中:
-
Run > Debug > Edit configurations...
-
点击 + > Remote,填写:
-
Host:
localhost
-
Port:
5005
-
-
点击 Debug。

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐
所有评论(0)