iOS: FFmpeg編譯和使用 學習

ffmpeg是一個多平臺多媒體處理工具,處理視頻和音頻的功能很是強大。目前在網上搜到的iOS上使用FFMPEG的資料都比較陳舊,而FFMPEG更新迭代比較快; 且網上的講解不夠詳細,對於初次接觸FFMPEG的新手(例如我)來講確實不太好使用。爲了防止忘記,這裏對iOS下使用FFMPEG作一個總結。html

1. FFMPEG層次結構的簡單理解linux

要使用FFMPEG,首先須要理解FFMPEG的代碼結構。根據志哥的提示,ffmpeg的代碼是包括兩部分的,一部分是library,一部分是tool。api都是在library裏面,若是直接調api來操做視頻的話,就須要寫c或者c++了。另外一部分是tool,使用的是命令行,則不須要本身去編碼來實現視頻操做的流程。實際上tool只不過把命令行轉換爲api的操做而已。ios

2. 預熱-在mac os下使用ffmpegc++

在mac os下使用ffmpeg比較簡單,能夠直接使用命令行來操做。首先安裝ffmpeg,這裏默認系統已經安裝好brew,只須要在終端上輸入:git

brew install ffmpeggithub

等待安裝結束便可。api

安裝結束後,嘗試如下命令:xcode

ffmpeg -i input.mp4 output.avi網絡

若是能順利轉換,代表安裝成功架構

3. 編譯能在iOS下使用的FFMPEG library庫

這一步是編譯1所說的library,編譯好以後能夠調用FFMPEG的api。網上有一些方法,但都要本身手動編譯,稍顯複雜並且比較陳舊。按照app store的需求,編譯出來的包還必須支持arm64。我在萬能的github中找到一個可以"一鍵編譯"的腳本,地址以下:

https://github.com/kewlbear/FFmpeg-iOS-build-script

並且寫這個腳本的歪果仁挺好人,更新很及時,已經更新到了最新的2.5.3版本。下載下來,只有一個build-ffmpeg.sh腳本文件。在終端中轉至腳本的目錄,執行命令:

./build-ffmpeg.sh

腳本則會自動從github中把ffmpeg源碼下到本地並開始編譯。

編譯結束後,文件目錄以下:

【iOS開發】iOS下使用FFMPEG的一些總結

其中,ffmpeg-2.5.3是源碼,FFmpeg-iOS是編譯出來的庫,裏面有咱們須要的.a靜態庫,一共有7個。

執行命令:

lipo -info libavcodec.a

查看.a包支持的架構,這幾個包都支持了armv7 armv7s i386 x86_64 arm64這幾個架構,這個腳本果然是業界良心啊~~~

4.在xcode中引入FFMPEG library庫

新建工程,把上面編譯好的FFmpeg-iOS拖到xcode工程中,添加一個頭文件引用

#include "avformat.h"

添加一個api語句:

av_register_all();

添加一個空的類,把執行文件.m後綴改成.mm,開啓混編模式。

添加相應的framework,包括avfoundation和coremedia。

運行工程,若是沒有報錯,則代表編譯成功。

5.在xcode項目中使用命令行

執行到第4步,已經可使用library庫了。可是若是要對視頻進行操做,仍是須要手動寫不少代碼去調用api,工做量較大,天然不如直接寫命令行方便。爲了命令行可以在xcode工程中使用,還須要作如下工做:

(1)添加源碼中的tools,具體文件包括:

【iOS開發】iOS下使用FFMPEG的一些總結

(2)添加Header Search Paths

在target--build setting中搜索Header Search Paths,並在Header Search Paths下面添加源碼ffmpeg-2.5.3和scratch的路徑。

(3)修改ffmpeg.h和ffmpeg.c源碼

若是此時run這個工程,則會報錯,緣由是工程裏面有2個main函數,此時處理方法爲:

在ffmpeg.h中添加一個函數聲明:

int ffmpeg_main(int argc, char **argv);

在ffmpeg.c中找到main函數,把main函數改成ffmpeg_main。

(4)調用命令行範例

添加頭文件:#import "ffmpeg.h"

調用命令行

int numberOfArgs = 16;

char** arguments = calloc(numberOfArgs, sizeof(char*));

arguments[0] = "ffmpeg";

arguments[1] = "-i";

arguments[2] = inputPath;

arguments[3] = "-ss";

arguments[4] = "0";

arguments[5] = "-t";

arguments[6] = durationChar;

