feat: 动态链接到 opus 和 alsa 基础库,完成音频录制播放和编解码
This commit is contained in:
+89
-7
@@ -31,17 +31,99 @@ RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
ENV PATH="/root/.cargo/bin:${PATH}"
|
||||
RUN rustup target add armv7-unknown-linux-gnueabihf
|
||||
|
||||
# 配置 Cargo
|
||||
ENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc
|
||||
ENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_RUSTFLAGS="-C link-arg=-Wl,-dynamic-linker,/lib/ld-linux-armhf.so.3"
|
||||
|
||||
# 设置 CC
|
||||
ENV CC_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-gcc
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY run.sh /usr/local/bin/run
|
||||
COPY runtime.sh /usr/local/bin/runtime
|
||||
RUN chmod +x /usr/local/bin/run /usr/local/bin/runtime
|
||||
|
||||
# 解压 root.squashfs
|
||||
COPY root.squashfs /app/root.squashfs
|
||||
RUN mkdir -p /opt/sysroot \
|
||||
&& unsquashfs -d /opt/sysroot -f /app/root.squashfs \
|
||||
&& rm -rf /app/root.squashfs
|
||||
|
||||
# 创建专门针对 armhf 的源文件
|
||||
# 注意:ubuntu:20.04 默认源里没有 armhf,必须添加 ubuntu-ports 源
|
||||
RUN echo "deb [arch=armhf] http://mirrors.aliyun.com/ubuntu-ports/ focal main restricted universe multiverse" > /etc/apt/sources.list.d/armhf.list && \
|
||||
echo "deb [arch=armhf] http://mirrors.aliyun.com/ubuntu-ports/ focal-updates main restricted universe multiverse" >> /etc/apt/sources.list.d/armhf.list && \
|
||||
# 限制原有的源只针对 amd64,防止冲突
|
||||
sed -i 's/deb http/deb [arch=amd64] http/g' /etc/apt/sources.list
|
||||
|
||||
# 安装 armhf 架构的开发库
|
||||
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||
--mount=type=cache,target=/var/lib/apt,sharing=locked \
|
||||
dpkg --add-architecture armhf && \
|
||||
apt-get update && apt-get install -y \
|
||||
crossbuild-essential-armhf \
|
||||
libasound2-dev:armhf \
|
||||
libopus-dev:armhf \
|
||||
libssl-dev:armhf
|
||||
|
||||
# --------------------------------------------------------------------------------
|
||||
# 构建混合 Sysroot
|
||||
# 目标:结合 OpenWrt 的运行时库 (root.squashfs) 和 Toolchain/Ubuntu 的开发文件
|
||||
# --------------------------------------------------------------------------------
|
||||
|
||||
# 修复 OpenWrt sysroot:自动为所有共享库创建 .so 符号链接
|
||||
# OpenWrt 只有 libfoo.so.1,编译链接需要 libfoo.so
|
||||
RUN find /opt/sysroot/lib /opt/sysroot/usr/lib -name "lib*.so.*" | while read src; do \
|
||||
# 获取库名 (例如 libopus.so.0.8.0 -> libopus.so)
|
||||
link_name=$(echo "$src" | sed 's/\.so\..*$/.so/'); \
|
||||
if [ ! -e "$link_name" ]; then \
|
||||
ln -s "$(basename "$src")" "$link_name"; \
|
||||
fi \
|
||||
done
|
||||
|
||||
# 注入 Toolchain 的 C 运行时启动文件 (Scrt1.o, crti.o 等)
|
||||
RUN cp -r /opt/toolchain/arm-linux-gnueabihf/libc/usr/lib/*crt*.o /opt/sysroot/usr/lib/ 2>/dev/null || \
|
||||
cp -r /opt/toolchain/arm-linux-gnueabihf/libc/lib/*crt*.o /opt/sysroot/usr/lib/ 2>/dev/null || true
|
||||
|
||||
# 特殊处理 glibc 的 linker script
|
||||
# OpenWrt rootfs 中没有开发用的 libc.so (linker script) 和 libc_nonshared.a
|
||||
# 我们需要从 toolchain 中复制 libc_nonshared.a,并手动创建 libc.so 脚本
|
||||
# 这样 linker 才能正确处理 libc.so.6 和 ld-linux-armhf.so.3 的依赖关系 (解决 __tls_get_addr 错误)
|
||||
RUN cp /opt/toolchain/arm-linux-gnueabihf/libc/usr/lib/libc_nonshared.a /opt/sysroot/usr/lib/ && \
|
||||
# 删除之前步骤可能自动生成的软链接
|
||||
rm -f /opt/sysroot/usr/lib/libc.so && \
|
||||
# 创建 linker script
|
||||
echo 'OUTPUT_FORMAT(elf32-littlearm)' > /opt/sysroot/usr/lib/libc.so && \
|
||||
echo 'GROUP ( /lib/libc.so.6 /usr/lib/libc_nonshared.a AS_NEEDED ( /lib/ld-linux-armhf.so.3 ) )' >> /opt/sysroot/usr/lib/libc.so && \
|
||||
# 同理处理 libpthread.so (如果存在 libpthread.so.0)
|
||||
if [ -f /opt/sysroot/lib/libpthread.so.0 ]; then \
|
||||
cp /opt/toolchain/arm-linux-gnueabihf/libc/usr/lib/libpthread_nonshared.a /opt/sysroot/usr/lib/ 2>/dev/null || true; \
|
||||
rm -f /opt/sysroot/usr/lib/libpthread.so; \
|
||||
echo 'OUTPUT_FORMAT(elf32-littlearm)' > /opt/sysroot/usr/lib/libpthread.so; \
|
||||
echo 'GROUP ( /lib/libpthread.so.0 /usr/lib/libpthread_nonshared.a )' >> /opt/sysroot/usr/lib/libpthread.so; \
|
||||
fi
|
||||
|
||||
# 注入 Ubuntu 的开发头文件和 pkg-config 定义 (用于 alsa, opus 等)
|
||||
# 我们只取头文件和 .pc 文件,不取库文件,确保链接的是 OpenWrt 的库
|
||||
RUN mkdir -p /opt/sysroot/usr/include /opt/sysroot/usr/lib/pkgconfig && \
|
||||
cp -r /usr/include/alsa /opt/sysroot/usr/include/ 2>/dev/null || true && \
|
||||
cp -r /usr/include/opus /opt/sysroot/usr/include/ 2>/dev/null || true && \
|
||||
cp -r /usr/include/arm-linux-gnueabihf/* /opt/sysroot/usr/include/ 2>/dev/null || true && \
|
||||
cp -r /usr/lib/arm-linux-gnueabihf/pkgconfig/* /opt/sysroot/usr/lib/pkgconfig/ 2>/dev/null || true
|
||||
|
||||
# 优化 Rust 链接参数
|
||||
# 1. 优先使用 -L 指定 sysroot 路径
|
||||
# 2. 使用 -rpath-link 解决库的间接依赖问题
|
||||
# 3. 确保 dynamic-linker 路径正确 (OpenWrt glibc 常用路径)
|
||||
ENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc
|
||||
ENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_RUSTFLAGS="\
|
||||
-C link-arg=--sysroot=/opt/sysroot \
|
||||
-C link-arg=-Wl,-dynamic-linker,/lib/ld-linux-armhf.so.3 \
|
||||
-C link-arg=-L/opt/sysroot/usr/lib \
|
||||
-C link-arg=-L/opt/sysroot/lib \
|
||||
-C link-arg=-Wl,-rpath-link,/opt/sysroot/usr/lib \
|
||||
-C link-arg=-Wl,-rpath-link,/opt/sysroot/lib"
|
||||
|
||||
# 强制 pkg-config 只在 sysroot 中寻找,忽略宿主机容器路径
|
||||
ENV PKG_CONFIG_ALLOW_CROSS=1
|
||||
ENV PKG_CONFIG_SYSROOT_DIR=/opt/sysroot
|
||||
ENV PKG_CONFIG_LIBDIR=/opt/sysroot/usr/lib/pkgconfig:/opt/sysroot/lib/pkgconfig
|
||||
|
||||
# 设置编译器,强制指定 sysroot
|
||||
ENV CC_armv7_unknown_linux_gnueabihf="arm-linux-gnueabihf-gcc --sysroot=/opt/sysroot"
|
||||
|
||||
CMD ["/bin/bash"]
|
||||
|
||||
+8
-14
@@ -12,25 +12,19 @@ PROXY="http://host.docker.internal:7890"
|
||||
# --build-arg https_proxy=$PROXY \
|
||||
# -t open-xiaoai-runtime .
|
||||
|
||||
# # 2. 编译 hello world demo
|
||||
# 2. 编译 hello world demo
|
||||
echo "Compiling hello demo..."
|
||||
rm -rf ../hello/target # 先清理旧的构建产物,防止缓存干扰
|
||||
docker run --rm -v $(pwd)/../hello:/app/hello open-xiaoai-runtime \
|
||||
cargo build --manifest-path hello/Cargo.toml --target armv7-unknown-linux-gnueabihf --release
|
||||
|
||||
# # 3. 运行编译出的二进制文件 (通过 QEMU 模拟)
|
||||
echo "Running compiled binary via QEMU..."
|
||||
docker run --rm -v $(pwd)/../hello/target/armv7-unknown-linux-gnueabihf/release/hello:/app/hello \
|
||||
-v $(pwd)/root.squashfs:/app/root.squashfs open-xiaoai-runtime \
|
||||
run /app/hello
|
||||
# 3. 运行编译出的二进制文件 (通过 QEMU 模拟)
|
||||
# echo "Running compiled binary via QEMU..."
|
||||
# docker run --rm -v $(pwd)/../hello/target/armv7-unknown-linux-gnueabihf/release/hello:/app/hello open-xiaoai-runtime \
|
||||
# run /app/hello
|
||||
|
||||
# 4. 交互模式运行
|
||||
# docker run --rm -it --privileged \
|
||||
# -v $(pwd)/root.squashfs:/app/root.squashfs \
|
||||
# open-xiaoai-runtime \
|
||||
# run
|
||||
# docker run --rm -it --privileged open-xiaoai-runtime run
|
||||
|
||||
# 5. 上传二进制文件到小爱音箱
|
||||
# dd if=target/armv7-unknown-linux-gnueabihf/release/hello \
|
||||
# | ssh -o HostKeyAlgorithms=+ssh-rsa root@192.168.31.235 "dd of=/data/hello"
|
||||
# ssh -o HostKeyAlgorithms=+ssh-rsa root@192.168.31.235 "dd if=/data/hello" | hello
|
||||
# dd if=apps/hello/target/armv7-unknown-linux-gnueabihf/release/hello \
|
||||
# | ssh -o HostKeyAlgorithms=+ssh-rsa root@192.168.31.235 "dd of=/data/hello"
|
||||
@@ -5,24 +5,6 @@ set -e
|
||||
# 定义 sysroot 目录
|
||||
SYSROOT_DIR="/opt/sysroot"
|
||||
|
||||
# 如果 /opt/sysroot 已经有内容(通过 -v 挂载了 squashfs-root),则跳过解压
|
||||
if [ -d "$SYSROOT_DIR/bin" ] || [ -d "$SYSROOT_DIR/lib" ]; then
|
||||
echo "Using existing sysroot at $SYSROOT_DIR"
|
||||
else
|
||||
mkdir -p "$SYSROOT_DIR"
|
||||
# 优先使用环境变量指定的 squashfs 文件,否则使用默认的 root.squashfs
|
||||
ROOTFS_FILE="${SYSROOT_FILE:-root.squashfs}"
|
||||
|
||||
if [ -f "$ROOTFS_FILE" ]; then
|
||||
echo -e "\n🔥 Unsquashing $ROOTFS_FILE to $SYSROOT_DIR...\n"
|
||||
unsquashfs -d "$SYSROOT_DIR" -f "$ROOTFS_FILE"
|
||||
echo -e "\n✅ Unsquashed rootfs\n"
|
||||
else
|
||||
echo "Error: Sysroot file $ROOTFS_FILE not found and $SYSROOT_DIR is empty."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# 检查第一个参数。如果在 sysroot 下存在该文件,则自动补全路径
|
||||
# 比如 `run /bin/sh` 会被转换为 `run /opt/sysroot/bin/sh`
|
||||
if [ -n "$1" ] && [ -f "$SYSROOT_DIR$1" ]; then
|
||||
|
||||
Reference in New Issue
Block a user