因为毕设项目涉及在安卓手机上处理音视频,需要使用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
- 下载相关工具。点击工具栏的SDK Manager,点击SDK Tools,选中CMake和NDK(Side by side),点击apply即可下载所需工具
- 新建Android C++工程
- 下载OpenCV-Android-SDK。下载地址OpenCV Release
- Android工程引入OpenCV-SDK。点击 File -> new -> Import Module, 选中 OpenCV-Android-SDK/sdk/java 文件夹,点击确定,就会自动识别处模块,导入完成后,会在工程目录下发现 OpenCV 的库,settings.gradle 文件也会相应改变
- 修改OpenCV库的SDK版本。点击 File -> Project Structure- Modules ,选中OpenCV模块,修改SDK版本为你工程对应的SDK版本
- APP引入OpenCV依赖。打开app目录下的build.gradle,添加OpenCV依赖
此时点击 File -> Project Structure-Dependencies,选中app模块,可以看到已经引入opencv452的依赖dependencies { ... //path后填的之前导入的OpenCV Module名称 implementation project(path: ':opencv452') }
- 修改Opencv模块中的build.gradle设置。
- 首行的
apply plugin: ‘com.android.application’
修改为apply plugin: ‘com.android.library’
- 去掉
defaultConfig { applicationId "org.opencv" }
- 首行的
- 将 OpenCV-Android-SDK/sdk/native/libs 目录下全部内容复制到工程目录/app/src/main/jniLibs 目录下(jniLibs目录需要新建)
- 复制头文件。将 OpenCV-Android-SDK/sdk/native/jni/include 目录下全部内容复制到 工程目录/app/libs/include 目录下
- 编辑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)
- 修改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'] } }
- 在android {defaultConfig {cmake { … }}}中添加,具体内容见下方代码
- 初始化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") } }
- 创建BaseApplication或者在MainActivity中初始化,在onCreate()中调用
以上就是引入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
- 使用homebrew安装yasm、pkg-config、gcc
- 下载FFmpeg和NDK,官网下载Download FFmpeg和NDK官网下载NDK
- 在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