arguments[7] = "-vcodec";

arguments[8] = "copy";

arguments[9] = "-acodec";

arguments[10] = "aac";

arguments[11] = "-strict";

arguments[12] = "-2";

arguments[13] = "-b:a";

arguments[14] = "32k";

arguments[15] = outputPath;

int result = ffmpeg_main(numberOfArgs, arguments);

其中inputpath和outputpath是文件路徑。經測試,這兩個路徑不支持asset-library://協議和file:// 協議,因此若是是要用相冊的文件,我目前的解決辦法是把它拷貝到沙盒裏面。

6. 改關閉進程爲關閉線程

若是順利進行到了第5步,在app中是可以用命令行處理視頻了,但會出現一個問題,app會退出。經肖大神提醒,發現了命令行執行完畢以後會退出進程。而iOS下只能啓動一個進程,所以必須改關閉進程爲關閉線程,或者直接把關閉進程的方法給注掉。

在ffmpeg.c中能夠看到,執行退出進程的方法是exit_program,定位到了cmdutils.c中執行了c語言的exit方法。這裏我將它改成了pthread_exit(須要添加#include 頭文件)。在xcode項目中使用時,則能夠用NSThread來新開一個線程,執行完畢以後,把線程關閉了便可。再使用NSThreadWillExitNotification通知,便可監聽線程退出的狀況。

7. 修復ffmpeg.c裏面的一個bug

在實際項目中,可能須要屢次調用命令行,但在屢次調用命令行的過程當中,發現ffmpeg.c的代碼中會訪問空屬性致使程序崩潰。逐步debug後發現,不少指針已經置空了,但它們的計數卻沒有置零,不知道是否是ffmpeg.c的一個bug。修復方法以下:在ffmpeg_cleanup方法下,將各個計數器置零,包括:

nb_filtergraphs

nb_output_files

nb_output_streams

nb_input_files

nb_input_streams

置零以後,重複使用ffmpeg_main方法一切正常。



文/L1先生(簡書做者)
原文連接:http://www.jianshu.com/p/52516bdc1eb5
著做權歸做者全部,轉載請聯繫做者得到受權,並標註「簡書做者」。

1、背景

  網上有不少FFmpeg編譯配置的資料,大部分都是關於FFmpeg最新的版本(2.0)的,我一開始也想着編寫一個2.0版本的,能夠放到接手的那個項目中,發現各類問題(沒法快進,沒有聲音),再看一下代碼一堆警告,緣由很簡單,使用的FFMpeg庫太新了,不少接口變更了。因爲手上沒有多少信息,不知道那個項目使用的是哪一個版本的FFmpeg庫,一點點找,終於知道原來使用的是0.7.x的。找到目標版本的FFmpeg本覺得萬事大吉了,後來才發現原來這纔是坑的開始,有歷經一系列磨難,最後終於把編譯問題解決了。

  

2、FFmpeg最新版本的庫編譯

  FFmpeg最新版本的應該是2.1的,歷史版本詳見http://www.ffmpeg.org/releases/,在這個網站上咱們能夠下到全部歷史版本的庫。FFmpeg是一個跨平臺的用C語言寫成的庫,包含了編碼,解碼,色彩空間轉換等庫。編譯須要用到命令行,對於咱們這些沒搞事後臺或者linux開發的腳本知識欠缺的人來講的確算是一個挑戰。慶幸的是如今網絡這麼方便,不會作問Google,很快就找到了一個在xcode5下一鍵編譯FFmpeg庫的腳本。這個腳本是個老外寫的,真心強大,從下載到編譯到構建最後的Fat庫一鼓作氣。

  腳本地址: https://gist.github.com/m1entus/6983547

  運行這個腳本須要依賴一個庫Perl寫的腳本,搜了一下網上目前編譯FFmpeg庫的帖子基本都會提到這個腳本,腳本地址以下: https://github.com/mansr/gas-preprocessor

  下載完這兩個腳本後,編譯FFmpeg庫的準備工做就基本完成了,接着依次執行下面幾步:

  一、拷貝gas-preprocessor.pl文件到 /usr/bin目錄下。

  二、修改gas-preprocessor.pl文件的權限

  注:須要有讀,寫和執行的權限。具體操做爲,首先在命令行下進入/usr/bin目錄,而後執行chmod命令,以下圖所示:

  三、切換build-ffmpeg.sh腳本的目錄下,使用命令sh build-ffmpeg.sh 運行該腳本便可。

 

  注:  1) build-ffmpeg.sh腳本的父目錄的名字不能包括空格,不然可能致使構建失敗。

      2) build-ffmpeg.sh腳本中能夠配置編譯的FFMpeg版本,以及使用iOS SDK的版本,以下圖所示:

 

  該腳本中默認採用的FFmpeg是2.0版本,使用iOS 7.0的SDK編譯,c語言編譯器採用clang,應用中能夠根據實際項目須要選中不一樣的FFmpeg和iOS SDK版本。

  根據上面的步驟看來,編譯工做也沒有什麼複雜的,爲何我會說踩了不少坑呢?這個問題我會一點點兒解釋。

 

