NDK Clang 编译 FFmpeg 4.4.1 + fdk-aac 2.0.2 + x264 20191217

现在编译 FFmpeg 已经全面采用 Clang 了,gcc 被高版本 NDK 废弃,所以从网上搜索出来的编译脚本既有使用低版本 NDK 编译 FFmpeg 的,也有采用高版本 NDK 编译的。采用低版本 NDK 编译 FFmpeg 虽然也是一种手段,但是采用高版本 NDK 编译 FFmpeg 一定是“大势所趋”。

另外就算采用低版本 NDK 去编译 FFmpeg,你会发现高版本的 FFmpeg 编译会报各种错误,有的人甚至修改了 FFmpeg 源码,虽然修改源码最终可以编译完成,但是终究不是解决问题的良方,毕竟注释掉的代码也是屏蔽了库本身的一些功能点,除非确保不使用这些功能,而且也明确知道这些屏蔽或者修改点,不会对 FFmpeg 内部模块调用造成影响。

在开始编写整个编译脚本之前,先要了解一下 GCC、LLVM、Clang 的“来龙去脉”,这有助于我们了解 NDK 编译链到底做了些什么工作,指导我们如何编写正确的编译脚本。

在 Android 实际使用 FFmpeg 的过程中,偶尔需要使用到 fdk-aac 作为 aac 音频编码器,另外也会用到 x264 来编码 h264,所以编译过程中也集成了 fdk-aac 和 x264 来扩展 FFmpeg 的编码器。当然具体如何灵活定制自己的 FFmpeg ,这需要自行去开启一些编解码器开关等。

一、GCC LLVM Clang

传统的编译器通常分为三个部分,前端(frontEnd)、优化器(Optimizer)和后端(backEnd)。在编译过程中,前端主要负责词法和语法分析,将源代码转化为抽象语法树;优化器则是在前端的基础上,对得到的中间代码进行优化,使代码更加高效;后端则是将已经优化的中间代码转化为针对各自平台的机器代码。

GCC

GCC(GNU Compiler Collection,GNU 编译器套装),是一套由 GNU 开发的编程语言编译器。GCC 原名为 GNU C 语言编译器,因为它原本只能处理 C 语言。GCC 快速演进,变得可处理 C++、Fortran、Pascal、Objective-C 和 Java,以及 Ada 等他语言。
在这里插入图片描述

LLVM

LLVM (Low Level Virtual Machine,底层虚拟机) 提供了与编译器相关的支持,能够进行程序语言的编译期优化、链接优化、在线编译优化和代码生成。简而言之,可以作为多种编译器的后端来使用。

苹果公司一直使用 GCC 作为官方的编译器。GCC 作为一款开源的编译器,一直做得不错,但 Apple 对编译工具会提出更高的要求。原因主要有以下两点:

其一,是 Apple 对 Objective-C 语言(包括后来对 C 语言)新增很多特性,但 GCC 开发者并不买 Apple 的账——不给实现,因此索性后来两者分成两条分支分别开发,这也造成 Apple 的编译器版本远落后于 GCC 的官方版本。

其二,GCC 的代码耦合度太高,很难独立,而且越是后期的版本,代码质量越差,但 Apple 想做的很多功能(比如更好的 IDE 支持),需要模块化的方式来调用 GCC,但 GCC 一直不给做。
在这里插入图片描述

Clang

Clang 是 LLVM 的前端,可以用来编译 C、C++ 和 Objective-C 等语言。Clang 则是以 LLVM 为后端的一款高效易用,并且与 IDE 结合很好的编译前端。

Clang 只支持 C、C++ 和 Objective-C 三种语言。2007 年开始开发,C 编译器最早完成,而由于 Objective-C 只是 C 语言的一个简单扩展,相对简单,很多情况下甚至可以等价地改写为 C 语言对 Objective-C 运行库的函数调用,因此在 2009 年时,已经完全可以用于生产环境。C++ 在后来也得到了支持。
在这里插入图片描述

GCC 和 Clang 对比

Clang 优点

速度快:通过编译 OS X 上几乎包含了所有 C 头文件的 carbon.h 的测试,包括预处理 (Preprocess),语法(lex),解析(parse),语义分析(Semantic Analysis),抽象语法树生成(Abstract Syntax Tree)的时间,Clang 比 GCC 快 2 倍多。

内存占用小:Clang 内存占用是源码的 130%,Apple GCC 则超过 10 倍。

诊断信息可读性强:其中错误的语法不但有源码提示,还会在错误的调用和相关上下文的下方有~~~~~和^的提示,相比之下 GCC 的提示很天书。

