阅读 684

Android项目引入OpenCV和FFmpeg

因为毕设项目涉及在安卓手机上处理音视频,需要使用OpenCV和FFmpeg库,所以前段时间一直在找Android如何引入OpenCV和FFmpeg,相关教程在网上可以搜到很多,但因为教程编写的时间都很久,很多都存在问题。本文以Opencv-4.5.1和FFmpeg-4.4版本为例,记录下Android项目引入两个音视频库的过程。

Opencv-4.5.2

本次教程引入OpenCV的方式:OpenCV Android SDK + OpenCV动态库 + JNI(可以同时使用Java和C++进行开发)

开发环境:macOS Big Sur-11.2.3 + Android Studio-4.1.2

  1. 下载相关工具。点击工具栏的SDK Manager,点击SDK Tools,选中CMake和NDK(Side by side),点击apply即可下载所需工具

    2021-04-15_15-55-26 (1)

  2. 新建Android C++工程

    2021-04-15_13-12-36

  3. 下载OpenCV-Android-SDK。下载地址OpenCV Release

    image-20210415130831706

  4. Android工程引入OpenCV-SDK。点击 File -> new -> Import Module, 选中 OpenCV-Android-SDK/sdk/java 文件夹,点击确定,就会自动识别处模块,导入完成后,会在工程目录下发现 OpenCV 的库,settings.gradle 文件也会相应改变

    2021-04-15_13-20-05

  5. 修改OpenCV库的SDK版本。点击 File -> Project Structure- Modules ,选中OpenCV模块,修改SDK版本为你工程对应的SDK版本

    2021-04-15_13-27-32

  6. APP引入OpenCV依赖。打开app目录下的build.gradle,添加OpenCV依赖

    dependencies {
        ...
        //path后填的之前导入的OpenCV Module名称
    	implementation project(path: ':opencv452')
    }
    复制代码

    此时点击 File -> Project Structure-Dependencies,选中app模块,可以看到已经引入opencv452的依赖

    2021-04-15_13-36-50

  7. 修改Opencv模块中的build.gradle设置。

    • 首行的apply plugin: ‘com.android.application’ 修改为apply plugin: ‘com.android.library’

    • 去掉

          defaultConfig {
              applicationId "org.opencv"
          }
      复制代码

    最后Opencv模块中的build.gradle如下图所示:

    image-20210415134705933

  8. 将 OpenCV-Android-SDK/sdk/native/libs 目录下全部内容复制到工程目录/app/src/main/jniLibs 目录下(jniLibs目录需要新建)

  9. 复制头文件。将 OpenCV-Android-SDK/sdk/native/jni/include 目录下全部内容复制到 工程目录/app/libs/include 目录下

  10. 编辑CMakeList.txt。打开/app/src/main/cpp/CMakeLists.txt文件,添加以下内容

    # 该变量为真时会创建完整版本的Makefile
    set(CMAKE_VERBOSE_MAKEFILE on)
    
    #调用头文件的具体路径
    include_directories(../../../libs/include)
    
    #增加opencv的动态库
    add_library(opencv_lib SHARED IMPORTED)
    
    #建立链接, IMPORTED_LOCATION 后跟的是jniLibs的相对路径
    set_target_properties(opencv_lib PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libopencv_java4.so)
    复制代码
  11. 修改build.gradle。修改app模块下的build.gradle,添加以下内容

    • 在android {defaultConfig {cmake { … }}}中添加,具体内容见下方代码

          defaultConfig {
              applicationId "com.bupt.compose3dvideo"
              minSdkVersion 25
              targetSdkVersion 30
              versionCode 1
              versionName "1.0"
      
              testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
              externalNativeBuild {
                  cmake {
                      cppFlags ""
                      //以下为需要添加的代码
                      arguments "-DANDROID_STL=c++_shared"
                      //可选 'arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64'
                      abiFilters 'x86', 'armeabi-v7a'
                  }
              }
          }
      复制代码
    • 在android {…}中添加,具体内容见下方代码

          //以下为需要添加的代码
          sourceSets {
              main {
                  jni.srcDirs = ['/src/main/cpp']
                  jniLibs.srcDirs = ['src/main/jniLibs']
              }
          }
      复制代码
  12. 初始化OpenCV。

    • 创建BaseApplication或者在MainActivity中初始化,在onCreate()中调用OpenCVLoader.initDebug()即可初始化OpenCV

    • 如果在BaseApplication中初始化,需要手动添加以下代码,MainActivity中已存在,不需手动添加

          companion object {
              // Used to load the 'native-lib' library on application startup.
              init {
                  System.loadLibrary("native-lib")
              }
          }
      复制代码

