此前项目一直使用的 FFmpeg.so 是我从其他团队项目中直接复制过来的,但原来的项目团队不再维护这个库,其中 x264 模块由于一些版权问题需要剔除,所以需要自己重新编译。在编译的过程中踩了很多坑,以及编译 congfigure 有太多的配置,如何减少整体编译出来的大小也是需要花点精力的,本文主要记录编译流程以及相关配置介绍。

编译介绍

自己编译主要参考《一键编译32_64位FFmpeg.4.2.2》,最开始的时候自己一直在 Mac M1 上编译,各种流程也是一比一复刻,但是仍然会有各种问题出现,最常见的就是:

aarch64-linux-android21-clang is unable to create an executable file.
C compiler test failed.

我反复检查了自己的 NDK 的配置,确保是正确的,文章也有提及处理方式,但是尝试下来都无效,在网上搜了一大篇解决方式,也都无效。不过我看他们很多都是用 Linux 系统进行的编译,遂改为使用 Linux 编译,再重新尝试,似乎没有那些个奇奇怪怪的错误了,也打出了最终的包,最后的 so 大小也符合要求。

编译环境

编译脚本

这是我的一份编译脚本,我的需求是进行本地视频抽帧,所以不需要像滤镜、编码、音频相关的配置,只需要视频解码相关的配置,具体配置在下一节有讲解。

#!/bin/bash
export NDK=.../android-ndk-r20b
TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64

function build_android
{

./configure \
--prefix=$PREFIX \
--enable-neon \
--disable-x86asm \
--disable-hwaccels \
--disable-gpl \
--disable-nonfree \
--disable-version3 \
--disable-postproc \
--disable-bsfs \
--disable-protocols \
--enable-protocol=file \
--disable-indevs \
--disable-outdevs \
--disable-debug \
--enable-small \
--enable-jni \
--disable-mediacodec \
--disable-decoder=h264_mediacodec \
--enable-swscale \
--enable-static \
--disable-shared \
--disable-filters \
--disable-avfilter \
--disable-encoders \
--disable-muxers \
--disable-demuxers \
--enable-demuxer=avi \
--enable-demuxer=flv \
--enable-demuxer=h261 \
--enable-demuxer=h263 \
--enable-demuxer=h264 \
--enable-demuxer=hevc \
--enable-demuxer=mov \
--enable-demuxer=m4v \
--disable-decoders \
--enable-decoder=h263 \
--enable-decoder=h263i \
--enable-decoder=h263p \
--enable-decoder=h264 \
--enable-decoder=hevc \
--enable-decoder=flv \
--enable-decoder=mpeg4 \
--disable-parsers \
--enable-parser=h264 \
--enable-parser=h261 \
--enable-parser=h263 \
--enable-parser=mpeg4video \
--enable-parser=mpegvideo \
--disable-htmlpages \
--disable-manpages \
--disable-podpages \
--disable-txtpages \
--disable-vaapi \
--disable-v4l2-m2m \
--disable-nvdec \
--disable-nvenc \
--disable-ffnvcodec \
--disable-dxva2 \
--disable-d3d11va \
--disable-cuvid \
--disable-cuda-llvm \
--disable-cuda-nvcc \
--disable-audiotoolbox \
--disable-amf \
--disable-iconv \
--disable-libxcb \
--disable-libxcb-shm \
--disable-libxcb-xfixes \
--disable-libxcb-shape \
--disable-lzma \
--disable-sdl2 \
--disable-securetransport \
--disable-xlib \
--disable-zlib \
--disable-programs \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-avdevice \
--disable-symver \
--cross-prefix=$CROSS_PREFIX \
--target-os=android \
--arch=$ARCH \
--cpu=$CPU \
--cc=$CC \
--cxx=$CXX \
--enable-cross-compile \
--sysroot=$SYSROOT \
--extra-cflags="-Os -fpic $OPTIMIZE_CFLAGS" \
--extra-ldflags="$ADDI_LDFLAGS"

make clean
make -j16
make install

}

#arm64-v8a
ARCH=arm64
CPU=armv8-a
API=21
CC=$TOOLCHAIN/bin/aarch64-linux-android$API-clang
CXX=$TOOLCHAIN/bin/aarch64-linux-android$API-clang++
SYSROOT=$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot
CROSS_PREFIX=$TOOLCHAIN/bin/aarch64-linux-android-
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS="-march=$CPU"