兼容性好:Clang 从一开始就被设计为一个 API,允许它被源代码分析工具和 IDE 集成。GCC 被构建成一个单一的静态编译器,这使得它非常难以被作为 API 并集成到其他工具中。

Clang 有静态分析,GCC 没有。

Clang 使用 BSD 许可证,商业友好,GCC 使用 GPL 许可证。

GCC 优点

支持 JAVA、ADA 和 FORTRAN 等。

GCC 支持更多平台。

GCC 更流行,广泛使用,支持完备。

GCC 基于 C,不需要 C++ 编译器即可编译。

二、NDK 版本

Android NDK r17c(2018 年 6 月)

  • GCC 不再受支持,且将在 NDK r18 中移除。
  • libc++ 现在是 CMake 和独立工具链的默认 STL。如果您已手动选择其他 STL,我们强烈建议您迁移到 libc++。请注意,ndk-build 仍默认为无 STL。
  • gnustl 和 stlport 已被弃用,并且将在 NDK r18 中被移除。
  • 取消了对 ARMv5 (armeabi)、MIPS 和 MIPS64 的支持。尝试编译任何此类 ABI 都会导致错误。
  • r18 中将取消对 ICS(android-14 和 android-15)的支持。
    从 2019 年 8 月开始,在您上传 APK 时,Play 商店会要求支持 64 位系统。请立即开始移植,以免到时出现意外。
    在这里插入图片描述

从 NDK 版本更新说明中不难知道 GCC 在 r17c 之后彻底被废弃了,所以使用 GCC 编译 FFmpeg 就得使用 r17c 之前的 NDK 版本。

这里采用了 Android NDK r21e LTS(2021 年 1 月)作为 NDK 编译版本。合并 FFmpeg 静态库到到一个 so 则使用了 arm-linux-androideabi-ld 和 aarch64-linux-android-ld 工具。

三、GNU binutils

NDK 交叉编译 FFmpeg 期间使用了不少 GNU binutils,理解了它们的作用,才能明白 FFmpeg 编译脚本中的一些关键编译链配置项的含义。

工具描述
addr2line给出一个可执行文件的内部地址,addr2line 使用文件中的调试信息将地址翻译成源代码文件名和行号。
ar这是一个程序,可通过从文档中增加、删除和析取文件来维护库文件。通常使用该工具是为了创建和管理链接程序使用的目标库文档。
asGNU 汇编器。实际上它是一族汇编器,因为它可以被编译或能够在各种不同平台上工作。
c++filt程序接受被 C++ 编译程序转换过的名字(不是被重载的) ,而且将该名字翻译成初始形式。
elfedit更新 ELF 文件的 ELF 头。
gprof该程序会监督编译程序的执行过程,并报告程序中各个函数的运行时间,可以根据所提供的配置文件来优化程序。
ldGNU 链接程序。该程序将目标文件的集合组合成可执行程序。
ld.bfd到 ld 的硬链接。
libbfd二进制文件描述器库。该程序是 binutils 包的一部分
libiberty包含多个 GNU 程序会使用的途径,包括 getopt、obstack、strerror、strtol 和 strtoul。
libopcodes一个库,用于处理 opcodes——处理器指令的 “可读文本” 版本;用于编制 objdump 这样的工具。
nlmconv将可重定位的目标文件转换成 NetWare 可加载模块(NetWare Loadable Module,NLM) 。
nm列出目标文件中定义的符号。
objcopy将目标文件从一种二进制格式复制和翻译到另外一种。
objdump显示一个或多个目标文件中保存的多种不同信息。
ranlib创建和添加到 ar 文档的索引。该索引被 ld 使用来定位库中的模块。
readelf从 ELF 格式的目标文件显示信息。
size列出目标文件中每个部分的名字和尺寸。
strings浏览所有类型的文件,析取出用于显示的字符串。
strip从目标文件或文档库中去掉符号表,以及其他调试所需的信息。
windresWindow 资源文件编译程序。

下面来看一下 android-ndk-r17c/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin 都有那些工具。不难看出上面列表里的工具基本上都涵盖了,我们编译 FFmpeg 只使用了很少一部分工具,当然编译不同的平台工具类路径要改变。比如用到了 gcc、ld、nm 等。

