美文网首页
ffmpeg入门(二)

ffmpeg入门(二)

作者: 匠人plus | 来源:发表于2023-01-24 15:03 被阅读0次

    背景

    上一篇文章介绍了ffmpeg在windows平台的使用,这一篇以Android 平台为例,介绍下ffmpeg的编译,以及在Android应用层如何使用。

    编译ffmpeg

    环境

    为保证统一的环境,本文使用wmware虚拟机进行编译。
    我本地有两套linux 环境,kali linux和r0env,其实只要普通的ubuntu 就能支持编译,推荐使用kali,很多组件免安装。

    下载

    wget http://ffmpeg.org/releases/ffmpeg-5.1.2.tar.xz

    解压

    tar -xvJf ffmpeg-5.1.2.tar.xz

    查看编译配置

    ./configure --help

    编译

    对于Android 环境,需要生成动态链接库,需要依赖Android ndk,
    下载ndk,粘贴到虚拟机目录,

    wget https://dl.google.com/android/repository/android-ndk-r25b-linux.zip
    

    解压ndk到该目录

    unzip android-ndk-r25b-linux.zip
    

    修改configure文件的路径配置,注意空格是必须的

    SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)'
    LIB_INSTALL_EXTRA_CMD='$$(RANLIB) "$(LIBDIR)/$(LIBNAME)"'
    SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'
    SLIB_INSTALL_LINKS='$(SLIBNAME)'
    

    在ffmpeg根目录下新建android_build.sh,执行

    chmod 777 android_build.sh
    

    为避免出现bad interpreter: /bin/bash^M: 没有那个文件或目录问题,执行

    sed -i 's/\r$//' android_build.sh
    

    格式处理之后,再执行

    ./android_build.sh
    

    android_build.sh 的 具体配置如下:

    #!/bin/bash
    
    # 如果编译的时候,编译日志输出,同时最后成功,但是没有找到文件夹,说明编译是失败的,
    
    
    # 设置你自己的NDK位置
     NDK_HOME=/root/ndk/android-ndk-r25b
    # 设置你自己的平台,Mac上的是 darwin-x86_64 ,linux上的是 linux-x86_64,linux上的是 windows-x86_64
     NDK_HOST_PLATFORM=linux-x86_64
    # 动态库 so 编译后的输出文件夹
     PREFIX=$(pwd)/android-build-out
    
    # 删除 so 的输出文件夹
    #  rm -rf $PREFIX
    
    # gcc的编译选项拓展参数
    # ADDI_CFLAGS="-marm"
    
    # 编译的库应用于什么系统,android 的可以有 linux、android
     TARGET_OS=android
    
    # 下面的参数都是需要变化的
    # 处理器类型(有 arm、aarch64、x86、x86_64)
     # ARCH_TYPE=arm
     ARCH_TYPE=""
    # CPU架构类型
     # CPU_TYPE=armv7-a
     CPU_TYPE=""
    
    # ndk 的cpu架构对应的文件夹,就是(${NDK_HOME}/toolchains/llvm)这部分
    # NDK_TOOLCHAINS_CPU_TYPE_FOLDER=llvm
     NDK_TOOLCHAINS_CPU_TYPE_FOLDER=""
    # ndk 的bin目录下以指定字符串开头的文件,后面的 - 表示通配
    # NDK_TOOLCHAINS_BIN_FOLDER_FILES="arm-linux-androideabi-"
     NDK_TOOLCHAINS_BIN_FOLDER_FILES=""
     TOOLCHAIN=""
    # Android交叉编译的工具链
    # /root/ndk/android-ndk-r25b/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang
    # ${NDK_HOME}/toolchains/llvm/prebuilt/${NDK_HOST_PLATFORM}/bin/aarch64-linux-android21-
    # CROSS_PREFIX="${TOOLCHAIN}/bin/${NDK_TOOLCHAINS_BIN_FOLDER_FILES}"
    CROSS_PREFIX=""
    # SYSROOT 指向的目录,即 ${NDK_HOME}/platforms 目录下的具体平台文件夹的cpu架构文件夹
    # SYSROOT_PLATFORM_AND_ARCH=arm-linux-androideabi
    SYSROOT_PLATFORM_AND_ARCH=""
    
    # 交叉编译工具的根路径(android平台使用的库和头文件的路径)
    # /root/ndk/android-ndk-r25b/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android
     # SYSROOT="${TOOLCHAIN}/sysroot/"
     # usr/lib/${SYSROOT_PLATFORM_AND_ARCH}/
     SYSROOT=""
    
     # android 里的 abi 类型[例如:armeabi, arm64-v8a, armeabi-v7a, x86, x86_64]
     ANDROID_ABI=""
    
    # ffmpeg 的一些常见选项
     COMMON_OPTIONS="
        --target-os=${TARGET_OS} \
        --enable-static \
        --enable-shared \
        --enable-small \
        --disable-programs \
        --disable-ffmpeg \
        --disable-ffplay \
        --disable-ffprobe \
        --disable-doc \
        --disable-symver \
        --disable-asm \
        --enable-cross-compile \
        --enable-pic \
        --disable-neon \
        --disable-debug \
        "
    CC=""
    CXX=""
    NM=""
    STRIP=""
    # 更新需要拼接的参数(不重新调用一下,最新值就没有替换上去)
    update_param() {
        TOOLCHAIN="${NDK_HOME}/toolchains/${NDK_TOOLCHAINS_CPU_TYPE_FOLDER}/prebuilt/${NDK_HOST_PLATFORM}"
        CROSS_PREFIX="${TOOLCHAIN}/bin/${NDK_TOOLCHAINS_BIN_FOLDER_FILES}-"
        SYSROOT="${TOOLCHAIN}/sysroot"
        CC="${TOOLCHAIN}/bin/${NDK_TOOLCHAINS_BIN_FOLDER_FILES}-clang"
        CXX="${TOOLCHAIN}/bin/${NDK_TOOLCHAINS_BIN_FOLDER_FILES}-clang++"
        LD="${TOOLCHAIN}/bin/${NDK_TOOLCHAINS_BIN_FOLDER_FILES}-clang"
        AR="${TOOLCHAIN}/bin/llvm-ar"
        NM="${TOOLCHAIN}/bin/llvm-nm"
        STRIP="${TOOLCHAIN}/bin/llvm-strip"
        RUNLIB="${TOOLCHAIN}/bin/llvm-ranlib"
        CC_L="${TOOLCHAIN}/lib64/clang/14.0.6/lib/linux"
        SYSROOT_L="${SYSROOT}/usr/lib/${SYSROOT_PLATFORM_AND_ARCH}"
        LD_L="${TOOLCHAIN}/bin/ld.lld"
    }
    
    # 声明一个编译 Android 的方法
    build_android() 
    {
        update_param
    
        ./configure \
        --prefix=${PREFIX} \
        --libdir=${PREFIX}/libs/${ANDROID_ABI} \
        --incdir=${PREFIX}/includes \
        --pkgconfigdir=${PREFIX}/pkgconfig/${ANDROID_ABI} \
        --arch=${ARCH_TYPE} \
        --cpu=${CPU_TYPE} \
        --cross-prefix=${CROSS_PREFIX} \
        --sysroot=${SYSROOT} \
        --extra-cflags="-Os -fpic -DBIONIC_IOCTL_NO_SIGNEDNESS_OVERLOA -DVK_ENABLE_BETA_EXTENSIONS=0" \
        --extra-ldflags="-lc -ldl -lm -lz -llog -lgcc -L${PREFIX}/libs" \
        --extra-ldexeflags=-pie \
        --cc=${CC} \
        --cxx=${CXX} \
        --ld=${LD} \
        --ar=${AR} \
        --ranlib=${RUNLIB} \
        --nm=${NM} \
        --strip=${STRIP} \
        ${COMMON_OPTIONS} \
        ${ADDITIONAL_CONFIGURE_FLAG}
        
        make clean
        make -j8
        make install
        echo "${ANDROID_ABI} install完成了"
        
        
        # ${LD_L} -L${PREFIX}/libs/${ANDROID_ABI} -L${CC_L} \
        # -rpath-link=${SYSROOT_L} -L${SYSROOT_L} -soname libffmpeg.so \
        # -shared -nostdlib -Bsymbolic --whole-archive --no-undefined -o ${PREFIX}/libffmpeg.so \
        # -lavcodec -lavdevice -lavfilter -lavformat -lavutil -lswscale -lswresample \
        # -lc -ldl -lm -lz -llog \
        # --dynamic-linker=/system/bin/linker
    
        # echo "${ANDROID_ABI} 编译完成了"
    }
    
    #编译 armeabi-v7a
    ANDROID_ABI=armeabi-v7a
    ARCH_TYPE=arm
    CPU_TYPE=armv7-a
    NDK_TOOLCHAINS_CPU_TYPE_FOLDER=llvm
    API=21
    NDK_TOOLCHAINS_BIN_FOLDER_FILES=armv7a-linux-androideabi${API}
    SYSROOT_PLATFORM_AND_ARCH=arm-linux-androideabi/${API}
    build_android
    
    # # 编译 arm64-v8a
    # ANDROID_ABI=arm64-v8a
    # ARCH_TYPE=aarch64
    # CPU_TYPE=armv8-a
    # NDK_TOOLCHAINS_CPU_TYPE_FOLDER=llvm
    # API=21
    # NDK_TOOLCHAINS_BIN_FOLDER_FILES=aarch64-linux-android${API}
    # SYSROOT_PLATFORM_AND_ARCH=aarch64-linux-android/${API}
    # build_android
    
    # # 编译 x86
    # ANDROID_ABI=x86
    # ARCH_TYPE=x86
    # CPU_TYPE=i686
    # NDK_TOOLCHAINS_CPU_TYPE_FOLDER=llvm
    # API=21
    # NDK_TOOLCHAINS_BIN_FOLDER_FILES=i686-linux-android${API}
    # SYSROOT_PLATFORM_AND_ARCH=x86_64-linux-android/${API}
    # build_android
    
    # # 编译 x86_64
    # ANDROID_ABI=x86_64
    # ARCH_TYPE=x86_64
    # CPU_TYPE=x86_64
    # NDK_TOOLCHAINS_CPU_TYPE_FOLDER=llvm
    # API=21
    # NDK_TOOLCHAINS_BIN_FOLDER_FILES=x86_64-linux-android${API}
    # SYSROOT_PLATFORM_AND_ARCH=i686-linux-android/${API}
    # build_android
    
    

    编译之后的产物:每种架构都包含了 libavcodec、libavdevice、libavfilter、libavformat、libavutil、libswscale、libswresample。


    1674385483168.png

    这样多个so不便于项目管理,可以通过改进脚本去只生成一个so。

    1674466957714.png

    遇到的问题:
    1.clang is unable to create an executable file. C compiler test failed,可以查看ffbuild 目录下的config.log文件,该文件记录了编译日志
    2.include/vulkan/vulkan.h:89:10: fatal error: 'vulkan_beta.h' file not found,找到ndk发现该文件确实不存在,解决办法:
    配置extra-cflags时添加 -DVK_ENABLE_BETA_EXTENSIONS=0,
    或者禁用vulkan: --disable-vulkan
    3.注意配置nm和strip
    4.编译static的时候,遇到install-libavdevice-static报错,发现ranlib的配置缺少空格

    gcc版本的ndk编译ffmpeg的编译可以参考以下文章:http://www.ihubin.com/blog/android-ffmpeg-demo-3/

    编译脚本总结

    以下是编译脚本相关参数的含义,参考了该文章https://juejin.cn/post/7149468268674154510#heading-3

    --disable-static : 不构建静态库
    --enable-shared : 编译生成动态库
    --enable-small : 降低库体积
    --disable-programs : 不构建命令行程序(指ffmpeg、ffplay以及ffprobe)
    --disable-ffmpeg : 不构建ffmpeg程序
    --disable-ffplay : 不构建ffplay程序
    --disable-ffprobe : 不构建ffprobe程序
    --disable-doc : 不产生文档
    --disable-htmlpages : 不产生 HTML类型的文档
    --disable-manpages : 不产生 manpage类型的文档
    --disable-podpages : 不产生 POD 类型的文档
    --disable-txtpages : 不产生 text 类型的文档
    --enable-cross-compile : 开启交叉编译
    --enable-pic : 生成位置无关代码
    --disable-asm:禁止所有汇编优化
    --disable-neon:禁止NEON优化
    --disable-debug:禁止调试符号,脚本中默认开启。

    获取生成的文件

    打包out目录
    zip -r ffmpeg_out.zip .
    在虚拟机的可视化界面,复制生成的 ffmpeg_out.zip,粘贴到电脑中。
    ffmpeg编译之后的是so,并没有自带的java层调用库,所以我们使用的时候可以自己写一个类库,或者用网上的写好第三方库。

    Android调用

    创建Android C++项目

    image.png

    引用so库,编写c调用类

    在 app/src/main/ 目录下,新建文件夹,并命名为 jniLibs ,接着,在 jniLibs 目录下,新建 armeabi-v7a 目录


    image.png

    在 cpp 目录下,新建 ffmpeg 目录,然后把编译时生成的 includes 文件粘贴进来。


    1674523456106.png

    修改native-lib.cpp

    #include <jni.h>
    #include <string>
    #include <unistd.h>
    
    
    extern "C" {
    #include <libavcodec/avcodec.h>
    #include <libavformat/avformat.h>
    #include <libavfilter/avfilter.h>
    #include <libavcodec/jni.h>
    JNIEXPORT jstring JNICALL
    Java_com_example_ffmpeglib_MainActivity_ffmpegInfo(JNIEnv *env, jobject  /* this */) {
        std::string ffmpeg = avcodec_configuration();
        return env->NewStringUTF(ffmpeg.c_str());
    }
    }
    

    遇到的问题:
    No toolchains found in the NDK toolchains folder for ABI with prefix: arm-linux-androideabi
    配置ndk路径即可

    编写java调用类

    1674629487426.png

    执行程序,可以看到编译信息


    device-2023-01-25-145303.png

    编译jni生成aar

    将项目改造成app和lib两个module,将刚才的代码移植到module中,可以编译生成aar,包含lib中的so和jni链接生成的动态库。


    image.png

    主项目引用或者发布到私服,即可在项目正常使用。

    相关文章

      网友评论

          本文标题:ffmpeg入门(二)

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