scrcpy 开发环境搭建与源码走读之一

阅读: 评论:0

scrcpy 开发环境搭建与源码走读之一

scrcpy 开发环境搭建与源码走读之一

此系列博文主要记录本人在scrcpy项目重构过程中记录文件,共大家参考和备忘。

环境说明

  • 1.操作系统 ubuntu20-64
  • 2.虚拟机 VMware 15
  • 3.源码 1.19 版本
    git clone -b v1.19
    cd scrcpy

第一步 搭建 scrcpy 编译开发环境

1.1 安装 meson

sudo apt install python3-pip
pip3 install meson

1.2 安装 ninja

sudo apt install ninja-build

1.3 安装 java vm

sudo apt install openjdk-11-jdk
export PATH=“ J A V A H O M E / b i n : JAVA_HOME/bin: JAVAH​OME/bin:PATH”

1.4 安装 android sdk

android sdk安装,本人是通过在ubuntu20中安装 AndroidStudio-2021 的IDE环境,
然后通过adk管理方式,下载sdk包,内容如下:

robot@ubuntu:~$ ls Android/Sdk/platforms/
android-22  android-24  android-26  android-28  android-30  android-31
robot@ubuntu:~$ ls Android/Sdk/build-tools/
29.0.2  30.0.2  31.0.0

1.5 安装 依赖库

sudo apt install gcc git pkg-config meson ninja-build libsdl2-dev libavcodec-dev libavdevice-dev libavformat-dev libavutil-dev
sudo apt install ffmpeg libsdl2-2.0-0 adb
sudo snap install gradle

1.6 编译过程参考第四部分

参考说明问题,详细见:scrcpy源码主目录下BUILD.md 文件说明。

第二步 scrcpy 客户端程序走读

bool scrcpy(const struct scrcpy_options *options) {static struct scrcpy scrcpy;struct scrcpy *s = &scrcpy;server_init(&s->server);										///> 1. server_init()struct server_params params = {.serial = options->serial,.port_range = options->port_range,.bit_rate = options->bit_rate,.max_fps = options->max_fps,.display_id = options->display_id,.codec_options = options->codec_options,.encoder_name = options->encoder_name,.force_adb_forward = options->force_adb_forward,};server_start(&s->server, &params);								///> 2. server_start();server_started = true;sdl_init_and_configure(options->display, options->render_driver,options->disable_screensaver);server_connect_to(&s->server, device_name, &frame_size);		///> 3. server_connect_to();file_handler_init(&s->file_handler, s->server.serial,options->push_target);				///> 4. file_handler_init();  socket init & 服务端代码adb pushdecoder_init(&s->decoder);										///> 5. decoder_init();av_log_set_callback(av_log_callback);							///> 6. av_log_set_callback();static const struct stream_callbacks stream_cbs = {				///> 7. stream_init();.on_eos = stream_on_eos,};stream_init(&s->stream, s->server.video_socket, &stream_cbs, NULL);stream_add_sink(&s->stream, &dec->packet_sink);					///> 8. stream_add_sink(); decstream_add_sink(&s->stream, &rec->packet_sink);					///> 9. stream_add_sink(); reccontroller_init(&s->controller, s-&l_socket);		///> 10. controller_init(); control_socketcontroller_start(&s->controller);								///> 11. controller_start();struct screen_params screen_params = {.window_title = window_title,.frame_size = frame_size,.always_on_top = options->always_on_top,.window_x = options->window_x,.window_y = options->window_y,.window_width = options->window_width,.window_height = options->window_height,.window_borderless = options->window_borderless,.rotation = options->rotation,.mipmaps = options->mipmaps,.fullscreen = options->fullscreen,.buffering_time = options->display_buffer,};screen_init(&s->screen, &screen_params);						///> 12. screen_init();decoder_add_sink(&s->decoder, &s->screen.frame_sink);			///> 13. decoder_add_sink();#ifdef HAVE_V4L2sc_v4l2_sink_init(&s->v4l2_sink, options->v4l2_device, frame_size,options->v4l2_buffer);				///> 14. sc_v4l2_sink_init();decoder_add_sink(&s->decoder, &s->v4l2_sink.frame_sink);
#endifstream_start(&s->stream);         ///> 14+.流启动配置,第一次发布时遗漏咯,很抱歉.补充    input_manager_init(&s->input_manager, &s->controller, &s->screen, options);		///> 15. input_manager_init();ret = event_loop(s, options);													///> 16. event_loop();///> 程序推出释放资源相关内容screen_hide_window(&s->screen);controller_stop(&s->controller);file_handler_stop(&s->file_handler);screen_interrupt(&s->screen);server_stop(&s->server);stream_join(&s->stream);sc_v4l2_sink_destroy(&s->v4l2_sink);screen_join(&s->screen);screen_destroy(&s->screen);controller_join(&s->controller);controller_destroy(&s->controller);recorder_destroy(&s->recorder);file_handler_join(&s->file_handler);file_handler_destroy(&s->file_handler);server_destroy(&s->server);       										///> 销毁 serverreturn ret;
}    
  1. server_init()

