天天看點

iOS: FFmpeg編譯和使用 學習

ffmpeg是一個多平台多媒體處理工具,處理視訊和音頻的功能非常強大。目前在網上搜到的iOS上使用FFMPEG的資料都比較陳舊,而FFMPEG更新疊代比較快; 且網上的講解不夠詳細,對于初次接觸FFMPEG的新手(例如我)來說确實不太好使用。為了防止忘記,這裡對iOS下使用FFMPEG做一個總結。

1. FFMPEG層次結構的簡單了解

要使用FFMPEG,首先需要了解FFMPEG的代碼結構。根據志哥的提示,ffmpeg的代碼是包括兩部分的,一部分是library,一部分是tool。api都是在library裡面,如果直接調api來操作視訊的話,就需要寫c或者c++了。另一部分是tool,使用的是指令行,則不需要自己去編碼來實作視訊操作的流程。實際上tool隻不過把指令行轉換為api的操作而已。

2. 預熱-在mac os下使用ffmpeg

在mac os下使用ffmpeg比較簡單,可以直接使用指令行來操作。首先安裝ffmpeg,這裡預設系統已經安裝好brew,隻需要在終端上輸入:

brew install ffmpeg

等待安裝結束即可。

安裝結束後,嘗試以下指令:

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

著作權歸作者所有,轉載請聯系作者獲得授權,并标注“簡書作者”。

一、背景

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

  

二、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庫的準備工作就基本完成了,接着依次執行下面幾步:

  1、拷貝gas-preprocessor.pl檔案到 /usr/bin目錄下。

  2、修改gas-preprocessor.pl檔案的權限

  注:需要有讀,寫和執行的權限。具體操作為,首先在指令行下進入/usr/bin目錄,然後執行chmod指令,如下圖所示:

iOS: FFmpeg編譯和使用 學習

  3、切換

build-ffmpeg.sh腳本的目錄下,使用指令

sh build-ffmpeg.sh 運作該腳本即可。

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

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

iOS: FFmpeg編譯和使用 學習

  該腳本中預設采用的FFmpeg是2.0版本,使用iOS 7.0的SDK編譯,c語言編譯器采用clang,應用中可以根據實際項目需要選中不同的FFmpeg和iOS SDK版本。

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

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

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

iOS: FFmpeg編譯和使用 學習

  提示位置選項--disable-iconv,根據提示我們輸入./configure檢視所有可用選項。指令行下切換到實際的FFmpeg源碼目錄下,檢視幫助如下圖:

iOS: FFmpeg編譯和使用 學習

  我們可以看到很多選項,英語不難,就是有些選項描述的太簡潔了,是以實際使用時如果不确定的話,我們可以去問問google。

  好了回過頭來看看這個configure檔案到底有什麼作用呢?

  1、裁剪

  我們知道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

  舉個例子,如果我們需要做一款本地視訊播放器,那麼我們可以使用如下配置:

  

iOS: FFmpeg編譯和使用 學習

  當然你還可以根據幫助清單進行更細粒度的裁剪,例如隻支援哪幾種格式的解碼等等。

  2、指定編譯環境

  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等。   

  3、指定靜态庫的安裝路徑

  指定執行make install指令時編譯好的靜态庫和相關頭檔案拷貝到的位置,即FFmpeg庫編譯後輸出的路徑。通常我們隻需要設定“--prefix=PREFIX”選項即可。例如我們需要将最後生成靜态庫的路徑指向“build/armv7”下,則設定--prefix="build/armv7";

   

四、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即可:

iOS: FFmpeg編譯和使用 學習

  設定好xcode的編譯環境以後,隻需要将該腳本拷貝到FFMpeg源檔案路徑下運作即可一鍵生成armv7,armv7s,i386以及合成後的全平台庫。

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

  第四部分中我們對build-ffmpeg.sh的腳本進行了修改和精簡後得到了build-ffmpeg0.7.sh,我們隻需要運作該腳本就可以一鍵完成FFmpeg 0.7版本庫的編譯工作了。編譯後我們得到的是lib目錄(包含所有生成的靜态庫)以及include目錄(包含相應的頭檔案),使用時我們隻需要将這些檔案添加到工程中即可。

  問題到這裡似乎就全部解決了,如果順利的話,恭喜你,你可以直接使用了。

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

  1、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如下圖所示:

iOS: FFmpeg編譯和使用 學習

  如果你的FFmpeg庫正好是放在目前的路徑下,且為了偷懶設定了遞歸包含頭檔案的話,那麼你很可能就會遇到time.h沖突的問題。因為xcode工程預設的設定是優先查找使用者路徑,編譯時FFmpeg中libavutil下的time.h就會優先被連結,進而導緻不會再連結系統time.h檔案,最終導緻編譯失敗。

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

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

  b、設定Always Search User Paths為NO。

  2、gcc c compiletest error問題

  xcode5下面編譯FFmpeg都采用clang,同樣也會遇到類似問題。這個問題通常出現在配置檔案錯誤的情況下,一般都是gcc路徑錯誤,當然也可能是其他編譯參數錯誤問題。

  出現這個問題我們應該首先檢查gcc的路徑是否正确,如果确認了指定路徑上存在gcc程式,但是還是報錯的,我們再去檢查目前要編譯的平台和指定的gcc路徑是否一緻,如果你使用iPhoneOS.platform下面的gcc去編譯i386平台的庫那肯定是不會測試通過的。

  3、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你可以得到更詳細的資訊,于是打開該檔案,你可以在最開始的地方看到你的配置語句,如果是用腳本,這塊兒會顯示最終解釋後(替換參數為真實值)的配置語句。然後緊跟着一堆具體的配置,通常哭啼的錯誤資訊會在該檔案的最末尾。我遇到的問題的資訊如下:

  

iOS: FFmpeg編譯和使用 學習

  看到标紅的這個區域了沒有,提示“-mfloat-abi=softfp”選項不支援,删掉該選項後,在運作時配置就通過了。其他配置問題,都可以通過檢視config.log來擷取更詳細的錯誤資訊。

  4、由于未導入libbz動态庫的問題

  如果導入FFmpeg庫了,并且配置了頭檔案搜尋路徑,遇到"Undefined symbols for architecture armv7s: _BZ2_bzDecompressInit",如下圖所示:

iOS: FFmpeg編譯和使用 學習

  這個問題是由于沒有導入“libbz2.dylib”庫的原因,導入庫即可解決該問題。

  5、libavcodec/audioconvert.h頭檔案缺失問題

  不知道為什麼執行make install的時候libavcodec中的audioconvert.h怎麼沒有拷貝到include目錄下的libavcodec中去,檢視發現原來libavutil目錄下已經有一個audioconvert.h了。解決這個問題隻需要從FFmpeg庫的libavcodec中拷貝audioconvert.h頭檔案到include的libavcodec目錄中即可解決。

六、雜談

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

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

七、編譯腳本及參考資料

  1、編譯腳本  

  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

  2、參考資料

  模拟器與真機下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

轉載于:https://www.cnblogs.com/xujiahui/p/6011950.html