vdi-cloud/tools/build-rkmpp-client.sh

167 lines
8.5 KiB
Bash

#!/bin/bash
# Run on the ARM64 terminal. All sources, libraries and output stay in TASK_ROOT.
# No apt install, system library replacement, desktop restart or sudo is needed.
set -euo pipefail
TASK_ROOT="${VDI_RKMPP_BUILD_ROOT:-$HOME/vdi-build/rkmpp-experiment}"
SDK="$TASK_ROOT/deps/usr"
PREFIX="$TASK_ROOT/runtime"
JOBS="${VDI_BUILD_JOBS:-2}"
mkdir -p "$TASK_ROOT"/{src,debs,build} "$PREFIX"
clone_source() {
local name="$1" ref="$2" url="$3"
if [ ! -d "$TASK_ROOT/src/$name/.git" ]; then
git clone --depth=1 --branch "$ref" "$url" "$TASK_ROOT/src/$name"
fi
test "$(git -C "$TASK_ROOT/src/$name" rev-parse HEAD)" = "$4" || {
echo "Unexpected $name source revision; inspect before building" >&2; exit 1;
}
}
clone_source mpp develop https://github.com/rockchip-linux/mpp.git 7916c3cc000fefc0bdf96636b7d54fe174724f44
clone_source ffmpeg 6.1 https://github.com/nyanmisaka/ffmpeg-rockchip.git 797387de1c9caa9170100d7217ee093d1897f750
clone_source moonlight v6.2.0 https://github.com/moonlight-stream/moonlight-qt.git de2467e433821664cdd2224aad8c89a625be1ad9
git -C "$TASK_ROOT/src/moonlight" submodule update --init --recursive --depth=1
# Versioned VDI interaction patches. Explicit directory supports copying this
# builder to the box without the rest of the repository. Refuse unknown drift.
PATCH_DIR="${VDI_MOONLIGHT_PATCH_DIR:-$(cd "$(dirname "$0")/../terminal/moonlight/patches" && pwd)}"
apply_vdi_patch() {
local source="$1" patch="$2"
test -r "$patch" || { echo "Missing VDI patch: $patch" >&2; exit 1; }
if git -C "$source" apply --reverse --check "$patch" 2>/dev/null; then
return
fi
git -C "$source" apply --check "$patch"
git -C "$source" apply "$patch"
}
apply_vdi_patch "$TASK_ROOT/src/moonlight" "$PATCH_DIR/0001-vdi-input-and-latest-frame.patch"
apply_vdi_patch "$TASK_ROOT/src/moonlight/moonlight-common-c/moonlight-common-c" \
"$PATCH_DIR/0002-vdi-absolute-click-barrier.patch"
apply_vdi_patch "$TASK_ROOT/src/moonlight" "$PATCH_DIR/0003-vdi-input-priority.patch"
# Moonlight 6.2.0 uses std::optional in drm.h but relies on a transitive
# include from Qt 6. Our Qt 5 build needs the standard header explicitly.
export VDI_MOONLIGHT_SOURCE="$TASK_ROOT/src/moonlight"
python3 - <<'PY'
import os
from pathlib import Path
header = Path(os.environ['VDI_MOONLIGHT_SOURCE']) / 'app/streaming/video/ffmpeg-renderers/drm.h'
text = header.read_text()
if '#include <optional>' not in text:
assert '#include <set>' in text
header.write_text(text.replace('#include <set>', '#include <optional>\n#include <set>', 1))
PY
# Download only the required headers and private runtime libraries from the
# configured distribution repository. apt does not install or upgrade anything.
cd "$TASK_ROOT/debs"
apt-get download libdrm-dev libopus-dev libsdl2-dev libsdl2-2.0-0 \
libsdl2-ttf-dev libsdl2-ttf-2.0-0 libudev-dev libxkbcommon-dev \
libegl-dev libgles-dev libgles1 libgbm-dev libwayland-dev libwayland-bin wayland-protocols \
qtdeclarative5-dev qtdeclarative5-dev-tools qt5-qmltooling-plugins \
qtquickcontrols2-5-dev libqt5quickcontrols2-5 libqt5quicktemplates2-5 \
libqt5svg5-dev libqt5svg5 libqt5qmlworkerscript5 libqt5quickparticles5 \
libqt5quickshapes5 libqt5quicktest5 libx264-dev \
qml-module-qtquick2 qml-module-qtquick-window2 qml-module-qtquick-layouts \
qml-module-qtqml-models2 qml-module-qtquick-controls2 qml-module-qtquick-templates2 \
libharfbuzz-dev libfreetype-dev libpng-dev zlib1g-dev libbrotli-dev \
libglib2.0-dev libgraphite2-dev libffi-dev
for package in ./*.deb; do dpkg-deb -x "$package" "$TASK_ROOT/deps"; done
# Relocate pkg-config prefixes, preserving the system /usr and its libraries.
export VDI_PRIVATE_SDK="$SDK"
python3 - <<'PY'
import os
from pathlib import Path
sdk = Path(os.environ['VDI_PRIVATE_SDK'])
for pc in sdk.rglob('*.pc'):
text = pc.read_text()
if str(sdk) not in text:
pc.write_text(text.replace('/usr', str(sdk)))
for pri in sdk.rglob('qt_lib_*.pri'):
pri.write_text(pri.read_text().replace('$$QT_MODULE_LIB_BASE', str(sdk / 'lib/aarch64-linux-gnu'))
.replace('$$QT_MODULE_INCLUDE_BASE', str(sdk / 'include/aarch64-linux-gnu/qt5'))
.replace('$$QT_MODULE_BIN_BASE', str(sdk / 'lib/qt5/bin')))
# A -dev symlink can target a different distro patch version than is installed.
# Link to the existing matching SONAME; never replace the system runtime.
for link in (sdk / 'lib/aarch64-linux-gnu').glob('*.so'):
if link.is_symlink() and not link.exists():
target = Path(os.readlink(link)).name
if '.so.' not in target:
continue
soname = target.split('.so.')[0] + '.so.' + target.split('.so.')[1].split('.')[0]
system_lib = Path('/lib/aarch64-linux-gnu') / soname
if system_lib.exists():
link.unlink()
link.symlink_to(system_lib)
PY
export PKG_CONFIG_PATH="$PREFIX/lib/pkgconfig:$SDK/lib/aarch64-linux-gnu/pkgconfig:$SDK/share/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}"
export LD_LIBRARY_PATH="$PREFIX/lib:$SDK/lib/aarch64-linux-gnu${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
export QMAKEPATH="$SDK/lib/aarch64-linux-gnu/qt5"
export QML2_IMPORT_PATH="$SDK/lib/aarch64-linux-gnu/qt5/qml"
export CPLUS_INCLUDE_PATH="$SDK/include/aarch64-linux-gnu:$SDK/include${CPLUS_INCLUDE_PATH:+:$CPLUS_INCLUDE_PATH}"
export C_INCLUDE_PATH="$SDK/include/aarch64-linux-gnu:$SDK/include${C_INCLUDE_PATH:+:$C_INCLUDE_PATH}"
if [ "${VDI_BUILD_STAGE:-all}" != moonlight ]; then
echo '=== build MPP ==='
cmake -S "$TASK_ROOT/src/mpp" -B "$TASK_ROOT/build/mpp" \
-DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="$PREFIX" \
-DRKPLATFORM=ON -DHAVE_DRM=ON -DBUILD_SHARED_LIBS=ON -DBUILD_TEST=OFF
cmake --build "$TASK_ROOT/build/mpp" -j "$JOBS"
cmake --install "$TASK_ROOT/build/mpp"
echo '=== build FFmpeg with RKMPP ==='
mkdir -p "$TASK_ROOT/build/ffmpeg"
cd "$TASK_ROOT/build/ffmpeg"
"$TASK_ROOT/src/ffmpeg/configure" --prefix="$PREFIX" \
--enable-gpl --enable-version3 --enable-shared --disable-static \
--enable-libdrm --enable-rkmpp --disable-rkrga --enable-libx264 \
--disable-doc --disable-debug --disable-autodetect --disable-network \
--disable-everything --enable-avcodec --enable-avformat --enable-avfilter \
--enable-swscale --enable-swresample --enable-ffmpeg --enable-ffprobe \
--enable-decoder=h264,hevc,h264_rkmpp,hevc_rkmpp,rawvideo,wrapped_avframe \
--enable-parser=h264,hevc --enable-demuxer=h264,hevc \
--enable-muxer=h264,null,rawvideo --enable-protocol=file \
--enable-encoder=libx264,wrapped_avframe,rawvideo --enable-indev=lavfi \
--enable-filter=testsrc2,null,format,hwdownload,scale \
--extra-cflags="-I$PREFIX/include -I$SDK/include" \
--extra-ldflags="-L$PREFIX/lib -L$SDK/lib/aarch64-linux-gnu -Wl,-rpath,$PREFIX/lib -Wl,-rpath,$SDK/lib/aarch64-linux-gnu"
make -j "$JOBS"
make install
"$PREFIX/bin/ffmpeg" -hide_banner -decoders | grep rkmpp
fi
echo '=== build Moonlight ==='
mkdir -p "$TASK_ROOT/build/moonlight"
cd "$TASK_ROOT/build/moonlight"
qmake "$TASK_ROOT/src/moonlight/moonlight-qt.pro" \
CONFIG+=release CONFIG+=recheck CONFIG+=disable-libva CONFIG+=disable-libvdpau \
CONFIG+=disable-libplacebo CONFIG+=disable-discord \
"QMAKE_LIBDIR+=$SDK/lib/aarch64-linux-gnu" \
"QMAKE_LIBDIR+=$PREFIX/lib" \
"QMAKE_RPATHDIR+=$SDK/lib/aarch64-linux-gnu" \
"QMAKE_RPATHDIR+=$PREFIX/lib" \
"QMAKE_INCDIR+=$SDK/include/aarch64-linux-gnu/qt5" \
"QMAKE_INCDIR+=$SDK/include/aarch64-linux-gnu" \
"QMAKE_INCDIR+=$SDK/include"
make qmake_all
if ! grep -q -- '-DHAVE_EGL' app/Makefile.Release; then
echo 'EGL renderer missing; refusing to build a client without the DRM PRIME display path' >&2
exit 1
fi
# qmake/make does not invalidate old objects when feature defines change.
# In particular a stale ffmpeg.o can silently omit EGL despite a successful
# configure check. Clean the app whenever its compiler/link flags change.
flags_hash="$(grep -E '^(DEFINES|INCPATH|LIBS|CXXFLAGS)[[:space:]]*=' app/Makefile.Release | sha256sum | cut -d ' ' -f 1)"
if [ "$(cat .vdi-build-flags.sha256 2>/dev/null || true)" != "$flags_hash" ]; then
make -C app -f Makefile.Release clean
printf '%s\n' "$flags_hash" > .vdi-build-flags.sha256
fi
make -j "$JOBS"
echo '=== complete; experimental binary only, production unchanged ==='
echo "$TASK_ROOT/build/moonlight/app/moonlight"
git -C "$TASK_ROOT/src/mpp" rev-parse HEAD
git -C "$TASK_ROOT/src/ffmpeg" rev-parse HEAD
git -C "$TASK_ROOT/src/moonlight" rev-parse HEAD
sha256sum "$TASK_ROOT"/debs/*.deb > "$TASK_ROOT/dependency-sha256.txt"