struct scrcpy {						///> 封装 scrcpy 对象内容 struct server server;									//> 1. 通信的server对象struct screen screen;									//> 2. 投屏 screen 对象struct stream stream;									//> 3. 视频流 stream 对象struct decoder decoder;struct recorder recorder;
#ifdef HAVE_V4L2struct sc_v4l2_sink v4l2_sink;
#endifstruct controller controller;struct file_handler file_handler;struct input_manager input_manager;
};
  1. server_start();
bool
server_start(struct server *server, const struct server_params *params) {push_server(params->serial);											///> 2.1 from client adb push to server enable_tunnel_any_port(server, params->port_range,params->force_adb_forward);					///> 2.2 开启ADB端口转发,走 WIFI 模式,否则认为是USB ADB 模式.server->process = execute_server(server, params);						///> 2.3 建立本地的 ADB client 连接本地 ADB SERVER 。bool ok = sc_thread_create(&server->wait_server_thread, run_wait_server,"wait-server", server);						///> 2.4 本地 ADB SERVER 建立与 ANDROID ADB DAEMAIN 守护线程通信。}

获取本地环境变量中,是否有 “SCRCPY_SERVER_PATH” 内容,如果没有设置环境变量、则使用缺省的 server-path 内容(scrcpy软件开机执行内容);

  • push_server(params->serial); 函数源码走读
///> 2.1.  params->serial 是ADB PUSH中是用的 -s 参数.
static bool
push_server(const char *serial) {char *server_path = get_server_path();    /* 软件没有安装,使用当前路径下的 scrcpy-server 作为执行路径 */if (!server_path) {return false;}/* ** adb push * local_path = /build/server/scrcpy-server           编译输出的服务端程序。* remote_path="/data/local/tmp/scrcpy-server.jar"    ANDROID 设备路径和文件名称.**/process_t process = adb_push(serial, server_path, DEVICE_SERVER_PATH);   }///> 2.2 adb_push 函数
process_t adb_push(const char *serial, const char *local, const char *remote) {const char *const adb_cmd[] = {"push", local, remote};process_t proc = adb_execute(serial, adb_cmd, ARRAY_LEN(adb_cmd));}///> 2.3. adb_execute 函数
process_t adb_execute(const char *serial, const char *const adb_cmd[], size_t len) {argv[0] = get_adb_command();argv[1] = "-s";argv[2] = serial;     ///> memcpy(&argv[i], adb_cmd, len * sizeof(const char *));enum process_result r = process_execute(argv, &process);return process;
}///> 2.4. 建立管道、执行系统调用功能: /usr/share/adb push -s serial local_path remote_path 
enum process_result process_execute(const char *const argv[], pid_t *pid) {int fd[2];pipe(fd);pid = fork();fcntl(fd[1], F_SETFD, FD_CLOEXEC);execvp(argv[0], (char *const *)argv);write(fd[1], &ret, sizeof(ret));close(fd[1]);return ret;
}
  • enable_tunnel_any_port()
    软件运行检测是否开启ADB FORWORD 功能,如果开启则执行 enable_tunnel_any_port() 函数。

enable_tunnel_any_port(server, params->port_range,params->force_adb_forward)
{for (;;) {///> 遍历 adb port 列表,找出可以使用的端口。if (enable_tunnel_forward(server->serial, port)) {// successserver->local_port = port;return true;}}}
  • server->process = execute_server(server, params)
    通过ADB SHELL 运行 scrcpy-server 程序方法。
server->process = execute_server(server, params)
{const char *const cmd[] = {"shell","CLASSPATH=" DEVICE_SERVER_PATH, 								//> = "/data/local/tmp/scrcpy-server.jar","app_process",
#ifdef SERVER_DEBUGGER
# define SERVER_DEBUGGER_PORT "5005"
# ifdef SERVER_DEBUGGER_METHOD_NEW/* Android 9 and above */"-XjdwpProvider:internal -XjdwpOptions:transport=dt_socket,suspend=y,""server=y,address="
# else/* Android 8 and below */"-agentlib:jdwp=transport=dt_socket,suspend=y,server=y,address="
# endifSERVER_DEBUGGER_PORT,
#endif"/", // unused&#bile.scrcpy.Server",SCRCPY_VERSION,log_level_to_server_string(params->log_level),max_size_string,bit_rate_string,max_fps_string,lock_video_orientation_string,server->tunnel_forward ? "true" : "false",params->crop ? params->crop : "-","true", // always send frame meta (packet boundaries + timestamp)params->control ? "true" : "false",display_id_string,params->show_touches ? "true" : "false",params->stay_awake ? "true" : "false",params->codec_options ? params->codec_options : "-",params->encoder_name ? params->encoder_name : "-",params->power_off_on_close ? "true" : "false",};return adb_execute(server->serial, cmd, ARRAY_LEN(cmd));
}///> adb_execute() 是ADB 接口的API函数
adb_execute(server->serial, cmd, ARRAY_LEN(cmd))
{}

第三步 修改 scrcpy 客户端 adb.c 中代码,观察ADB执行过程内容

在 adb.c 文件的135行下,增加如下代码


memcpy(&argv[i], adb_cmd, len * sizeof(const char *));argv[len + i] = NULL;
///> 增加代码如下:memset(buf,0,sizeof(buf));for(i=0;i <= (int)len; i++){memset(cmd,0,sizeof(cmd));sprintf(cmd,"%s ",argv[i]);length = strlen(cmd);memcpy(&buf[base],cmd,length);base += length;}printf("%s, ADB-CMD: %s n",__FILE__,buf);
///> 增加代码结束enum process_result r = process_execute(argv, &process);if (r != PROCESS_SUCCESS) {show_adb_err_msg(r, argv);process = PROCESS_NONE;}

第四步 meson 编译源码与验证ANDROID 服务端程序启动逻辑

robot@ubuntu:~/scrcpy/scrcpy$ export ANDROID_SDK_ROOT=~/Android/Sdk
### (1).  meson 编译源码
robot@ubuntu:~/scrcpy/scrcpy$ meson build --buildtype release --strip -Db_lto=true
Directory already configured.Just run your build command (e.g. ninja) and Meson will regenerate as necessary.
If ninja fails, run "ninja reconfigure" or "meson --reconfigure"
to force Meson to regenerate.If build failures persist, run "meson setup --wipe" to rebuild from scratch
using the same options as passed when configuring the build.
To change option values, run "meson configure" instead.### (2). ninja 链接编译结果
robot@ubuntu:~/scrcpy/scrcpy$ ninja -C build
ninja: Entering directory `build'
[0/3] Generating scrcpy-server with a custom commandDeprecated Gradle features were used in this build, making it incompatible with Gradle 7.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See .3/userguide/command_line_interface.html#sec:command_line_warningsBUILD SUCCESSFUL in 4s
24 actionable tasks: 1 executed, 23 up-to-date
[1/3] Compiling C object app/scrcpy.p/
../app/src/adb.c: In function 'adb_execute':
../app/src/adb.c:138:15: warning: comparison of integer expressions of different signedness: 'int' and 'size_t' {aka 'long unsigned int'} [-Wsign-compare]138 |     for(i=0;i <= len; i++){|               ^~
[2/3] Linking target app/scrcpy### (3). 执行编译结果
robot@ubuntu:~/scrcpy/scrcpy$ ./run buildINFO: scrcpy 1.19 <;
../app/src/adb.c, ADB-CMD:adb push build/server/scrcpy-server /data/local/tmp/scrcpy-server.jar     // 第一步 ADB PUSH 服务端程序到android手机的路径和文件名称.
build/server/scrcpy-server: 1 file pushed. 2.7 MB/s (37330 bytes in 0.013s)
../app/src/adb.c, ADB-CMD:adb reverse localabstract:scrcpy tcp:27183  ../app/src/adb.c, ADB-CMD:adb shell CLASSPATH=/data/local/tmp/scrcpy-server.jar                    // 第二步 ADB SHELL CLASSPATH 方式启动 android 手机端程序.app_process / bile.scrcpy.Server 1.19 info 0 8000000 0 -1 false - true true 0 false false - - false  [server] INFO: Device: HUAWEI PRA-AL00X (Android 8.0.0)
../app/src/adb.c, ADB-CMD:adb reverse --remove localabstract:scrcpy  
INFO: Renderer: opengl
INFO: OpenGL version: 3.3 (Compatibility Profile) Mesa 21.0.3
INFO: Trilinear filtering enabled
INFO: Initial texture: 1080x1920                                                                    // 第三步 通过 USB 方式运行 scrcpy 程序成功。

第五部 WIFI方式运行 scrcpy结果日志

### 打开 adb tcpip 5555 端口
robot@ubuntu:~/scrcpy/scrcpy$ adb tcpip 5555### 链接手机的wifi网络
robot@ubuntu:~/scrcpy/scrcpy$ adb connect 192.168.5.107:5555
connected to 192.168.5.107:5555### 通过 WIFI 链接手机上的 scrcpy 程序运行
robot@ubuntu:~/scrcpy/scrcpy$ ./build/app/scrcpy -s 192.168.5.107:5555 -b 5000000
INFO: scrcpy 1.19 <;
../app/src/adb.c, ADB-CMD: adb -s 192.168.5.107:5555 push  
/usr/local/share/scrcpy/scrcpy-server:...shed. 1.3 MB/s (37330 bytes in 0.028s)
../app/src/adb.c, ADB-CMD: adb -s 192.168.5.107:5555 reverse  
error: more than one device/emulator
ERROR: "adb reverse" returned with value 1
WARN: 'adb reverse' failed, fallback to 'adb forward'
../app/src/adb.c, ADB-CMD: adb -s 192.168.5.107:5555 forward  
../app/src/adb.c, ADB-CMD: adb -s 192.168.5.107:5555 shell CLASSPATH=/data/local/tmp/scrcpy-server.jar app_process / bile.scrcpy.Server 1.19 info 0 5000000 0 -1 true - true true 0 false false -  
[server] INFO: Device: HUAWEI PRA-AL00X (Android 8.0.0)
../app/src/adb.c, ADB-CMD: adb -s 192.168.5.107:5555 forward  
INFO: Renderer: opengl
INFO: OpenGL version: 3.3 (Compatibility Profile) Mesa 21.0.3
INFO: Trilinear filtering enabled
INFO: Initial texture: 1080x1920

编者注: 就不贴图,第四部分程序运行日志已经非常清晰。
scrcpy的程序启动逻辑是源码执行结果,没有任何意义,部分读者可能会在网上看到相关分析,
请以此源码实际输出为准。
至于adb shell CLASSPATH 运行android 程序说明,请自行搜索知乎上的相关说明。

本文发布于:2024-03-11 15:31:21,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/1710553755142185.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:源码   环境   scrcpy
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23