欢迎光临
我们一直在努力

DroidDeck源码构建完整教程:Docker+NDK交叉编译PulseAudio音频模块全流程

DroidDeck源码构建完整教程:Docker+NDK交叉编译PulseAudio音频模块全流程

【免费下载链接】DroidDeck DroidDeck brings the SteamOS experience to Android 【免费下载链接】DroidDeck 项目地址: https://gitcode.com/gh_mirrors/dr/DroidDeck

DroidDeck 是一款把 SteamOS 体验带到 Android 上的开源项目——在 Adreno 手柄/平板上运行 Steam Big Picture,并通过 ARM64 Proton 玩 Windows 游戏。这篇教程带你用 Docker + Android NDK 从零完成 DroidDeck 源码构建:交叉编译 PulseAudio 13.0 音频模块、打包进 pulseaudio.tzst 资源包,最终产出一份可安装的签名 APK,并附上常见报错的快速排查方法。

DroidDeck 项目社交预览图,Android 上运行 SteamOS 的开源项目

一、先搞清楚项目怎么跑的 🔍

在动手之前,理解"要构建出什么"能少走很多弯路。DroidDeck 的产物是一个 Android APK,核心能力由三层组成:

层作用关键位置
音频层 在 Android 上跑 PulseAudio 13.0 守护进程,用 AAudio/DirectAudio 作为音频输出 app/src/main/assets/pulseaudio.tzst、app/src/main/jniLibs/arm64-v8a/
Linux 运行层 proot + preload 库,让 x86/ARM64 的 Linux 程序跑在 Android 上 tools/linuxfs/、tools/proot/
Android 应用层 Kotlin 写的启动器、会话管理、组件管理 UI app/src/main/java/com/droiddeck/launcher/

音频正是这次构建的主角:仓库里预置了为 Android bionic 编译好的 libpulse.so、libpulsecore-13.0.so、libpulsecommon-13.0.so(位于 app/src/main/jniLibs/arm64-v8a/),但我们必须自己用 NDK 交叉编译两个"音频输出模块"(sink)——它们把 PulseAudio 的音频流接到 Android 的 AAudio / DirectSound 驱动上。这就是整条构建链中最值得学的部分。

二、构建环境清单:一次备齐不返工 ✅

tools/build_local.sh 会在启动时逐项检查环境,缺一样都会直接报错退出。提前对照这张清单:

  • Docker:用于运行 Ubuntu 24.04 交叉编译容器(构建 glibc 版 ARM64 preload 库)
  • Java 17:Gradle 构建所需,设置 JAVA_HOME 指向 JDK 17
  • Android SDK + NDK:设置 ANDROID_HOME(或 ANDROID_SDK_ROOT),且 sdk/ndk/ 下有完整 NDK;sdk/platform-tools/adb 必须存在
  • 命令行工具:curl、tar、zstd、shasum、unzip
  • gh(GitHub CLI):仅当仓库内存在固定的 release 依赖(Gamescope、wlroots、esync 包)时需要,脚本用它下载并做 sha256 校验

💡 脚本会自动在 sdk/ndk/ 里找版本号最高的完整 NDK(目录内含 source.properties)。如果你曾用 sdkmanager 安装到一半,可以显式设置 DROIDDECK_NDK_VERSION 指定版本。

三、获取源码

git clone https://gitcode.com/gh_mirrors/dr/DroidDeck
cd DroidDeck

四、Docker 交叉编译:glibc ARM64 预加载库

打开 tools/local-cross.Dockerfile 你会看到一个极简的 Dockerfile:基于 ubuntu:24.04,安装 binutils-aarch64-linux-gnu / gcc-aarch64-linux-gnu 交叉工具链。