3、編譯較早期版本的FFmpeg本庫

  第二部分中咱們介紹了一個牛逼的腳本,一鍵編譯,這給咱們形成了一種錯覺,FFmpeg編譯不過如此嗎!若是咱們嘗試一下把腳本中的VERSION變成0.7試試,運行腳本,發現編譯報錯。以下圖所示:

  提示位置選項--disable-iconv,根據提示咱們輸入./configure查看全部可用選項。命令行下切換到實際的FFmpeg源碼目錄下,查看幫助以下圖:

  咱們能夠看到不少選項,英語不難,就是有些選項描述的太簡潔了,因此實際使用時若是不肯定的話,咱們能夠去問問google。

  好了回過頭來看看這個configure文件到底有什麼做用呢?

  一、裁剪

  咱們知道FFmpeg庫是一個很是龐大的庫,包括編碼,解碼以及流媒體的支持等,若是不作裁剪所有編譯進來的話,最後生成的靜態庫會很大。實際使用中咱們可能只想用到解碼(例如播放器),所以咱們可使用相關選項指定編譯時禁用編碼部分。固然咱們還能夠作進一步的裁剪,例如只打開部分經常使用格式的解碼,禁用掉其餘的解碼,這樣編譯出來的靜態庫將會更小。

  要想裁剪,咱們的先知道有哪些部分,使用下面的命令能夠查看FFMpeg庫支持的組件列表。

1
2
3
4
5
6
7
8
9
10
11
--list-decoders          show all available decoders
--list-encoders          show all available encoders
--list-hwaccels          show all available hardware accelerators
--list-muxers            show all available muxers
--list-demuxers          show all available demuxers
--list-parsers           show all available parsers
--list-protocols         show all available protocols
--list-bsfs              show all available bitstream filters
--list-indevs            show all available input devices
--list-outdevs           show all available output devices
--list-filters           show all available filters

  咱們能夠根據實際須要把不用的部分都禁用掉,這樣編譯快,包也會比較小,經常使用的裁剪選項以下:

1
2
3
4
5
6
7
8
9
10
11
12
--disable-doc             do  not build documentation
--disable-ffmpeg         disable ffmpeg build
--disable-ffplay         disable ffplay build
--disable-ffserver       disable ffserver build
--disable-network        disable network support [no]
--disable-encoder=NAME   disable encoder NAME
--enable-encoder=NAME    enable encoder NAME
--disable-encoders       disable all encoders
--disable-decoder=NAME   disable decoder NAME
--enable-decoder=NAME    enable decoder NAME
--disable-decoders       disable all decoders
--disable-hwaccel=NAME   disable hwaccel

  舉個例子,若是咱們須要作一款本地視頻播放器,那麼咱們可使用以下配置:

  

  固然你還能夠根據幫助列表進行更細粒度的裁剪,例如只支持哪幾種格式的解碼等等。

 

  二、指定編譯環境

  FFMpeg做爲一個跨平臺的庫,不一樣的平臺,不一樣的人的計算機上編譯器的路徑均可能不盡相同,因此咱們須要爲編譯腳本指定編譯器的路徑。同事咱們還能夠指定其餘編譯選項,如是否交叉編譯,目標平臺系統,CPU架構,須要依賴的其餘庫的路徑已經指定是否禁用匯編優化等。

1
2
3
4
5
6
7
8
9
10
11
--enable-cross-compile   assume a cross-compiler is used
--sysroot=PATH           root of cross-build tree
--sysinclude=PATH        location of cross-build system headers
--target-os=OS           compiler targets OS []
--cc=CC                  use C compiler CC [gcc]
--extra-cflags=ECFLAGS   add ECFLAGS to CFLAGS []
--extra-ldflags=ELDFLAGS add ELDFLAGS to LDFLAGS []
--arch=ARCH              select architecture []
--cpu=CPU                select the minimum required CPU (affects
                          instruction selection, may crash on older CPUs)
