Android Studio集成FFmpeg和OpenCV

Android Studio集成FFmpeg和OpenCV

最近想從事音視頻方面的開發(fā),所以研究了FFmpeg和OpenCV,主要是家里沒有Mac,所以就想用安卓平臺學(xué)學(xué)習(xí)。這時就需要Android Studio集成FFmpeg和OpenCV。折騰了好一會,終于可以使用了。下面分享一下怎么使用最新的Android Studio3.2版本來集成FFmpeg和OpenCV,相關(guān)項目的鏈接在此。因為編譯好的OpenCV和FFmpeg文件非常大,所以我沒有上傳到Github里,而是放到了網(wǎng)盤里,讀者可以在這里下載該項目的相關(guān)文件。下面所有代碼都在小米平板4上和小米8上測試通過,還有華為和OPPO的一些機(jī)器。

首先是集成OpenCV,這相對于FFmpeg來說要簡單不少。官網(wǎng)已經(jīng)提供編譯好的各種平臺so文件下載。opencv-android下載頁面,這里說一下版本問題,一般官網(wǎng)提供的最新版本都是可以和目前最新的Android Studio兼容的。下載后解壓可以得三個文件夾:

解壓的OpenCV目錄

其中sample應(yīng)該是樣列了,apk里面是各平臺的OpenCV Manager APP安裝文件,不知道有什么用。sdk文件夾是我們最需要關(guān)心的。
sdk文件夾里的東西

我們需要集成的都是個文件夾里的東西。下面就開始新建Android項目

新項目

這里勾選了c++支持,因為后面還要集成FFmpeg


從Android5開始支持
空白項目

一路完成后,就會獲得一個新的Android項目,這下你可以試著運(yùn)行一下。
下面就是導(dǎo)入OpenCV SDK了。


import OpenCV SDK

選擇[File]->[New]->Import Module... 然后再選擇OpenCV SDK目錄里面的sdk/java目錄,這里系統(tǒng)會自動當(dāng)前OpenCV的版本,如下圖


導(dǎo)入OpenCV,其版本為3.4.3
因為我用新項目發(fā)現(xiàn)Gradle總是報錯,不想浪費(fèi)時間調(diào)了,我就用我自己項目來示列吧(我自己項目沒有加上343這個版本號)

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

這時選擇[File]->[Project Structure...] 選擇APP,右邊的選項卡選擇 [Dependencies]可以看到最下面已經(jīng)有了該模塊,如果沒有, 也可以在這里手動添加。點(diǎn)擊下面的+,選擇Module Dependency,再選擇OpenCV


添加OpenCV Module dependency

然后在app目錄下新建libs文件夾,將OpenCV Android SDK目錄native/libs里面所有的文件復(fù)制到app的libs文件夾里面。注意這里只有so文件,沒有a文件。


libs目錄下的OpenCV動態(tài)庫

然后再修改這兩個build.gradle文件


兩個build.gradle文件

將兩個文件里面的minSdkVersion改成21,targetSdkVersion改成26,(其實這個是要看項目情況的,改成其他值也行,但是要一至)


修改minSdkVersion和targetSdkVersion

最后一步就是在Module:app對應(yīng)的build.gradle文件加入以下內(nèi)容,注意是在android標(biāo)簽里
    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標(biāo)簽里)

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

至此Android Studio已經(jīng)配置好了OpenCV開發(fā)環(huán)境,接下來清空項目,然后寫代碼來驗證一下環(huán)境配置。

在MainActivity對應(yīng)的布局XML文件中加入一個Button按鈕一張圖片,圖片加載本地一張圖片,然后在MainActivity中為該Button添加一個事件,使該圖片變成灰階圖片。上面的代碼我就不列出來了,都是最基礎(chǔ)的。不過在此之前,需要在該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方法里調(diào)用這個方法。再在Button的響應(yīng)事件里寫以下代碼

        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()

代碼完成,啟動真機(jī)調(diào)試,不出意外就可以看到該圖片變灰了。

成功將一張圖片變成灰階

我發(fā)現(xiàn)有的機(jī)器上使用OpenCV非常卡,比如紅米Note4X 但是配置更低的華為和Oppo手機(jī)確不會有這種問題,目前還不清楚為什么會這樣。

好了,OpenCV的集成工作到此就完成了,下面將繼續(xù)集成FFmpeg,一般是先要將FFMpeg的源代碼編譯成Android所需要的so和頭文件,但是我使用最新的FFmpeg源代碼和NDK總是編譯出現(xiàn)問題,折騰了好幾天就放棄了,使用了別人編譯好的文件。
首先將下載好的ffmpeg_opencv_android文件解壓,里面有三個夾,其中l(wèi)ibs是OpenCV相關(guān)文件,這里就不用管了,jniLibs是編譯好的FFmpeg文件,cpp里是FFmpeg頭文件,這里需要將要這兩個文件夾拖到項目目錄->app->src->main目錄下


放置好的FFmpeg相關(guān)文件

再在app目錄下添加CMakeLists.txt文件,在里面添加以下內(nèi)容

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} )

這里面的設(shè)置參數(shù)其他我也不懂,有興趣有同學(xué)可以搜索相關(guān)文章了解。
接下來就是修改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++代碼更麻煩點(diǎn),新建一個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成功
最后就是實現(xiàn)C++的ffmpegInfo橋接方法了,在cpp目錄下的已經(jīng)存在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報錯,可以從錯誤信息找到正確的方法全名。
關(guān)于JNI這方面我還不太懂,所以我也是參考別人的教程寫的,只不過我用了Kotlin實現(xiàn)了。最后讓我們來寫代碼測試是FFmpeg是否集成成功
在一個空的頁面添加一個占整個頁面的TextView,然后在進(jìn)入這個頁面時添加以下代碼

 txtfinfo.text = FFmpegBridge.ffmpegInfo()

就這一行就行了
啟動真機(jī)調(diào)試,進(jìn)入該頁面后不出意外就可以看到打印出的FFmpeg相關(guān)信息


FFmpeg集成成功后獲取的信息

到此已經(jīng)全部完成Android Studio集成OpenCV和FFmpeg。 目前我還是初步學(xué)習(xí)階段,集成成功之后還要在這方面投入很多時間來學(xué)習(xí)。接下來需要總結(jié)iOS平臺怎么集成OpenCV和FFmpeg。最后再次放出項目地址

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,363評論 6 532
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,497評論 3 416
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,305評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,962評論 1 311
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,727評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,193評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,257評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,411評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,945評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,777評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,978評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,519評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,216評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,657評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,960評論 2 373

推薦閱讀更多精彩內(nèi)容