tools/build_local.sh 首次运行时会自动构建镜像(标签 droiddeck-local-cross:24.04-v2,–platform linux/amd64,Apple 芯片的 Mac 上也能跑),随后在容器内完成三件事(tools/build_local.sh#L113-L171):

  • libfakeinput.so —— 用 aarch64-linux-gnu-g++ -static-libstdc++ 编译 Steam 假输入库,强制静态链接 C++ 运行时,脚本随后用 readelf 校验 NEEDED 中不得出现 libstdc++.so.6;
  • libblsession.so / libblfastpath.so —— 编译 tools/linuxfs/preload/ 与 tools/proot/fastpath/ 的 preload 钩子,并校验 socket、bind、openat 等关键符号全部导出;
  • 安装 linuxfs 覆盖层 —— 桌面脚本、mangoapp 等装入 app/src/main/assets/linuxfs/,供 APK 解压到设备。
  • 构建结束时会通过 trap 自动还原 pulseaudio.tzst 等被临时替换的文件,中断也不会污染你的工作区。

    五、核心步骤:NDK 交叉编译 PulseAudio 13.0 音频模块

    这是整篇文章的重点。构建脚本 tools/build_local.sh#L286-L305 分三步走:

    1. 准备 PulseAudio 13.0 源码

    脚本会自动下载官方 PulseAudio 13.0 源码包并缓存到 ~/.cache/droiddeck-build/;如果你想复用本地已有的源码树,设置环境变量即可跳过下载:

    export DROIDDECK_PA13_SOURCE_DIR=/path/to/pulseaudio-13.0

    脚本用 src/pulse/version.h.in 是否存在来验证源码目录是否有效。

    2. 用 NDK 编译两个音频 sink

    真正的编译由 tools/aaudio-sink/build.sh 完成,它做了几件很巧的事:

    • 自动识别宿主机(darwin-arm64 / darwin-x86_64 / linux-x86_64 / linux-aarch64),选择 NDK 对应的预编译工具链(tools/aaudio-sink/build.sh#L15-L33),Mac 和 Linux 上无需任何额外配置;
    • 跳过 PulseAudio 的 configure:用 tools/aaudio-sink/config.h 这份"手工版 config"提供版本号和特性宏(PACKAGE_VERSION 13.0、HAVE_MEMFD 等),再配合 sed 生成 version.h,直接对着上游头文件编译单个模块;
    • 调用 NDK clang(API 26)编译出 module-aaudio-sink.so 和 module-directaudio-sink.so,链接目标就是仓库里预置的 libpulse* 三个库(tools/aaudio-sink/build.sh#L63-L64);
    • 编译后做符号体检:确认 pa__init、pa__done 等 PulseAudio 加载器要求的 7 个导出符号齐全,且 NEEDED 依赖只允许是设备上一定存在的库(libaaudio.so、libc.so 等),多一个都直接失败。

    DroidDeck 的 Components 组件管理页面,管理 Proton、DXVK、FEX 等运行时组件

    3. 编译 DirectAudio 中继进程

    tools/directaudio-relay/build.sh 会编译 directaudio-relay.c——一个独立的 bionic 辅助进程:它替无法直接持有音频流的驱动实例代管所有 AAudio 流,通过 Unix socket 与游戏进程交接共享环形缓冲区,并把麦克风共享给 Steam 客户端。构建参数要求 NDK r27、API 28,并带 -Wl,-z,max-page-size=16384 保证 16KB 大页兼容。

    4. 塞进 pulseaudio.tzst 资源包

    构建脚本会解开仓库中提交的 app/src/main/assets/pulseaudio.tzst 音频包,把两个新编译的模块安装到包内的 modules/arm64/ 目录,再用 zstd -19 最高压缩率重新打包(tools/build_local.sh#L317-L332)。注意脚本有防呆检查:如果提交的包里已经存在编译好的 sink,会直接报错退出,避免重复打包。

    六、一条命令完成 APK 构建

    环境就绪后,在项目根目录执行:

    ./tools/build_local.sh

    默认构建 release 版(可用 DROIDDECK_BUILD_VARIANT=debug 切换),它还会依次完成:

  • Gradle 打包:./gradlew assembleRelease -PndkVersion=<你的NDK版本>,产出 app/build/outputs/apk/release/app-release.apk;
  • 会话资源检查:python3 tools/release/check_session_assets.py 校验 APK 完整性;
  • 音频包复检:从最终 APK 里解出 assets/pulseaudio.tzst,确认 pactl 和两个 arm64 音频模块都在(tools/build_local.sh#L340-L352);
  • 依赖体检:再开一个 Docker 容器,用 readelf 核对 APK 里所有 .so 的 NEEDED 都能解析(tools/build_local.sh#L354-L375);
  • 对齐与签名:zipalign -p 4 对齐后,用 AOSP testkey(keystore/ 下另有 release 签名指纹)做 v1/v2/v3 三套签名并逐项校验,确保 APK 可覆盖安装。
  • 全部通过后,终端会打印 APK 路径和 SHA-256 值。

    DroidDeck 在 Android 上运行 Linux 桌面环境,终端显示 aarch64 架构

    七、安装到设备并验证 🚀

    ./tools/deploy_local.sh

    该脚本会通过 adb 把刚构建的 APK 推送到已连接的设备。安装后打开应用、安装 Linux 运行时并启动会话,如果游戏声音正常输出、麦克风可用,说明你交叉编译的 PulseAudio 音频模块已经在工作了。

    八、常见坑位快速排查 🛠️

    报错信息原因与解法
    Android SDK not found 未设置 ANDROID_HOME / ANDROID_SDK_ROOT,或 SDK 下没有 platform-tools/adb
    No Android NDK found under … NDK 不完整(sdkmanager 中断会留下没有 source.properties 的目录),重装或设置 DROIDDECK_NDK_VERSION
    Docker 报 No such image 且重建卡住 Docker Desktop 虚拟机刚重启、镜像仓库还没加载完;脚本内置了最多 1 分钟的等待重试,稍候即可
    提示需要 GitHub CLI 存在 release.env 固定依赖(Gamescope/wlroots/esync)时脚本用 gh 下载校验包,先安装 gh 并登录
    The committed audio bundle already contains a built ARM64 sink 上次构建中断后 pulseaudio.tzst 没还原干净,git checkout — app/src/main/assets/pulseaudio.tzst 恢复即可

    写在最后

    DroidDeck 的本地构建把"交叉编译"这件看似专业的事拆成了三个清晰的环节:Docker 管 glibc 侧的 preload 库,NDK 管 bionic 侧的音频模块,Gradle 管最终的 APK 组装,而 tools/build_local.sh 用符号校验、依赖体检、签名验证把这些环节串成了一条可靠的流水线。走通这一遍,你不仅能得到属于自己的签名 APK,也拿到了理解 Android 交叉编译体系的一个完整样本——推荐顺手读一下 README.md 的 Build 章节和 docs/development/ 下的开发文档,继续深入。

    【免费下载链接】DroidDeck DroidDeck brings the SteamOS experience to Android 【免费下载链接】DroidDeck 项目地址: https://gitcode.com/gh_mirrors/dr/DroidDeck

    创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

    赞(0)
    未经允许不得转载:171主机测评 » DroidDeck源码构建完整教程:Docker+NDK交叉编译PulseAudio音频模块全流程
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址