--disable-asm            disable all assembler optimizations

  sysroot即iOS SDK的路徑,注意編譯真機版本的庫時須要使用iPhoneOS.platform中SDK的路徑,編譯模擬器版本的庫使用iPhoneSimulator.platform中SDK的路徑。target-os填寫darwin(蘋果系統的內核),arch能夠根據具體的狀況添加i386(模擬器),armv6,armv7等。cpu根據具體類型可填寫cortex-a8,cortox-a9,i386等。   

 

  三、指定靜態庫的安裝路徑

  指定執行make install命令時編譯好的靜態庫和相關頭文件拷貝到的位置,即FFmpeg庫編譯後輸出的路徑。一般咱們只須要設置「--prefix=PREFIX」選項便可。例如咱們須要將最後生成靜態庫的路徑指向「build/armv7」下,則設置--prefix="build/armv7";

   

4、FFmpeg0.7版本庫一鍵編譯腳本

  經過第三部分的介紹,相信咱們應該對FFmpeg的配置都有了一個初步的認識,咱們再回到第三部分開始時咱們運行build-ffmpeg.sh的碰到的問題,通過查看configure的幫助,咱們發現0.7這個版本的FFmpeg庫倒是沒有"--disable-iconv"選項。這個牛逼的腳本是針對當前較新的FFmpeg庫寫的,在低版本中沒有一些配置選項也是正常。

  下面給出通過修改後的腳本,腳本中對原先的腳本進行了精簡,去掉了下載部分的代碼。

  build-ffmpeg0.7

  注:因爲FFmpeg庫比較陳舊,該腳本使用xcode4.6下,編譯器爲GCC,採用6.1的SDK進行編譯。若是你的機器上裝的同事安裝了xcode4.x和xcode5的話,能夠在命令行下使用以下命令切換當前的默認編譯環境爲xcode4.6便可:

  設置好xcode的編譯環境之後,只須要將該腳本拷貝到FFMpeg源文件路徑下運行便可一鍵生成armv7,armv7s,i386以及合成後的全平臺庫。

 

