#!/usr/bin/env bash # Build libvpx, opus, and speex for a single Android ABI using the NDK. # Usage: ./scripts/build-native-codecs.sh arm64-v8a [armeabi-v7a|x86_64] # # Output: build/native//libvpx.a, libopus.a, libspeex.a # The CMakeLists.txt picks these up automatically during the Gradle build. set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT="$(dirname "$SCRIPT_DIR")" THIRD_PARTY="$ROOT/third-party" BUILD_NATIVE="$ROOT/build/native" # Work around make's inability to handle paths with spaces by using symlinks. SAFE_ROOT="/tmp/ac-mobile-android-build" if [[ ! -L "$SAFE_ROOT" ]] || [[ "$(readlink -f "$SAFE_ROOT")" != "$(readlink -f "$ROOT")" ]]; then rm -f "$SAFE_ROOT" ln -sf "$(readlink -f "$ROOT")" "$SAFE_ROOT" fi SAFE_THIRD_PARTY="$SAFE_ROOT/third-party" SAFE_BUILD_NATIVE="$SAFE_ROOT/build/native" ANDROID_ABI="${1:-arm64-v8a}" source "$SCRIPT_DIR/android-ndk.sh" NDK_HOME="$(androidcast_find_ndk_root "$ROOT")" export ANDROID_NDK_HOME="$NDK_HOME" case "$ANDROID_ABI" in arm64-v8a) HOST_ARCH="aarch64-linux-android"; API=21 ;; armeabi-v7a) HOST_ARCH="armv7a-linux-androideabi"; API=21 ;; x86_64) HOST_ARCH="x86_64-linux-android"; API=21 ;; x86) HOST_ARCH="i686-linux-android"; API=21 ;; *) echo "Unsupported ABI: $ANDROID_ABI" >&2; exit 1 ;; esac OUT="$BUILD_NATIVE/$ANDROID_ABI" mkdir -p "$OUT" # Resolve toolchain TOOLCHAIN="$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64" CC="$TOOLCHAIN/bin/${HOST_ARCH}${API}-clang" CXX="$TOOLCHAIN/bin/${HOST_ARCH}${API}-clang++" AR="$TOOLCHAIN/bin/llvm-ar" RANLIB="$TOOLCHAIN/bin/llvm-ranlib" STRIP="$TOOLCHAIN/bin/llvm-strip" if [[ ! -x "$CC" ]]; then echo "ERROR: Clang not found at $CC" >&2 exit 1 fi LD="$TOOLCHAIN/bin/ld.lld" [[ -x "$LD" ]] || LD="$TOOLCHAIN/bin/lld" SYSROOT="$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/sysroot" # Export so configure scripts see them export CC CXX AR RANLIB STRIP LD export LDFLAGS="--sysroot=$SYSROOT" export CFLAGS="--sysroot=$SYSROOT" export CXXFLAGS="--sysroot=$SYSROOT" echo "==> ABI=$ANDROID_ABI API=$API CC=$CC LD=$LD" # ─── libvpx ────────────────────────────────────────────────────────────────── build_libvpx() { local src="$SAFE_THIRD_PARTY/libvpx" local bld="$SAFE_BUILD_NATIVE/vpx-build-$ANDROID_ABI" local lib="$OUT/libvpx.a" if [[ -f "$lib" ]]; then echo "==> libvpx already built for $ANDROID_ABI ($lib)" return 0 fi echo "==> Building libvpx for $ANDROID_ABI..." rm -rf "$bld" mkdir -p "$bld" # Use generic-gnu to avoid Android-specific assembler/linker issues with cross-compilation. # The clang compiler provides correct codegen; assembly optimizations are secondary. local vpx_target="generic-gnu" cd "$bld" "$src/configure" \ --target="$vpx_target" \ --disable-examples \ --disable-tools \ --disable-docs \ --disable-unit-tests \ --enable-vp8 \ --enable-vp9 \ --enable-static \ --disable-shared \ --disable-webm-io \ --disable-libyuv \ --extra-cflags="-fPIC --sysroot=$SYSROOT" \ --prefix="$bld/install" 2>&1 | tail -5 make -j"$(nproc)" libvpx.a 2>&1 | tail -5 cp libvpx.a "$lib" echo "==> libvpx built: $lib ($(du -sh "$lib" | cut -f1))" cd "$ROOT" } # ─── libopus ───────────────────────────────────────────────────────────────── build_libopus() { local src="$SAFE_THIRD_PARTY/opus" local bld="$SAFE_BUILD_NATIVE/opus-build-$ANDROID_ABI" local lib="$OUT/libopus.a" if [[ -f "$lib" ]]; then echo "==> libopus already built for $ANDROID_ABI ($lib)" return 0 fi echo "==> Building libopus for $ANDROID_ABI..." rm -rf "$bld" mkdir -p "$bld" # Run autogen if configure doesn't exist if [[ ! -f "$src/configure" ]]; then ( cd "$src" && ./autogen.sh 2>&1 | tail -3 ) fi cd "$bld" CC="$CC -fPIC" CXX="$CXX -fPIC" AR="$AR" RANLIB="$RANLIB" \ "$src/configure" \ --host="$HOST_ARCH" \ --disable-shared \ --enable-static \ --disable-doc \ --disable-extra-programs \ --prefix="$bld/install" 2>&1 | tail -5 make -j"$(nproc)" 2>&1 | tail -5 cp .libs/libopus.a "$lib" 2>/dev/null || cp src/.libs/libopus.a "$lib" 2>/dev/null || \ find "$bld" -name 'libopus.a' -exec cp {} "$lib" \; # Create opus/ include subdirectory that CMakeLists expects for #include mkdir -p "$OUT/include/opus" cp "$src/include/"*.h "$OUT/include/opus/" 2>/dev/null || true echo "==> libopus built: $lib ($(du -sh "$lib" | cut -f1))" cd "$ROOT" } # ─── libspeex ──────────────────────────────────────────────────────────────── build_libspeex() { local src="$SAFE_THIRD_PARTY/speex" local bld="$SAFE_BUILD_NATIVE/speex-build-$ANDROID_ABI" local lib="$OUT/libspeex.a" if [[ -f "$lib" ]]; then echo "==> libspeex already built for $ANDROID_ABI ($lib)" return 0 fi echo "==> Building libspeex for $ANDROID_ABI..." rm -rf "$bld" mkdir -p "$bld" if [[ ! -f "$src/configure" ]]; then ( cd "$src" && ./autogen.sh 2>&1 | tail -3 ) fi cd "$bld" CC="$CC -fPIC" CXX="$CXX -fPIC" AR="$AR" RANLIB="$RANLIB" \ "$src/configure" \ --host="$HOST_ARCH" \ --disable-shared \ --enable-static \ --disable-oggtest \ --prefix="$bld/install" 2>&1 | tail -5 # Build only the library directory (not speexdec/speexenc tools) make -j"$(nproc)" -C libspeex 2>&1 | tail -5 find "$bld/libspeex" -name 'libspeex.a' | head -1 | xargs -I{} cp {} "$lib" [[ -f "$lib" ]] || find "$bld" -name 'libspeex.a' | head -1 | xargs -I{} cp {} "$lib" # Copy generated speex_config_types.h which is needed at build time if [[ -f "$bld/include/speex/speex_config_types.h" ]]; then mkdir -p "$OUT/include/speex" cp "$bld/include/speex/speex_config_types.h" "$OUT/include/speex/" fi echo "==> libspeex built: $lib ($(du -sh "$lib" | cut -f1))" cd "$ROOT" } build_libvpx build_libopus build_libspeex echo "" echo "==> Native libs for $ANDROID_ABI:" ls -lh "$OUT/"*.a 2>/dev/null || echo " (none)"