snake@ubuntu:~/Android/android-ndk-r17c/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin$ ls
arm-linux-androideabi-addr2line   arm-linux-androideabi-gcov
arm-linux-androideabi-ar          arm-linux-androideabi-gcov-tool
arm-linux-androideabi-as          arm-linux-androideabi-gprof
arm-linux-androideabi-c++         arm-linux-androideabi-ld
arm-linux-androideabi-c++filt     arm-linux-androideabi-ld.bfd
arm-linux-androideabi-cpp         arm-linux-androideabi-ld.gold
arm-linux-androideabi-dwp         arm-linux-androideabi-nm
arm-linux-androideabi-elfedit     arm-linux-androideabi-objcopy
arm-linux-androideabi-g++         arm-linux-androideabi-objdump
arm-linux-androideabi-gcc         arm-linux-androideabi-ranlib
arm-linux-androideabi-gcc-4.9     arm-linux-androideabi-readelf
arm-linux-androideabi-gcc-4.9.x   arm-linux-androideabi-size
arm-linux-androideabi-gcc-ar      arm-linux-androideabi-strings
arm-linux-androideabi-gcc-nm      arm-linux-androideabi-strip
arm-linux-androideabi-gcc-ranlib

四、详细编译脚本

最终的组织目录结构如下:
在这里插入图片描述
FFmpeg4Android 文件夹下的 fdk-aac、ffmpeg 和 x264 分别是对应的源码文件存放目录。tools 目录下存放对应的编译脚本,而最外层的 build_arm64-v8a.sh 和 build_armeabi-v7a.sh 分别用来编译 arm64-v8a 和 armeabi-v7a 版本的 so 库。

编译环境

OS 要求:Ubuntu 16.04

编译 FFmpeg 源码包要求:

FFmpeg 4.4.1

fdk-acc 2.0.2

x264 x264-snapshot-20191217

NDK:android-ndk-r21e

一定要注意设置正确的编译链路径,错误的编译链直接导致编译各种报错,让你丈二的和尚摸不着头脑!如果采用相同的脚本编译,请替换下面的 NDK 路径为自己的正确 NDK 路径。

build_arm64-v8a.sh 和 build_armeabi-v7a.sh 是最外层调用的编译入口脚本,直接执行即可分别生成对应的 ABI so 文件。

build_arm64-v8a.sh

#!/bin/sh
# 根目录
export ROOT_SOURCE=$(cd `dirname $0`; pwd)

export NDK=/home/snake/Android/android-ndk-r21e
export TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64
export SYSROOT=$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot

# 编译的API
export ANDROID_API=21
export TARGET=aarch64-linux-android

export AR=$TOOLCHAIN/bin/aarch64-linux-android-ar
export CC=$TOOLCHAIN/bin/$TARGET$ANDROID_API-clang
export CXX=$TOOLCHAIN/bin/$TARGET$ANDROID_API-clang++
export LD=$TOOLCHAIN/bin/aarch64-linux-android-ld
export RANLIB=$TOOLCHAIN/bin/aarch64-linux-android-ranlib
export STRIP=$TOOLCHAIN/bin/aarch64-linux-android-strip
export NM=$TOOLCHAIN/bin/aarch64-linux-android-nm

# 编译目录是否存在
if [ ! -d "build" ]; then
  mkdir ${ROOT_SOURCE}/build/
fi

# 判断编译目录是否存在源码, 不存在则复制到编译目录
if [ ! -d "build/ffmpeg" ]; then
  cp -R ffmpeg/ ${ROOT_SOURCE}/build/ffmpeg/
fi

if [ ! -d "build/x264" ]; then
  cp -R x264/ ${ROOT_SOURCE}/build/x264/
fi

if [ ! -d "build/fdk-aac" ]; then
  cp -R fdk-aac/ ${ROOT_SOURCE}/build/fdk-aac/
fi

# 到工具目录执行编译
cd tools

# 编译x264
if [ ! -x "libx264/build_x264_arm64-v8a.sh" ]; then
    echo "can not find x264 build script"
else
    chmod a+x libx264/build_x264_arm64-v8a.sh
    ./libx264/build_x264_arm64-v8a.sh
fi

# 编译fdk-aac
if [ ! -x "fdk-aac/build_fdk_aac_arm64_v8a.sh" ]; then
    echo "can not find x264 build script"
else
    chmod a+x ./fdk-aac/build_fdk_aac_arm64_v8a.sh
    ./fdk-aac/build_fdk_aac_arm64_v8a.sh
fi

# 编译ffmpeg
if [ ! -x "ffmpeg/build_ffmpeg_arm64-v8a.sh" ]; then
  echo "can not find ffmpeg build script"
else
    chmod a+x ./ffmpeg/build_ffmpeg_arm64-v8a.sh
    ./ffmpeg/build_ffmpeg_arm64-v8a.sh
fi

build_armeabi-v7a.sh

#!/bin/sh
# 根目录
export ROOT_SOURCE=$(cd `dirname $0`; pwd)

export NDK=/home/snake/Android/android-ndk-r21e
export TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64
export SYSROOT=$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot

