基于NDK编译Android平台的FFmpeg动态库,这件事情我们早在去年就做过了,详细的可以参考我的博文:
https://blog.csdn.net/ericbar/article/details/76602720,
但是FFmpeg的基线版本以及ndk编译链是不断更新的,近期我打算基于最新的FFmpeg版本和ndk版本编译libffmpeg.so时,又遇到了麻烦,各种编译问题。通过这两天的努力,终于编译成功了。于是,打算另起一篇新的文章,将经验和大家分享一下。
从ffmpeg.org官网获知,现在最新的稳定版本分别是FFmpeg 3.4.2 “Cantor”和FFmpeg 4.0 “Wu”,分别下载源码ffmpeg-3.4.2.tar.xz和ffmpeg-4.0.tar.xz,并解压缩到自己的工作目录:
tar xvf ffmpeg-3.4.2.tar.xz
tar xvf ffmpeg-4.0.tar.xz
参考前述博文,在ndk的官方网站(可能需要科学上网)https://developer.android.com/ndk/downloads/index.html 获知最新的ndk稳定版本是r16b,于是下载 android-ndk-r16b-linux-x86_64.zip 并解压,放在 /opt 目录下。
参考前述博文,将config.sh的SYSROOT,LIBPATH以及TOOLCHAIN换成对应的r16b版本,如下:
SYSROOT=/opt/android-ndk-r16b/platforms/android-19/arch-arm
LIBPATH=/opt/android-ndk-r16b/platforms/android-19/arch-arm/usr/lib/
TOOLCHAIN=/opt/android-ndk-r16b/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/
执行后报错,
ffmpeg@ubuntu:~/work/android/ffmpeg-3.4.2$ ./config.sh
please wait...
preparing to configure...
/opt/android-ndk-r16b/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc is unable to create an executable file.
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
[email protected] mailing list or IRC #ffmpeg on irc.freenode.net.
Include the log file "ffbuild/config.log" produced by configure as this will help
solve the problem.
分析编译链的代码结构,发现android-ndk-r16b\platforms\android-19\arch-arm\usr下只有lib文件夹,没有include文件夹,相关include头文件则位于android-ndk-r16b\sysroot\usr\include下,通过尝试修改
–sysroot=$SYSROOT指向lib路径,并添加配置选项–sysinclude指向include文件夹,configure仍然失败。
通过搜索大量的网站,有建议说需要采用standalone的工具链来进行编译,通过尝试获得成功,下面是总结的步骤:
1. 选择make_standalone_toolchain的方式来制作编译链;
在刚才下载的android-ndk-r16b\build\tools目录下,有一个Python脚本make_standalone_toolchain.py,通过它可以自动生成我们需要的编译链环境;在此目录下,执行如下命令:
python make_standalone_toolchain.py --api 19 --install-dir /home/ffmpeg/work/toolchain/android/linux-x86_64/ndk-r16/android-19/arm --arch arm --stl libc++ --force
其中,/home/ffmpeg/work/toolchain/android/linux-x86_64/ndk-r16/android-19/arm是我们打算把工具链安装的路径,你可以根据自己需求自定义。另外,我们仍然选择api 19作为我们的编译版本,等待一段时间后,会自动在这个目录下生成完整的编译链,如下图所示:
2. 有了正确的编译链后,只需要修改对应的config.sh和make.sh即可,相关目录指向调整如下(参考我的编译链路径):
SYSROOT=/home/ffmpeg/work/toolchain/android/linux-x86_64/ndk-r16/android-19/arm/sysroot
LIBPATH=/home/ffmpeg/work/toolchain/android/linux-x86_64/ndk-r16/android-19/arm/sysroot/usr/lib
TOOLCHAIN=/home/ffmpeg/work/toolchain/android/linux-x86_64/ndk-r16/android-19/arm
3.配置选项调整
从FFmpeg 3.4.2开始,配置选项里的–disable-sdl 需要修改成–disable-sdl2 。对于FFmpeg 4.0,如下几个选项需要去掉,具体变成什么配置,这次暂时不做研究,我只是简单的做了屏蔽处理:
--disable-ffplay \
--disable-ffprobe \
--disable-ffserver \
后期有时间再研究相关配置。
4. 编译问题
事情本来到这里应该很顺利了,但是在编译源码时,遇到莫名其妙的错误:
CC libavcodec/hevc_filter.o
CC libavcodec/hevc_mp4toannexb_bsf.o
CC libavcodec/hevc_mvs.o
libavcodec/hevc_mvs.c: In function 'derive_spatial_merge_candidates':
libavcodec/hevc_mvs.c:208:15: error: 'y0000000' undeclared (first use in this function)
((y ## v) >> s->ps.sps->log2_min_pu_size))
^
libavcodec/hevc_mvs.c:204:14: note: in definition of macro 'TAB_MVF'
tab_mvf[(y) * min_pu_width + x]
^
libavcodec/hevc_mvs.c:274:16: note: in expansion of macro 'TAB_MVF_PU'
(cand && !(TAB_MVF_PU(v).pred_flag == PF_INTRA))
^
libavcodec/hevc_mvs.c:368:23: note: in expansion of macro 'AVAILABLE'
is_available_b0 = AVAILABLE(cand_up_right, B0) &&
^
libavcodec/hevc_mvs.c:208:15: note: each undeclared identifier is reported only once for each function it appears in
((y ## v) >> s->ps.sps->log2_min_pu_size))
^
libavcodec/hevc_mvs.c:204:14: note: in definition of macro 'TAB_MVF'
tab_mvf[(y) * min_pu_width + x]
^
libavcodec/hevc_mvs.c:274:16: note: in expansion of macro 'TAB_MVF_PU'
(cand && !(TAB_MVF_PU(v).pred_flag == PF_INTRA))
^
libavcodec/hevc_mvs.c:368:23: note: in expansion of macro 'AVAILABLE'
is_available_b0 = AVAILABLE(cand_up_right, B0) &&
^
libavcodec/hevc_mvs.c:207:15: error: 'x0000000' undeclared (first use in this function)
TAB_MVF(((x ## v) >> s->ps.sps->log2_min_pu_size), \
或者是下面这种错误,
libavcodec/aaccoder.c: In function 'search_for_ms':
libavcodec/aaccoder.c:803:25: error: expected identifier or '(' before numeric constant
libavcodec/aaccoder.c:865:28: error: lvalue required as left operand of assignment
libavcodec/aaccoder.c:866:25: error: 'B1' undeclared (first use in this function)
libavcodec/aaccoder.c:866:25: note: each undeclared identifier is reported only once for each function it appears in
ffbuild/common.mak:60: recipe for target 'libavcodec/aaccoder.o' failed
make: *** [libavcodec/aaccoder.o] Error 1
在网上找到大神的解答(http://alientechlab.com/how-to-build-ffmpeg-for-android/),原来是r16b编译链的termbits.h(实际是asm-generic/termbits.h)宏定义了B0,
#define B0 0000000
导致编译链宏定义和局部变量冲突,好大的坑!在编译链头文件里,把这个宏定义语句屏蔽即可。
最后,你应该能顺利的编译出你想要的libffmpeg.so库了。
开源项目给我们带来便利的同时,也给我们带来了不少的坑,填坑是依赖开源项目开发必备的基本技能,但是,只要坚持寻找问题的各种解决办法,总会有柳暗花明的时刻到来。