Android FFmpeg 交叉编译全指南:NDK编译 + CMake 集成

开发环境搭建

下载最新版Android Studio,安装SDK和模拟器

在Android Studio中配置:Setting → Android SDK → SDK Tools → 勾选CMake 和 NDK → Apply,按照提示下载安装

  • SDK是Android应用开发的基础工具包,适合大多数上层逻辑开发。
  • NDK是补充工具,用于需要直接控制硬件或高性能计算的场景。允许开发者使用C/C++编写高性能代码,直接运行于Android设备硬件。

创建Native C++项目

JNI:Java 原生接口

JNI中只能调用C函数(C++因为函数重载名字会被修改,所以要用extern "C"修饰)

可以在头文件中添加#include <android/log.h> ,使用

__android_log_print(*ANDROID_LOG_INFO*, "Test", "test for android log");语句测试

FFmpeg交叉编译

基础知识

应用二进制接口(ABI):armeabi / x86

  • CPU指令集
  • 内存字节序
  • 可执行二进制的格式
  • 解析的各种约定:对齐限制、堆栈使用和调用函数
  • 系统库集

常见的架构包括:

目录名 架构 Android ABI名称 常见设备 位数
aarch64-linux-android ARM64(ARMv8-A) arm64-v8a 现代Android手机(如骁龙8系、天玑9000) 64位
arm-linux-androideabi ARMv7(兼容ARMv6) armeabi-v7a 旧款手机(如骁龙625、Helio P10) 32位
i686-linux-android x86(Intel 32位) x86 模拟器或老旧x86平板(如部分ZenFone) 32位
x86_64-linux-android x86-64(Intel 64位) x86_64 现代x86模拟器或Chromebook 64位
riscv64-linux-android RISC-V 64位 riscv64 新兴架构(如部分IoT设备或实验性设备) 64位

NEON(浮点数协处理器 )是ARM架构中的一种SIMD(单指令多数据)指令集扩展,专门用于加速浮点数和整数运算,尤其在多媒体处理、信号计算等场景中性能提升显著。

具体步骤

网络上交叉编译Android平台的ffmpeg都是手动执行脚本,例如:https://github.com/AnJoiner/FFmpegCommand/blob/master/ffmpeg-wiki/编译FFmpeg.md

可是这种硬编码随着ffmpeg版本和ndk版本不同存在很大的缺陷:ndk的目录组织结构在不同版本差异很大,导致toolchain等路径都得重新设置。现代 NDK(r21+)提供了更便捷的 android.toolchain.cmake 工具链文件,可以大幅简化交叉编译流程。

  • 无需手动配置编译器路径:CMake 自动识别 NDK 中的工具链。
  • 支持多架构并行编译:通过 ANDROID_ABI 参数一键切换。
  • 更好的兼容性:避免因路径或环境变量错误导致的编译失败。

通过使用**ExternalProject_Add 命令,在 CMake 中集成外部项目的构建过程**(如 FFmpeg 这种非 CMake 项目)

makefile 复制代码
include(ExternalProject)  # 必须包含该模块

ExternalProject_Add(
    <项目名称>            # 自定义标识(如 "ffmpeg")
    
    # --- 关键参数 ---
    SOURCE_DIR      <path>       # 源码目录(如 ${CMAKE_SOURCE_DIR}/ffmpeg)
    CONFIGURE_COMMAND <cmd>      # 配置命令(如 ./configure --prefix=...)
    BUILD_COMMAND     <cmd>      # 构建命令(如 make -j8)
    INSTALL_COMMAND   <cmd>      # 安装命令(如 make install)
    
    # --- 可选参数 ---
    BUILD_IN_SOURCE   TRUE       # 在源码目录内构建(适用于 FFmpeg)
    INSTALL_DIR       <path>     # 安装路径(等价于 --prefix)
    DEPENDS           <targets>  # 依赖的其他 CMake 目标
)

参考官方文档:https://developer.android.com/ndk/guides/cmake?hl=zh-cn#command-line

