30.Android Studio下FFmpeg的編譯和使用(四.Android Studio ndk開發環境和CMakeLists腳本編寫)

項目源碼

1.環境配置

ffmpeg庫已經編譯好了,接下來準備將so引入Android studio進行開發
我們創建一個新的項目,注意在創建過程中這幾個選項的勾選


6C~L[SE8UA3Z]JEAFB~CU24.png

勾選添加C++支持,Android studio會自動幫我們做一些配置,後邊進行簡單的解釋


Y%~`T`0D1PM`(`}}$ENC9G2.png

C++ Standard:使用哪種 C++ 標準。選擇 Toolchain Default 會使用默認的 CMake 設置。有C11和C14兩種,我們選擇C11

Exceptions Support:如果希望啓用對 C++ 異常處理的支持,請選中此複選框。如果啓用此複選框,Android Studio 會將 -fexceptions 標誌添加到模塊級 build.gradle 文件的 cppFlags 中,Gradle 會將其傳遞到 CMake。

Runtime Type Information Support:(Run-Time Type Identification),通過運行時類型信息程序能夠使用基類的指針或引用來檢查這些指針或引用所指的對象的實際派生類型。如果希望支持 RTTI,請選中此複選框。如果啓用此複選框,Android Studio 會將 -frtti 標誌添加到模塊級 build.gradle 文件的 cppFlags 中,Gradle 會將其傳遞到 CMake。

項目創建出來之後,可以看到,項目默認創建了一個調用C++代碼的小demo輸出一行字符串。在app根目錄可以看到一個CMakeLists.txt的文件,這是添加c++支持後默認創建的cmake腳本,我們將使用這個腳本對ffmpeg進行編譯

打開app目錄下的build.gradle,可以看到下邊兩項配置

apply plugin: 'com.android.application'

android {
    ...
    defaultConfig {
        ...
        externalNativeBuild {
            cmake {
                //C++標準選擇C11之後做的配置
                cppFlags "-std=c++11"
            }
        }
    }
 
    externalNativeBuild {
        cmake {
            //指定的CMakeLists腳本文件的路徑
            path "CMakeLists.txt"
        }
    }
}

這是工具自動做好的配置,接下來還需要我們手動做一些處理,來完善ffmpeg編譯的環境。
第一. ffmpeg播放視頻會涉及到操作內存卡,所以需要配置存儲權限,6.0及以上Android版本還要記得動態權限獲取的配置,這裏不多說

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

第二. 在app main目錄下創建jniLibs目錄,將我們創建好的so放在這個目錄下,或者直接把so放在libs目錄下,但是這種方式需要我們在build.gradle中配置路徑,這裏選用第二種方式


dir.png.png

然後打開app下build.gradle文件,在android/defaultConfig節點下添加如下配置,指定so文件的存放目錄,默認是jniLibs

      sourceSets{
            //將so放在libs文件夾下,需要指定這個路徑,因爲默認路徑是jniLibs
            main{
                jniLibs.srcDirs=['libs']
            }
        }

還有一點,因爲我們只編譯了armeabi-v7a版本的ffmpeg,所以需要指定過濾版本,在android/defaultConfig/externalNativeBuild節點中添加

           ndk{
                abiFilters "armeabi-v7a"
            }

此時,整個build.gradle文件應該是這樣的(只留下了ndk相關的配置)

apply plugin: 'com.android.application'

android {
    ...
    defaultConfig {
        ...
        externalNativeBuild {
            cmake {
                cppFlags "-std=c++11"
            }
            //默認情況下,Gradle 會針對 [NDK 支持的 ABI](https://developer.android.google.cn/ndk/guides/abis.html?hl=zh-cn#sa) 
            //將原生庫構建到單獨的 .so文件中,並將其全部打包到 APK 中。如果希望 Gradle 僅構建和打包原生
            //庫的特定 ABI 配置,可以在模塊級build.gradle文件中使用 ndk.abiFilters標誌指定這些配置
            ndk{
                //abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a', 'arm64-v8a'
                abiFilters "armeabi-v7a"
            }
        }
        sourceSets{
            //將so放在libs文件夾下,需要指定這個路徑,因爲默認路徑是jniLibs
            main{
                jniLibs.srcDirs=['libs']
            }
        }
    }
    ...
    //將 Gradle 關聯到原生庫,需要提供一個指向 CMake 或 ndk-build 腳本文件的路徑。在構建應用時,Gradle
    //會以依賴項的形式運行 CMake 或 ndk-build,並將共享的庫打包到 APK 中
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }
}

dependencies {
    ...
}

2.CMakeLists.txt腳本文件的編寫

CMake 構建腳本是一個純文本文件,必須將其命名爲 CMakeLists.txt,一般放在項目根目錄(app的根目錄),後邊會附上我測試成功的CMakeLists.txt,
CMakeLists Android官方教程

add_library():

該命令用於向CMake構建腳本添加源文件和庫,它有三個參數,每個參數的解釋如下

