Android Studio集成FFmpeg和OpenCV

Android Studio集成FFmpeg和OpenCV

最近想从事音视频方面的开发,所以研究了FFmpeg和OpenCV,主要是家里没有Mac,所以就想用安卓平台学学习。这时就需要Android Studio集成FFmpeg和OpenCV。折腾了好一会,终于可以使用了。下面分享一下怎么使用最新的Android Studio3.2版本来集成FFmpeg和OpenCV,相关项目的链接在此。因为编译好的OpenCV和FFmpeg文件非常大,所以我没有上传到Github里,而是放到了网盘里,读者可以在这里下载该项目的相关文件。下面所有代码都在小米平板4上和小米8上测试通过,还有华为和OPPO的一些机器。

首先是集成OpenCV,这相对于FFmpeg来说要简单不少。官网已经提供编译好的各种平台so文件下载。opencv-android下载页面,这里说一下版本问题,一般官网提供的最新版本都是可以和目前最新的Android Studio兼容的。下载后解压可以得三个文件夹:

解压的OpenCV目录

其中sample应该是样列了,apk里面是各平台的OpenCV Manager APP安装文件,不知道有什么用。sdk文件夹是我们最需要关心的。
sdk文件夹里的东西

我们需要集成的都是个文件夹里的东西。下面就开始新建Android项目

新项目

这里勾选了c++支持,因为后面还要集成FFmpeg


从Android5开始支持
空白项目

一路完成后,就会获得一个新的Android项目,这下你可以试着运行一下。
下面就是导入OpenCV SDK了。


import OpenCV SDK

选择[File]->[New]->Import Module... 然后再选择OpenCV SDK目录里面的sdk/java目录,这里系统会自动当前OpenCV的版本,如下图


导入OpenCV,其版本为3.4.3
因为我用新项目发现Gradle总是报错,不想浪费时间调了,我就用我自己项目来示列吧(我自己项目没有加上343这个版本号)

然后在你的app->build.gradle->dependencies里加上 implementation project(':OpenCVLibrary343') 也就是你刚导入的Module的名字

这时选择[File]->[Project Structure...] 选择APP,右边的选项卡选择 [Dependencies]可以看到最下面已经有了该模块,如果没有, 也可以在这里手动添加。点击下面的+,选择Module Dependency,再选择OpenCV


添加OpenCV Module dependency

然后在app目录下新建libs文件夹,将OpenCV Android SDK目录native/libs里面所有的文件复制到app的libs文件夹里面。注意这里只有so文件,没有a文件。


libs目录下的OpenCV动态库

然后再修改这两个build.gradle文件


两个build.gradle文件

将两个文件里面的minSdkVersion改成21,targetSdkVersion改成26,(其实这个是要看项目情况的,改成其他值也行,但是要一至)


修改minSdkVersion和targetSdkVersion

最后一步就是在Module:app对应的build.gradle文件加入以下内容,注意是在android标签里
    task nativeLibsToJar(type: Jar, description: 'create a jar archive of the native libs') {
        destinationDir file("$buildDir/native-libs")
        baseName 'native-libs'
        from fileTree(dir: 'libs', include: '**/*.so')
        into 'lib/'
    }

    tasks.withType(JavaCompile) {
        compileTask -> compileTask.dependsOn(nativeLibsToJar)
    }

然后在编译片段添加台下代码(dependencies标签里)

implementation fileTree(dir: "$buildDir/native-libs", include: 'native-libs.jar')

至此Android Studio已经配置好了OpenCV开发环境,接下来清空项目,然后写代码来验证一下环境配置。

在MainActivity对应的布局XML文件中加入一个Button按钮一张图片,图片加载本地一张图片,然后在MainActivity中为该Button添加一个事件,使该图片变成灰阶图片。上面的代码我就不列出来了,都是最基础的。不过在此之前,需要在该Activity加载OpenCV本地库。

   private fun loadOpenCV(){
        val success = OpenCVLoader.initDebug()
        if (success){
            Log.i("cvtag","OpenCV Libraries loaded...")
        }
        else{
            Toast.makeText(this.applicationContext,"Warning:Could not load the OpenCV Libraries", Toast.LENGTH_SHORT).show()
        }
    }

在onCreate方法里调用这个方法。再在Button的响应事件里写以下代码

        imgGray.isDrawingCacheEnabled = true //the id of the imageview
        val bitmap = Bitmap.createBitmap(imgGray.drawingCache)
        imgGray.isDrawingCacheEnabled = false
        val src = Mat()
        val dst = Mat()
        Utils.bitmapToMat(bitmap,src)
        Imgproc.cvtColor(src,dst, Imgproc.COLOR_BGRA2GRAY)
        Utils.matToBitmap(dst,bitmap)
        imgGray.setImageBitmap(bitmap)
        src.release()
        dst.release()

代码完成,启动真机调试,不出意外就可以看到该图片变灰了。

成功将一张图片变成灰阶

我发现有的机器上使用OpenCV非常卡,比如红米Note4X 但是配置更低的华为和Oppo手机确不会有这种问题,目前还不清楚为什么会这样。

好了,OpenCV的集成工作到此就完成了,下面将继续集成FFmpeg,一般是先要将FFMpeg的源代码编译成Android所需要的so和头文件,但是我使用最新的FFmpeg源代码和NDK总是编译出现问题,折腾了好几天就放弃了,使用了别人编译好的文件。
首先将下载好的ffmpeg_opencv_android文件解压,里面有三个夹,其中libs是OpenCV相关文件,这里就不用管了,jniLibs是编译好的FFmpeg文件,cpp里是FFmpeg头文件,这里需要将要这两个文件夹拖到项目目录->app->src->main目录下