5、如何使用以及編譯連接中可能遇到的問題

  第四部分中咱們對build-ffmpeg.sh的腳本進行了修改和精簡後獲得了build-ffmpeg0.7.sh,咱們只須要運行該腳本就能夠一鍵完成FFmpeg 0.7版本庫的編譯工做了。編譯後咱們獲得的是lib目錄(包含全部生成的靜態庫)以及include目錄(包含相應的頭文件),使用時咱們只須要將這些文件添加到工程中便可。

  問題到這裏彷佛就所有解決了,若是順利的話,恭喜你,你能夠直接使用了。

  若是你跟我同樣的"不幸"的話,可能還會遇到一些其餘問題。下面是我遇到的問題及解決辦法:

  一、time.h重複問題

  咱們知道通常靜態庫都是搭配頭文件使用的,要在項目裏面使用FFmpeg庫,咱們出了須要在xcode的build phases中添加靜態庫之外,還須要導入該庫對應的頭文件。FFmpeg庫對應的頭文件有不少,一般會採用設置header search path的方式來導入頭文件,這樣作有兩個好處: 第一能夠避免對咱們的工程結構形成干擾。第二能夠在必定程序上下降頭文件衝突。

  time.h衝突的問題就是屬於頭文件衝突,系統的標準庫中有time.h文件,FFmpeg應該是在1.1以後也加入了一個time.h文件,路徑爲libavutil/time.h。因此若是你使用的是FFmpeg1.1以後的版本,那麼在使用中就可能會碰到頭文件衝突的問題。解決這個問題,網上流傳一個方法是修改FFmpeg庫中time.h文件的名字,我以爲這太麻煩了,並且也容易出錯。後來查看FFmpeg源碼的時候偶然發現它自身內部引用這個time.h的時候都有帶一層父目錄,如#include "libavutil/time.h"。所以想是否是經過指定頭文件搜索路徑就能夠解決這個問題。

  打開工程設置頁面,搜索header search path以下圖所示:

  若是你的FFmpeg庫正好是放在當前的路徑下,且爲了偷懶設置了遞歸包含頭文件的話,那麼你極可能就會遇到time.h衝突的問題。由於xcode工程默認的設置是優先查找用戶路徑,編譯時FFmpeg中libavutil下的time.h就會優先被連接,從而致使不會再連接系統time.h文件,最終致使編譯失敗。

  解決這個問題有兩個辦法:

  a、取消掉Header Search Paths中的遞歸引用。

  b、設置Always Search User Paths爲NO。

 

  二、gcc c compiletest error問題
  xcode5下面編譯FFmpeg都採用clang,一樣也會遇到相似問題。這個問題一般出如今配置文件錯誤的狀況下,通常都是gcc路徑錯誤,固然也多是其餘編譯參數錯誤問題。

  出現這個問題咱們應該首先檢查gcc的路徑是否正確,若是確認了指定路徑上存在gcc程序,可是仍是報錯的,咱們再去檢查當前要編譯的平臺和指定的gcc路徑是否一致,若是你使用iPhoneOS.platform下面的gcc去編譯i386平臺的庫那確定是不會測試經過的。

 

  三、C compiler test failed問題
  編譯i386版本的FFmpeg庫和armv版本庫可能用到的參數不盡相同,例如我遇到這個問題,個人編譯選項中有一項以下:

  --extra-cflags='-arch i386 -mfloat-abi=softfp -miphoneos-version-min=5.0'

  在我確認其餘參數(如cpu,arch)都正確的狀況下,依然提示咱們「C compiler test failed.」 後面緊跟着一句查看config.log你能夠獲得更詳細的信息,因而打開該文件,你能夠在最開始的地方看到你的配置語句,若是是用腳本,這塊兒會顯示最終解釋後(替換參數爲真實值)的配置語句。而後緊跟着一堆具體的配置,一般哭啼的錯誤信息會在該文件的最末尾。我遇到的問題的信息以下:

  

  看到標紅的這個區域了沒有,提示「-mfloat-abi=softfp」選項不支持,刪掉該選項後,在運行時配置就經過了。其餘配置問題,均可以經過查看config.log來獲取更詳細的錯誤信息。

 

  四、因爲未導入libbz動態庫的問題

  若是導入FFmpeg庫了,而且配置了頭文件搜索路徑,遇到"Undefined symbols for architecture armv7s: _BZ2_bzDecompressInit",以下圖所示:

 

  這個問題是因爲沒有導入「libbz2.dylib」庫的緣由,導入庫便可解決該問題。

 

  五、libavcodec/audioconvert.h頭文件缺失問題

  不知道爲何執行make install的時候libavcodec中的audioconvert.h怎麼沒有拷貝到include目錄下的libavcodec中去,查看發現原來libavutil目錄下已經有一個audioconvert.h了。解決這個問題只須要從FFmpeg庫的libavcodec中拷貝audioconvert.h頭文件到include的libavcodec目錄中便可解決。

 

6、雜談

  感謝我所遭遇的"不幸",若是當時接受的項目使用的最新版本的FFmpeg庫,我可能就直接運行一下那個牛逼的腳本,而後一切就能夠順順利利。若是真是那樣的話,我可能也就不會花時間去學習基本的腳本知識,去了解FFmpeg庫的相關配置,這樣的結果就是下次當我中獎遇到FFmpeg庫編譯連接等問題時,只能一籌莫展。

  說了這麼多,當咱們使用一個技術的時候,不該該僅僅停留在會用的層次,花點兒時間瞭解一下背後的原理會更讓你對該技術有個更深的理解,多學,多看,多思考,最終會有有所收穫的。

 

7、編譯腳本及參考資料

  一、編譯腳本  

  gas-preprocessor腳本地址https://github.com/mansr/gas-preprocessor  

  FFmpeg 2.x一鍵化編譯腳本: https://gist.github.com/m1entus/6983547

  FFmpeg0.7一鍵化編譯腳本: https://gist.github.com/smileEvday/7565260

 

  二、參考資料

  模擬器與真機下ffmpeg的編譯方法(總結版)

  http://www.cocoachina.com/iphonedev/toolthain/2011/1020/3395.html

  編譯在ios4.3中使用的ffmpeg庫(轉)

  http://www.cocoachina.com/bbs/simple/?t70887.html

  Installing ffmpeg ios libraries armv7, armv7s, i386 and universal on Mac with 10.8 

  http://stackoverflow.com/questions/18003034/installing-ffmpeg-ios-libraries-armv7-armv7s-i386-and-universal-on-mac-with-10/19370679#19370679

相關文章
相關標籤/搜索