# 编译的API
export ANDROID_API=19

export AR=$TOOLCHAIN/bin/arm-linux-androideabi-ar
export CC=$TOOLCHAIN/bin/armv7a-linux-androideabi$ANDROID_API-clang
export CXX=$TOOLCHAIN/bin/armv7a-linux-androideabi$ANDROID_API-clang++
export LD=$TOOLCHAIN/bin/arm-linux-androideabi-ld
export RANLIB=$TOOLCHAIN/bin/arm-linux-androideabi-ranlib
export STRIP=$TOOLCHAIN/bin/arm-linux-androideabi-strip
export NM=$TOOLCHAIN/bin/arm-linux-androideabi-nm

# 编译目录是否存在
if [ ! -d "build" ]; then
  mkdir ${ROOT_SOURCE}/build/
fi

# 判断编译目录是否存在源码, 不存在则复制到编译目录
if [ ! -d "build/ffmpeg" ]; then
  cp -R ffmpeg/ ${ROOT_SOURCE}/build/ffmpeg/
fi

if [ ! -d "build/x264" ]; then
  cp -R x264/ ${ROOT_SOURCE}/build/x264/
fi

if [ ! -d "build/fdk-aac" ]; then
  cp -R fdk-aac/ ${ROOT_SOURCE}/build/fdk-aac/
fi

# 到工具目录执行编译
cd tools

# 编译x264
if [ ! -x "libx264/build_x264_armeabi-v7a.sh" ]; then
    echo "can not find x264 build script"
else
    chmod a+x ./libx264/build_x264_armeabi-v7a.sh
    ./libx264/build_x264_armeabi-v7a.sh
fi

# 编译fdk-aac
if [ ! -x "fdk-aac/build_fdk-aac_armeabi_v7a.sh" ]; then
    echo "can not find x264 build script"
else
    chmod a+x ./fdk-aac/build_fdk-aac_armeabi_v7a.sh
    ./fdk-aac/build_fdk-aac_armeabi_v7a.sh
fi

# 编译ffmpeg
if [ ! -x "ffmpeg/build_ffmpeg_armeabi-v7a.sh" ]; then
  echo "can not find ffmpeg build script"
else
    chmod a+x ./ffmpeg/build_ffmpeg_armeabi-v7a.sh
    ./ffmpeg/build_ffmpeg_armeabi-v7a.sh
fi

fdk-aac 2.0.2 编译需要修改源代码,如果不修改会报错 log/log.h 无法找到,需要做对应的代码行注释。

修改 fdk-aac/libSBRdec/src/lpp_tran.cpp,分别注释 122、342、940 行,源码里明显是判断 __ANDROID__ 是否目标平台为 android,但是 android 系统的 log 路径并非 log/log.h,而是 android/log.h,因此直接注释 fdk-aac 源码即可,因为这部分功能在 fdk-aac 中主要为打印出错 log 而已。

build_fdk_aac_arm64_v8a.sh

#!/bin/bash

# fdk-aac 源码目录
FDK_AAC_SOURCE=${ROOT_SOURCE}/build/fdk-aac
# 输出路径
PREFIX=${FDK_AAC_SOURCE}/android/arm64-v8a

export AS=$TOOLCHAIN/bin/aarch64-linux-android-as

CFLAGS=" "

FLAGS="--enable-static --host=aarch64-linux-android --target=android --disable-asm"

export CXXFLAGS=$CFLAGS
export CFLAGS=$CFLAGS

cd ${FDK_AAC_SOURCE}
./configure $FLAGS \
--enable-pic \
--enable-strip \
--prefix=${PREFIX}

make clean
make -j4
make install

build_fdk-aac_armeabi_v7a.sh

#!/bin/bash

# fdk-aac 源码目录
FDK_AAC_SOURCE=${ROOT_SOURCE}/build/fdk-aac
# 输出路径
PREFIX=${FDK_AAC_SOURCE}/android/armeabi-v7a

export AS=$TOOLCHAIN/bin/arm-linux-androideabi-as

CFLAGS=""

FLAGS="--enable-static --host=arm-linux-android --target=android --disable-asm "

export CXXFLAGS=$CFLAGS
export CFLAGS=$CFLAGS

cd ${FDK_AAC_SOURCE}
./configure $FLAGS \
--enable-pic \
--enable-strip \
--prefix=${PREFIX}

$ADDITIONAL_CONFIGURE_FLAG
make clean
make -j4
make install

FFmpeg 编译脚本中使用到了 cc、cxx(这个应该是不需要的,FFmpeg 是纯 C 编写的,只不过这部分配置使用了别人成功编译的脚本,没有剔除进行测试)、ar、nm、ranlib、strip、ld 等工具。