放置好的FFmpeg相关文件

再在app目录下添加CMakeLists.txt文件,在里面添加以下内容

cmake_minimum_required(VERSION 3.4.1)

#set(distribution_DIR ${CMAKE_SOURCE_DIR}/libs)            # 文件夹为libs的情况下
set(distribution_DIR ${CMAKE_SOURCE_DIR}/src/main/jniLibs) # 文件夹为jniLibs的情况下
message("distribution_DIR:" ${distribution_DIR})

add_library( libavcodec
        SHARED
        IMPORTED )

add_library( libavfilter
        SHARED
        IMPORTED )

add_library( libavformat
        SHARED
        IMPORTED )

add_library( libavutil
        SHARED
        IMPORTED )


add_library( libswresample
        SHARED
        IMPORTED )

add_library( libswscale
        SHARED
        IMPORTED )

set_target_properties( libavcodec
        PROPERTIES IMPORTED_LOCATION
        ${distribution_DIR}/${ANDROID_ABI}/libavcodec.so)

set_target_properties( libavfilter
        PROPERTIES IMPORTED_LOCATION
        ${distribution_DIR}/${ANDROID_ABI}/libavfilter.so)

set_target_properties( libavformat
        PROPERTIES IMPORTED_LOCATION
        ${distribution_DIR}/${ANDROID_ABI}/libavformat.so)

set_target_properties( libavutil
        PROPERTIES IMPORTED_LOCATION
        ${distribution_DIR}/${ANDROID_ABI}/libavutil.so)


set_target_properties( libswresample
        PROPERTIES IMPORTED_LOCATION
        ${distribution_DIR}/${ANDROID_ABI}/libswresample.so)

set_target_properties( libswscale
        PROPERTIES IMPORTED_LOCATION
        ${distribution_DIR}/${ANDROID_ABI}/libswscale.so)

add_library(
        native-lib
        SHARED
        ${CMAKE_SOURCE_DIR}/src/main/cpp/native-lib.cpp) #如果是C++则为native-lib.cpp

find_library(
        log-lib
        log)

include_directories(src/main/cpp/include)

target_link_libraries( # Specifies the target library.
        native-lib
        libavcodec
        libavfilter
        libavformat
        libavutil
        libswresample
        libswscale
        ${log-lib} )

这里面的设置参数其他我也不懂,有兴趣有同学可以搜索相关文章了解。
接下来就是修改app目录下的build.gradle文件


修改好的build.gradle文件
   sourceSets {
        main {
            jniLibs.srcDirs = ['src/main/jniLibs']
        }
    }
    externalNativeBuild {
        cmake {
           cppFlags "-std=c++11"
        }
    }

    externalNativeBuild {
        //配置CMakeLists文件地址
        cmake {
            path 'CMakeLists.txt'
        }
    }

这样基本就配置完成了,下面写代码测试一下
和iOS不一样,在Android项目里写C++代码更麻烦点,新建一个Kotlin和C++相互操作的桥接文件FFmpegBridge.kt文件,在里面加载各个FFmpeg库

class FFmpegBridge{
     companion object {
         init {
             System.loadLibrary("avutil");
             System.loadLibrary("fdk-aac");
             System.loadLibrary("avcodec");
             System.loadLibrary("avformat");
             System.loadLibrary("swscale");
             System.loadLibrary("swresample");
             System.loadLibrary("avfilter");
             System.loadLibrary("native-lib")
         }
         external fun ffmpegInfo(): String
     }
}

可以看见Kotlin的语法和Java还是有比较大的差距的,这里的桥接方法是external fun ffmpegInfo(): String用来测试项目是否集成FFmpeg成功
最后就是实现C++的ffmpegInfo桥接方法了,在cpp目录下的已经存在native-lib.cpp文件,打开该文件添加以下方法

#include <jni.h>
#include <android/log.h>
//编码
#ifdef __cplusplus
extern "C"
{
#endif
#include "include/libavcodec/avcodec.h"
//封装格式处理
#include "include/libavformat/avformat.h"
//像素处理
#include "include/libswscale/swscale.h"

JNIEXPORT jstring JNICALL
Java_stan_androiddemo_tool_ffmpeg_FFmpegBridge_00024Companion_ffmpegInfo(JNIEnv *env,jobject) {
    char info[10000] = { 0 };
    sprintf(info, "%s\n", avcodec_configuration());
    return env->NewStringUTF(info);
}
#ifdef __cplusplus
}
#endif

注意这里,因为是写C++所以以加上#ifdef __cplusplus,还有就上方法命名一定要按命名空间+类名+方法名的形式,如果你不知道怎么命名可以先跑一次代码让Android Stduio报错,可以从错误信息找到正确的方法全名。
关于JNI这方面我还不太懂,所以我也是参考别人的教程写的,只不过我用了Kotlin实现了。最后让我们来写代码测试是FFmpeg是否集成成功
在一个空的页面添加一个占整个页面的TextView,然后在进入这个页面时添加以下代码

 txtfinfo.text = FFmpegBridge.ffmpegInfo()

就这一行就行了
启动真机调试,进入该页面后不出意外就可以看到打印出的FFmpeg相关信息


FFmpeg集成成功后获取的信息

到此已经全部完成Android Studio集成OpenCV和FFmpeg。 目前我还是初步学习阶段,集成成功之后还要在这方面投入很多时间来学习。接下来需要总结iOS平台怎么集成OpenCV和FFmpeg。最后再次放出项目地址

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容