嗨~,大家好!
我是石頭~
由于工作有點忙,有段時間沒更新文章了.
這個系列的文章都是驗證過的,大家在閱讀的時候可以放心的實踐。
xxx
這篇文章主要介紹的是:
- 環(huán)境搭建--FFmpeg在Android中的開發(fā)(CMake)
- 相關技術理論:CMake,so版本
先介紹下我的運行環(huán)境
ffmpeg編譯環(huán)境
選擇自己系統(tǒng)對應的ndk
Android studio版本
IDE版本
等等,你們可能在想,~what?
我們之前不是都用mk去編譯的嗎,咋又用上了cmake?
首先,Android studio默認的編譯方式就是CMake
。
其次,cmake具有一些其他的優(yōu)勢,后面介紹.
CMake入門
現(xiàn)在我們先稍稍了解一下CMake,這樣才能對我們的集成,或者開發(fā)得心應手。
CMake 是一個開源的跨平臺自動化構建系統(tǒng)。官網地址:CMake
1.1 CMake 的特點
- 1)跨平臺,并可生成 native 編譯配置文件,在 Linux/Unix 平臺,生成 makefile,在
Mac 平臺,可以生成 xcode,在 Windows 平臺,可以生成 MSVC 的工程文件。
2)能夠管理大型項目;
3)簡化編譯構建過程和編譯過程。Cmake 的工具鏈非常簡單:cmake+make。
4)可擴展,可以為 cmake 編寫特定功能的模塊,擴充 cmake 功能。
好了,我們先了解一些特性就好了,講多了概念的東西你們就迷糊了,所以我們先實踐,在搭建完工程之后我們再來想想使用這些東西的原理,語法等等其他的東西,現(xiàn)在蠢蠢欲動的我們開始吧 (^ v ^)~~~.
實戰(zhàn)篇
Android CMake 的使用
先決條件
- 在打開的項目中,從菜單欄選擇 Tools > Android > SDK Manager。
- 點擊 SDK Tools 標簽。
- 選中 LLDB、CMake 和 NDK 旁的復選框,如下圖所示所示。從 SDK 管理器中安裝 LLDB、CMake 和 NDK
- 點擊 Apply,然后在彈出式對話框中點擊 OK。
- 安裝完成后,點擊 Finish,然后點擊 OK。
step1:新建一個項目
創(chuàng)建一個包含CMake新工程
之后一直
Next
,直到Finish
。這樣我們生成了一個帶有
CMake
編譯文件的項目。在Android模式下帶有CMake文件的項目
step2:把我們之前的編譯好的庫放進去
添加編譯好的庫
為什么要建立
armeabi-v7a
文件夾呢?這是因為Android studio編譯的時候是通過文件夾的名字去區(qū)別是x86,還是v7a等不同型號的so庫的。
step3:修改app
模塊下的build.gradle
添加框住的2部分代碼
因為我們我們在上一次編譯FFmpeg的時候就是指定了編譯的CPU型號,現(xiàn)在我們也要編譯我們的c或者c++對應的
so庫
,所以要指定對應的CPU型號,不然會編譯所有的型號的so庫
。所以現(xiàn)在我們只編譯了abi是
armeabi-v7a
的.so文件現(xiàn)在我們對比下加了
abiFilters "armeabi-v7a"
跟沒加的區(qū)別.
沒有過濾會編譯所有版本的so
加了abiFilters
step4:修改CMakeLists.txt
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
# 用來指定 CMake 最低版本為3.4.1,如果沒指定,執(zhí)行 cmake 命令時可能會出錯
cmake_minimum_required(VERSION 3.4.1)
# 添加在native層log庫
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
set(distribution_DIR ${CMAKE_SOURCE_DIR}/../libs)
include_directories(libs/include)
# FFmpeg編譯出了6個庫,這里添加----avutil
add_library( avutil
SHARED
IMPORTED )
set_target_properties( avutil
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/armeabi-v7a/libavutil.so )
# FFmpeg編譯出了6個庫,這里添加----swresample
add_library( swresample
SHARED
IMPORTED )
set_target_properties( swresample
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/armeabi-v7a/libswresample.so )
# FFmpeg編譯出了6個庫,這里添加----avcodec
add_library( avcodec
SHARED
IMPORTED )
set_target_properties( avcodec
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/armeabi-v7a/libavcodec.so )
# FFmpeg編譯出了6個庫,這里添加----avfilter
add_library( avfilter
SHARED
IMPORTED)
set_target_properties( avfilter
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/armeabi-v7a/libavfilter.so )
# FFmpeg編譯出了6個庫,這里添加----swscale
add_library( swscale
SHARED
IMPORTED)
set_target_properties( swscale
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/armeabi-v7a/libswscale.so )
# FFmpeg編譯出了6個庫,這里添加----avformat
add_library( avformat
SHARED
IMPORTED)
set_target_properties( avformat
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/armeabi-v7a/libavformat.so )
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
add_library( native-lib
SHARED
src/main/cpp/native-lib.cpp
# TODO 我們之后自己編寫的cpp文件都會添加在這里比如
# src/main/cpp/test1.cpp
# src/main/cpp/test2.cpp
)
target_link_libraries( # Specifies the target library.
native-lib
GLESv2 EGL
OpenSLES
android
# 這里需要注意下,下面這些ffmpeg的so庫編譯是有先后順序的
# 下面的順序是沒問題的,如果想驗證編譯順序,可以自己變換順序試試.
avutil avformat avcodec swscale swresample avfilter
# Links the target library to the log library
# included in the NDK.
${log-lib} )
至此,我們終于把環(huán)境搭好了(^ v ^).
如果沒有搭好就不要灰心,先clone一下這個項目繼續(xù)后面的學習,到后面自己重新再搭一遍.
不要因為在這里卡太久而放棄了入門音視頻的路,加油~~~,記得繼續(xù)學習后面的部分喲
你們是不是感覺項目搭建起來了就可以開干了呢~~
兄弟呀~~~~冷靜,一開始我就說過,我們先動手做,有印象之后再講原理,你不能學會了實戰(zhàn),就不要原理了吧 (^ v ^),下面來講講上面的技術所涉及的一些原理和理論的部分.
理論篇--CMake
1.2 CMake學習
在Android Developers有加上CMake的相應文章
Google 官方網站上有對 CMake 的使用示范,可以參考 官方指南。
CMake基本語法
- 使用
#
號作為注釋; - 變量使用
${}
方式取值,但是在 IF 控制語句中是直接使用變量名; - 指令名(參數(shù)1 參數(shù)2 …),其中參數(shù)之間使用空格或分號隔開;
- 指令與大小寫無關,但參數(shù)和變量是大小寫相關的;
CMake常用指令
---------------------------------------------------------------
1. set 指令
語法:set(VAR [VALUE])
這個指令是用來顯式地定義變量,多個變量用空格或分號隔開
例如:set(distribution_DIR ${CMAKE_SOURCE_DIR}/../libs)
Tips: 當需要用到定義的 distribution_DIR 變量時,需要用${var}的形式來引用,
如:${distribution_DIR}
不過,在 IF 控制語句中可以直接使用變量名。
---------------------------------------------------------------
2. add_library 指令
語法:add_library(libname [SHARED | STATIC | MODULE] [EXCLUDE_FROM_ALL] [source])
將一組源文件 source 編譯出一個庫文件,并保存為 libname.so (lib 前綴是生成文件時 CMake自動添加上去的)。
其中有三種庫文件類型,不寫的話,默認為 STATIC:
SHARED: 表示動態(tài)庫,可以在(Java)代碼中使用 System.loadLibrary(name) 動態(tài)調用;
STATIC: 表示靜態(tài)庫,集成到代碼中會在編譯時調用;
MODULE: 只有在使用 dyId 的系統(tǒng)有效,如果不支持 dyId,則被當作 SHARED 對待;
EXCLUDE_FROM_ALL: 表示這個庫不被默認構建,除非其他組件依賴或手工構建
#將compress.c 編譯成 libcompress.so 的共享庫
add_library(compress SHARED compress.c)
add_library 命令也可以用來導入第三方的庫:
add_library(libname [SHARED | STATIC | MODULE | UNKNOWN] IMPORTED)
如,導入 libjpeg.so
add_library(libjpeg SHARED IMPORTED)
導入庫后,當需要使用 target_link_libraries 鏈接庫時,可以直接使用該庫
---------------------------------------------------------------
3. set_target_properties 指令
語法: set_target_properties(target1 target2 … PROPERTIES prop1 value1 prop2 value2 …)
這條指令可以用來設置輸出的名稱(設置構建同名的動態(tài)庫和靜態(tài)庫,或者指定要導入的庫文件的路徑),對于動態(tài)庫,還可以用來指定動態(tài)庫版本和 API 版本。
如,set_target_properties(hello_static PROPERTIES OUTPUT_NAME “hello”)
設置同名的 hello 動態(tài)庫和靜態(tài)庫:
set_target_properties(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)
set_target_properties(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
指定要導入的庫文件的路徑
add_library(jpeg SHARED IMPORTED)
#注意要先 add_library,再 set_target_properties
set_target_properties(jpeg PROPERTIES IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/libs/${ANDROID_ABI}/libjpeg.so)
設置動態(tài)庫 hello 版本和 API 版本:
set_target_properties(hello PROPERTIES VERSION 1.2 SOVERSION 1)
和它對應的指令:
get_target_property(VAR target property)
如上面的例子,獲取輸出的庫的名字
get_target_property(OUTPUT_VALUE hello_static OUTPUT_NAME)
message(STATUS "this is the hello_static OUTPUT_NAME:"${OUTPUT_VALUE})
---------------------------------------------------------------
4. find_library 指令
語法:find_library( name1 path1 path2 …)
VAR 變量表示找到的庫全路徑,包含庫文件名 。例如:
find_library(libX X11 /usr/lib)
find_library(log-lib log) #路徑為空,查找系統(tǒng)環(huán)境變量路徑
---------------------------------------------------------------
5. include_directories 指令
語法:include_directories([AFTER | BEFORE] [SYSTEM] dir1 dir2…)
這個指令可以用來向工程添加多個特定的頭文件搜索路徑,路徑之間用空格分割,
如果路徑中包含了空格,可以使用雙引號將它括起來,
默認的行為是追加到當前的頭文件搜索路徑的后面。
---------------------------------------------------------------
6. target_link_libraries 指令
語法:target_link_libraries(target library library2…)
這個指令可以用來為 target 添加需要的鏈接的共享庫,
同樣也可以用于為自己編寫的共享庫添加共享庫鏈接。
如:
#指定 compress 工程需要用到 libjpeg 庫和 log 庫
target_link_libraries(compress libjpeg ${log-lib})
同樣,link_directories(directory1 directory2 …) 可以添加非標準的共享庫搜索路徑。
---------------------------------------------------------------
到這里,我們學習了實戰(zhàn)里面技術的理論部分.
下一次,我們開始真正的ffmpeg學習