build_ffmpeg_arm64-v8a.sh

#!/bin/sh

#编译目录
BUILD_SOURCE=${ROOT_SOURCE}/build
#FFmpeg的源码目录
FFMPEG_SOURCE=${BUILD_SOURCE}/ffmpeg

# x264的目录
X264_INCLUDE=${BUILD_SOURCE}/x264/android/arm64-v8a/include
X264_LIB=${BUILD_SOURCE}/x264/android/arm64-v8a/lib

# fdk-aac的目录
FDK_AAC_INCLUDE=${BUILD_SOURCE}/fdk-aac/android/arm64-v8a/include
FDK_AAC_LIB=${BUILD_SOURCE}/fdk-aac/android/arm64-v8a/lib

# arm v8a
CPU=arm64-v8a
OPTIMIZE_CFLAGS="-march=armv8-a "
ADDI_CFLAGS=""
PREFIX=${FFMPEG_SOURCE}/android/${CPU}

export AS=$TOOLCHAIN/bin/aarch64-linux-android-as

cd $FFMPEG_SOURCE

# 移除旧的文件夹
rm -rf ${PREFIX}
# 重新创建新的文件夹
mkdir -p ${PREFIX}

# 配置
./configure \
--prefix=${PREFIX} \
--arch=aarch64 \
--cpu=armv8-a \
--target-os=android \
--enable-cross-compile \
--cross-prefix=${TOOLCHAIN}/bin/aarch64-linux-android- \
--sysroot=${SYSROOT} \
--extra-cflags="-I${X264_INCLUDE} -I${FDK_AAC_INCLUDE} -I${SYSROOT}/usr/include" \
--extra-ldflags="-L${X264_LIB} -L${FDK_AAC_LIB}" \
--cc=${CC} \
--ar=${AR} \
--cxx=${CXX} \
--ranlib=${RANLIB} \
--strip=${STRIP} \
--nm=${NM} \
--disable-shared \
--enable-nonfree \
--enable-static \
--enable-gpl \
--enable-version3 \
--enable-pthreads \
--enable-runtime-cpudetect \
--enable-small \
--enable-network \
--disable-iconv \
--enable-asm \
--enable-neon \
--enable-yasm \
--disable-encoders \
--enable-libx264 \
--enable-libfdk_aac \
--enable-encoder=h263 \
--enable-encoder=libx264 \
--enable-encoder=libfdk-aac \
--enable-encoder=aac \
--enable-encoder=mpeg4 \
--enable-encoder=mjpeg \
--enable-encoder=png \
--enable-encoder=gif \
--enable-encoder=bmp \
--disable-muxers \
--enable-muxer=h264 \
--enable-muxer=flv \
--enable-muxer=gif \
--enable-muxer=image2 \
--enable-muxer=mp3 \
--enable-muxer=dts \
--enable-muxer=mp4 \
--enable-muxer=mov \
--enable-muxer=mpegts \
--enable-muxer=rtsp \
--disable-decoders \
--enable-jni \
--enable-mediacodec \
--enable-decoder=h264_mediacodec \
--enable-hwaccel=h264_mediacodec \
--enable-decoder=aac \
--enable-decoder=aac_latm \
--enable-decoder=mp3 \
--enable-decoder=h263 \
--enable-decoder=h264 \
--enable-decoder=mpeg4 \
--enable-decoder=mjpeg \
--enable-decoder=gif \
--enable-decoder=png \
--enable-decoder=bmp \
--enable-decoder=yuv4 \
--disable-demuxers \
--enable-demuxer=image2 \
--enable-demuxer=h263 \
--enable-demuxer=h264 \
--enable-demuxer=flv \
--enable-demuxer=gif \
--enable-demuxer=aac \
--enable-demuxer=ogg \
--enable-demuxer=dts \
--enable-demuxer=mp3 \
--enable-demuxer=mov \
--enable-demuxer=m4v \
--enable-demuxer=concat \
--enable-demuxer=mpegts \
--enable-demuxer=mjpeg \
--enable-demuxer=mpegvideo \
--enable-demuxer=rawvideo \
--enable-demuxer=yuv4mpegpipe \
--enable-demuxer=rtp \
--enable-demuxer=rtsp \
--enable-demuxer=sdp \
--disable-parsers \
--enable-parser=aac \
--enable-parser=ac3 \
--enable-parser=h264 \
--enable-parser=mjpeg \
--enable-parser=png \
--enable-parser=bmp \
--enable-parser=mpegvideo \
--enable-parser=mpegaudio \
--disable-protocols \
--enable-protocol=file \
--enable-protocol=hls \
--enable-protocol=concat \
--enable-protocol=rtmp \
--enable-protocol=rtmpe \
--enable-protocol=rtmps \
--enable-protocol=rtmpt \
--enable-protocol=rtmpte \
--enable-protocol=rtmpts \
--enable-protocol=rtp \
--enable-protocol=udp \
--enable-protocol=tcp \
--disable-filters \
--disable-filters \
--enable-filter=aresample \
--enable-filter=asetpts \
--enable-filter=setpts \
--enable-filter=ass \
--enable-filter=scale \
--enable-filter=concat \
--enable-filter=atempo \
--enable-filter=movie \
--enable-filter=overlay \
--enable-filter=rotate \
--enable-filter=transpose \
--enable-filter=hflip \
--enable-zlib \
--disable-outdevs \
--disable-doc \
--disable-ffplay \
--disable-ffmpeg \
--disable-debug \
--disable-ffprobe \
--disable-postproc \
--disable-avdevice \
--disable-symver \
--disable-stripping \
--extra-cflags="-Os -fpic ${OPTIMIZE_CFLAGS}" \
--extra-ldflags="${ADDI_LDFLAGS}" \
${ADDITIONAL_CONFIGURE_FLAG}