以上就是引入Opencv库的全部步骤,因为同时引入了OpenCV-Android-SDK和OpenCV动态库,会极大增加安装包体积,但好处是可以同时使用Java和C++编程,而且使用FFmpeg时也需要使用JNI,此时先引入OpenCV动态库,方便以后FFmpeg的引入。

FFmpeg-4.4

FFmpeg库需要自己先编译,再引入安卓项目。我遇到的最大的问题是编译FFmpeg的安卓版本,因为网上的教程主要是使用NDK-20以前的版本进行编译,而NDK-20前后版本编译方式有一些差距,所以一开始总是编译失败,后来找到一篇文章,按照教程终于编译成功。

编译Android版本的FFmpeg

开发环境:macOS Big Sur-11.2.3 + Android Studio-4.1.2 + NDK-r22

  1. 使用homebrew安装yasm、pkg-config、gcc

  2. 下载FFmpeg和NDK,官网下载Download FFmpeg和NDK官网下载NDK

  3. 在FFmpeg文件夹下新建build_android.sh文件,文件内容如下

    #!/bin/bash
    
    # 修改为你本地NDK目录
    NDK=/Users/xxx/android-ndk-r22b
    TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/darwin-x86_64
    API=30
    
    function build_android() {
        cd ffmpeg-4.4
        echo "Compiling FFmpeg for $CPU"
        ./configure \
            --prefix=$PREFIX \
            --libdir=$LIB_DIR \
            --enable-shared \
            --disable-static \
            --enable-jni \
            --disable-doc \
            --disable-symver \
            --disable-programs \
            --target-os=android \
            --arch=$ARCH \
            --cpu=$CPU \
            --cc=$CC \
            --cxx=$CXX \
            --enable-cross-compile \
            --sysroot=$SYSROOT \
            --extra-cflags="-Os -fpic $OPTIMIZE_CFLAGS" \
            --extra-ldflags="$ADDI_LDFLAGS" \
            --disable-asm \
            $COMMON_FF_CFG_FLAGS
        make clean
        make -j8
        make install
        echo "The Compilation of FFmpeg for $CPU is completed"
        echo "Output library : $LIB_DIR"
        cd ..
    }
    
    # 根据自己需要的 cpu, 放开对应的注释即可
    
    # ## armv8-a
    # function v8() {
    #     source "config-env.sh"
    #     OUTPUT_FOLDER="arm64-v8a"
    #     ARCH=arm64
    #     CPU="armv8-a"
    #     TOOL_CPU_NAME=aarch64
    #     TOOL_PREFIX="$TOOLCHAIN/bin/$TOOL_CPU_NAME-linux-android"
    
    #     CC="$TOOL_PREFIX$API-clang"
    #     CXX="$TOOL_PREFIX$API-clang++"
    #     SYSROOT="$NDK/toolchains/llvm/prebuilt/darwin-x86_64/sysroot"
    #     PREFIX="${PWD}/android/$OUTPUT_FOLDER"
    #     LIB_DIR="${PWD}/android/libs/$OUTPUT_FOLDER"
    #     OPTIMIZE_CFLAGS="-march=$CPU"
    #     build_android
    # }
    
    # ## armv7-a
    # function v7() {
    #     source "config-env.sh"
    #     OUTPUT_FOLDER="armeabi-v7a"
    #     ARCH="arm"
    #     CPU="armv7-a"
    #     TOOL_CPU_NAME=armv7a
    #     TOOL_PREFIX="$TOOLCHAIN/bin/arm-linux-androideabi"
    
    #     CC="$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang"
    #     CXX="$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang++"
    #     SYSROOT="$NDK/toolchains/llvm/prebuilt/darwin-x86_64/sysroot"
    #     PREFIX="${PWD}/android/$OUTPUT_FOLDER"
    #     LIB_DIR="${PWD}/android/libs/$OUTPUT_FOLDER"
    #     OPTIMIZE_CFLAGS="-march=$CPU"
    #     build_android
    # }
    
    # ## x86
    # function x86() {
    #     source "config-env.sh"
    #     OUTPUT_FOLDER="x86"
    #     ARCH="x86"
    #     CPU="x86"
    #     TOOL_CPU_NAME="i686"
    #     TOOL_PREFIX="$TOOLCHAIN/bin/${TOOL_CPU_NAME}-linux-android"
    
    #     CC="$TOOL_PREFIX$API-clang"
    #     CXX="$TOOL_PREFIX$API-clang++"
    #     SYSROOT="$NDK/toolchains/llvm/prebuilt/darwin-x86_64/sysroot"
    #     PREFIX="${PWD}/android/$OUTPUT_FOLDER"
    #     LIB_DIR="${PWD}/android/libs/$OUTPUT_FOLDER"
    #     OPTIMIZE_CFLAGS="-march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32"
    #     build_android
    # }
    
    ## x86_64
    function x86_64() {
        source "config-env.sh"
        OUTPUT_FOLDER="x86_64"
        ARCH="x86_64"
        CPU="x86-64"
        TOOL_CPU_NAME="x86_64"
        TOOL_PREFIX="$TOOLCHAIN/bin/${TOOL_CPU_NAME}-linux-android"
    
        CC="$TOOL_PREFIX$API-clang"
        CXX="$TOOL_PREFIX$API-clang++"
        SYSROOT="$NDK/toolchains/llvm/prebuilt/darwin-x86_64/sysroot"
        PREFIX="${PWD}/android/$OUTPUT_FOLDER"
        LIB_DIR="${PWD}/android/libs/$OUTPUT_FOLDER"
        OPTIMIZE_CFLAGS="-march=$CPU"
        build_android
    }
    
    # 简单使用说明
    
    if [ -z $1 ]; then
        echo "Usage:"
        echo "  $0 all"
        echo "  $0 armv8"
        echo "    all support cpu :armv7 armv8 x86 x86_64"
        exit -1
    fi
    
    # 检测构建全部
    
    HAVE_ALL=0
    
    for arg in $@; do
    
        if [ "$arg" == 'all' ]; then
            HAVE_ALL=1
        fi
    
    done
    
    if [ $HAVE_ALL -eq 1 ]; then
    
        x86
        x86_64
        v8
        v7
        exit 0
    
    fi
    
    # 根据输入参数检测构建哪种cpu
    
    for arg in $@; do
    
        if [ "$arg" == 'x86' ]; then
            x86
        fi
    
        if [ "$arg" == 'x86_64' ]; then
            x86_64
        fi
    
        if [ "$arg" == 'armv8' ]; then
            v8
        fi
    
        if [ "$arg" == 'armv7' ]; then
            v7
        fi
    
    done
    
    # 当前NDK版本为r22b, ffmpeg版本为4.4
    # 在原作者基础上有修改
    # 1. API等级是30
    # 2. 4.4 版本并不需要手动修改ffmpeg的configure文件,因为打出来的so文件是没有版本号的
    # 3. ffmpeg 目录结构有修改, 所以有一个cd的过程
    
    # 目录结构
    # tree -L 1
    # .
    # ├── android # 输出目录
    # ├── build_ffmpeg_android.sh # 脚本
    # ├── config-env.sh # 选项
    # └── ffmpeg-4.3.1 # ffmpeg 目录
    
    # 编译选项可以使用 ./configure -h 查看, 可以根据自己的需求开启或关闭编解码支持, 会显著降低so大小
    # 本选项编译出的文件文件尺寸如下:
    # ll android/libs/arm64-v8a 
    # total 11624
    # -rwxr-xr-x  1 caijinglong  staff   3.9M  7 26 16:28 libavcodec.so
    # -rwxr-xr-x  1 caijinglong  staff   106K  7 26 16:28 libavfilter.so
    # -rwxr-xr-x  1 caijinglong  staff   771K  7 26 16:28 libavformat.so
    # -rwxr-xr-x  1 caijinglong  staff   443K  7 26 16:28 libavutil.so
    # -rwxr-xr-x  1 caijinglong  staff    82K  7 26 16:28 libswresample.so
    # -rwxr-xr-x  1 caijinglong  staff   398K  7 26 16:28 libswscale.so
    
    # 如果想要和ijkplayer那样将所有so合并为一个libffmpeg.so库,可以看下面的说明
    # 说是合并,实际上so是不支持合并的, 需要编译出.a静态库而不是so动态库, 然后将多个静态库合并为一个so, 然后用TOOLCHAIN中的gcc命令进行合并
    # gcc -shared -o -Wl --whole-archive avcodec.a avfilter.a other.a -Wl,--no-whole-archive
    复制代码
  4. 在FFmpeg文件夹下新建config-env.sh文件,这个环境是 ijkplayer 的 ffmpeg 配置,文件内容如下

    #! /usr/bin/env bash
    
    # shell/config.sh
    # 这个来自ijkplayer的ffmpeg配置, 会在上面的build脚本中被引入
    
    #--------------------
    # Standard options:
    export COMMON_FF_CFG_FLAGS=
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --prefix=PREFIX"
    
    # Licensing options:
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-gpl"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-version3"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-nonfree"
    
    # Configuration options:
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-static"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-shared"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-small"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-runtime-cpudetect"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-gray"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-swscale-alpha"
    
    # Program options:
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-programs"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-ffmpeg"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-ffplay"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-ffprobe"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-ffserver"
    
    # Documentation options:
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-doc"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-htmlpages"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-manpages"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-podpages"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-txtpages"
    
    # Component options:
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-avdevice"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-avcodec"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-avformat"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-avutil"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-swresample"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-swscale"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-postproc"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-avfilter"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-avresample"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-pthreads"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-w32threads"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-os2threads"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-network"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-dct"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-dwt"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-lsp"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-lzo"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-mdct"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-rdft"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-fft"
    
    # Hardware accelerators:
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-d3d11va"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-dxva2"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-vaapi"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-vda"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-vdpau"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-videotoolbox"
    
    # Individual component options:
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-everything"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-encoders"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-encoder=png"
    
    # ./configure --list-decoders
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-decoders"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=aac"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=aac_latm"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=flv"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=h264"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=mp3*"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=vp6f"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=flac"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=hevc"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=vp8"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=vp9"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=h263"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=h263i"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=h263p"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=mpeg4"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-decoder=mjpeg"
    
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-hwaccels"
    
    # ./configure --list-muxers
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-muxers"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-muxer=mp4"
    
    # ./configure --list-demuxers
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-demuxers"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=aac"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=concat"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=data"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=flv"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=hls"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=live_flv"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=mov"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=mp3"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=mpegps"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=mpegts"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=mpegvideo"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=flac"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=hevc"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=webm_dash_manifest"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=mpeg4"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=rtsp"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=mjpeg"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=avi"
    
    # ./configure --list-parsers
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-parsers"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-parser=aac"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-parser=aac_latm"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-parser=h264"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-parser=flac"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-parser=hevc"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-parser=mpeg4"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-parser=mpeg4video"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-parser=mpegvideo"
    
    # ./configure --list-bsf
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-bsfs"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=chomp"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=dca_core"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=dump_extradata"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=hevc_mp4toannexb"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=imx_dump_header"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=mjpeg2jpeg"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=mjpega_dump_header"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=mov2textsub"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=mp3_header_decompress"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=mpeg4_unpack_bframes"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=noise"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=remove_extradata"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=text2movsub"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bsf=vp9_superframe"
    
    # ./configure --list-protocols
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocols"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=async"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=bluray"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=concat"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=crypto"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=ffrtmpcrypt"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=ffrtmphttp"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=gopher"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=icecast"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=librtmp*"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=libssh"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=md5"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=mmsh"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=mmst"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=rtmp*"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=rtmp"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=rtmpt"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=rtp"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=sctp"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=srtp"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=subfile"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=unix"
    
    #
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-devices"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-filters"
    
    # External library support:
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-iconv"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-audiotoolbox"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-videotoolbox"
    
    # ...
    
    # Advanced options (experts only):
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --cross-prefix=${FF_CROSS_PREFIX}-"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-cross-compile"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --sysroot=PATH"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --sysinclude=PATH"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --target-os=TAGET_OS"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --target-exec=CMD"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --target-path=DIR"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --toolchain=NAME"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --nm=NM"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --ar=AR"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --as=AS"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --yasmexe=EXE"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --cc=CC"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --cxx=CXX"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --dep-cc=DEPCC"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --ld=LD"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --host-cc=HOSTCC"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --host-cflags=HCFLAGS"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --host-cppflags=HCPPFLAGS"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --host-ld=HOSTLD"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --host-ldflags=HLDFLAGS"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --host-libs=HLIBS"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --host-os=OS"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --extra-cflags=ECFLAGS"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --extra-cxxflags=ECFLAGS"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --extra-ldflags=ELDFLAGS"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --extra-libs=ELIBS"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --extra-version=STRING"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --optflags=OPTFLAGS"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --build-suffix=SUFFIX"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --malloc-prefix=PREFIX"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --progs-suffix=SUFFIX"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --arch=ARCH"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --cpu=CPU"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-pic"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-sram"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-thumb"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-symver"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-hardcoded-tables"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-safe-bitstream-reader"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-memalign-hack"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-lto"
    
    # Optimization options (experts only):
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-asm"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-altivec"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-amd3dnow"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-amd3dnowext"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-mmx"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-mmxext"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-sse"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-sse2"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-sse3"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-ssse3"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-sse4"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-sse42"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-avx"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-fma4"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-armv5te"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-armv6"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-armv6t2"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-vfp"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-neon"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-vis"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-inline-asm"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-yasm"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-mips32r2"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-mipsdspr1"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-mipsdspr2"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-mipsfpu"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-fast-unaligned"
    
    # Developer options (useful when working on FFmpeg itself):
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-coverage"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-debug"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-debug=LEVEL"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-optimizations"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-extra-warnings"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-stripping"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --assert-level=level"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-memory-poisoning"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --valgrind=VALGRIND"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-ftrapv"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --samples=PATH"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-xmm-clobber-test"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-random"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-random"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-random=LIST"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-random=LIST"
    # export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --random-seed=VALUE"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-linux-perf"
    export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-bzlib"
    复制代码
  5. 从终端进入FFmpeg文件目录,执行脚本

    • chmod +x build_android.sh修改脚本执行权限
    • ./build_android.sh all 运行脚本
    • 编译过程中会提示clang等执行软件不安全,在Mac的设置-安全性与隐私-通用选项中点击允许即可
  6. 编译完成后,在FFmpeg文件夹中会生成一个android文件夹,其中.so文件就是编译好的库

