Scrcpy源碼分析系列
【投屏】Scrcpy源碼分析一(編譯篇)
【投屏】Scrcpy源碼分析二(Client篇-連接階段)
【投屏】Scrcpy源碼分析三(Client篇-投屏階段)
【投屏】Scrcpy源碼分析四(最終章 - Server篇)
1. Scrcpy介紹
Scrcpy是一款小巧的Android設(shè)備投屏軟件??梢钥缙脚_(tái),在Windows、Linux、MacOS上對(duì)Android設(shè)備進(jìn)行投屏、反控、文件拖入上傳等功能,開源且免費(fèi)。Scrcpy這個(gè)名字的由來是C語言有個(gè)字符串拷貝的函數(shù)叫strcpy()
,投屏就是Screen的拷貝,所以叫Scrcpy。
Scrcpy目前有Genymobile公司進(jìn)行開發(fā)和維護(hù),就是那個(gè)開發(fā)了著名的開源模擬器Genymotion的公司。
項(xiàng)目地址是:https://github.com/Genymobile/scrcpy,可以自行下載源碼。
2. 工程目錄簡(jiǎn)介
2.1 根目錄
Scycpy的工程根目錄如下圖所示:
乍看起來,像是Android工程,但又有點(diǎn)區(qū)別。其實(shí)Scrcpy工程是基于Meson編譯系統(tǒng)進(jìn)行構(gòu)建的。
對(duì)Meson不熟悉的小伙伴,可以看我的另一篇關(guān)于Meson簡(jiǎn)介的文章 -《Meson編譯系統(tǒng)》。如果時(shí)間緊張對(duì)Meson不想深入了解的小伙伴,這樣理解Meson就好:Meson就好比GCC,在配置文件中進(jìn)行一系列配置,比如依賴頭文件、庫等,編譯出一個(gè)可執(zhí)行文件。
Meson工程最重要的就是它的構(gòu)建描述文件,即meson.build
,類似Gradle工程中的build.gradle
。在工程根目錄和子模塊目錄下都有各自的meson.build
文件。我們可以看到在工程中根目錄中有一個(gè)meson.build
文件:
# 工程基本配置
project('scrcpy', 'c',
version: '1.25',
meson_version: '>= 0.48',
default_options: [
'c_std=c11',
'warning_level=2',
'b_ndebug=if-release',
])
# 添加編譯子目錄app
if get_option('compile_app')
subdir('app')
endif
# 添加編譯子目錄server
if get_option('compile_server')
subdir('server')
endif
run_target('run', command: ['scripts/run-scrcpy.sh'])
可以看到工程編譯兩個(gè)項(xiàng)目,app和server。既然Scrcpy是一個(gè)投屏軟件,那么可以理解是一個(gè)C/S軟件。Client端運(yùn)行在我們的電腦上,用于展示。Server端運(yùn)行在Android手機(jī),用于提供數(shù)據(jù)。
所以上面的工程目錄中,app目錄就是Client端,即電腦端的代碼。server目錄就是Server端,即手機(jī)上的代碼。
2.2 app目錄
我們進(jìn)到app目錄,正如前面介紹,它也有一個(gè)meson.build
文件,文件內(nèi)容較長(zhǎng),比較重要的部分是:
# 配置代碼集
src = [
'src/main.c',
'src/adb/adb.c',
...
]
...
# 設(shè)置編譯語言是C語言
cc = meson.get_compiler('c')
...
# 配置生成的目標(biāo)名稱
executable('scrcpy', src,
dependencies: dependencies,
include_directories: src_dir,
install: true,
c_args: [])
...
可以看到,client端就是編譯一系列的C代碼,生成名為scrcpy的可執(zhí)行文件。
2.3 server目錄
我們進(jìn)到server目錄中,可以看到也有一個(gè)meson.build
文件。同時(shí)我們可以發(fā)現(xiàn),其本質(zhì)也是一個(gè)Android工程,有build.gradle
文件。
正如本節(jié)開頭提到的,我們發(fā)現(xiàn)工程的根目錄和Android工程有點(diǎn)像也就是這個(gè)原因。Scrcpy的工程其實(shí)也是一個(gè)Android的Gradle工程,server是其中一個(gè)module,其會(huì)編譯出一個(gè)apk,作為server端運(yùn)行在Android設(shè)備端。
那么讓我們來看一下是如何觸發(fā)編譯的,首先還是看meson.build
文件:
prebuilt_server = get_option('prebuilt_server')
if prebuilt_server == ''
custom_target('scrcpy-server',
output: 'scrcpy-server',
command: [find_program('./scripts/build-wrapper.sh'), meson.current_source_dir(), '@OUTPUT@', get_option('buildtype')],
install_dir: 'share/scrcpy')
else
...
custom_target('scrcpy-server-prebuilt',
input: prebuilt_server,
output: 'scrcpy-server',
command: ['cp', '@INPUT@', '@OUTPUT@'],
install_dir: 'share/scrcpy')
endif
省略無關(guān)細(xì)節(jié),直接看大的結(jié)構(gòu)。server目錄的meson.build
只有一個(gè)大的判斷,根據(jù)屬性prebuilt_server
是否為空來決定是執(zhí)行編譯 或者 直接使用預(yù)編譯文件。我們分開來看:
2.3.1 執(zhí)行編譯
如果執(zhí)行編譯的話,就執(zhí)行./scrcpys/build-wrapper.sh
文件:
...
GRADLE=${GRADLE:-$PROJECT_ROOT/../gradlew}
if [[ "$BUILDTYPE" == debug ]]
then
"$GRADLE" -p "$PROJECT_ROOT" assembleDebug
cp "$PROJECT_ROOT/build/outputs/apk/debug/server-debug.apk" "$OUTPUT"
else
"$GRADLE" -p "$PROJECT_ROOT" assembleRelease
cp "$PROJECT_ROOT/build/outputs/apk/release/server-release-unsigned.apk" "$OUTPUT"
fi
看到這,熟悉Android開發(fā)的小伙伴就很熟悉了,最終使用gradlew
編譯server工程,生成.apk
。然后將生成的apk文件拷貝成BUILDDIR/server/scrcpy-server
,注意在此處apk后綴已經(jīng)沒有了,變成了scrcpy-server
文件。然后在安裝時(shí),會(huì)被安裝至/usr/local/share/scrcpy/scrcpy-server
。
2.3.2 使用預(yù)編譯文件
如果屬性prebuilt_server
不為空,則使用預(yù)編譯的文件,不執(zhí)行編譯。源碼中也有提到,使用預(yù)編譯文件是為了方便沒有Android SDK環(huán)境的電腦。
那么這個(gè)屬性是在哪設(shè)置的呢?預(yù)編譯文件又是從哪來的呢?我們跳到根目錄的 install_release.sh
文件,這也是后面我們執(zhí)行編譯的入口文件:
#!/usr/bin/env bash
set -e
# 設(shè)置meson編譯目錄
BUILDDIR=build-auto
# 下載server端的預(yù)編譯文件
PREBUILT_SERVER_URL=https://github.com/Genymobile/scrcpy/releases/download/v1.24/scrcpy-server-v1.24
PREBUILT_SERVER_SHA256=ae74a81ea79c0dc7250e586627c278c0a9a8c5de46c9fb5c38c167fb1a36f056
echo "[scrcpy] Downloading prebuilt server..."
wget "$PREBUILT_SERVER_URL" -O scrcpy-server
echo "[scrcpy] Verifying prebuilt server..."
echo "$PREBUILT_SERVER_SHA256 scrcpy-server" | sha256sum --check
# 進(jìn)行meson編譯,并設(shè)置prebuilt_server屬性
echo "[scrcpy] Building client..."
rm -rf "$BUILDDIR"
meson "$BUILDDIR" --buildtype=release --strip -Db_lto=true \
# 看這里,設(shè)置了prebuilt_server屬性
-Dprebuilt_server=scrcpy-server
cd "$BUILDDIR"
ninja
# 執(zhí)行安裝
echo "[scrcpy] Installing (sudo)..."
sudo ninja install
哦,原來預(yù)編譯的文件是從網(wǎng)上下的,同時(shí)在meson編譯時(shí)設(shè)置了prebuilt_server
屬性。同樣,預(yù)編譯文件會(huì)被拷貝成BUILDDIR/server/scrcpy-server
,然后在安裝時(shí),會(huì)被安裝至/usr/local/share/scrcpy/scrcpy-server
。
現(xiàn)在我們知道了app和server目錄的meson.build
文件在編譯時(shí)都做了什么。那么是如何觸發(fā)編譯的呢?
既然我們都已經(jīng)看到install_release.sh
了,那么我們下面就分析這個(gè)文件吧。
2.4 install_release.sh文件
Meson編譯系統(tǒng)基于Python3實(shí)現(xiàn),并依賴Ninja。Meson和Ninja相互配合。Meson 負(fù)責(zé)構(gòu)建項(xiàng)目依賴關(guān)系,Ninja 進(jìn)行編譯。
通常Meson編譯的步驟比較固定:
- 執(zhí)行 meson BUILDDIR - 指定編譯目錄BUILDDIR,并進(jìn)行meson工程項(xiàng)目構(gòu)>建。也就是編譯期間所有的臨時(shí)文件和生成的目標(biāo)文件都會(huì)在BUILDDIR中;
- 進(jìn)入目錄BUILDDIR;
- 執(zhí)行ninja進(jìn)行編譯和安裝。
現(xiàn)在我們?cè)倩仡^看install_release.sh
是不是結(jié)構(gòu)就比較清晰易懂了:
- 指定編譯目錄
build-auto
; - 從網(wǎng)上下載server的預(yù)編譯文件;
- 進(jìn)行meson工程配置,指定編譯目錄,設(shè)置
prebuilt_server
屬性; - 進(jìn)入
build-auto
目錄,執(zhí)行ninja進(jìn)行編譯和安裝。
install_release.sh
這個(gè)文件是整個(gè)編譯的入口文件,由它來發(fā)起整個(gè)工程的編譯和安裝。
3. 編譯&安裝&執(zhí)行
項(xiàng)目的編譯十分簡(jiǎn)單:
- 下載依賴庫
# for Debian/Ubuntu sudo apt install ffmpeg libsdl2-2.0-0 adb wget \ gcc git pkg-config meson ninja-build libsdl2-dev \ libavcodec-dev libavdevice-dev libavformat-dev libavutil-dev \ libusb-1.0-0 libusb-1.0-0-dev
- 執(zhí)行根目錄的
install_release.sh
進(jìn)行編譯即可./install_release.sh
- 卸載
sudo ninja -Cbuild-auto uninstall
更詳細(xì)的信息和其他平臺(tái)(比如Win和MacOS)的編譯步驟,可以參考官方文檔:https://github.com/Genymobile/scrcpy/blob/master/BUILD.md
編譯腳本中會(huì)用ninja自動(dòng)安裝,執(zhí)行安裝成功后,把安卓設(shè)備插上電腦,執(zhí)行./scrcpy
就可以運(yùn)行了,成功的話投屏界面就出來啦。
3.1 不使用預(yù)編譯文件
工程編譯默認(rèn)是使用預(yù)編譯文件的,如果我們要自己編譯server工程,只需要把下面兩處注釋掉即可:
#!/usr/bin/env bash
set -e
BUILDDIR=build-auto
# 下載邏輯全部注釋掉
#PREBUILT_SERVER_URL=https://github.com/Genymobile/scrcpy/releases/download/v1.24/scrcpy-server-v1.24
#PREBUILT_SERVER_SHA256=ae74a81ea79c0dc7250e586627c278c0a9a8c5de46c9fb5c38c167fb1a36f056
# echo "[scrcpy] Downloading prebuilt server..."
# wget "$PREBUILT_SERVER_URL" -O scrcpy-server
# echo "[scrcpy] Verifying prebuilt server..."
# echo "$PREBUILT_SERVER_SHA256 scrcpy-server" | sha256sum --check
echo "[scrcpy] Building client..."
rm -rf "$BUILDDIR"
meson "$BUILDDIR" --buildtype=release --strip -Db_lto=true \
# 設(shè)置prebuilt_server屬性也注釋掉
# -Dprebuilt_server=scrcpy-server
cd "$BUILDDIR"
ninja
echo "[scrcpy] Installing (sudo)..."
sudo ninja install
再編譯一次,就會(huì)編譯我們的server工程了。文章來源:http://www.zghlxwxcb.cn/news/detail-528374.html
4. 小結(jié)
這一篇我們探究了Scrcpy項(xiàng)目的工程結(jié)構(gòu)和編譯系統(tǒng)。涉及的點(diǎn)有Scrcpy的工程目錄、Meson編譯系統(tǒng)、工程編譯方法和預(yù)編譯文件邏輯。下一篇開始我們會(huì)繼續(xù)探究Client端,也就是PC端的代碼邏輯了。文章來源地址http://www.zghlxwxcb.cn/news/detail-528374.html
到了這里,關(guān)于【投屏】Scrcpy源碼分析一(編譯篇)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!