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。