参考编译脚本:https://umirtech.com/how-to-compile-build-ffmpeg-for-android-and-use-it-in-android-studio/

  1. 准备 FFmpeg 源码

    bash 复制代码
    git clone https://git.ffmpeg.org/ffmpeg.git ffmpeg
    cd ffmpeg
  2. 创建**CMakeLists.txt**

    在 FFmpeg 根目录新建 CMakeLists.txt,内容如下:

    makefile 复制代码
    cmake_minimum_required(VERSION 3.10)
    project(ffmpeg_android)
    # 基本配置(可通过命令行参数覆盖)
    set(ANDROID_ABI "arm64-v8a" CACHE STRING "Target ABI")
    set(ANDROID_STL "c++_static" CACHE STRING "C++ STL implementation")
    set(ANDROID_PLATFORM "android-24" CACHE STRING "Android platform version")
    
    # 指定 NDK 工具链文件(通过命令行参数传递)
    if(NOT CMAKE_TOOLCHAIN_FILE)
        message(FATAL_ERROR "Please specify NDK toolchain file with -DCMAKE_TOOLCHAIN_FILE=")
    endif()
    # 获取 NDK 工具链路径
    set(ANDROID_NDK_TOOLCHAIN_DIR "${CMAKE_ANDROID_NDK}/toolchains/llvm/prebuilt/darwin-x86_64/bin")
    
    # 从工具链文件中获取的信息
    message(STATUS "Using NDK: ${CMAKE_ANDROID_NDK}")
    message(STATUS "Using NDK toolchain dir: ${ANDROID_NDK_TOOLCHAIN_DIR}")
    message(STATUS "Target ABI: ${ANDROID_ABI}")
    message(STATUS "Android platform: ${ANDROID_PLATFORM}")
    message(STATUS "SYSROOT: ${CMAKE_SYSROOT}")
    message(STATUS "CC: ${CMAKE_C_COMPILER}")
    
    # 根据 ABI 设置 FFmpeg 特定参数
    if(ANDROID_ABI STREQUAL "arm64-v8a")
        set(TARGET_ARCH "aarch64")
        set(TARGET_CPU "armv8-a")
        set(TARGET_TRIPLE "aarch64-linux-android")
        set(EXTRA_CFLAGS "-fPIC -DANDROID -O3 -march=${TARGET_CPU} -fomit-frame-pointer")
        set(EXTRA_CONFIG "--enable-neon")
    elseif(ANDROID_ABI STREQUAL "armeabi-v7a")
        set(TARGET_ARCH "arm")
        set(TARGET_CPU "armv7-a")
        set(TARGET_TRIPLE "armv7a-linux-androideabi")
        set(EXTRA_CFLAGS "-fPIC -DANDROID -O3 -marm -march=${TARGET_CPU} -mfpu=neon -fomit-frame-pointer")
        set(EXTRA_CONFIG "--enable-neon --disable-armv5te --disable-armv6 --disable-armv6t2")
    endif()
    
    set(CMAKE_C_COMPILER "${ANDROID_NDK_TOOLCHAIN_DIR}/${TARGET_TRIPLE}${ANDROID_PLATFORM_LEVEL}-clang")
    set(CMAKE_CXX_COMPILER "${ANDROID_NDK_TOOLCHAIN_DIR}/${TARGET_TRIPLE}${ANDROID_PLATFORM_LEVEL}-clang++")
    
    # 设置 FFmpeg 配置选项
    set(ENABLED_CONFIG
        --enable-small
        --enable-avcodec
        --enable-avformat
        --enable-avutil
        --enable-swscale
        --enable-swresample
        --enable-demuxers
        --enable-parser=*
        --enable-decoders
        --enable-shared
        --enable-nonfree
        --enable-gpl
        --enable-small
        --enable-neon
        --enable-hwaccels
        --enable-avdevice
        --enable-jni
        --enable-mediacodec
        --enable-decoder=h264_mediacodec
        --enable-hwaccel=h264_mediacodec
    )
    
    set(DISABLED_CONFIG
        --disable-zlib
        --disable-v4l2-m2m
        --disable-cuda-llvm
        --disable-indevs
        --disable-libxml2
        --disable-avdevice
        --disable-network
        --disable-static
        --disable-debug
        --disable-ffmpeg
        --disable-ffplay
        --disable-ffprobe
        --disable-doc
        --disable-symver
        --disable-programs
    )
    
    # 添加 FFmpeg 为外部项目
    include(ExternalProject)
    ExternalProject_Add(ffmpeg
        SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}
        CONFIGURE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/configure
            --prefix=${CMAKE_BINARY_DIR}/ffmpeg_install/${ANDROID_ABI}
            --target-os=android
            --arch=${TARGET_ARCH}
            --cpu=${TARGET_CPU}
            --enable-cross-compile
            --cross-prefix=${ANDROID_TOOLCHAIN_PREFIX}
            --cc=${CMAKE_C_COMPILER}
            --cxx=${CMAKE_CXX_COMPILER}
            --sysroot=${CMAKE_SYSROOT}
            --extra-cflags=${EXTRA_CFLAGS}
            --extra-ldflags="-L${CMAKE_SYSROOT}/usr/lib/${TARGET_ARCH}-linux-android/${ANDROID_PLATFORM_LEVEL}"
            --ar=${CMAKE_AR}
            --nm=${CMAKE_NM}
            --ranlib=${CMAKE_RANLIB}
            --strip=${CMAKE_STRIP}
            ${ENABLED_CONFIG}
            ${DISABLED_CONFIG}
            ${EXTRA_CONFIG}
        BUILD_COMMAND make -j8
        INSTALL_COMMAND make install
        BUILD_IN_SOURCE TRUE
    )
  3. 执行 CMake 编译

    bash 复制代码
    mkdir build && cd build
    # NDK=<your ndk path> # 根据你的NDK路径设置NDK变量
    # NDK=/Users/cloud.wang/Library/Android/sdk/ndk/29.0.13113456
    cmake .. -DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake
    make
  4. 输出结果

    编译后的头文件、动态库(.so 文件)会生成在 build目录的 ffmpeg_install 下的includelib目录中。

