美文网首页安卓开发相关ndk小鹏-音频
ffmpeg4.4 for Android 编译 ndk(r21

ffmpeg4.4 for Android 编译 ndk(r21

作者: 爱Hiphop的程序员 | 来源:发表于2021-04-22 13:58 被阅读0次

    最近也是准备学习音视频相关的内容,可在第一步--编译就卡住了很久,遇到很多莫名其妙的坑,可谓编译的血泪史,但是在查阅几十篇博客和逛烂StackOverFlow之后,终于算是成功的把ffmpeg给编译出来了,现在分享给大家一些我踩过的坑。

    编译环境

    本机系统:macOS Big Sur 11.2.1

    ffmpeg: 4.4

    ndk:r21e

    一、准备工作

    下载 FFmpeg

    点击这个

    image-20210421180825019.png

    下载完成之后解压即可。

    下载ndk(需要翻墙),如果是Android开发者可以直接自己的Sdk目录下找到ndk(无需重新下载)。打开Android Studio去这个目录下面找

    image-20210421181344148.png

    可以看到这个目录下有个ndk的目录

    image-20210421181504187.png

    二、编写编译脚本

    1.先进入解压好的ffmpeg文件夹,找到configure文件

    image-20210421182246442.png

    用记事本或者VSCode之类的编辑器打开,找到以下代码并进行替换

    # 注释掉默认configure 文件中的配置:
    # SLIBNAME_WITH_MAJOR='$(SLIBNAME).$(LIBMAJOR)'
    # LIB_INSTALL_EXTRA_CMD='$(RANLIB) "$(LIBDIR)/$(LIBNAME)"'
    # SLIB_INSTALL_NAME='$(SLIBNAME_WITH_VERSION)'
    # SLIB_INSTALL_LINKS='$(SLIBNAME_WITH_MAJOR) $(SLIBNAME)'
    
    # 替换为:
    SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)'
    LIB_INSTALL_EXTRA_CMD='$(RANLIB) "$(LIBDIR)/$(LIBNAME)"'
    SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'
    SLIB_INSTALL_LINKS='$(SLIBNAME)'
    
    注意第二行的 LIB_INSTALL_EXTRA_CMD='$$(RANLIB) "$(LIBDIR)/$(LIBNAME)"'
    $$(RANLIB)和"$(LIBDIR)/$(LIBNAME)"之间有一个空格,好像空格打掉了编译会出问题
    

    改好之后记得保存

    2.新建构建脚本(重要)

    在解压出来的ffmpeg目录下打开命令行工具,输入

    touch build_android.sh
    

    然后随便找个编辑器打开这个编译文件,vim、Sublime、VSCode都行。

    复制粘贴以下编译代码,对编译代码不熟悉的,后面我会一步步的解析这个编译脚本

    #!/bin/bash
    # 以下路径需要修改成自己的NDK目录
    TOOLCHAIN=/xxxxxx/toolchains/llvm/prebuilt/darwin-x86_64
    # 最低支持的android sdk版本
    API=21
    
    function build_android
    {
    echo "Compiling FFmpeg for $CPU"
    ./configure \
     --prefix=$PREFIX \
     --disable-neon \
     --disable-hwaccels \
     --disable-gpl \
     --disable-postproc \
     --enable-shared \
     --enable-jni \
     --disable-mediacodec \
     --enable-small \
     --enable-gpl \
     --disable-decoder=h264_mediacodec \
     --disable-static \
     --disable-doc \
     --disable-programs \
     --disable-ffmpeg \
     --disable-ffplay \
     --disable-ffprobe \
     --disable-avdevice \
     --disable-symver \
     --cross-prefix=$CROSS_PREFIX \
     --target-os=android \
     --arch=$ARCH \
     --cpu=$CPU \
     --cc=$CC \
     --cxx=$CXX \
     --enable-cross-compile \
     --sysroot=$SYSROOT \
     --extra-cflags="-mno-stackrealign -Os -fpic $OPTIMIZE_CFLAGS" \
     --extra-ldflags="$ADDI_LDFLAGS" \
     $ADDITIONAL_CONFIGURE_FLAG
    make clean
    make -j4
    make install
    echo "The Compilation of FFmpeg for $CPU is completed"
    }
    
    #armv8-a
    ARCH=arm64
    CPU=armv8-a
    # r21版本的ndk中所有的编译器都在/toolchains/llvm/prebuilt/darwin-x86_64/目录下(clang)
    CC=$TOOLCHAIN/bin/aarch64-linux-android$API-clang
    CXX=$TOOLCHAIN/bin/aarch64-linux-android$API-clang++
    # NDK头文件环境
    SYSROOT=$TOOLCHAIN/sysroot
    CROSS_PREFIX=$TOOLCHAIN/bin/aarch64-linux-android-
    # so输出路径
    PREFIX=$(pwd)/android/$CPU
    OPTIMIZE_CFLAGS="-march=$CPU"
    build_android
    
    # 交叉编译工具目录,对应关系如下
    # armv8a -> arm64 -> aarch64-linux-android-
    # armv7a -> arm -> arm-linux-androideabi-
    # x86 -> x86 -> i686-linux-android-
    # x86_64 -> x86_64 -> x86_64-linux-android-
    
    # CPU架构
    #armv7-a
    ARCH=arm
    CPU=armv7-a
    CC=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang
    CXX=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang++
    SYSROOT=$TOOLCHAIN/sysroot
    CROSS_PREFIX=$TOOLCHAIN/bin/arm-linux-androideabi-
    PREFIX=$(pwd)/android/$CPU
    OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=vfp -marm -march=$CPU "
    build_android
    
    #x86
    ARCH=x86
    CPU=x86
    CC=$TOOLCHAIN/bin/i686-linux-android$API-clang
    CXX=$TOOLCHAIN/bin/i686-linux-android$API-clang++
    SYSROOT=$TOOLCHAIN/sysroot
    CROSS_PREFIX=$TOOLCHAIN/bin/i686-linux-android-
    PREFIX=$(pwd)/android/$CPU
    OPTIMIZE_CFLAGS="-march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32"
    build_android
    
    #x86_64
    ARCH=x86_64
    CPU=x86-64
    CC=$TOOLCHAIN/bin/x86_64-linux-android$API-clang
    CXX=$TOOLCHAIN/bin/x86_64-linux-android$API-clang++
    SYSROOT=$TOOLCHAIN/sysroot
    CROSS_PREFIX=$TOOLCHAIN/bin/x86_64-linux-android-
    PREFIX=$(pwd)/android/$CPU
    OPTIMIZE_CFLAGS="-march=$CPU -msse4.2 -mpopcnt -m64 -mtune=intel"
    # 方法调用
    build_android
    

    选一个编译单元来解析

    #armv8-a
    ARCH=arm64
    CPU=armv8-a
    # r21版本的ndk中所有的编译器都在/toolchains/llvm/prebuilt/darwin-x86_64/目录下(clang)
    CC=$TOOLCHAIN/bin/aarch64-linux-android$API-clang
    CXX=$TOOLCHAIN/bin/aarch64-linux-android$API-clang++
    # NDK头文件环境
    SYSROOT=$TOOLCHAIN/sysroot
    CROSS_PREFIX=$TOOLCHAIN/bin/aarch64-linux-android-
    # so输出路径
    PREFIX=$(pwd)/android/$CPU
    OPTIMIZE_CFLAGS="-march=$CPU"
    build_android
    

    ARCH、CPU、CC这些,相当于定义变量,后面可以通过 $+变量名 进行引用。这个我相信大家有一点编程基础的都不难理解。再变量定义过后最后一句build_android是执行定义的方法,也就是

    function build_android
    {
    echo "Compiling FFmpeg for $CPU"
    ./configure \
     --prefix=$PREFIX \
     --disable-neon \
     --disable-hwaccels \
     --disable-gpl \
     --disable-postproc \
     --enable-shared \
     --enable-jni \
     --disable-mediacodec \
     --enable-small \
     --enable-gpl \
     --disable-decoder=h264_mediacodec \
     --disable-static \
     --disable-doc \
     --disable-programs \
     --disable-ffmpeg \
     --disable-ffplay \
     --disable-ffprobe \
     --disable-avdevice \
     --disable-symver \
     --cross-prefix=$CROSS_PREFIX \
     --target-os=android \
     --arch=$ARCH \
     --cpu=$CPU \
     --cc=$CC \
     --cxx=$CXX \
     --enable-cross-compile \
     --sysroot=$SYSROOT \
     --extra-cflags="-mno-stackrealign -Os -fpic $OPTIMIZE_CFLAGS" \
     --extra-ldflags="$ADDI_LDFLAGS" \
     $ADDITIONAL_CONFIGURE_FLAG
    make clean
    make -j4
    make install
    echo "The Compilation of FFmpeg for $CPU is completed"
    }
    

    这一段。

    需要注意的点是

    编译脚本最前面的TOOLCHAIN变量,路径一定要写对,后面的变量如果有对TOOLCHAIN进行拼接,也一定要检查该目录下的文件路径是否正确,以及拼接之后的文件是否存在,因为不同版本ndk可能里面的内容或者位置不同,我不确定您看到这边文章的时候里面的文件是否是和我当前目录下的文件一致。如果发现我给出的编译脚本中的文件位置和您所使用的ndk文件位置有出入,以自己ndk版本的文件位置为准

    接下来一起来解析一下build_android这个方法。

    1.echo为打印输出语句,没什么可说的,相当于java中的print函数

    2.make clean 每次编译之前清空上次编译的内容

    3.make -j4 使用4核编译

    4.make install 开始编译

    重头戏在这条参数链上,也是我踩坑很久的地方

    ./configure 表示调用同级目录的configure文件,也就是我们最开始修改了4行代码的那个文件,后面的--xxxx-xxx 这些配置项具体的我就不细说,有兴趣的可以直接用Sublime、VSCode等编译器打开configure文件,找到Help options 里面每个参数都有解释,这里我截取一小段,虽然是英文,可能对英文不好的同学不太友好,但是这个年代不会还有人不会用翻译软件吧,不会吧,不会吧?

    image-20210422110213088.png

    坑1

    你会看到网上的有些编译脚本是这样的

    image-20210422110414829.png

    炸一看,好像没有什么问题,但是你开始编译,你就会发现你定义好的$PREFIX找不到文件或者目录甚至--disable-neon: command not found。是电脑出问题了?还是环境出问题了?还是打开的姿势不对?问题的原因在于在整个参数配置链中不能有注释及不该有的空格。我们看到的

    # 指定输出目录
    # 各种配置项,想详细了解的可以打开configure文件找到Help options:查看
    

    这类的注释都会影响编译。插入注释,在编译解析参数链的时候就会解析不到注释后的那一项参数配置,同理,乱加空格也会导致同样的错误。所以,无论在用网上谁的编译脚本进行编译的时候,都应该检查参数链中是否有注释或者是多余的空格。

    坑2

    你可能会看到有些编译脚本是这样

    image-20210422111929128.png

    炸是一看是不是还是没有什么问题?但是你会发现你$CXX路径找不到,此时你会去一个目录一个目录对这个文件路径是否正确。最后你会发现你指定的文件路径是完全正确的。那么问题出在哪里了呢?问题出在参数链如果要换行书写,每个参数指定完成之后必须以\结尾,因为默认参数链是需要一行书写完成的,但是为了书写的美观,我们选择了换行,所以每个参数指定完成后末尾记得加上\.

    到此,编译脚本就已经解析完成

    最后在shell中运行编译脚本,首先在macOS和linux下,需要给编译文件赋予权限,不然在编译过程中,会遇到一些权限问题,这里我们直接最高权限拉满

    chmod -R 777 build_android.sh
    

    然后为了方便查看日志,我们把编译日志输出到一个build_log下

    ./build_anroid.sh > build_log
    

    编译成功之后,会在ffmpeg目录下出现一个android目录,里面有我们编译的.so文件。

    至此,ffmpeg编译已经完成,音视频的学习算是踏出了第一步,对于ffmpeg的编译有什么问题,也欢迎各位在评论区留言讨论。

    另外转载请注明出处,peace !

    参考文献

    https://www.jianshu.com/p/117fe2ee048a

    https://stackoverflow.com/questions/36664451/ffmpeg-android-build

    相关文章

      网友评论

        本文标题:ffmpeg4.4 for Android 编译 ndk(r21

        本文链接:https://www.haomeiwen.com/subject/kbuzlltx.html