make clean
make -j8
make install

# 合并到libffmpeg.so
${LD} \
-rpath-link=$NDK/platforms/android-$ANDROID_API/arch-arm64/usr/lib \
-L$NDK/platforms/android-$ANDROID_API/arch-arm64/usr/lib \
-L${PREFIX}/lib \
-L${X264_LIB} \
-L${FDK_AAC_LIB} \
-soname libffmpeg.so -shared -nostdlib -Bsymbolic --whole-archive --no-undefined -o \
${PREFIX}/libffmpeg.so \
libavcodec/libavcodec.a \
libavfilter/libavfilter.a \
libswresample/libswresample.a \
libavformat/libavformat.a \
libavutil/libavutil.a \
libswscale/libswscale.a \
${X264_LIB}/libx264.a \
${FDK_AAC_LIB}/libfdk-aac.a \
-lc -lm -lz -ldl -llog --dynamic-linker=/system/bin/linker \
${TOOLCHAIN}/lib/gcc/aarch64-linux-android/4.9.x/libgcc_real.a

# strip 精简文件
$STRIP $PREFIX/libffmpeg.so
#回到根目录
cd ${ROOT_SOURCE}

build_ffmpeg_armeabi-v7a.sh

#!/bin/sh

#编译目录
BUILD_SOURCE=${ROOT_SOURCE}/build
#FFmpeg的源码目录
FFMPEG_SOURCE=${BUILD_SOURCE}/ffmpeg

# x264的目录
X264_INCLUDE=${BUILD_SOURCE}/x264/android/armeabi-v7a/include
X264_LIB=${BUILD_SOURCE}/x264/android/armeabi-v7a/lib

# fdk-aac的目录
FDK_AAC_INCLUDE=${BUILD_SOURCE}/fdk-aac/android/armeabi-v7a/include
FDK_AAC_LIB=${BUILD_SOURCE}/fdk-aac/android/armeabi-v7a/lib

# arm v7vfp
CPU=armeabi-v7a
OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=neon -marm -march=armv7-a "
ADDI_CFLAGS="-marm"
PREFIX=${FFMPEG_SOURCE}/android/${CPU}

export AS=$TOOLCHAIN/bin/arm-linux-androideabi-as

cd $FFMPEG_SOURCE

# 移除旧的文件夹
rm -rf ${PREFIX}
# 重新创建新的文件夹
mkdir -p ${PREFIX}