./configure报错输出的详细信息位于ffbuild/config.log文件中,如果遇到问题可以针对详细报错进行修改调试。

如果不愿意使用CMake,也可以尝试使用bash脚本,我通过参考博主MOHD UMIR的编译脚本,根据我的Mac环境进行修改后可以编译成功。

bash 复制代码
#!/bin/bash

### Describe Your Target Architectures ###
ARCH_LIST=( "armv8a" ) 

### Change As per Yours ####
BUILD_PLATFORM=darwin
ANDROID_API_LEVEL="24"
ANDROID_NDK_PATH="/Users/cloud.wang/Library/Android/sdk/ndk/29.0.13113456"
FFMPEG_SOURCE_DIR="/Users/cloud.wang/Code/ffmpeg7/ffmpeg-7.0.2"
FFMPEG_BUILD_DIR="/Users/cloud.wang/Code/build_ffmpeg_android"

### Enable FFMPEG BUILD MODULES ####
ENABLED_CONFIG="\
    --enable-small \
		--enable-avcodec \
		--enable-avformat \
		--enable-avutil \
		--enable-swscale \
		--enable-swresample \
		--enable-demuxers \
		--enable-parser=* \
		--enable-decoders \
		--enable-shared  \
		--enable-nonfree \
		--enable-gpl \
		--enable-small \
		--enable-neon \
		--enable-hwaccels \
		--enable-avdevice \
		--enable-jni \
		--enable-mediacodec \
		--enable-decoder=h264_mediacodec \
		--enable-hwaccel=h264_mediacodec "

### Disable FFMPEG BUILD MODULES ####
DISABLED_CONFIG="\
		--disable-zlib \
		--disable-v4l2-m2m \
		--disable-cuda-llvm \
		--disable-indevs \
		--disable-libxml2 \
		--disable-avdevice \
		--disable-network \
		--disable-static \
		--disable-debug \
		--disable-ffmpeg \
		--disable-ffplay \
		--disable-ffprobe \
		--disable-doc \
		--disable-symver \
		--disable-programs "

### Dont Change ####
SYSROOT="$ANDROID_NDK_PATH/toolchains/llvm/prebuilt/$BUILD_PLATFORM-x86_64/sysroot"
LLVM_AR="$ANDROID_NDK_PATH/toolchains/llvm/prebuilt/$BUILD_PLATFORM-x86_64/bin/llvm-ar"
LLVM_NM="$ANDROID_NDK_PATH/toolchains/llvm/prebuilt/$BUILD_PLATFORM-x86_64/bin/llvm-nm"
LLVM_RANLIB="$ANDROID_NDK_PATH/toolchains/llvm/prebuilt/$BUILD_PLATFORM-x86_64/bin/llvm-ranlib"
LLVM_STRIP="$ANDROID_NDK_PATH/toolchains/llvm/prebuilt/$BUILD_PLATFORM-x86_64/bin/llvm-strip"

