1. 程式人生 > >多媒體開發(7):編譯Android與iOS平臺的FFmpeg

多媒體開發(7):編譯Android與iOS平臺的FFmpeg

編譯FFmpeg,一個古老的話題,但我還是介紹一遍,就當記錄。之前介紹怎麼給視訊新增水印時,就已經提到FFmpeg的編譯,並且在編譯時指定了濾鏡的功能。 但是,在手機盛行的時代,你可能更需要的是能在iOS或Android平臺上執行的FFmpeg,而對於命令列的ffmpeg,你可以在個人電腦上面使用(因為它簡潔易操作),也可以在服務程式中使用(安裝FFmpeg後直接呼叫ffmpeg命令),比如小程經常在自己的mac機上使用ffmpeg命令。 **本文介紹怎樣編譯出iOS或Android平臺使用的FFmpeg連結庫。** 正如編譯macos平臺使用的FFmpeg一樣,編譯iOS或Android平臺使用的FFmpeg,主線也是先configure再make,只不過,有更多的細節需要考慮。 我使用的是macos系統,以下介紹的就是在mac上交叉編譯,編譯出移動平臺使用的FFmpeg。 ### (1)編譯環境準備 #### pkg-config FFmpeg在編譯時經常使用到第三方庫(比如x264、rtmp等),編譯器在查詢這些第三方庫的標頭檔案與庫檔案時,需要使用到程式pkg-config。 pkg-conifig給編譯器提供路徑與連結選項。第三方庫在make install時會生成pc字尾的檔案並拷貝到系統目錄,而pkg-config就是從這個pc檔案讀取出路徑資訊。 可以設定PKG_CONFIG_PATH這個環境變數,指定目錄,讓pkg-config到這個目錄下面去找pc檔案,如果不設定,則預設在/usr/local/lib/pkgconfig目錄下面查詢,比如某個時刻我的pkgconfig目錄下面是這樣的一堆pc檔案: ![pc檔案](https://free-picture1.oss-cn-shenzhen.aliyuncs.com/%E5%A4%9A%E5%AA%92%E4%BD%93%E5%BC%80%E5%8F%91/ffmpeg%E5%B0%B1%E7%94%A8-%E7%BC%96%E8%AF%91ffmpeg/pkg-config%E7%9B%AE%E5%BD%95.jpg) 這樣安裝pkg-config: > brew install pkg-config 安裝pkg-config後,可以這樣獲取第三方庫的路徑資訊: > pkg-config --cflags --libs freetype2 以下是對於pkg-config命令的一個載圖: ![pkg-config](https://free-picture1.oss-cn-shenzhen.aliyuncs.com/%E5%A4%9A%E5%AA%92%E4%BD%93%E5%BC%80%E5%8F%91/ffmpeg%E5%B0%B1%E7%94%A8-%E7%BC%96%E8%AF%91ffmpeg/pkg-config.png) 需要注意,雖然pkg-config查詢到的pc檔案裡面有記錄到第三方靜態庫的路徑,但實際在編譯FFmpeg靜態庫時,並不會連結上這個第三方庫,而且在FFmpeg的編譯指令碼中可以指定第三方庫的路徑。 #### freetype **此項只在使用濾鏡功能時需要安裝。** 如果編譯時遇到這樣的提示:freetype2 not found using pkg-config,那說明還沒有安裝freetype,這樣安裝即可: > brew install freetype #### clang編譯器 **此項只在編譯iOS平臺的FFmpeg時才需要。** 因為我的mac機已經安裝過xcode,所以clang已經存在。如果你的mac還沒有安裝clang的話,那建議把xcode安裝好。 #### asm編譯器 **此項只在編譯iOS平臺的FFmpeg時才需要。** x264或FFmpeg等,都有彙編程式碼,編譯這些彙編程式碼,需要使用更先進的編譯指令碼來處理,而mac系統沒有這樣的指令碼。 這個指令碼是**gas-preprocessor.pl**。 可以這樣下載並使用gas-preprocessor.pl: git clone git://github.com/mansr/gas-preprocessor.git sudo cp -f gas-preprocessor/gas-preprocessor.pl /usr/local/bin/ chmod +x /usr/local/bin/gas-preprocessor.pl #### yasm 另一個需要的工具是**yasm**彙編編譯器,可以這樣安裝: > brew install yasm #### NDK工具包 **此項只在編譯Android平臺的FFmpeg時才需要。** 可以使用ndk-r9d版本,或者最新的版本,來編譯FFmpeg,下載地址: https://developer.android.google.cn/ndk/downloads/index.html ### (2)FFmpeg原始碼下載 > git clone git://source.ffmpeg.org/ffmpeg.git ffmpeg ### (3)編譯指令碼 不必自己重寫了,找開源的專案過來修改一下(注意開源協議)就可以了,比如參考這個開源專案: https://github.com/yixia/FFmpeg-Vitamio.git 在這個專案裡面,有編譯Android跟iOS平臺的相應指令碼,而且有相應的優化處理。在移動平臺使用的庫都很注重兩個東西,一個是效能,另一個是體積大小。一個好的指令碼,既要根據不同的硬體型別作編譯上的優化,也要根據軟體需求裁剪FFmpeg的功能使得出來的庫儘可能小(畢竟FFmpeg的功能並非全部都用上)。 ### (4)指令碼修改 小程先介紹一下腳本里面的一些關鍵引數,這些引數並非平臺通用。 指定指令集: > --extra-cflags='-arch armv7s' --extra-ldflags='-arch armv7s' 指定cpu型別: > --arch=arm --cpu=cortex-a9 注意,應該根據不同的指令集使用不同的cpu優化;--arch=arm64,像這樣指定具體指令架構也是可以的。 指定系統: > --target-os=darwin 指定sdk: > --sysroot=/Applications/Xcode.app/.../xxx.sdk 指定編譯器: > --cc=xxx/clang 指定庫生成目錄: > --prefix=build 指定使用的muxer/demuxer/encoder/decoder等: > --enable-muxer=mp4 基本上使用上面介紹的指令碼就可以編譯了,但有時候也可以作一些修改,比如要加入第三方庫時,或者要對某個指令集作優化時,等等。 小程再提一些注意點,有可能幫你解決編譯過程中遇到的問題: --sysroot需要指定。iOS平臺為....sdk/,不包括usr/inclue;Android平臺是編譯鏈的目錄。 extra-cflags跟extra-ldflags要指定-arch(iOS)或-march(Android)。 在xcode8.3.2(sdk為10.3)上,armv7/armv7s/arm64不能使用"-mfloat-abi=hard"選項,並且arm64要指定-mcpu=cortex-a53。 在xcode9.2(sdk為11.2)上,需要--disable-asm。 對於實際專案來說,FFmpeg的編譯是關鍵的一步,應該多花時間去研究一些關鍵的細節--功能、效能跟體積大小都很重要。 ### (5)開始編譯與使用 執行指令碼即可。最終會生成二進位制庫,比如iOS一般為靜態庫(.a檔案),而Android一般為動態庫(.so檔案)。 在編譯得到FFmpeg的連結庫後,就可以呼叫它,讓它執行起來。這時,需要寫自己的呼叫程式。在這裡給出一個簡單的呼叫示例,並且不做程式碼解釋,只是讓有需要的讀者有一個感知。 ``` extern "C" { #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" } void dump_file_format(const char* filepath) { av_register_all(); av_log_set_level(AV_LOG_DEBUG); AVFormatContext* formatContext = avformat_alloc_context(); AVCodecContext* codecContext = NULL; int status = 0; bool success = false; int audioindex = -1; status = avformat_open_input(&formatContext, filepath, NULL, NULL); if (status == 0) { status = avformat_find_stream_info(formatContext, NULL); if (status >= 0) { for (int i = 0; i < formatContext->nb_streams; i ++) { if (formatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) { audioindex = i; break; } } if (audioindex > -1) { codecContext = formatContext->streams[audioindex]->codec; AVCodec* codec = avcodec_find_decoder(codecContext->codec_id); if (codec) { status = avcodec_open2(codecContext, codec, NULL); if (status == 0) { success = true; } } } } } if (success) { av_dump_format(formatContext, 0, filepath, false); av_log(NULL, AV_LOG_DEBUG, "format and decoder sucessful, and now in decoding each frame\n"); printf("sample_rate=%d, channels=%d\n", codecContext->sample_rate, codecContext->channels); } avformat_free_context(formatContext); } int main(int argc, const char *argv[]) { const char filepath[] = "test2.mp3"; dump_file_format(filepath); return 0; } ``` **好了,總結一下,本文介紹了在macos上,編譯出iOS平臺或Android平臺的FFmpeg的連結庫的過程,涉及到編譯環境的準備、編譯指令碼的理解與定製等內容。有緣再見,see you.** ---- ![動動腦,不會老](https://free-picture1.oss-cn-shenzhen.aliyuncs.com/%E5%A4%9A%E5%AA%92%E4%BD%93%E5%BC%80%E5%8F%91/ffmpeg%E5%B0%B1%E7%94%A8-%E7%BC%96%E8%AF%91ffmpeg/%E9%80%BB81_%E5%8F%91%E5%B8%