add_library(
             //這個參數指定你的源文件被編譯或者庫被引入後的名字,可以指
             //定任意你覺得合適的名字
             native-lib
             //第二個參數有STATIC 和SHARED兩種選擇,SHARED表示會編
             //譯成動態庫,STATIC 表示靜態庫
             SHARED
             //這個位置用於指定源文件的相對路徑(相對於CMakeLists.txt的
             //路徑),或者如果你是在引入其他庫,那麼這裏指定IMPORTED
             //屬性
             src/main/cpp/native-lib.cpp )
set_target_properties:

如果你add_library引入的是已經編譯好的庫文件,那麼你需要通過set_target_properties指定被引入的庫文件的路徑

//這兩個一一對應,這兩個命令結合可以引入一個so庫,一個so庫對應這兩個命令
add_library(
              avcodec 
              SHARED 
              IMPORTED)
set_target_properties(
              //指定是給誰設置屬性,這裏是上邊add的avcodec 
              avcodec 
              //指定是設置什麼樣的屬性,這裏是引入的路徑,是一個相對路徑
              PROPERTIES IMPORTED_LOCATION          
              //這裏具體指定相對於腳本文件的路徑
              ${FFMPEG_DIR}/libavcodec.so)
include_directories():

通過上邊兩個命令,庫文件會被添加進來,這些庫一般會依賴一些頭文件,這時我們可以通過include_directories來指定頭文件的位置,確保 CMake 在編譯時可以定位到頭文件

include_directories(
              ${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/include)

以ffmpeg的編譯爲例,我們會add很多的so庫,add_library了好多次,將所有需要的so添加,我們還add了自己的源文件,最終我們指定了這些源文件被編譯成ffmpeg的so

add_library(
            ffmpeg 
            SHARED 
            ${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/native-lib.cpp )

這些源文件依賴於添加的ffmpeg中的庫,所以最終我們要把我們自己編譯的庫和其他這些so鏈接到一塊,這時就需要命令

target_link_libraries( 
              ffmpeg 
              avcodec 
              avfilter 
              avformat 
              avutil 
              swresample 
              swscale 
              ${log-lib} )

看到這裏有個這樣的引入${log-lib},這個庫是ndk中提供的,通過find_library命令引入

find_library

Android NDK 提供了一套實用的原生 API 和庫。通過將NDK 庫包含到項目的 CMakeLists.txt腳本文件中,預構建的 NDK 庫已經存在於 Android 平臺上,因此,無需再構建或將其打包到 APK 中。由於 NDK 庫已經是 CMake 搜索路徑的一部分,甚至不需要在本地 NDK 安裝中指定庫的位置 - 只需要向 CMake 提供希望使用的庫的名稱,並將其關聯到自己的原生庫上即可。

將 find_library()命令添加到您的 CMake 構建腳本中以定位 NDK 庫,並將其路徑存儲爲一個變量。可以使用此變量在構建腳本的其他部分引用 NDK 庫。以下示例可以定位Android 特定的日誌支持庫並將其路徑存儲在 log-lib 中

find_library( log-lib log )
3.編譯ffmpeg的CMakeLists.txt完整腳本
#指定Cmake構建工具的最低版本
cmake_minimum_required(VERSION 3.4.1)

#設置頭文件路徑
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/include)

#設置FFmpeg庫路徑變量
#CMAKE_CURRENT_SOURCE_DIR,指的是當前處理的 CMakeLists.txt 所在的路徑,CMAKE_SOURCE_DIR,不論採用何種編譯方式,都是工程頂層目錄

set(FFMPEG_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libs/${ANDROID_ABI})

#添加avcodec
add_library(avcodec SHARED IMPORTED)
set_target_properties(avcodec PROPERTIES IMPORTED_LOCATION ${FFMPEG_DIR}/libavcodec.so)

#添加avfilter
add_library(avfilter SHARED IMPORTED)
set_target_properties(avfilter PROPERTIES IMPORTED_LOCATION ${FFMPEG_DIR}/libavfilter.so)

#添加avformat
add_library(avformat SHARED IMPORTED)
set_target_properties(avformat PROPERTIES IMPORTED_LOCATION ${FFMPEG_DIR}/libavformat.so)

#添加avutil
add_library(avutil SHARED IMPORTED)
set_target_properties(avutil PROPERTIES IMPORTED_LOCATION ${FFMPEG_DIR}/libavutil.so)

#添加swresample
add_library(swresample SHARED IMPORTED)
set_target_properties(swresample PROPERTIES IMPORTED_LOCATION ${FFMPEG_DIR}/libswresample.so)

#添加swscale
add_library(swscale SHARED IMPORTED)
set_target_properties(swscale PROPERTIES IMPORTED_LOCATION ${FFMPEG_DIR}/libswscale.so)

add_library( ffmpeg SHARED ${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/native-lib.cpp )

find_library( log-lib log )

target_link_libraries( ffmpeg avcodec avfilter avformat avutil swresample swscale ${log-lib} )
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章