这篇我们来学习实践ffmpeg的交叉编译,其中会涉及到
ffmpeg的版本
、
NDK的版本
、
编译脚本的编写
、
Gradler ABI处理
以及
CMakeLists.txt的针对不同ndk版本脚步的编写
在交叉编译的时候由于平台差异性大,需要工具来解决这一问题,就出现了各种工具链,即Toolchains。而NDK提供了交叉编译的一整套工具的集合,不同版本和配置会有不同差异,很容易就会出现各种问题,是一个不断折腾的过程,),本文记录学习实践的过程,
ffmpeg3.3.9+ndk16
,以及
ffmpeg4.3.1或者ffmpeg4.2.4+ndk21
欢迎交流讨论。
一、配置安装android交叉编译工具链
下载NDK
NDK有很多版本 14,16,21等等,我们该选择什么版本比较好呐?
现在一般都选用16以上的,16时编译工具链做了不少变化。而不同的ffmpeg版本支持的NDK版本也不同,比如,ffmpeg3.x的版本使用android-ndk-r16b,但是ffmpeg4.x的版本使用16就编译不过,这里选用的是android-ndk-r21e.
配置NDK环境
android-ndk-r21e
配置环境变量
vim ~/.bash_profile
export NDK_PATH=/Users/xxx/tools/android-ndk-r21e
export PATH=
P
A
T
H
:
NDK_PATH
编辑保存好之后执行下下面语句使之生效
. ~/.bash_profile
安装android交叉编译工具链(也可省略)
/Users/xxx/tools/android-ndk-r 21e/build/tools路径下有make-standalone-toolchain.sh和make_standalone_toolchain.py,我们使用py版本来配置android交叉编译工具链
用到make_standalone_toolchain.py
先创建交叉编译工具链输出的路径
/Users/xxx/tools/android-ndk-r21e/android-toolchains/android-19
mkdir android-toolchains
cd android-toolchains
mkdir android-19
cd android-19
with open(src, 'rb') as fsrc:
到android-ndk-r16b/build/tools
sudo ./make_standalone_toolchain.py --arch arm --api 19 --install-dir ../../android-toolchains/android-19/arm-arch
这个步骤相当于把配置的arch和api等相关信息在指定文件夹下面做个copy(个人理解),方便后续编写ffmpeg编译脚本时用指定toolchain。
之所以说这个步骤可以省略,是因为,也可以直接在后续编写ffmpeg编译脚本中指定不同ABI和API对应的toolchain路径。
二、手写FFmpeg编译脚本 进行编译(针对ffmpeg3.x和ffmpeg4.x版本)
下载ffmpeg
不同的ffmpeg版本使用编译时需要的ndk版本也会有不同,如果在上一步你下载配置的NDK是16,则下载3.x的ffmpeg。如果NDK是21,则下载4.x的ffmpeg。
这个编译的过程折腾了自己两三天的时间,为了搞清楚原因,避免后续踩坑,针对ffmpeg3.x和ffmpeg4.x都进行记录。
我们先来看下编译第三方库的一般通用步骤
看READMME.md
编译项目需要Makefile,如果有尝试使用make编译,如果没有,需要写makefile或者cmake构建
如果报错需要解决,一般都是Makefile的一些配置文件没有生成,运行下configure
生成配置文件后,再次运行make,但是编译后的文件(elf,so,a)只能在当前系统下运行。
如果需要需要跑到android或者ios需要交叉编译
需要给configure传一些脚本编译参数(关键!)
不要去copy,一定要手写,原因有三:
通过手写,搞清楚每个配置的含义
避免路径不同导致编译失败。
避免TAB和空格的转换导致编译失败(这个很坑!!)
下面我们分别编写下ffmpeg3.x和ffmpeg4.x对应的配置编译脚本。
2.1 针对ffmpeg3.x的编译脚本**
ffmpeg3.x_build.sh
ARCH=arm
CPU=armv7-a
PREFIX = $(pwd)/android
ANDROID_TOOLCHAINS_PATH=$NDK_PATH/android-toolchains/android-19/arch-arm
CROSS_PREFIX=$ANDROID_TOOLCHAINS_PATH/bin/arm-linux-androideabi-
SYSROOT=$NDROID_TOOLCHAINS_PATH/sysroot
echo "start build ffmpeg for $ARCH"
./configure --prefix =${PREFIX} \
--disable-static \
--enable-shared \
--enable-samall \
--disable-programs \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-doc \
--disable-ffserver \
--arch=$ARCH \
指定cpu类型
--cpu=$CPU \
--cross-prefix=${CROSS_PREFIX} \
--enable-crosss-compile \
--target-os=linux \
--sysroot=$SYSROOT \
--extra-cflags="-Os -fpic" \
make clean
make install
echo "end build ffmpeg for $ARCH"
编译生成的so的名称版本号在后面,这不符合在android中使用so的命名规范,为此修改configure脚本,替换以下四行内容,重新编译生成对应的so。
修改生成so的文件名称,andorid方便使用
在configure文件中,搜索build set找到对应的位置
SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)'
LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"'
SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'
SLIB_INSTALL_LINKS='$(SLIBNAME)'
2.2 针对ffmpeg4.x的版本
API=21
TOOLCHAIN=$NDK_PATH/toolchains/llvm/prebuilt/darwin-x86_64/
ARCH=arm64
CPU=armv8-a
CC=$TOOLCHAIN/bin/aarch64-linux-android$API-clang
CXX=$TOOLCHAIN/bin/aarch64-linux-android$API-clang++
SYSROOT=$NDK_PATH/toolchains/llvm/prebuilt/darwin-x86_64/sysroot
CROSS_PREFIX=$TOOLCHAIN/bin/aarch64-linux-android-
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS="-march=$CPU"
function build_android
echo "CC is $CC"
echo "CROSS_PREFIX is $CROSS_PREFIX"
./configure \
--prefix=$PREFIX \
--enable-shared \
--disable-static \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-avdevice \
--disable-doc \
--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" \
make clean
make install
build_android
Ffmpeg4.x的版本编译出来命名直接时正确的(但不带版本号),可以不用修改configure直接使用生成的so。
三、AS中引入使用ffmpeg动态库
首先在local.properties中指定对应的ndk.dir,之所以没有用AS自动配置的ndk版本,是因为通过自己手动配置更加灵活,指定不同的版本。
不同的camkelist版本以及gradle版本有不同的API
针对ffmpeg3.x和android-ndk-r16b
cmake_minimum_required(VERSION 3.4.1)
project("myffmpegdemo")
MESSAGE(STATUS "PROJECT_SOURCE_DIR =" ${PROJECT_SOURCE_DIR})
MESSAGE(STATUS "CMAKE_SOURCE_DIR =" ${CMAKE_SOURCE_DIR})
include_directories(${PROJECT_SOURCE_DIR}/ffmpeg/include)
link_directories(${CMAKE_SOURCE_DIR}/../jniLibs/armeabi-v7a)
add_library(
native-lib
SHARED
native-lib.cpp )
find_library(
log-lib
target_link_libraries(
native-lib
avcodec-57
avfilter-6
avformat-57
avutil-55
swresample-2
swscale-4
avdevice-57
postproc-54
${log-lib})
针对ffmpeg4.x和 android-ndk-r21e
cmake_minimum_required(VERSION 3.4.2)
project("myffmpeg42demo")
include_directories(${PROJECT_SOURCE_DIR}/ffmpeg/include)
message("CMAKE_CXX_FLAGS:" + ${CMAKE_CXX_FLAGS})
message("CMAKE_SOURCE_DIR:" + ${CMAKE_SOURCE_DIR})
message("CMAKE_ANDROID_ARCH_ABI版本是:" + ${CMAKE_ANDROID_ARCH_ABI})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI}")
add_library(
native-lib
SHARED
native-lib.cpp)
find_library(
log-lib
log)
target_link_libraries(
native-lib
avformat avcodec avfilter avutil swresample swscale
${log-lib})
如何适配so架构
每一种架构对应一种ABI,是可以向下兼容的
比如:项目里面没有armeabi-v7a的文件夹,如果你有两个文件夹armeabi和armeabi-v7a两个文件夹,armeabi里面有a.so 和 b.so, armeabi-v7a里面只有a.so,那么arm64-v8a的手机在用到b的时候发现有armeabi-v7a的文件夹,发现里面没有b.so,就报错了,如果没有armeabi-v7a文件夹,运行时发现没有适配armeabi-v7a,就会直接去找armeabi的so库,所以要么你别加armeabi-v7a,要么armeabi里面有的so库,armeabi-v7a里面也必须有。
四、遇到的问题
1. ffbuild/config.mak: No such file or directory
./ffmpeg_build3.sh
Unknown option "--disable-ffserver".
See ./configure --help for available options.
Makefile:2: ffbuild/config.mak: No such file or directory
Makefile:40: /tools/Makefile: No such file or directory
Makefile:41: /ffbuild/common.mak: No such file or directory
Makefile:94: /libavutil/Makefile: No such file or directory
Makefile:94: /ffbuild/library.mak: No such file or directory
Makefile:96: /fftools/Makefile: No such file or directory
Makefile:97: /doc/Makefile: No such file or directory
Makefile:98: /doc/examples/Makefile: No such file or directory
Makefile:163: /tests/Makefile: No such file or directory
make: *** No rule to make target `/tests/Makefile
Makefile:2: ffbuild/config.mak: No such file or directory
Makefile:40: /tools/Makefile: No such file or directory
Makefile:41: /ffbuild/common.mak: No such file or directory
Makefile:94: /libavutil/Makefile: No such file or directory
Makefile:94: /ffbuild/library.mak: No such file or directory
Makefile:96: /fftools/Makefile: No such file or directory
Makefile:97: /doc/Makefile: No such file or directory
Makefile:98: /doc/examples/Makefile: No such file or directory
Makefile:163: /tests/Makefile: No such file or directory
make: *** No rule to make target `/tests/Makefile
Makefile:2: ffbuild/config.mak: No such file or directory
Makefile:40: /tools/Makefile: No such file or directory
Makefile:41: /ffbuild/common.mak: No such file or directory
Makefile:94: /libavutil/Makefile: No such file or directory
Makefile:94: /ffbuild/library.mak: No such file or directory
Makefile:96: /fftools/Makefile: No such file or directory
Makefile:97: /doc/Makefile: No such file or directory
Makefile:98: /doc/examples/Makefile: No such file or directory
Makefile:163: /tests/Makefile: No such file or directory
make: *** No rule to make target `/tests/Makefile
解决方案:一般是configure配置出错了,或者换行带有空格或者copy的tab被转为了空格导致,解决方案搞清楚ffmpeg编译脚本的意义,按照规范手写。
2. 4.x的版本编译的so,在编译时报错 incompatible target
/Users/yabin/tools/android-ndk-r21e/toolchains/llvm/prebuilt/darwin-x86_64/lib/gcc/arm-linux-androideabi/4.9.x/../../../../arm-linux-androideabi/bin/ld: error: /Users/yabin/work/tmp/myffmpegDemo/app/src/main/cpp/../jniLibs/armeabi-v7a/libswscale.so: incompatible target
把ndk的版本从21换成16也一样的错误
ndk.dir=/Users/yabin/tools/android-ndk-r21e
ndk.dir=/Users/yabin/tools/android-ndk-r16b
/Users/yabin/tools/android-ndk-r16b/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/lib/gcc/arm-linux-androideabi/4.9.x/../../../../arm-linux-androideabi/bin/ld: error: /Users/yabin/work/tmp/myffmpegDemo/app/src/main/cpp/../jniLibs/armeabi-v7a/libswscale.so: incompatible target
解决方案:
换成了3.3.9版本的ffmpeg的编译产物,可以了
根本原因在于不同NDK版本对应的Cmakelist中引入so的方式不同。
3. AS引入so后在armeabi中加入so,正确配置后,编译报错“armeabi is no longer supported. Use armeabi-v7a”
CMake Error at /Users/yabin/tools/android-ndk-r21e/build/cmake/android.toolchain.cmake:174 (message):
armeabi is no longer supported. Use armeabi-v7a.
Error while executing process cmake/3.10.2.4988404/bin/cmake with arguments
Unknown argument -
abiFilters Collection contains no element matching the predicate.
No valid Native abi found to request!
解决方案:使用ndk21不支持armeabi,改为armeabi-v7a
4. 【ffmpeg】编译时报错:error: undefined reference to 'av_codec_next(AVCodec const
解决方案:ffmpeg库的接口都是c函数,其头文件也没有extern "C"的声明,所以在cpp文件里调用ffmpeg函数要加extern “C” 。
[1.0-FFMPEG-Android利用ndk(r20)编译最新版本ffmpeg4.2.1] (juejin.cn/post/684490…)
Shell 脚本 - 自己动手编译 FFmpeg
AndroidStudio NDK的接入FFmpeg填坑记
雷神-最简单的基于FFmpeg的移动端例子:Android HelloWorld
【ffmpeg】编译时报错:error: undefined reference to `av...
重新编译使用CMAKE的旧项目的问题处理
Android 关于arm64-v8a、armeabi-v7a、armeabi、x86下的so文件兼容问题
针对NDK16和NDK21以及ffmpeg3.x和ffmpeg4.x配置编译
手写ffmpeg配置和编译脚本
AS中引用使用ffmpeg动态库以及不同NDK版本的引入方式
了解cpu架构的兼容性
遇到的问题解决
ffmpeg编译是比较折腾的过程,不同的NDK版本、ffmpeg版本有不同的配置和编译方式,还记得去年年初的时候折腾过一两天没搞定,这次有花费了一两天终于把ffmpeg3.x和ffmpeg4.x的都编译生成对应的so,并在AS中引入使用。
另外把配置编译的ffmpeg以及android的demo上传到github,供参考。
github.com/ayyb1988/ff…,
github.com/ayyb1988/ff…
其中ffmpeg_build.sh放入到下载的ffmpeg文件夹中,进行配置编译。
感谢你的阅读