MAC环境下源码编译Android平台FFMpeg

FFMpeg

FFmpeg是一个自由软件,可以运行音频和视频多种格式的录影、转换、流功能,包含了用于多个项目中音频和视频的解码器库libavcodec,以及音频与视频格式转换库libavformat。

指令集

CPU执行任务时都需要遵从一定的规范,程序在被执行前都需要先翻译为CPU可以理解的语言,这种规范或语言就是指令集(ISA,Instruction Set Architecture)。常见的指令集有x86x86_64armmips

交叉编译

  • 本地编译

    本地编译,意思就是在当前平台生成出当前平台的可执行的代码,只能在当前平台中执行。比如在x86平台上编译x86平台可执行的程序。

  • 交叉编译

    交叉编译,意思就是在当前平台上生成另外一个平台的可执行代码,当前平台不可执行,目标平台可执行。比如在x86平台上编译出在arm平台上可执行的程序。

  • 交叉编译工具链

    交叉编译工具链是为了跨平台编译而提供的一整套编译工,包含预处理、编译、链接、汇编等等。

NDK

NDK是Android提供的Native开发工具集,包括交叉编译工具链,可以快速开发C、C++动态库并打包进apk。在SDK Manager中可以非常方便下载NDK,如图所示:

(这里插个讲解,上图中的CMake是AS2.2之后实行的编译Native代码的方式,LLDB是调试Native代码用的)下载的NDK可以在sdk目录下找到,如图所示:

MAC下编译Android平台可用的FFMpeg链接库

  • 下载源代码,本文直接从github仓库拉下来的,版本是3.4

    FFMpeg官方网址
    FFMpegGithub地址

  • 编写编译shell脚本

    在根目录下创建build_android.sh脚本文件,键入(路径确认改)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    #!/bin/bash

    # 编译临时位置,先自己创建好
    TEMPDIR=/Users/abbott/WorkSpace/jiahuan/ffmpeg/temp

    # NDK工具目录
    NDK=/Users/abbott/WorkSpace/Tools/Android/sdk/ndk-bundle

    # 设置编译平台,一般是APK最低支持的版本
    SYSROOT=$NDK/platforms/android-21/arch-arm

    # Include头文件位置 (这个跟NDK的版本有关,如果在编译的过程中发生头文件找不到的问题,记得这里,或者看参考里的第一篇文章)
    ISYSROOT=$NDK/sysroot
    ASM=$ISYSROOT/usr/include/arm-linux-androideabi

    # 交叉编译工具链,如果不是在其他环境中编译可以选择相应的环境
    TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64

    # 编译后的文件输出
    CPU=armv7-a
    PREFIX=$(pwd)/android/$CPU
    ADDI_CFLAGS="-marm -D__ANDROID_API__=21"

    # 配置有很多选项,这里不详细讲解
    ./configure \
    --enable-cross-compile \
    --enable-shared \
    --disable-static \
    --disable-doc \
    --disable-ffmpeg \
    --disable-ffplay \
    --disable-ffprobe \
    --disable-avdevice \
    --disable-symver \
    --prefix=$PREFIX \
    --cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \ # 这里会找到目录下的gcc编译工具
    --target-os=android \
    --arch=arm \
    --sysroot=$SYSROOT \
    --extra-cflags="-I$ASM -isysroot $ISYSROOT -Os -fpic $ADDI_CFLAGS"

    $ADDITIONAL_CONFIGURE_FLAG

    make -j4 #这个可以指定编译线程数,加快编译 make -j4
    make install

以上脚本,其实就是非常经典的源码编译三部曲configuremakemake install。运行此脚本(记得改脚本权限+x),等待一定的时间后,可以在设定的输出文件夹中找到编译好的多个动态链接库文件,如图所示:

其中,include文件夹是开发中需要包含的一些头文件,share文件夹里面是一些example。到这里就可以在java层加载动态链接库,jni层引入头文件做相关开发了。

将生成的多个库文件打包成一个动态链接库

原先打包出来的库有多个,在开发时觉得文件太多,很是不爽,而且生成的库的名字不能让开发者一眼就看出是哪一个三方库,所以想编译成一个文件,并且取个代表性的名字。首先修改脚本,将原先脚本configure里的

1
2
--enable-shared \  
--disable-static \

修改成

1
2
--disable-shared \  
--enable-static \

这里不再去编译动态库了,而是去生成静态库,接着在原脚本最后加上链接成一个动态库的脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$TOOLCHAIN/bin/arm-linux-androideabi-ld \
-rpath-link=$SYSROOT/usr/lib \
-L$SYSROOT/usr/lib \
-L$PREFIX/lib \
-soname libffmpeg.so \
-shared -nostdlib \
-Bsymbolic \
--whole-archive --no-undefined \
-o $PREFIX/libffmpeg.so \
$PREFIX/lib/libavcodec.a \
$PREFIX/lib/libavfilter.a \
$PREFIX/lib/libswresample.a \
$PREFIX/lib/libavformat.a \
$PREFIX/lib/libavutil.a \
$PREFIX/lib/libswscale.a \
-lc -lm -lz -ldl -llog \
$TOOLCHAIN/lib/gcc/arm-linux-androideabi/4.9.x/libgcc.a

重新运行脚本之后,等待片刻,如图所示:

编译过程中可能遇到的问题

大部分都是跟NDK的版本有关

  • 头文件找不到,脚本中-I$ASM -isysroot $ISYSROOT指定头文件路径。
  • 最后一步链接的时候undefined reference to 'stderr',参考References中第三篇文章,也可以降级NDK。
  • 其他问题,Google。

References

ffmpeg使用NDK编译时遇到的一些坑编译Android平台使用的FFmpeg库
NDK Unified Headers