# 配置
./configure \
--prefix=${PREFIX} \
--arch=arm \
--cpu=armv7-a \
--target-os=android \
--enable-cross-compile \
--cross-prefix=${TOOLCHAIN}/bin/arm-linux-androideabi- \
--sysroot=${SYSROOT} \
--extra-cflags="-I${X264_INCLUDE} -I${FDK_AAC_INCLUDE} -I${SYSROOT}/usr/include" \
--extra-ldflags="-L${X264_LIB} -L${FDK_AAC_LIB}" \
--cc=${CC} \
--ar=${AR} \
--cxx=${CXX} \
--ranlib=${RANLIB} \
--strip=${STRIP} \
--nm=${NM} \
--disable-shared \
--enable-nonfree \
--enable-static \
--enable-gpl \
--enable-version3 \
--enable-pthreads \
--enable-runtime-cpudetect \
--enable-small \
--enable-network \
--disable-iconv \
--enable-asm \
--enable-neon \
--enable-yasm \
--disable-encoders \
--enable-libfdk-aac \
--enable-libx264 \
--enable-encoder=h263 \
--enable-encoder=libx264 \
--enable-encoder=libfdk_aac \
--enable-encoder=aac \
--enable-encoder=mpeg4 \
--enable-encoder=mjpeg \
--enable-encoder=png \
--enable-encoder=gif \
--enable-encoder=bmp \
--disable-muxers \
--enable-muxer=h264 \
--enable-muxer=flv \
--enable-muxer=gif \
--enable-muxer=image2 \
--enable-muxer=mp3 \
--enable-muxer=dts \
--enable-muxer=mp4 \
--enable-muxer=mov \
--enable-muxer=mpegts \
--enable-muxer=rtsp \
--disable-decoders \
--enable-jni \
--enable-mediacodec \
--enable-decoder=h264_mediacodec \
--enable-hwaccel=h264_mediacodec \
--enable-decoder=aac \
--enable-decoder=aac_latm \
--enable-decoder=mp3 \
--enable-decoder=h263 \
--enable-decoder=h264 \
--enable-decoder=mpeg4 \
--enable-decoder=mjpeg \
--enable-decoder=gif \
--enable-decoder=png \
--enable-decoder=bmp \
--enable-decoder=yuv4 \
--disable-demuxers \
--enable-demuxer=image2 \
--enable-demuxer=h263 \
--enable-demuxer=h264 \
--enable-demuxer=flv \
--enable-demuxer=gif \
--enable-demuxer=aac \
--enable-demuxer=ogg \
--enable-demuxer=dts \
--enable-demuxer=mp3 \
--enable-demuxer=mov \
--enable-demuxer=m4v \
--enable-demuxer=concat \
--enable-demuxer=mpegts \
--enable-demuxer=mjpeg \
--enable-demuxer=mpegvideo \
--enable-demuxer=rawvideo \
--enable-demuxer=yuv4mpegpipe \
--enable-demuxer=rtp \
--enable-demuxer=rtsp \
--enable-demuxer=sdp \
--disable-parsers \
--enable-parser=aac \
--enable-parser=ac3 \
--enable-parser=h264 \
--enable-parser=mjpeg \
--enable-parser=png \
--enable-parser=bmp\
--enable-parser=mpegvideo \
--enable-parser=mpegaudio \
--disable-protocols \
--enable-protocol=file \
--enable-protocol=hls \
--enable-protocol=concat \
--enable-protocol=rtmp \
--enable-protocol=rtmpe \
--enable-protocol=rtmps \
--enable-protocol=rtmpt \
--enable-protocol=rtmpte \
--enable-protocol=rtmpts \
--enable-protocol=rtp \
--enable-protocol=udp \
--enable-protocol=tcp \
--disable-filters \
--enable-filter=aresample \
--enable-filter=asetpts \
--enable-filter=setpts \
--enable-filter=ass \
--enable-filter=scale \
--enable-filter=concat \
--enable-filter=atempo \
--enable-filter=movie \
--enable-filter=overlay \
--enable-filter=rotate \
--enable-filter=transpose \
--enable-filter=hflip \
--enable-zlib \
--disable-outdevs \
--disable-doc \
--disable-ffplay \
--disable-ffmpeg \
--disable-debug \
--disable-ffprobe \
--disable-postproc \
--disable-avdevice \
--disable-symver \
--disable-stripping \
--extra-cflags="-Os -fpic ${OPTIMIZE_CFLAGS}" \
--extra-ldflags="${ADDI_LDFLAGS}" \
${ADDITIONAL_CONFIGURE_FLAG}

make clean
make -j8
make install

# 合并到libffmpeg.so
${LD} \
-rpath-link=$NDK/platforms/android-$ANDROID_API/arch-arm/usr/lib \
-L$NDK/platforms/android-$ANDROID_API/arch-arm/usr/lib \
-L${PREFIX}/lib \
-L${X264_LIB} \
-L${FDK_AAC_LIB} \
-soname libffmpeg.so -shared -nostdlib -Bsymbolic --whole-archive --no-undefined -o \
${PREFIX}/libffmpeg.so \
libavcodec/libavcodec.a \
libavfilter/libavfilter.a \
libswresample/libswresample.a \
libavformat/libavformat.a \
libavutil/libavutil.a \
libswscale/libswscale.a \
${X264_LIB}/libx264.a \
${FDK_AAC_LIB}/libfdk-aac.a \
-lc -lm -lz -ldl -llog --dynamic-linker=/system/bin/linker \
${TOOLCHAIN}/lib/gcc/arm-linux-androideabi/4.9.x/libgcc_real.a

