Mac环境下使用NDK编译FFmpeg

Mac环境下使用NDK编译FFmpeg

1. 环境和所需的文件

先是环境:

  • FFmpeg:3.3.9
  • NDK:android-ndk-r14b
  • 系统:MacOS 10.15
  • 编译目标:给 Android 用的 ARM 平台

再给出几个下载链接:

不建议使用 AndroidStudio 自带的 ndk-bundle 编译,不建议使用最新版 FFmpeg,不建议使用最新版 NDK,理由不明(笑)。

使用最新的 FFmpeg 4.x,按照网上主流的做法,修改 configure 文件然后新建脚本 build_android.sh 的方法,会报如下错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Makefile:2: config.mak: No such file or directory
Makefile:67: /common.mak: No such file or directory
Makefile:114: /libavutil/Makefile: No such file or directory

......

C compiler test failed.

If you think configure made a mistake, make sure you are using the latest
version from Git. If the latest version fails, report the problem to the
ffmpeg-user@ffmpeg.org mailing list or IRC #ffmpeg on irc.freenode.net.
Include the log file "config.log" produced by configure as this will help
solve the problem.
sed: config.h: No such file or directory
sed: config.h: No such file or directory

......

Makefile:2: config.mak: No such file or directory

......

make: *** No rule to make target `/tests/Makefile'. Stop.

报了一堆 No such file or directory 错误,第一行的 config.mak 也可能是 ffbuild/config.mak,带着错误去搜也搜不到什么头绪,根据报错信息去 FFmpeg 的根目录下或者 ffbuild/ 下也确实找不到所谓的 config.mak,一个解决方案是:

  1. 先不要修改 configure 文件,直接执行:./configure,但是大概率会报这个错误:

    1
    nasm/yasm not found or too old. Use --disable-x86asm for a crippled build.

    只要按照提示加上 --disable-x86asm 参数即可,重新执行完会自动生成缺失的文件。

  2. 再重新执行脚本:sudo ./build_android.sh

但是这么做,如果是最新的 NDK 或者最新的 FFmpeg,执行完倒是不报错,但是并不会编译输出 SO 文件,所以还是老老实实用已有方案的版本吧!


2. 编译SO文件

2.1 配置NDK

首先,NDK 下载好后,放到自定义目录中,需要添加到环境变量中去:

1
2
3
4
5
6
7
8
9
10
# 如果用的是 bash 终端:
vim ~/.bash_profile

# 如果用的是 zsh 终端:
vim ~/.zshrc

# 添加环境变量(按照实际目录):
export NDK_HOME=/Applications/AndroidStudio/android-ndk-r14b/build
......
export PATH=${NDK_HOME}:${PATH}:

保存退出,然后刷新并测试环境变量,执行:ndk-build,如果有类似以下输出则表明配置成功:

1
2
Android NDK: Could not find application project directory !
......

还需要给所有文件加上权限,进入 NDK 的目录并执行:

1
chmod -R 777 ./*

温馨提示:可千万不要把 ./* 给打错了,要是不小心把“.”漏了可就麻烦了(请务必不要尝试)。

2.2 配置FFmpeg

把下载好的 FFmpeg 源码解压到任意目录,同样也按上述操作给所有文件加上权限,接下来再在 FFmpeg 根目录下创建一个脚本(命名随意):build_android.sh,修改内容:

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
#!/bin/bash

# 以实际自定义临时目录为准,必须指定否则报错:Unable to create temporary file in
export TMPDIR=/....../TEMP
# 以 NDK 实际路径为准
NDK=/Applications/Androidstudio/android-ndk-r14b
# 编译目标平台,本例选择最低 API 22 的 ARM 平台,输出 SO 文件放在 Android 项目的 libs/armeabi/ 目录下
# x86 架构则选择 arch-x86
PLATFORM=$NDK/platforms/android-22/arch-arm
# 编译所需工具链,arm-linux-androideabi 对应上面的 ARM 平台,4.9 为版本号,以 NDK 中的实际版本为准
TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64
function build_one
{
./configure \
--prefix=$PREFIX \
--target-os=linux \
--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
--arch=arm \
--sysroot=$PLATFORM \
--extra-cflags="-I$PLATFORM/usr/include" \
--cc=$TOOLCHAIN/bin/arm-linux-androideabi-gcc \
--nm=$TOOLCHAIN/bin/arm-linux-androideabi-nm \
--disable-shared \
--enable-runtime-cpudetect \
--enable-gpl \
--enable-small \
--enable-cross-compile \
--disable-debug \
--enable-static \
--disable-doc \
--disable-asm \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-ffserver \
--disable-postproc \
--disable-avdevice \
--disable-symver \
--disable-stripping \
$ADDITIONAL_CONFIGURE_FLAG
sed -i '' 's/HAVE_LRINT 0/HAVE_LRINT 1/g' config.h
sed -i '' 's/HAVE_LRINTF 0/HAVE_LRINTF 1/g' config.h
sed -i '' 's/HAVE_ROUND 0/HAVE_ROUND 1/g' config.h
sed -i '' 's/HAVE_ROUNDF 0/HAVE_ROUNDF 1/g' config.h
sed -i '' 's/HAVE_TRUNC 0/HAVE_TRUNC 1/g' config.h
sed -i '' 's/HAVE_TRUNCF 0/HAVE_TRUNCF 1/g' config.h
sed -i '' 's/HAVE_CBRT 0/HAVE_CBRT 1/g' config.h
sed -i '' 's/HAVE_RINT 0/HAVE_RINT 1/g' config.h
make clean
make -j4
make install
$TOOLCHAIN/bin/arm-linux-androideabi-ld \
-rpath-link=$PLATFORM/usr/lib \
-L$PLATFORM/usr/lib \
-L$PREFIX/lib \
-soname libffmpeg.so -shared -nostdlib -Bsymbolic --whole-archive --no-undefined -o \
$PREFIX/libffmpeg.so \
libavcodec/libavcodec.a \
libavfilter/libavfilter.a \
libswresample/libswresample.a \
libavformat/libavformat.a \
libavutil/libavutil.a \
libswscale/libswscale.a \
-lc -lm -lz -ldl -llog --dynamic-linker=/system/bin/linker \
$TOOLCHAIN/lib/gcc/arm-linux-androideabi/4.9.x/libgcc.a
}
# 平台类型,可以是 arm 或 armv7-a 或 x86 等
CPU=armv7-a
OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=vfp -marm -march=$CPU "
# 输出 SO 的目录
PREFIX=./android/$CPU
ADDITIONAL_CONFIGURE_FLAG=
build_one

接下来在当前目录执行:

1
2
# 按实际创建的脚本名
./build_android.sh

需要等一段编译时间,但总之是成功编译了,脚本配置中的“编译后 SO 文件输出目录” PREFIX=./android/$CPU-vfp 表示:当前目录/android/平台类型,比如我当前目录为 /Applications/FFmpeg,平台类型是 arm,则输出目录为:/Applications/FFmpeg/android/arm/

编译完可能会报如下错误:

1
2
install: /usr/local/share/man/man1/ffmpeg.1: Permission denied
make: *** [install-man] Error 71

权限错误,也可能是 man3 报的错,简便一点的方法就是直接用 Super User 权限安装:

1
sudo ./build_android.sh

安装完成后,在 FFmpeg 根目录下的 android/armv7-a/ 下即可看到 libffmpeg.so 文件,复制到 Android 项目中再配置 NDK 即可。


参考文献