Building the Opus Codec for Android with DRED Support

This guide provides step-by-step instructions and a shell script to compile the Opus audio codec from the source for the Android platform. The build will be configured to be static and will have the DRED (Deep REDundancy) feature enabled, which is excellent for handling packet loss in real-time communication.

1. Prerequisites

Before you begin, ensure you have the following software installed on your development machine (Linux or macOS is recommended):

  • Git: To clone the Opus source code repository.
  • Android NDK: The Native Development Kit is essential for cross-compiling for Android. Download the latest version from the official Android NDK page.
  • Autotools: autoconf, automake, and libtool are required by the Opus build system.
    • On Debian/Ubuntu: sudo apt-get install autoconf automake libtool
    • On macOS (with Homebrew): brew install autoconf automake libtool

2. Setup and Configuration

First, clone the official Opus repository and prepare your environment.

# Clone the Opus repository if it doesn't exist
if [ ! -d "opus" ]; then
    echo "Cloning Opus repository..."
    git clone https://github.com/xiph/opus.git
fi

# Navigate into the source directory and update the source
cd opus
echo "Pulling latest changes from Opus repository..."
git pull

# Run the autogen script to create the configure script
./autogen.sh

3. The Android Build Script

The most reliable way to build for multiple Android architectures is to use a shell script. The script below sets the correct environment variables and compiler flags for the NDK toolchain.

Create a new file named build_opus_android.sh in the opus directory and paste the following content into it.

#!/bin/bash

# Exit immediately if a command exits with a non-zero status.
set -e

# --- Configuration ---
# Set the path to your Android NDK root directory
export NDK_ROOT="/path/to/your/android-ndk" # <-- IMPORTANT: CHANGE THIS

# Set the Android API level you want to target. 21 is a good minimum for modern apps.
export API_LEVEL=21

# List of architectures to build.
# Common architectures are: aarch64 (arm64-v8a), armv7a (armeabi-v7a), x86_64, x86
TARGET_ARCHS=("aarch64" "armv7a" "x86_64")

# --- DO NOT EDIT BELOW THIS LINE ---

# Check if NDK_ROOT is set correctly
if [ ! -d "$NDK_ROOT" ]; then
    echo "Error: NDK_ROOT is not set or not a valid directory."
    echo "Please edit this script and set NDK_ROOT to your Android NDK location."
    exit 1
fi

# Determine the host OS for the NDK toolchain path
HOST_TAG=""
if [[ $(uname -s) == "Linux" ]]; then
    HOST_TAG="linux-x86_64"
elif [[ $(uname -s) == "Darwin" ]]; then
    HOST_TAG="darwin-x86_64"
else
    echo "Unsupported host OS. This script supports Linux and macOS."
    exit 1
fi

# Set the NDK toolchain path
export TOOLCHAIN="$NDK_ROOT/toolchains/llvm/prebuilt/$HOST_TAG"
export PATH="$TOOLCHAIN/bin:$PATH"

# --- Build Function ---
build_for_arch() {
    ARCH=$1
    echo "================================================="
    echo "Building Opus for ANDROID architecture: $ARCH"
    echo "================================================="

    # Set target host based on architecture
    case $ARCH in
        "aarch64")
            TARGET_HOST="aarch64-linux-android"
            ;;
        "armv7a")
            TARGET_HOST="armv7a-linux-androideabi"
            ;;
        "x86_64")
            TARGET_HOST="x86_64-linux-android"
            ;;
        "x86")
            TARGET_HOST="i686-linux-android"
            ;;
        *)
            echo "Unsupported architecture: $ARCH"
            exit 1
            ;;
    esac

    # Set compiler and other toolchain executables
    export AR=$TOOLCHAIN/bin/llvm-ar
    export CC=$TOOLCHAIN/bin/$TARGET_HOST$API_LEVEL-clang
    export CXX=$TOOLCHAIN/bin/$TARGET_HOST$API_LEVEL-clang++
    export LD=$TOOLCHAIN/bin/ld
    export RANLIB=$TOOLCHAIN/bin/llvm-ranlib
    export STRIP=$TOOLCHAIN/bin/llvm-strip

    # Define the output directory for this architecture
    OUTPUT_DIR="$(pwd)/android-build/$ARCH"
    mkdir -p $OUTPUT_DIR

    # Clean previous build artifacts if a Makefile exists
    if [ -f "Makefile" ]; then
        make clean
    fi

    # Configure the build
    ./configure \
        --host=$TARGET_HOST \
        --prefix="$OUTPUT_DIR" \
        --enable-static \
        --disable-shared \
        --disable-doc \
        --disable-extra-programs \
        --with-sysroot="$TOOLCHAIN/sysroot" \
        --enable-dred  # <-- This is the flag that enables DRED support

    # Compile and install
    make -j$(nproc)
    make install

    echo "-------------------------------------------------"
    echo "Successfully built Opus for $ARCH"
    echo "Output libraries are in: $OUTPUT_DIR"
    echo "-------------------------------------------------"
}

# --- Main Execution ---
# Loop through all target architectures and build
for ARCH in "${TARGET_ARCHS[@]}"; do
    build_for_arch $ARCH
done

echo "================================================="
echo "All Android builds completed successfully!"
echo "================================================="

4. How to Run the Script

  1. Modify the NDK_ROOT variable in the build_opus_android.sh script to point to the location where you extracted the Android NDK.

Run the script from within the opus source directory:

./build_opus_android.sh

Make the script executable:

chmod +x build_opus_android.sh

The script will now configure, compile, and install the Opus library for each architecture specified in the TARGET_ARCHS array.

5. Verifying the Output

After the script finishes, you will have a new directory named android-build inside your opus folder. The structure will look like this:

opus/
├── android-build/
│   ├── aarch64/
│   │   ├── include/opus/
│   │   │   ├── opus.h
│   │   │   ├── opus_defines.h
│   │   │   └── ...
│   │   └── lib/
│   │       ├── libopus.a   <-- The static library
│   │       └── pkgconfig/
│   ├── armv7a/
│   │   ├── include/
│   │   └── lib/
│   └── x86_64/
│       ├── include/
│       └── lib/
└── ...

You now have the static library (libopus.a) and the necessary headers for each Android architecture, ready to be integrated into your project.

6. Integrating into Your Android Project (CMake)

To use these prebuilt libraries in your Android app, you'll need to configure your project's CMakeLists.txt file.

  1. Copy the entire android-build directory into your app's app/src/main/cpp directory (or a similar location).
  2. Modify your CMakeLists.txt to add the library and link against it.
# Add Opus as a prebuilt static library
add_library(opus-prebuilt STATIC IMPORTED)

# Set the location of the .a file based on the Android ABI
set_target_properties(opus-prebuilt PROPERTIES IMPORTED_LOCATION
    ${CMAKE_SOURCE_DIR}/android-build/${ANDROID_ABI}/lib/libopus.a)

# Include the Opus header directory
include_directories(${CMAKE_SOURCE_DIR}/android-build/${ANDROID_ABI}/include)

# ... later, when you define your app's native library ...

# Link your native library against the prebuilt Opus library
target_link_libraries(
    your-native-lib-name  # The name of your app's library
    opus-prebuilt
    log
)

Now, when you build your Android app, Gradle and CMake will automatically select the correct libopus.a for the target device's architecture and link it with your native code. You can include <opus/opus.h> in your C/C++ files to use the Opus API.