configure_ffmpeg(){
   TARGET_ARCH=$1
   TARGET_CPU=$2
   CROSS_PREFIX=$3
   EXTRA_CFLAGS=$4
   EXTRA_CONFIG=$5
   
   CLANG="${CROSS_PREFIX}clang"
   CLANGXX="${CROSS_PREFIX}clang++"
   PREFIX="${FFMPEG_BUILD_DIR}/$TARGET_ARCH-$ANDROID_API_LEVEL"
   
   cd $FFMPEG_SOURCE_DIR
   
   ./configure \
   --disable-everything \
   --target-os=android \
   --arch=$TARGET_ARCH \
   --cpu=$TARGET_CPU \
   --enable-cross-compile \
   --cross-prefix="$CROSS_PREFIX" \
   --cc="$CLANG" \
   --cxx="$CLANGXX" \
   --sysroot="$SYSROOT" \
   --prefix="$PREFIX" \
   --extra-cflags="-fPIC -DANDROID $EXTRA_CFLAGS" \
   --extra-ldflags="-L$SYSROOT/usr/lib/$TARGET_ARCH-linux-android/$ANDROID_API_LEVEL" \
   ${ENABLED_CONFIG} \
   ${DISABLED_CONFIG} \
   --ar="$LLVM_AR" \
   --nm="$LLVM_NM" \
   --ranlib="$LLVM_RANLIB" \
   --strip="$LLVM_STRIP" \
   ${EXTRA_CONFIG}

   make clean
   make -j 8
   make install -j2
   
}

echo -e "\e[1;32mCompiling FFMPEG for Android...\e[0m"

for ARCH in "${ARCH_LIST[@]}"; do
    case "$ARCH" in
        "armv8-a"|"aarch64"|"arm64-v8a"|"armv8a")
            echo -e "\e[1;32m$ARCH Libraries\e[0m"
            TARGET_ARCH="aarch64"
            TARGET_CPU="armv8-a"
            TARGET_ABI="aarch64"
            CROSS_PREFIX="$ANDROID_NDK_PATH/toolchains/llvm/prebuilt/$BUILD_PLATFORM-x86_64/bin/$TARGET_ABI-linux-android${ANDROID_API_LEVEL}-"
            EXTRA_CFLAGS="-O3 -march=$TARGET_CPU -fomit-frame-pointer"
            EXTRA_CONFIG="\
            		--enable-neon "
            ;;
        "armv7-a"|"armeabi-v7a"|"armv7a")
            echo -e "\e[1;32m$ARCH Libraries\e[0m"
            TARGET_ARCH="arm"
            TARGET_CPU="armv7-a"
            TARGET_ABI="armv7a"
            CROSS_PREFIX="$ANDROID_NDK_PATH/toolchains/llvm/prebuilt/$BUILD_PLATFORM-x86_64/bin/$TARGET_ABI-linux-androideabi${ANDROID_API_LEVEL}-"
            EXTRA_CFLAGS="-O3 -marm -march=$TARGET_CPU -mfpu=neon -fomit-frame-pointer"
            EXTRA_CONFIG="\
            		--disable-armv5te \
            		--disable-armv6 \
            		--disable-armv6t2 \
            		--enable-neon "
            ;;
        "x86-64"|"x86_64")
            echo -e "\e[1;32m$ARCH Libraries\e[0m"
            TARGET_ARCH="x86_64"
            TARGET_CPU="x86-64"
            TARGET_ABI="x86_64"
            CROSS_PREFIX="$ANDROID_NDK_PATH/toolchains/llvm/prebuilt/$BUILD_PLATFORM-x86_64/bin/$TARGET_ABI-linux-android${ANDROID_API_LEVEL}-"
            EXTRA_CFLAGS="-O3 -march=$TARGET_CPU -fomit-frame-pointer"
            		
            EXTRA_CONFIG="\
            		  "
            ;;
        "x86"|"i686")
            echo -e "\e[1;32m$ARCH Libraries\e[0m"
            TARGET_ARCH="i686"
            TARGET_CPU="i686"
            TARGET_ABI="i686"
            CROSS_PREFIX="$ANDROID_NDK_PATH/toolchains/llvm/prebuilt/$BUILD_PLATFORM-x86_64/bin/$TARGET_ABI-linux-android${ANDROID_API_LEVEL}-"
            EXTRA_CFLAGS="-O3 -march=$TARGET_CPU -fomit-frame-pointer"
            EXTRA_CONFIG="\
            		--disable-asm "
            ;;
           * )
            echo "Unknown architecture: $ARCH"
            exit 1
            ;;
    esac
    configure_ffmpeg "$TARGET_ARCH" "$TARGET_CPU" "$CROSS_PREFIX" "$EXTRA_CFLAGS" "$EXTRA_CONFIG"
