创作参考了很多前辈们的博文。没有这些前辈们,估计现在我还在ffmpeg的坑里面打转。。
首先要感谢这位前辈的博文:https://www.cnblogs.com/tplusy/p/11012149.html。本文的思路就是源自于Ta。虽然Ta是在Windows上进行处理的,但是却给我了启发。好了,下面开始正题。
事前工作
准备一台运行Ubuntu的电脑/虚拟机。我这边使用的是vmware虚拟机,系统为ubuntu 18.04 Server。同时安装好必要的依赖。
下载NDK
到这里下载NDK。我这边选择了NDK r19c的版本。下载完成后解压到目录中备用。
下载FFmpeg源码
FFmpeg的官网地址: https://ffmpeg.org/download.html 。找到右下角的source code,通过git clone下来,然后可以直接进行使用,或者也可以直接下载打包好的tar.bz2,但是建议还是通过git去clone源码。我这边clone后选择的是切换到release/4.2的分支上进行编译操作。
题外废话
最开始我用了r13b的NDK,打算用gcc去进行编译。后来发现搞不灵光,总是有莫名其妙的错误。后来参考前辈的博文,就去下了r19c的NDK,用llvm的clang进行编译。说实话clang确实方便,可以完全不用改动FFmpeg的源码,而且FFmpeg也已经在configure文件中加入了android平台的支持。经研究,当前的分支release/4.2中,如果编译平台为android,则默认使用clang进行编译。
编写编译脚本
我用的脚本如下(llvm的clang编译),仅供参考:
- #!/bin/bash
- #Android System API Level,你要运行在什么系统上,就填写系统API Level
- #但是这个API Level必须要能够在NDK中找得到,详见下面的ANDROID_CROSS_PREFIX
- API_LEVEL=23
- #设置ndk目录
- NDK=<改成你的ndk路径>/android-ndk-r19c
- #llvm toolchain路径。linux下是linux-x86_64,windows下则是windows开头的。
- TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64
- #sysroot 这个一定要设置成 ndk的llvm 路径下的 sysroot
- #这里有个坑要注意,sysroot文件夹在r19c后才存在,r18b中是没有的!
- SYSROOT=$TOOLCHAIN/sysroot
- #ar nm 的prefix。这里一定要保证和编译的系统位数保持一致
- PLATFORM=aarch64-linux-android
- #ASM 路径, 同上必须是llvm 目录下的 asm
- #说实话,我不知道为啥要ASM。。
- ASM=$SYSROOT/usr/include/$PLATFORM
- #完整的 cross prefix
- CROSS_PREFIX=$TOOLCHAIN/bin/$PLATFORM-
- #专门给ndk clang/clang++ 的 cross prefix
- ANDROID_CROSS_PREFIX=$TOOLCHAIN/bin/aarch64-linux-android$API_LEVEL-
- #临时文件目录
- TMPDIR="./temp"
- #CPU架构
- #64位arm:aarch64
- #32位arm:armv7-a
- #64位intel/amd:x86_64
- #32位intel/amd:x86
- ARCH=aarch64
- #操作系统
- OS=android
- #安装位置
- PREFIX=~/ffmpeg_out/android/$ARCH
- #额外C参数
- ADDI_CFLAGS=""
- #这里面不能写注释,否则会报错
- ./configure \
- --prefix=$PREFIX \
- --enable-cross-compile \
- --cross-prefix=$CROSS_PREFIX \
- --target-os=$OS \
- --arch=$ARCH \
- --pkg-config=$(which pkg-config) \
- --cc=${ANDROID_CROSS_PREFIX}clang \
- --cxx=${ANDROID_CROSS_PREFIX}clang++ \
- --disable-asm \
- --disable-x86asm \
- --disable-stripping \
- --sysroot=$SYSROOT \
- --fatal-warnings \
- --enable-gpl \
- --enable-version3 \
- --enable-nonfree \
- --disable-ffplay \
- --disable-ffprobe \
- --enable-pic \
- --enable-jni \
- --enable-shared \
- --enable-mediacodec \
- --enable-decoder=h264_mediacodec \
- --enable-decoder=hevc_mediacodec \
- --enable-decoder=mpeg4_mediacodec \
- --enable-decoder=vp8_mediacodec \
- --enable-decoder=vp9_mediacodec \
- --extra-cflags="-Os -fpic -I$ASM -isysroot $SYSROOT" \
- --extra-ldflags="$ADDI_LDFLAGS"
- make clean
- make -j 4
- make install
因为我这边的测试平台是A64的开发板,cpu是64位的,系统是android6,所以我这边选择编译的是aarch64平台(性能更好),同时手动指定了cc和cxx。为什么要手动指定?请看下面:
- set_default target_os
- if test "$target_os" = android; then
- cc_default="clang"
- fi
- ar_default="${cross_prefix}${ar_default}"
- cc_default="${cross_prefix}${cc_default}"
- cxx_default="${cross_prefix}${cxx_default}"
- nm_default="${cross_prefix}${nm_default}"
- pkg_config_default="${cross_prefix}${pkg_config_default}"
cc和cxx默认和ar,nm等使用同一个cross_prefix。而在NDK中,他俩的prefix和其他的可是不一样的,cc和cxx的文件名后面有个api level的数字。这就会导致找不到cc和cxx。为了解决这个问题,这位前辈在configure文件中添加了一个单独的clang的prefix:点我去看 。他这么做确实可以,但是需要改动源码,比较麻烦。所以我选择手动指定,偷懒成功。
编译
脚本弄好后,编译就很简单了,直接执行脚本,然后看着屏幕不停的滚动,然后惬意的等一小会儿就好了。完成之后就可以到输出文件夹里面找到ffmpeg和一堆so文件,把so拷贝到/system/lib64中,ffmpeg拷贝到/system/bin下面,修正权限,然后就可以直接执行ffmpeg啦!
后记
在android 4.4之后,添加了新的保护机制,可执行文件必须是采用PIE编译的,即必须为地址无关代码,否则会提示:error: only position independent executables (PIE) are supported.。但是我上述的编译脚本中并未加上-fPIE -pie,却也能在A64开发板上运行。这个问题有待后续在别的平台上进行考证。已经确认编译器会自动加上-fPIE fpie选项
遇到的坑
坑一号:makefile找不到
表现为类似下面的情况:
- ./android_config.sh: line 36: --enable-shared: command not found
- 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:91: /libavutil/Makefile: No such file or directory
- Makefile:91: /ffbuild/library.mak: No such file or directory
- Makefile:93: /fftools/Makefile: No such file or directory
- Makefile:94: /doc/Makefile: No such file or directory
- Makefile:95: /doc/examples/Makefile: No such file or directory
- Makefile:160: /tests/Makefile: No such file or directory
- make: *** No rule to make target `/tests/Makefile'. Stop.
- 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:91: /libavutil/Makefile: No such file or directory
- Makefile:91: /ffbuild/library.mak: No such file or directory
- Makefile:93: /fftools/Makefile: No such file or directory
- Makefile:94: /doc/Makefile: No such file or directory
- Makefile:95: /doc/examples/Makefile: No such file or directory
- Makefile:160: /tests/Makefile: No such file or directory
这位前辈对于这个问题的解决方法不对,我在这个问题上研究了好久才发现根本不是那么一回事,希望不要再有人被他坑了(他总结的第2条): https://www.laoyuyu.me/2019/05/23/android/clang_compile_ffmpeg/ 。
这个问题产生的原因是编译脚本路径或者脚本执行的路径不正确。即使你在脚本里面cd到了ffmpeg的文件夹中也没用,非得把编译脚本放进ffmpeg的文件夹中才行。也许还有其他的办法,但是最省事的还是放一块儿吧。。。
坑二号:xxxxxx is unable to create an executable file
- 检查是否设置了临时目录。
- 检查ndk版本,android官方从r18b开始,已经移除了gcc这个编译工具,推荐clang。详情见ndk r18b修订内容
坑三号:/android_config.sh: line xx: xxxxx No such file or directory
configure命令之后如果使用了反斜杠进行换行的话,是不可以在其中添加注释的。把注释删除即可。
坑四号:xxxxx : not executable: 64-bit ELF file
这位前辈的博文:https://blog.csdn.net/u010651541/article/details/50177867,里面提到了关于64-bit ELF的问题,其实并不仅仅是Ta所描述的- c 的问题。如果编译的平台是x86_64的程序,放到aarch64的Android上去运行也是会提示这个错误的。