在Mac下使用NDK編譯FFmpeg3.3.4

概述

FFmpeg是一套非常強大的音視頻處理工具,許多開發過多媒體的朋友都繞不開它,圍繞著FFmpeg可以進行諸如音視頻解碼,裁剪,拼接,音視頻合并,以及支持多種流媒體的協議等等

今天就用目前最新的ffmpeg3.3.4源碼,使用NDK進行交叉編譯,生成Android項目上可以使用的庫,然后在APP上輸出當前FFmpeg的配置

我的編譯環境和IDE如下:

  • ffmpeg 3.3.4 版本源碼
  • macOS 10.12.5
  • NDK 13.1.3345770
  • Android Studio 2.3.3

編譯FFmpeg類庫

下載FFmpeg源碼

下載源碼的方式有兩種:

我這里是通過官網下載的,解壓后如下:

build_ffmpeg_1.png

配置腳本

配置configure

由于默認configure腳本編譯出來的動態庫版本號在文件名后綴.so之后,在Android上是識別不了的,比如長下面這樣:

build_ffmpeg_2.png

這時候需要對源碼根目錄下的configure進行一下小修改,我這個FFmpeg版本是在3305行開始,把下面四行

SLIBNAME_WITH_MAJOR='$(SLIBNAME).$(LIBMAJOR)'
LIB_INSTALL_EXTRA_CMD='$$(RANLIB) "$(LIBDIR)/$(LIBNAME)"'
SLIB_INSTALL_NAME='$(SLIBNAME_WITH_VERSION)'
SLIB_INSTALL_LINKS='$(SLIBNAME_WITH_MAJOR) $(SLIBNAME)'

注釋掉或者替換成下面這段

SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)'  
LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"'  
SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'  
SLIB_INSTALL_LINKS='$(SLIBNAME)'

修改后的配置如下圖,我這里選擇的是注釋掉:

build_ffmpeg_3.png

配置my_build_android.sh

這個文件源碼是沒有的,需要我們在源碼根目錄下手動新建一個。此腳本配置網上有很多,但是因為不同的人編譯時用的系統,用的NDK版本,都不可能完全一樣,所以這里一定要根據自己的實際情況,一步步找到自己系統ndk所在的目錄和arm-linux-androideabi-xx的版本,寫到配置上面

另外如果你復制網上的腳本時候有帶上空格等情況,運行腳本的時候也有可能報錯,這時候需要好好檢查

這里提供一份我自己編譯時用到的腳本供參考:

# ndk環境    
export NDK=/Users/Lyh/Library/Android/sdk/ndk-bundle
export SYSROOT=$NDK/platforms/android-21/arch-arm
export TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64
CPU=armv7-a

# 要保存動態庫的目錄,這里保存在源碼根目錄下的android/armv7-a
export PREFIX=$(pwd)/android/$CPU
ADDI_CFLAGS="-marm"
function build_android
{
./configure --target-os=linux --prefix=$PREFIX \
    --enable-cross-compile \
    --enable-shared \
    --disable-static \
    --disable-doc \
    --disable-ffmpeg \
    --disable-ffplay \
    --disable-ffprobe \
    --disable-ffserver \
    --disable-avdevice \
    --disable-doc \
    --disable-symver \
    --cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
    --arch=arm \
    --sysroot=$SYSROOT \
    --extra-cflags="-Os -fpic $ADDI_CFLAGS" \
    --extra-ldflags="$ADDI_LDFLAGS" \
    $ADDITIONAL_CONFIGURE_FLAG
make clean
# 不確定自己上面的目錄或者環境有沒有錯誤時
# 可以先注釋一下下面兩個命令
make
make install
}
build_android

執行腳本生成so和頭文件

進入源碼所在目錄,建好my_build_android.sh后,第一次執行會提示權限不夠,分配權限后重新執行即可,如下圖:

build_ffmpeg_4.png

NDK環境和目錄執行完正常如下(此時因為不確定環境有無錯誤,還未執行make):

build_ffmpeg_5.png

這里有個警告可以先忽略,我們在終端繼續先執行make和再make install

大約二十分鐘左右以后,可以看到已經生成動態庫(lib目錄)和頭文件(include目錄):

build_ffmpeg_6.png

注意

這里有個小技巧,就是在第一次運行你的腳本還不確定有沒有錯時,可以先把makemake install注釋掉,這樣就不會因為你腳本配置的目錄或者環境有問題時,仍然去繼續makemake install的執行,然后把你環境的錯誤給沖掉,給查找問題帶來困難

此外make執行的時間比較長,如果是到生成.o文件時出錯被中斷了,或者make執行完了,才發現沒有正確生成so的時候再回去檢查原因,就浪費非常多時間了

總結

先確保NDK環境和目錄沒報錯,再去執行make命令

在Android項目上使用

創建Android項目

跟創建一般的項目稍有不同的是下面兩個勾選
添加C++支持

build_ffmpeg_7.png
build_ffmpeg_8.png

把生成的頭文件和so導入到Android項目

