FFmpeg 7.1.1 | 编译 Android 动态链接库

1,425 阅读4分钟

时光飞逝,距离 《NDK 是什么 | FFmpeg 5.0 编译 so 库》 一文已经过去快三年了,曾经最新的 FFmpeg 5.0 ,如今也来到了 FFmpeg 7.1.1。本文就来通过 NDK 编译一下 Android 的 ffmpeg 动态链接库。关于 NDK 是什么可以详见上篇文章,这里不再赘述。


1. 准备编译环境

如果把 ffmpeg 源码编译动态链接库视为一道菜,那么:

厨房: 我用的是 Windows 的 Linux 子系统 WSL2

厨具 : 用的 ndk 版本为 26.1.10909125

食材: 在 https://www.ffmpeg.org/download.html 下载源码


2. 开始烹饪

首先将源码压缩包通过命令解压:

tar -xvf ffmpeg-7.1.1.tar.xz


然后进入 ffmpeg-7.1.1 目录,创建 build_android.sh 编译脚本。这里只编译了 arm64 的架构

cd ffmpeg-7.1.1
vim build_android.sh

#!/bin/bash

# 配置路径
NDK_PATH=/home/toly/SDK/AndroidSdk/ndk/26.1.10909125
TOOLCHAIN=$NDK_PATH/toolchains/llvm/prebuilt/linux-x86_64  # 根据系统调整

# 配置编译器
API_LEVEL=24
TARGET=aarch64-linux-android
CC=$TOOLCHAIN/bin/${TARGET}${API_LEVEL}-clang
CXX=$TOOLCHAIN/bin/${TARGET}${API_LEVEL}-clang++

# 设置 arm64 编译参数
ARCH=arm64  # 可选:arm, arm64, x86, x86_64
OUTPUT_DIR=$(pwd)/android/$ARCH
CPU=armv8-a


# 执行 FFmpeg 配置
./configure \
  --target-os=android \
  --prefix=$OUTPUT_DIR \
  --arch=aarch64 \
  --cpu=$CPU \
  --cc=$CC \
  --cxx=$CXX \
  --strip=$TOOLCHAIN/bin/llvm-strip \
  --enable-cross-compile \
  --sysroot=$TOOLCHAIN/sysroot \
  --enable-shared \
  --disable-static \
  --disable-doc \
  --disable-programs \
  --disable-avdevice \
  --disable-avfilter \
  --disable-postproc \
  --disable-symver \
  --extra-cflags="-O3 -fPIC"

# 清理并编译
make clean
make -j$(nproc)
make install

最后,给 build_android.sh 脚本可执行权限,运行即可。完成后,在 android 文件夹下会有我们需要的头文件和 so 链接库文件:

chmod +x build_android.sh
./build_android.sh


3. 盛菜上桌

接下来,创建一个 Native C++ 的 Android 项目,验证编译的结果是否正确。

把生面产出的文件,放下项目的对应位置。由于只打包了 arm64 , 可以在 app/build.gradle 中的 defaultConfig 里指定 ndk 支持的架构:

把默认的 hello 字符串改为 av_version_info 可以查看 FFmpeg 版本号; avcodec_configuration 可以查看编译时的配置信息:

#include <jni.h>
#include <string>
extern "C"{
#include <libavcodec/avcodec.h>
}
extern "C" JNIEXPORT jstring JNICALL
Java_com_toly1994_toly_1ffmpeg_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    
//    return env->NewStringUTF( av_version_info()); // 查看版本号
    return env->NewStringUTF(avcodec_configuration()); // 查看编译配置信息
}
版本编译信息

另外,CMakeLists.txt 中的内容和上一篇一样; 《Android NDK 开发 | CMake 使用手册 - 初见篇》 一文介绍过 CMake 的使用,后续有空会继续补充。

cmake_minimum_required(VERSION 3.22.1)

project("toly_ffmpeg")

#引入头文件
include_directories(includes)

# 定义当前 so 库 - 在 java 代码中加载
add_library(toly_ffmpeg SHARED native-lib.cpp)

# 添加 ffmpeg 的 avcodec、swresample、avutil 模块 start======
set(ABI_DIR ${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI})

add_library(avcodec SHARED IMPORTED)
set_target_properties(avcodec PROPERTIES IMPORTED_LOCATION ${ABI_DIR}/libavcodec.so)