Android工程引入FFmpeg

  1. 新建C++项目或者使用上文OpenCV项目

  2. 将FFmpeg文件夹中的android/libs对应CPU型号的.so文件,复制到安卓工程app/src/main/jniLibs/对应CPU型号的文件夹内

  3. 修改build.gradle文件

    • 在android {defaultConfig {cmake { … }}}中添加,具体内容见下方代码
        defaultConfig {
            applicationId "com.bupt.compose3dvideo"
            minSdkVersion 25
            targetSdkVersion 30
            versionCode 1
            versionName "1.0"
    
            testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
            externalNativeBuild {
                cmake {
                    cppFlags ""
                    //以下为需要添加的代码
                    //可选 'arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64'
                    abiFilters 'x86', 'armeabi-v7a'
                }
            }
        }
    复制代码
    • 在android {…}中添加,具体内容见下方代码

          //以下为需要添加的代码
          sourceSets {
              main {
                  jni.srcDirs = ['/src/main/cpp']
                  jniLibs.srcDirs = ['src/main/jniLibs']
              }
          }
      复制代码
  4. 编辑CMakeList.txt。打开/app/src/main/cpp/CMakeLists.txt文件,添加以下内容

    add_library(avcodec
            SHARED
            IMPORTED)
    # 注意,IMPORTED_LOCATION是引入.so文件的路径,此路经必须正确
    set_target_properties(avcodec
            PROPERTIES IMPORTED_LOCATION
            ${CMAKE_CURRENT_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libavcodec.so)
    
    add_library(avformat
            SHARED
            IMPORTED)
    set_target_properties(avformat
            PROPERTIES IMPORTED_LOCATION
            ${CMAKE_CURRENT_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libavformat.so)
    
    add_library(avutil
            SHARED
            IMPORTED)
    set_target_properties(avutil
            PROPERTIES IMPORTED_LOCATION
            ${CMAKE_CURRENT_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libavutil.so)
    
    add_library(swresample
            SHARED
            IMPORTED)
    set_target_properties(swresample
            PROPERTIES IMPORTED_LOCATION
            ${CMAKE_CURRENT_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libswresample.so)
    
    add_library(swscale
            SHARED
            IMPORTED)
    set_target_properties(swscale
            PROPERTIES IMPORTED_LOCATION
            ${CMAKE_CURRENT_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libswscale.so)
    
    add_library(avfilter
            SHARED
            IMPORTED)
    set_target_properties(avfilter
            PROPERTIES IMPORTED_LOCATION
            ${CMAKE_CURRENT_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libavfilter.so)
    复制代码
  5. 在com/bupt/opencvandffmpeg目录下新建jni文件夹,新建FFmpegCmd类,实现FFmpeg函数的调用,类内容如下:

    object FFmpegCmd {
        private external fun run(cmdLen: Int, cmd: Array<String>): Int
        external fun test(): String?
        fun run(cmd: Array<String>): Int {
            return run(cmd.size, cmd)
        }
    
        init {
            System.loadLibrary("avutil")
            System.loadLibrary("avcodec")
            System.loadLibrary("swresample")
            System.loadLibrary("avformat")
            System.loadLibrary("swscale")
            System.loadLibrary("avfilter")
        }
    }
    复制代码

    之后就可以调用FFmpegCmd类中的函数。

    // 这里记录一下普通JNI使用方式:
    // 1. 在 Java 层编写 native 方法
    // 2. 使用 javah -jni 生成本地接口
    // 3. 在 C++ 层实现接口
    // 4. 在 Java 层调用 native 方法。
    复制代码

总结

虽然成功引入了Opencv和FFmpeg库,但是仍存在很多问题,首先就是引入库文件后导致安装包体积过大的问题,以后有待优化;其次是安卓JNI的使用问题,因为是使用C++编程,对于C++我还不是很熟悉,需要继续学习。

在成功引入Opencv和FFmpeg库之前,每次遇到error都让我很焦躁,因为觉得按照网上教程的步骤来一定会成功,然而不断的error让我有想砸鼠标的冲动,幸好在查找资料、学习进步的过程中不断积累经验,总结之前失败的原因,最终成功引入Opencv和FFmpeg库,所以写下这边文章以记录自己成功的经验。总结的经验就是一定要耐心查找资料,而且要了解这些开源库各版本之间编译或者使用的区别,如果遇到编译error,可以直接复制error信息上网查找解决方案,一步一步来最后终会成功!

参考资料:

AndroidStudio使用OpenCV的三种方式

Android 记录在macOS上使用 NDK20 编译ffmpeg4.2.2的过程

文章分类
Android