1. 程式人生 > >編譯FFmpeg

編譯FFmpeg

ffmepg 編譯 ios

編譯FFmpeg,這是一個古老的話題,但是小程還是介紹一遍,就當是記錄。

小程之前介紹怎麽給視頻添加水印時,就已經提到FFmpeg的編譯,因為需要在編譯時指定濾鏡的功能,讀者可以關註“廣州小程”微信公眾號並查閱這篇文章(音視頻->FFmpeg應用)。

但是,在手機盛行的時代,讀者可能更需要的是能在iOS或Android平臺上運行的FFmpeg,而對於命令行的ffmpeg,讀者可以在個人電腦上面使用(因為它簡潔易操作),也可以在服務程序中使用(安裝FFmpeg後直接調用ffmpeg命令),比如小程經常在自己的mac機上使用ffmpeg命令。

本文介紹如何編譯出iOS或Android平臺使用的FFmpeg鏈接庫。

正如編譯macos平臺使用的FFmpeg一樣,編譯iOS或Android平臺使用的FFmpeg,主線也是先configure再make,只不過,有更多的細節需要考慮。

小程使用的是macos系統。

(1)編譯環境準備

(a)pkg-config

FFmpeg在編譯時經常使用到第三方庫(比如x264、rtmp等),編譯器在查找這些第三方庫的頭文件與庫文件時,需要使用到程序pkg-config。

pkg-conifig給編譯器提供路徑與鏈接選項。第三方庫在make install時會生成pc後綴的文件並拷貝到系統目錄,而pkg-config就是從這個pc文件讀取出路徑信息。

這樣安裝pkg-config:

brew install pkg-config

安裝pkg-config後,可以這樣得到第三方庫的路徑信息:

pkg-config --cflags --libs librtmp

以下是對於pkg-config命令的一個載圖:
技術分享圖片

可以設置PKG_CONFIG_PATH這個環境變量,讓pkg-config到這個目錄下面去找pc文件,如果不設置,則默認在/usr/local/lib/pkgconfig目錄下面查找。

需要註意,雖然pkg-config查找到的pc文件裏面有記錄到第三方靜態庫的路徑,但實際在編譯FFmpeg靜態庫時,並不會鏈接上這個第三方庫,而且在FFmpeg的編譯腳本中可以指定第三方庫的路徑。

(b)clang編譯器

此項只在編譯iOS平臺的FFmpeg時才需要。

小程的mac機已經安裝過xcode,所以clang已經存在。如果讀者還沒有安裝clang的話,那建議把xcode安裝好。

(c)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匯編編譯器,可以這樣安裝:

brew install yasm

(d)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文件)。

至此,已經介紹完在iOS或Android平臺編譯FFmpeg的過程。

在編譯得到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的鏈接庫的過程。涉及到編譯環境的準備、編譯腳本的理解與定制,難度系數為2。

編譯FFmpeg