# strip 精简文件
$STRIP $PREFIX/libffmpeg.so
#回到根目录
cd ${ROOT_SOURCE}

build_x264_arm64-v8a.sh

#!/bin/sh

# 编译选项
EXTRA_CFLAGS="-march=armv8-a -D__ANDROID__ -D__ARM_ARCH_8__ -D__ARM_ARCH_8A__"

# x264 源码目录
X264_SOURCE=${ROOT_SOURCE}/build/x264
# 输出路径
PREFIX=${X264_SOURCE}/android/arm64-v8a

# 配置和编译
cd ${X264_SOURCE}
./configure \
--host=aarch64-linux-android \
--sysroot=${SYSROOT} \
--cross-prefix=${TOOLCHAIN}/bin/aarch64-linux-android- \
--prefix=${PREFIX} \
--enable-static \
--enable-pic \
--enable-strip \
--disable-cli \
--disable-win32thread \
--disable-avs \
--disable-swscale \
--disable-lavf \
--disable-ffms \
--disable-gpac \
--disable-lsmash \
--extra-cflags="-Os -fpic ${EXTRA_CFLAGS}" \
--extra-ldflags="" \
${ADDITIONAL_CONFIGURE_FLAG}

make clean
make -j4
make install

build_x264_armeabi-v7a.sh

#!/bin/sh

# 编译选项
EXTRA_CFLAGS="-march=armv7-a -mfloat-abi=softfp -mfpu=neon -D__ARM_ARCH_7__ -D__ARM_ARCH_7A__"

# x264 源码目录
X264_SOURCE=${ROOT_SOURCE}/build/x264
# 输出路径
PREFIX=${X264_SOURCE}/android/armeabi-v7a

# 配置和编译
cd ${X264_SOURCE}
./configure \
--host=arm-linux-androideabi \
--cross-prefix=${TOOLCHAIN}/bin/arm-linux-androideabi- \
--sysroot=${SYSROOT} \
--prefix=${PREFIX} \
--enable-static \
--enable-pic \
--enable-strip \
--disable-cli \
--disable-win32thread \
--disable-avs \
--disable-swscale \
--disable-lavf \
--disable-ffms \
--disable-gpac \
--disable-lsmash \
--extra-cflags="-Os -fpic ${EXTRA_CFLAGS}" \
--extra-ldflags="" \
${ADDITIONAL_CONFIGURE_FLAG}

make clean
make -j4
make install

最终生成的 libffmpeg.so 文件和头文件在 FFmpeg4Android/build/ffmpeg/android/armeabi-v7a 和
FFmpeg4Android/build/ffmpeg/android/arm64-v8a 下。fdk-aac 和 x264 的头文件位于自己的 build 目录下。

思考:以上的很多编译链工具看样子还是使用了 GNU binutils,实际上也有对应的 llvm 工具,比如 android-ndk-r21e/toolchains/llvm/prebuilt/linux-x86_64/bin 目录下可以看到:

llvm-addr2line
llvm-ar
llvm-as
llvm-cfi-verify
llvm-config
llvm-cov
llvm-dis
llvm-lib
llvm-link
llvm-modextract
llvm-nm
llvm-objcopy
llvm-objdump
llvm-profdata
llvm-ranlib
llvm-readelf
llvm-readobj
llvm-size
llvm-strings
llvm-strip
llvm-symbolizer

可能完全可以使用 llvm 里的工具替换 GNU binutils 做为 FFmpeg 编译链。

在 build_armeabi-v7a.sh 中做了个测试,的确可以正常编译出 libffmpeg.so,只不过中间编译 x264 会报一些 error,但全部被自动 ignored 掉了。

/home/snake/Android/android-ndk-r21e/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip: error: 'common/arm/cpu-a.o': not stripping symbol 'cpu_enable_armv7_counter' because it is named in a relocation
Makefile:293: recipe for target 'common/arm/cpu-a.o' failed
make: [common/arm/cpu-a.o] Error 1 (ignored)

build_armeabi-v7a.sh 编译链修改点

export AR=$TOOLCHAIN/bin/llvm-ar
......
export LD=$TOOLCHAIN/bin/ld.lld
export RANLIB=$TOOLCHAIN/bin/llvm-ranlib
export STRIP=$TOOLCHAIN/bin/llvm-strip
export NM=$TOOLCHAIN/bin/llvm-nm

参考资料:

  1. https://developer.android.google.cn/ndk/downloads/revision_history
  2. https://www.kernel.org/doc/html/latest/kbuild/llvm.html
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TYYJ-洪伟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值