add_library(swresample SHARED IMPORTED)
set_target_properties(swresample PROPERTIES IMPORTED_LOCATION ${ABI_DIR}/libswresample.so)

add_library(avutil SHARED IMPORTED)
set_target_properties(avutil PROPERTIES IMPORTED_LOCATION ${ABI_DIR}/libavutil.so)
# 添加 ffmpeg 的 avcodec、swresample、avutil 模块 end======

find_library(log-lib log)
target_link_libraries(
        toly_ffmpeg
        avcodec
        swresample
        avutil
        ${log-lib})

整体来说,这道菜做的还是比较顺利的。


4. 编译脚本分析

编译脚本中最核心的是执行源码中的 configure 文件,这是 FFmpeg 的配置脚本。 前面的代码都是为了执行它而准备的参数:

NDK_PATH=/home/toly/SDK/AndroidSdk/ndk/26.1.10909125

指定本地 NDK 路径,根据 FFmpeg 交叉编译需要。NDK 可以通过 Android SDK 的管理工具下载:

TOOLCHAIN=$NDK_PATH/toolchains/llvm/prebuilt/linux-x86_64

TOOLCHAIN: 指定 LLVM 工具链路径,根据系统编译环境系统架构选择 linux-x86_64。我们可以进入 NDK 对应的文件夹中,看到它:


API_LEVEL=24
TARGET=aarch64-linux-android  
CC=$TOOLCHAIN/bin/${TARGET}${API_LEVEL}-clang    
CXX=$TOOLCHAIN/bin/${TARGET}${API_LEVEL}-clang++  

CCCXX: 分别为 C 和 C++ 编译器,后缀 ${API_LEVEL}-clang 是 LLVM 编译器要求的命名格式。这里对应的是这两个文件:


ARCH=arm64  # 可选:arm, arm64, x86, x86_64
CPU=armv8-a
OUTPUT_DIR=$(pwd)/android/$ARCH
  • ARCH=arm64: 编译架构,这里是 arm64,对应的目标是 aarch64-linux-android
  • CPU=armv8-a: 目标 CPU 架构,这里编译 armv8-a
  • OUTPUT_DIR: 编译产物输出目录,当前路径下的 android/arm64

然后是执行 configure 脚本:

# 执行 FFmpeg 配置
./configure \
  --target-os=android \
  --prefix=$OUTPUT_DIR \
  --arch=aarch64 \
  --cpu=$CPU \
  --cc=$CC \
  --cxx=$CXX \
  --strip=$TOOLCHAIN/bin/llvm-strip \
  --enable-cross-compile \
  --sysroot=$TOOLCHAIN/sysroot \
  --enable-shared \
  --disable-static \
  --disable-doc \
  --disable-programs \
  --disable-avdevice \
  --disable-avfilter \
  --disable-postproc \
  --disable-symver \
  --extra-cflags="-O3 -fPIC"
  • --target-os=android:目标平台为 Android
  • --prefix:安装路径(输出路径)
  • --arch=aarch64:设置目标架构
  • --cpu=armv8-a:设置目标 CPU 架构,arm64 架构常见选项
  • --cc / --cxx:使用 clang 交叉编译器
  • --strip:使用 LLVM 工具链提供的 strip 工具去除符号
  • --enable-cross-compile:开启交叉编译
  • --sysroot:NDK 的 sysroot,指定头文件与库路径
  • --enable-shared / --disable-static:只生成 .so 动态库
  • --disable-doc / --disable-programs:不构建文档与工具程序(如 ffmpeg 命令行工具)
  • --disable-avdevice --disable-avfilter --disable-postproc:裁剪不必要的功能模块,减小体积
  • --disable-symver:禁用 symbol versioning(可能为兼容 Android)
  • --extra-cflags:添加优化选项 -O3,开启 -fPIC(位置无关代码,构建动态库必须)

5. 尾声

到这里,我们就完成了 FFmpeg7.1.1 最新版本,编译为 Android 的动态链接库,完成了万里长征的第一步。后面有机会可能会研究一下 FFmpeg,希望下一篇不会是下一个三年,再编译一个 FFmpeg 9.0, 哈哈, 敬请期待 ~

更多文章和视频知识资讯,大家可以关注我的公众号、掘金和 B 站 。