這里我們參照NDK-Sample的hello-libs寫法,smaple的下載地址是:https://github.com/googlesamples/android-ndk ,已經有的可以不用再下
把第三方的so放到項目根目錄中,結構如下:

build_ffmpeg_9.png

編寫CmakeList和Gradle

把hello-libs項目里的CmakeList.txt文件復制到我們的項目中進行改造,原sample的cmakelist內容如下:


#
# Copyright (C) The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

cmake_minimum_required(VERSION 3.4.1)

# configure import libs
set(distribution_DIR ${CMAKE_SOURCE_DIR}/../../../../distribution)

add_library(lib_gmath STATIC IMPORTED)
set_target_properties(lib_gmath PROPERTIES IMPORTED_LOCATION
    ${distribution_DIR}/gmath/lib/${ANDROID_ABI}/libgmath.a)

# shared lib will also be tucked into APK and sent to target
# refer to app/build.gradle, jniLibs section for that purpose.
# ${ANDROID_ABI} is handy for our purpose here. Probably this ${ANDROID_ABI} is
# the most valuable thing of this sample, the rest are pretty much normal cmake
add_library(lib_gperf SHARED IMPORTED)
set_target_properties(lib_gperf PROPERTIES IMPORTED_LOCATION
    ${distribution_DIR}/gperf/lib/${ANDROID_ABI}/libgperf.so)

# build application's shared lib
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")

add_library(hello-libs SHARED
            hello-libs.cpp)

target_include_directories(hello-libs PRIVATE
                           ${distribution_DIR}/gmath/include
                           ${distribution_DIR}/gperf/include)

target_link_libraries(hello-libs
                      android
                      lib_gmath
                      lib_gperf
                      log)

把大段的注釋刪掉,再改寫后變成了下面這樣


cmake_minimum_required(VERSION 3.4.1)

# configure import libs
# 這里跟原Demo寫法不一樣要注意一下,因為Demo的CMakelist文件是在app下面的cpp目錄中
set(distribution_DIR ${CMAKE_SOURCE_DIR}/../ffmpeg)

add_library(avcodec-57 SHARED IMPORTED)
set_target_properties(avcodec-57  PROPERTIES IMPORTED_LOCATION
    ${distribution_DIR}/lib/armeabi-v7a/libavcodec-57.so)

# build application's shared lib
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")

add_library(native-lib SHARED
            src/main/cpp/native-lib.cpp)

include_directories(native-lib PRIVATE
                    ${distribution_DIR}/include)

target_link_libraries(native-lib
                      android
                      avcodec-57
                      log)

說明:

  • 有個set方法設置so路徑的地方需要格外注意一下,這個錯了下面都會影響到
  • Demo的hello-libs是同時添加靜態庫和動態庫,這里不需要,就把添加靜態庫部分刪掉了

這里我們暫且只加入avcodec-57這個動態庫

另外app目錄下的gradle文件也要配置一下:

android {
  compileSdkVersion 26
  buildToolsVersion "26.0.0"
  defaultConfig {
    applicationId "org.lyh.ffmpegdemo"
    minSdkVersion 14
    targetSdkVersion 26
    versionCode 1
    versionName "1.0"
    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    ndk {
      abiFilters 'armeabi-v7a'
    }
    externalNativeBuild {
      cmake {
        cppFlags "-frtti -fexceptions"
      }
    }
  }
  buildTypes {
    release {
      minifyEnabled false
      proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
  }

  sourceSets {
    main {
      // 注意路徑要寫對
      jniLibs.srcDirs = ['../ffmpeg/lib']
    }
  }

  externalNativeBuild {
    cmake {
      path 'CMakeLists.txt'
    }
  }
}

重點注意的是jniLibs.srcDirs的目錄要配置正確,架構這里只生成armv7-a

編寫C代碼和Java代碼

C代碼如下:

#include <jni.h>
#include <string>

extern "C" {
#include "libavcodec/avcodec.h"
    JNIEXPORT jstring JNICALL
    Java_org_lyh_ffmpegdemo_MainActivity_stringFromJNI(
            JNIEnv *env,
            jobject /* this */) {
    //    std::string hello = "Hello from C++";
        char info[10000] = {0};
        sprintf(info, "%s\n", avcodec_configuration());
        return env->NewStringUTF(info);
    }
}

原先自動生成的項目是運行后屏幕顯示”Hello from C++“。這里修改一下返回的字符串,改成得到FFmpeg的配置信息返回到Java層,然后顯示到Textview中。

Java代碼沒做修改,如下:

public class MainActivity extends AppCompatActivity {

  // Used to load the 'native-lib' library on application startup.
  static {
    System.loadLibrary("native-lib");
  }

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // Example of a call to a native method
    TextView tv = (TextView) findViewById(R.id.sample_text);
    tv.setText(stringFromJNI());
  }

  /**
   * A native method that is implemented by the 'native-lib' native library,
   * which is packaged with this application.
   */
  public native String stringFromJNI();
}

運行結果

最后運行如下:

build_ffmpeg_10.png

參考資料:

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

推薦閱讀更多精彩內容