Scrcpy开发者文档中文翻译

近期需要研究一下Scrcpy的源码,虽然英文都看得懂,但是查看速度比起母语看起来还是比较慢的,就想着翻译一下,也方便大家。

源文档:https://github.com/Genymobile/scrcpy/blob/master/DEVELOP.md

Scrcpy 开发者文档

概述

这个应用主要由两部分组成:

  • 服务端(scrcpy-server),将会在设备中(指手机等移动设备)运行
  • 客户端(scrcpy binary),将会在主机电脑上运行。

客户端负责将服务器推送(使用adb push)到设备并开始执行。

一旦客户端和服务器相互连接,服务器首先发送设备信息(设备名称和初始屏幕尺寸),然后就可以开始发送设备屏幕的原始 H.264 视频流。 客户端解码视频帧,并在没有缓冲的情况下尽快显示它们,以最大限度地减少延迟。 客户端并不知道设备旋转(由服务器处理),它只知道视频帧的尺寸。

客户端捕获相关的键盘和鼠标事件,并将其传输到服务器,服务器将它们注入设备。

服务端

权限

捕获屏幕要求一些授予给shell的权限。

该服务端是一个Java应用(通过 public static void main(String... args)方法),经由Android框架编译后由Shell运行在Android设备中。

为了运行这样一个Java应用,必须对类进行dexed(通常是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 是推送Server的一个很好的候选位置,因为它可以被 shell 读写,但不是全局可写的,所以恶意应用程序可能不会在Client执行之前替换Server。

比起原始的dex文件,app_process 接受包含 classes.dex(例如 APK)的 jar。为了简化并且使用gradle构建系统的优点,Server构建为(无签名的)APK(重命名为 scrcpy-server)。

隐藏方法

尽管是针对 Android 框架编译的,但隐藏的方法和类无法直接访问(并且它们可能因 Android 版本而异)。但是可以使用反射来调用它们。与隐藏组件的通信可以通过包装类和 aidl 实现。

线程

该Server使用了3个线程。

主线程,将视频编码并流式传输到客户端;
控制器线程,监听来自客户端的控制消息(通常是键盘和鼠标事件);
接收器线程(由控制器管理),向客户端发送设备消息(目前,它仅用于发送设备剪贴板内容)。
由于视频编码通常是硬件,因此在两个不同的线程中进行编码和流式传输没有任何好处。(这应该是解释为什么在主线程进行编码而且传输)

屏幕视频编码

编码由 ScreenEncoder管理。

视频使用 MediaCodec API 进行编码。编解码器从与显示器关联的表面获取其输入,并将生成的 H.264 流写入提供的输出流(连接到客户端的套接字)。

在设备旋转时,编解码器、表面和显示器被重新初始化,并产生新的视频流。

只有当表面发生变化时才会产生新的框架。这很好,因为它避免了发送不必要的帧,但也有缺点:

如果设备屏幕没有变化,它不会在启动时发送任何帧,
快速运动更改后,最后一帧可能质量较差。
这两个问题都由标志 KEY_REPEAT_PREVIOUS_FRAME_AFTER 来 解决

输入事件注入

控制器从客户端接收控制消息(在单独的线程中运行)。 有几种类型的输入事件:

  • 键码(参见 KeyEvent),
  • 文本(特殊字符可能无法直接由键码处理),
  • 鼠标移动/点击,
  • 鼠标滚动,
  • 其他命令(例如打开屏幕或复制剪贴板)。

其中一些需要向系统注入输入事件。 为此,他们使用隐藏方法 InputManager.injectInputEvent(由我们的 InputManager wrapper 展现)。

客户端

客户端依赖于 SDL,它为 UI、输入事件、线程等提供跨平台 API。

视频流由 libav (FFmpeg) 解码。

初始化

启动时,除了libav 和 SDL 初始化外,客户端还必须在设备上推送并启动服务器,并打开两个套接字(一个用于视频流,一个用于控制)以便它们可以进行通信。

注意,客户端-服务器角色在应用程序级别表示:

  • 服务器提供视频流并处理来自客户端的请求,
  • 客户端通过服务器控制设备。

但是,在网络级别,角色是相反的:

  • 客户端打开服务器套接字并在启动服务器之前监听端口,
  • 服务器连接到客户端。

这种角色反转保证了连接不会因为竞争条件而失败,并且避免了轮询。

(请注意,在 TCP/IP 上,由于 adb reverse 中的错误,角色不会反转。请参阅提交 1038bad 问题 #5。)

一旦连接上服务端,服务端会发送设备信息(名称和初始屏幕尺寸)。因此,客户端可以在第一帧可用之前初始化窗口和渲染器。

为了最大限度地减少启动时间,SDL 在监听来自服务器的连接时进行初始化(参见提交 90a46b4)。

线程

客户端使用 4 个线程:

  • 主线程,执行 SDL 事件循环,
  • 流线程,接收视频并用于解码和录制,
  • 控制器线程,向服务器发送控制消息,
  • 接收器线程(由控制器管理),从服务器接收设备消息。

此外,如果需要,可以启动另一个线程来处理 APK 安装或文件推送请求(通过在主窗口上拖放)或在控制台中定期打印帧率。

客户端会在单独的线程中从套接字(连接到设备上的服务器)接收视频

如果存在解码器(即未设置 --no-display),则它使用 libav 解码来自套接字的 H.264 流,并在新帧可用时通知主线程。

内存中同时有两帧:

  • 解码帧,由解码器从解码器线程写入,
  • 渲染帧,在主线程的纹理中渲染。

当新的解码帧可用时,解码器交换解码和渲染帧(具有适当的同步)。 因此,当主线程渲染最后一帧时,它立即开始解码新帧。

如果存在记录器(即启用 --record),则它将原始 H.264 数据包多路复用到输出视频文件。

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

控制器

控制器负责向设备发送控制消息。 它在一个单独的线程中运行,以避免在主线程上进行 I/O。

在主线程上接收到的 SDL 事件上,输入管理器创建适当的控制消息。 它负责将 SDL 事件转换为 Android 事件(使用 convert)。 它将控制消息推送到由控制器持有的队列。 在它自己的线程上,控制器从队列中获取消息,将其序列化并发送给客户端。

用户界面和事件循环

初始化、输入事件和渲染都在主线程中管理

事件在事件循环中处理,它更新屏幕或委托给输入管理器

Hack

需要更多的详情,请阅读代码!

如果你找到了Bug,或者你有什么令人惊叹的想法需要实现,请讨论和贡献(指的是提出issue和merge request)。

调试服务端

这个服务端将在客户端启动时推送到设备上。

如果要调试它,那就在配置时开始调试器:

meson x -Dserver_debugger=true
# or, if x is already configured
meson configure x -Dserver_debugger=true

如果你的设备在Android 8或者以下版本运行,需要额外设置“server_debugger_method ”为“old”:

meson x -Dserver_debugger=true -Dserver_debugger_method=old
# or, if x is already configured
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。

猜你喜欢

转载自blog.csdn.net/u013379032/article/details/118966723