Clang编译Android平台能用的FFmpeg可执行程序

创作参考了很多前辈们的博文。没有这些前辈们,估计现在我还在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编译),仅供参考:

  1. #!/bin/bash
  2.  
  3. #Android System API Level,你要运行在什么系统上,就填写系统API Level
  4. #但是这个API Level必须要能够在NDK中找得到,详见下面的ANDROID_CROSS_PREFIX
  5. API_LEVEL=23
  6.  
  7. #设置ndk目录
  8. NDK=<改成你的ndk路径>/android-ndk-r19c
  9.  
  10. #llvm toolchain路径。linux下是linux-x86_64,windows下则是windows开头的。
  11. TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64
  12.  
  13. #sysroot 这个一定要设置成 ndk的llvm 路径下的 sysroot
  14. #这里有个坑要注意,sysroot文件夹在r19c后才存在,r18b中是没有的!
  15. SYSROOT=$TOOLCHAIN/sysroot
  16.  
  17. #ar nm 的prefix。这里一定要保证和编译的系统位数保持一致
  18. PLATFORM=aarch64-linux-android
  19.  
  20. #ASM 路径, 同上必须是llvm 目录下的 asm
  21. #说实话,我不知道为啥要ASM。。
  22. ASM=$SYSROOT/usr/include/$PLATFORM
  23.  
  24. #完整的 cross prefix
  25. CROSS_PREFIX=$TOOLCHAIN/bin/$PLATFORM-
  26.  
  27. #专门给ndk clang/clang++ 的 cross prefix
  28. ANDROID_CROSS_PREFIX=$TOOLCHAIN/bin/aarch64-linux-android$API_LEVEL-
  29.  
  30. #临时文件目录
  31. TMPDIR="./temp"
  32.  
  33. #CPU架构
  34. #64位arm:aarch64
  35. #32位arm:armv7-a
  36. #64位intel/amd:x86_64
  37. #32位intel/amd:x86
  38. ARCH=aarch64
  39.  
  40. #操作系统
  41. OS=android
  42.  
  43. #安装位置
  44. PREFIX=~/ffmpeg_out/android/$ARCH
  45.  
  46. #额外C参数
  47. ADDI_CFLAGS=""
  48.  
  49. #这里面不能写注释,否则会报错
  50. ./configure \
  51. --prefix=$PREFIX \
  52. --enable-cross-compile \
  53. --cross-prefix=$CROSS_PREFIX \
  54. --target-os=$OS \
  55. --arch=$ARCH \
  56. --pkg-config=$(which pkg-config) \
  57. --cc=${ANDROID_CROSS_PREFIX}clang \
  58. --cxx=${ANDROID_CROSS_PREFIX}clang++ \
  59. --disable-asm \
  60. --disable-x86asm \
  61. --disable-stripping \
  62. --sysroot=$SYSROOT \
  63. --fatal-warnings \
  64. --enable-gpl \
  65. --enable-version3 \
  66. --enable-nonfree \
  67. --disable-ffplay \
  68. --disable-ffprobe \
  69. --enable-pic \
  70. --enable-jni \
  71. --enable-shared \
  72. --enable-mediacodec \
  73. --enable-decoder=h264_mediacodec \
  74. --enable-decoder=hevc_mediacodec \
  75. --enable-decoder=mpeg4_mediacodec \
  76. --enable-decoder=vp8_mediacodec \
  77. --enable-decoder=vp9_mediacodec \
  78. --extra-cflags="-Os -fpic -I$ASM -isysroot $SYSROOT" \
  79. --extra-ldflags="$ADDI_LDFLAGS"
  80. make clean
  81. make -j 4
  82. make install

因为我这边的测试平台是A64的开发板,cpu是64位的,系统是android6,所以我这边选择编译的是aarch64平台(性能更好),同时手动指定了cc和cxx。为什么要手动指定?请看下面:

  1. set_default target_os
  2. if test "$target_os" = android; then
  3. cc_default="clang"
  4. fi
  5.  
  6. ar_default="${cross_prefix}${ar_default}"
  7. cc_default="${cross_prefix}${cc_default}"
  8. cxx_default="${cross_prefix}${cxx_default}"
  9. nm_default="${cross_prefix}${nm_default}"
  10. 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找不到

表现为类似下面的情况:

  1. ./android_config.sh: line 36: --enable-shared: command not found
  2. Makefile:2: ffbuild/config.mak: No such file or directory
  3. Makefile:40: /tools/Makefile: No such file or directory
  4. Makefile:41: /ffbuild/common.mak: No such file or directory
  5. Makefile:91: /libavutil/Makefile: No such file or directory
  6. Makefile:91: /ffbuild/library.mak: No such file or directory
  7. Makefile:93: /fftools/Makefile: No such file or directory
  8. Makefile:94: /doc/Makefile: No such file or directory
  9. Makefile:95: /doc/examples/Makefile: No such file or directory
  10. Makefile:160: /tests/Makefile: No such file or directory
  11. make: *** No rule to make target `/tests/Makefile'. Stop.
  12. Makefile:2: ffbuild/config.mak: No such file or directory
  13. Makefile:40: /tools/Makefile: No such file or directory
  14. Makefile:41: /ffbuild/common.mak: No such file or directory
  15. Makefile:91: /libavutil/Makefile: No such file or directory
  16. Makefile:91: /ffbuild/library.mak: No such file or directory
  17. Makefile:93: /fftools/Makefile: No such file or directory
  18. Makefile:94: /doc/Makefile: No such file or directory
  19. Makefile:95: /doc/examples/Makefile: No such file or directory
  20. 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

  1. 检查是否设置了临时目录。
  2. 检查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上去运行也是会提示这个错误的。

还有更多的坑,等待被发掘……

猜你喜欢

转载自blog.csdn.net/jiabailong/article/details/105265865
今日推荐