最近一个项目中需要用到 screen mapping、remote control 等功能,仔细对比之后选择了 scrcpy 这个方案。scrcpy 是一种主要用于 display 和 control 的简单易行的联机方案,主要特点是非侵入性(不需要主动安装应用程序),结构简单,方法易行。下面,让我们来简要分析一下它的工作原理。
我们以 Win 版为例,解压文件后,可以看到如下这么几个文件。
adb.exe
AdbWinApi.dll
AdbWinUsbApi.dll
avcodec-58.dll
avformat-58.dll
avutil-56.dll
scrcpy-noconsole.exe
scrcpy-server
scrcpy.exe
SDL2.dll
swresample-3.dll
swscale-5.dll
仔细阅读源码,发现工作原理很简单,那就是:从 server 端,这里指定为 PC 端,发送一个 jar 文件到 client 端。然后在 client 端执行,利用的是 Android 平台中 classloader 执行命令行的特性,先将 jar 包利用 adb 推送到 /data/local/tmp,然后执行,但是整个过程用户除了运行一个可执行命令、都是无感知的,这操作好骚气~~~
另外一点,前几期阶段,server 文件只是一个 jar 包,而后期 scrcpy 改变了策略,由这个文件来生成 jar 包,这样做是为了什么?小编我先在这里卖个关子。
上述文件执行之后,会在 server / client 之间生成一个 socks 连接,该 socks 连接中传输的是什么?没错,看到 ffmpeg 没有,传输的就是 stream 流,最后经 ffmpeg 解码还原成图像。至此,图像传输的基本过程结束。下面贴出一段代码,可以看到虽然 scrcpy 这个小工具结构简单、但是还是能够打印输出/调试信息,功能还是蛮全的。
public static DesktopConnection open(Device device, boolean tunnelForward) throws IOException {
LocalSocket videoSocket;
LocalSocket controlSocket;
if (tunnelForward) {
LocalServerSocket localServerSocket = new LocalServerSocket(SOCKET_NAME);
try {
System.out.println("Waiting for video socket connection...");
videoSocket = localServerSocket.accept();
System.out.println("video socket is connected.");
// send one byte so the client may read() to detect a connection error
videoSocket.getOutputStream().write(0);
try {
System.out.println("Waiting for input socket connection...");
controlSocket = localServerSocket.accept();
System.out.println("input socket is connected.");
} catch (IOException | RuntimeException e) {
videoSocket.close();
throw e;
}
} finally {
localServerSocket.close();
}
} else {
videoSocket = connect(SOCKET_NAME);
try {
controlSocket = connect(SOCKET_NAME);
} catch (IOException | RuntimeException e) {
videoSocket.close();
throw e;
}
}
DesktopConnection connection = new DesktopConnection(videoSocket, controlSocket);
Size videoSize = device.getScreenInfo().getVideoSize();
connection.send(Device.getDeviceName(), videoSize.getWidth(), videoSize.getHeight());
return connection;
}
需要注意的是,这个过程每次都需要执行一遍,也就是说,每次都会推送一遍这个 file....
效果图如下
,