编译针对于Android平台的ffmpeg 4.1版本(最新版)
前言
如题,本文主要内容是总结在编译FFmpeg过程中遇到一些坑.这里要注意一点是使用不同版本的NDK会遇到对应的不同的坑,这点要注意.本文重点讲编译过程,遇到的坑.关于如何编译,也会略带说点,但是不会说的多仔细,网上关于如何编译的文章,说的也挺多的,流程都差不多,区别也就是configure的配置会因需求而变动等.
编译流程
先大致说下,编译流程,这里为了方便讲解不考虑交叉编译.
-
进入FFmpeg官网,或者github上面把源码下载到本地.
下载地址:
(1) ffmpeg官网下载地址
(2)github上FFmpeg的下载地址 -
准备好NDK
去官网下载好ndk,版本的话,推荐r17以下(包括r17c),为什么不用最新版本的r19b呢?这个问题稍后再回答.
Android NDK 下载地址 -
编写build for Android 脚本
进去FFmpeg目录内,会找到一个configure脚本文件,执行 ./configure --help 命令,你会看到一大串的帮助文档,慢慢看,根据你的项目需要来对编译的FFmpeg进行enable or disable.
这里要说一点的,如果你想编译FFmpeg动态库的话,就是.so库,需要对configure脚本进行修改.
修改前:
SLIBNAME_WITH_MAJOR='$(SLIBNAME).$(LIBMAJOR)'
LIB_INSTALL_EXTRA_CMD='$$(RANLIB) "$(LIBDIR)/$(LIBNAME)"'
SLIB_INSTALL_NAME='$(SLIBNAME_WITH_VERSION)'
SLIB_INSTALL_LINKS='$(SLIBNAME_WITH_MAJOR) $(SLIBNAME)'
修改后:
SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)'
LIB_INSTALL_EXTRA_CMD='$$(RANLIB) "$(LIBDIR)/$(LIBNAME)"'
SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'
SLIB_INSTALL_LINKS='$(SLIBNAME)'
修改的目的是为了生成的动态库命名方式可以被Android平台理解,识别.
如果要是编译FFmpeg静态库的话,那就没必要进行上面的修改了.
下面我们在与configure脚本同级目录的地方,创建一个build_android.sh脚本来帮助编译FFmpeg. build_android.sh内容如下:
#!/bin/bash
set -x
API=14
NDK=/NDK安装位置/android-ndk-r15c
SYSROOT=$NDK/platforms/android-$API/arch-arm/
TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64
function function_one
{
./configure \
--prefix=$PREFIX \
--disable-shared \
--enable-static \
--disable-doc \
--disable-ffplay \
--disable-ffprobe \
--disable-symver \
--disable-ffmpeg \
--cc=$TOOLCHAIN/bin/arm-linux-androideabi-gcc \
--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
--target-os=linux \
--arch=arm \
--enable-cross-compile \
--sysroot=$SYSROOT \
--extra-cflags="-fPIC -DANDROID -D__thumb__ -mthumb -Wfatal-errors -Wno-deprecated -mfloat-abi=softfp -marm -march=armv7-a" \
--enable-neon
make clean all
make
make install
}
CPU=armv7-a
PREFIX=$(pwd)/android/$CPU
function_one
现在libavresample 和 libpostproc 默认不启用,已经deprecated,如果想编译这两个库需要设置:
–enable-avresample
–enable-gpl
–enable-nonfree
–enable-postproc
上面的编译脚本编译出来的FFmpeg库还是很完整,挺大的,我们现实项目中一般不需要这么功能齐全,只需要使用其中的部分功能,这里可以根据需要,自行对FFmpeg裁剪,自定义.具体可自定义内容,执行 ./configure.sh --help命令自行查看研究.这里就不细说了.
理想状态下,经过上面的步骤后,会成功的生成android/armv7-a/ 文件,里面会有lib,include目录,lib文件夹里面就是需要的编译好的,适用于Android平台的库文件.
但是往往事实没那么顺利.上面也说了,理想状态下…接下来,坑来了…
遇到的坑
(一)
上面演示的build_android.sh 脚本中使用的ndk版本是r15c,那在FFmpeg 4.1版本中,你应该会遇到这个error:
libavformat/udp.c: In function 'udp_set_multicast_sources':
libavformat/udp.c:290:28: error: request for member 's_addr' in something not a structure or union
网上很少关于这个错误的描述,官方的回复也没看出来啥子有用的价值.
https://trac.ffmpeg.org/ticket/7741
解决方法:
有两种解决方案
1.ndk版本升到r17c
2.如果不想升ndk版本的,那就修改libavformat/udp.c 文件,把报错的相关代码注释掉就好.前提是你的项目中用不到这块功能.
在低版本的ndk应该都会有这个问题,我有试过r9b版本的,也有这个问题.
(二)
如果使用ndk版本是r17c的话,那上面的问题就不会遇到,但是会遇到新的问题.
CC libavdevice/alldevices.o
In file included from ./libavformat/internal.h:24:0,
from libavdevice/alldevices.c:23:
/opt/andorid_sdk/adt-bundle-linux-x86-20131030/android-ndk-r17c/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/lib/gcc/arm-linux-androideabi/4.9.x/include/stdint.h:9:26: fatal error: stdint.h: No such file or directory
出现这个错误是因为NDK r17c版本将头文件和库文件进行了分离,我们指定的sysroot文件夹下只有库文件,而头文件放在了NDK目录下的sysroot内.
解决方法: 需在–extra-cflags中添加 “-isysroot $NDK/sysroot”
还有有关汇编的头文件也进行了分离.
解决方法:根据目标平台进行指定 “-I$NDK/sysroot/usr/include/arm-linux-androideabi”,将 “arm-linux-androideabi” 改为需要的平台就可以
修改之后的build_android.sh :
#!/bin/bash
set -x
API=14
NDK=/opt/andorid_sdk/adt-bundle-linux-x86-20131030/android-ndk-r17c
SYSROOT=$NDK/platforms/android-$API/arch-arm/
TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64
function function_one
{
./configure \
--prefix=$PREFIX \
--disable-shared \
--enable-static \
--disable-doc \
--disable-ffplay \
--disable-ffprobe \
--disable-symver \
--disable-ffmpeg \
--cc=$TOOLCHAIN/bin/arm-linux-androideabi-gcc \
--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
--target-os=linux \
--arch=arm \
--enable-cross-compile \
--sysroot=$SYSROOT \
--extra-cflags="-I$NDK/sysroot/usr/include/arm-linux-androideabi -isysroot $NDK/sysroot -fPIC -DANDROID -D__thumb__ -mthumb -Wfatal-errors -Wno-deprecated -mfloat-abi=softfp -marm -march=armv7-a" \
--enable-neon
make clean all
make
make install
}
CPU=armv7-a
PREFIX=$(pwd)/android/$CPU
function_one
(三)
libavcodec/aaccoder.c: In function 'search_for_ms':
libavcodec/aaccoder.c:803:25: error: expected identifier or '(' before numeric constant
int B0 = 0, B1 = 0;
^
这是由于定义冲突导致的一个error,和ndk
版本有关,我在使用r15c和r9b版本的时候都没有遇到这个问题.
解决方法:修改libavcodec/aaccoder.c 文件 B0改成b0(ps:就是把int型变量名改一下,避免冲突,名字随便起)
(四)
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文件的变量B0改成b0,xB0改成xb0,yB0改成yb0
(五)
libavcodec/opus_pvq.c: In function 'quant_band_template':
libavcodec/opus_pvq.c:498:9: error: expected identifier or '(' before numeric constant
int B0 = blocks;
^
解决方法:将libavcodec/opus_pvq.c文件的变量B0改成b0
结语
除了上面的问题外,中间也遇到过configure.sh脚本没执行成功的问题,原因大都是空格键,一些久的参数过时了,等问题.比如-ffserver这个参数已经不用了.
上面提到说推荐ndk版本是r17c以下的,是因为我们的build_android.sh,编译脚本用用的编译工具是gcc,但是ndk r17c以后的版本,把gcc给移除了.所以如果现在用最新的版本r19b的话,会出现关于gcc的error.考虑更换编译工具不用gcc,应该就能解决问题,但是不知道又会不会出现其他的问题…这个还需要研究,后续有发现,还会持续更新补充.
参考文章
http://alientechlab.com/how-to-build-ffmpeg-for-android/
https://medium.com/@karthikcodes1999/cross-compiling-ffmpeg-4-0-for-android-b988326f16f2