开发环境搭建
下载最新版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/
-
准备 FFmpeg 源码
bashgit clone https://git.ffmpeg.org/ffmpeg.git ffmpeg cd ffmpeg
-
创建**
CMakeLists.txt
**在 FFmpeg 根目录新建
CMakeLists.txt
,内容如下:makefilecmake_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 )
-
执行 CMake 编译
bashmkdir 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
-
输出结果
编译后的头文件、动态库(
.so
文件)会生成在build
目录的ffmpeg_install
下的include
和lib
目录中。
./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库。