build_android

#armv7-a
ARCH=arm
CPU=armv7-a
API=21
CC=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang
CXX=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang++
SYSROOT=$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot
CROSS_PREFIX=$TOOLCHAIN/bin/arm-linux-androideabi-
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=vfp -marm -march=$CPU "

build_android

配置介绍

通过执行 ./configure --help 能得到所有的配置选项,整个配置也非常好理解,通过 list-xxx 可以知道有哪些子选项,然后再通过 --disable-xxx,或者 --enable-xxx 进行关闭或者打开。以解码为例:

--disable-decoders       # 先关闭所有的解码器
--enable-decoder=flv # 然后只打开flv、mpeg4 的支持
--enable-decoder=mpeg4

对于--enable-decoder=xxx中的xxx可以通过 ./configure --list-decoders 进行查看,同理 encoders demuxers muxers 等都是类似的处理,通过 --help 可以通过 list--xxx 查看不同功能的可以支持的配置,主要有以下:

--list-decoders          show all available decoders
--list-encoders show all available encoders
--list-hwaccels show all available hardware accelerators
--list-demuxers show all available demuxers
--list-muxers show all available muxers
--list-parsers show all available parsers
--list-protocols show all available protocols
--list-bsfs show all available bitstream filters
--list-indevs show all available input devices
--list-outdevs show all available output devices
--list-filters show all available filters

其他的配置就是一些实际性的开关配置,列一些常用的配置:

配置产物为静态库(.a)或者动态库(.so)

--enable-static         do not build static libraries [no]
--enable-shared build shared libraries [no]

配置减少包大小

--enable-small           optimize for size instead of speed

--enable-small 的配置项,其实是在config.h里声称了CONFIG_SMALL选项,然后代码内根据CONFIG_SMALL做了一些调整,比如某些string类型就被省掉了,还有一些内置生成的table, 体积也被裁减掉了,用速度换体积。比如这里:

#if CONFIG_SMALL
#define CRC_TABLE_SIZE 257
#else
#define CRC_TABLE_SIZE 1024
#endif

配置FFmpeg协议,由于我们使用本地文件,需要再加一个: --enable-protocol=file,要不然解码会报协议相关错误

--disable-protocols      disable all protocols

我们只需要在代码中使用 FFmpeg,所以直接禁用命令行工具

--disable-programs       do not build command line programs
--disable-ffmpeg disable ffmpeg build
--disable-ffplay disable ffplay build
--disable-ffprobe disable ffprobe build

还有几个比较重要的就是,主要是

--disable-avdevice       disable libavdevice build
--disable-swresample disable libswresample build
--disable-swscale disable libswscale build
--disable-postproc disable libpostproc build
--disable-avfilter disable libavfilter build

编译遇见的坑

1、aarch64-linux-android21-clang is unable to create an executable file.
C compiler test failed.

这个问题是困扰我最久的,按照解决方法:
原因 1: FFmpeg 4.2.2 版本默认使用了 clang 进行编译
解决:

//1\. 修改 configure 文件
vim configure
//2\. 把 默认的 clang 修改为 gcc
if test "$target_os" = android; then
# cc_default="clang"
cc_default="gcc"
fi

原因2,检查路径是否正确,主要是 NDK 的位置,以及不同 NDK 相关库可能存在一定的丢失。

这个问题我在 macOS 上未解决,换用 CentOS 没有出现过这个编译问题

2、 编译包大小一直不变

最开始我正常编译的时候发现怎么改配置,最后的包大小都没有发生变化,但是命令行里面各种流程又是在走,最终也有产物。这里一定要关注在执行了编译脚本之后,查看最开始的日志,看看具体是一些什么错,这里日志会刷得很快,如果包大小一直没有发生变化的话,可以执行完之后快速停止,看看是什么错。一般就是C compiler test failed. 或者找不到你的配置,改对即可。正常编译,会在开始后列出你的编译配置。

3、x86asm 相关的问题

编译的时候遇到一些 x86asm 的错,按照文章所说即可