done

Android Studio配置

在编译好Android平台的ffmpeg库文件后,需要在Android Studio中进行一些配置才能够实现在Android中调用ffmpeg。

首先我们需要将头文件和库文件拷贝到项目中,最好是拷贝到src/main/cpp目录下(和CMakeLists.txt同级),拷贝后的目录结构如下:

bash 复制代码
cpp
├── CMakeLists.txt
├── include
│   └── arm64-v8a
│       ├── libavcodec
│       ├── libavfilter
│       ├── libavformat
│       ├── libavutil
│       ├── libpostproc
│       ├── libswresample
│       └── libswscale
├── lib
│   └── arm64-v8a
│       ├── libavcodec.so
│       ├── libavfilter.so
│       ├── libavformat.so
│       ├── libavutil.so
│       ├── libpostproc.so
│       ├── libswresample.so
│       └── libswscale.so
└── native-lib.cpp

接下来需要在CMakeLists.txt中配置ffmpeg的头文件和库文件,在CMakeLists.txt中添加以下内容。需要注意的是我这里目前只添加了avcodec库,实际项目中根据需要应该添加需要的所有的ffmpeg库。

bash 复制代码
# FFmpeg
message(STATUS "ANDROID_ABI: ${ANDROID_ABI}")
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/${ANDROID_ABI})
set(ffmpeg_lib ${CMAKE_CURRENT_SOURCE_DIR}/lib/${ANDROID_ABI})
add_library(avcodec SHARED IMPORTED)
set_target_properties(avcodec PROPERTIES IMPORTED_LOCATION ${ffmpeg_lib}/libavcodec.so)

add_library(${CMAKE_PROJECT_NAME} SHARED
        # List C/C++ source files with relative paths to this CMakeLists.txt.
        native-lib.cpp)

target_link_libraries(${CMAKE_PROJECT_NAME}
        # ffmpeg
        avcodec
        # List libraries link to the target library
        android
        log)

值得注意的是CMake中的${ANDROID_ABI} 变量的内容是被Android Studio自动设置的,为了指定编译的ABI,需要在build.gradle.kts(:app)中进行配置。找到android下配置添加以下内容:

kotlin 复制代码
defaultConfig {
		//...
    externalNativeBuild {
        //...
        ndk {
            abiFilters += "arm64-v8a"
        }
    }
}

sourceSets {
    getByName("main") {
        jniLibs.srcDirs("src/main/cpp/lib")
    }
}

ndk.abiFilters指定了ndk编译的abi平台,jniLibs指定了需要打包的库文件目录。需要注意的是jniLibs要求路径下是对应abi平台名称的文件夹,文件夹中是对应的库文件。

旧版本Android Studio可能是在build.gradle中配置,基本内容是一样的,不过语法有些不同。

修改完成同步之后,在native-lib.cpp中添加以下内容:

cpp 复制代码
#include <jni.h>
#include <string>
#include <android/log.h>

extern "C" {
#include "libavcodec/avcodec.h"
}

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_androidvlc_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    hello += avcodec_configuration();
    __android_log_print(ANDROID_LOG_INFO, "Test", "test for android log: %s", hello.c_str());
    return env->NewStringUTF(hello.c_str());
}

运行成功在应用界面看到ffmpeg详细的编译信息则说明配置成功,可以在Android Studio中调用ffmpeg库。

相关推荐
Gary Studio1 小时前
Android AIDL HAL工程结构示例
android
y = xⁿ2 小时前
MySQL八股知识合集
android·mysql·adb
andr_gale2 小时前
04_rc文件语法规则
android·framework·aosp
happybasic3 小时前
在CMD下使用FFmpeg将.wav文件转换成指定的格式~
ffmpeg
祖国的好青年3 小时前
VS Code 搭建 React Native 开发环境(Windows 实战指南)
android·windows·react native·react.js
黄林晴4 小时前
警惕!AGP 9.2 别只改版本号,R8 规则与构建链路全线收紧
android·gradle
小米渣的逆袭4 小时前
Android ADB 完全使用指南
android·adb
儿歌八万首4 小时前
Jetpack Compose Canvas 进阶:结合 animateFloatAsState 让自定义图形动起来
android·动画·compose
zhangphil5 小时前
Android Page 3 Flow读sql数据库媒体文件,Kotlin
android·kotlin