此前项目一直使用的 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 大小也符合要求。

编译环境

编译脚本

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#!/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 进行关闭或者打开。以解码为例:

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

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

1
2
3
4
5
6
7
8
9
10
11
--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)

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

配置减少包大小

1
--enable-small           optimize for size instead of speed

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

1
2
3
4
5
#if CONFIG_SMALL
#define CRC_TABLE_SIZE 257
#else
#define CRC_TABLE_SIZE 1024
#endif

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

1
--disable-protocols      disable all protocols

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

1
2
3
4
--disable-programs       do not build command line programs
--disable-ffmpeg disable ffmpeg build
--disable-ffplay disable ffplay build
--disable-ffprobe disable ffprobe build

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

1
2
3
4
5
--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
2
3
4
5
6
7
8
//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 的错,按照文章所说即可