標籤:
本篇介紹在Android/Ndk環境下FFmpeg的編譯及使用, FFmpeg內建了H264、AAC、MP3的解碼器,但卻沒有(或沒有好的)相應的編碼器。相應的編碼器需要使用第三方庫。推薦使用的第三方庫為x264(H264編碼) 、FDK_AAC(AAC編碼),lame(MP3編碼)。
在順序上,應該先編譯好第三方庫,最後再編譯FFmpeg庫。
【本書說明:本文邵發,本文選自《FFmpeg視音頻編程指南》。有關本書的詳細資料請訪問官網:http://www.afanihao.cn 】
【權利聲明:作者保留本文的全部權利。作者授權任何人都可以自由轉載本文,但轉載時必須遵守以下限制:①轉載時必須全文轉載,不得有任何修改,包括“權利聲明”和“本書說明”部分 ② 僅限於網路轉載,即最終結果公佈於網路上。凡是不遵守以上兩條的轉載行為視為侵權行為。除非本人允許,任何人不得將本文內容用於任何的其他用途。】
1.1 NDK環境的準備
本篇的示範樣本使用的是android-ndk-r8e版本,但理論上也適用於更新的NDK版本。為了讓你的編譯過程極其順利,應該對NDK作以下改動:
進入目錄 platforms/android-14/arch-arm/usr/lib
應該可以看到一些檔案,例如 crtbegin_dynamic.o ,crtbegin_static.o crtend_so.o , crtbegin_so.o ,crtend_android.o,等等。
把這個目錄下的所有檔案拷貝到
\toolchains\arm-linux-androideabi-4.6\prebuilt\linux-x86\lib\gcc\arm-linux-androideabi\4.6
否則在連結的時候ld會提示“找不到crtbegin_so.o和crtend_so.o”的錯誤。
應該把NDK的相關工具都把加PATH環境變數中,例如,
#!/bin/sh export PATH=$PATH:/opt/ndk/android-ndk-r8e/ export PATH=$PATH: /opt/ndk/android-ndk-r8e/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/ |
1.2 FDK_AAC庫的編譯
如果你不打算使用AAC編碼的功能,則可以略過本節。目前(2015年3月),fdk_aac的官方發布地址是在source_forge上,也許以後會變,作者可以自行搜尋。目前地址為:
http://sourceforge.net/projects/opencore-amr/files/fdk-aac/
本文使用的版本是fdk-aac-0.1.3,為減少不必要的麻煩,讀者也請下載這個包。
1.2.1 修改configure指令碼
修改指令碼的目的是去除目標庫的版本號碼,以適應Android/NDK的對庫的載入要求。簡單地講,這是因為在Android/NDK項目的java代碼中,使用System.loadLibrary()函數來載入so檔案時,動態庫是不能加版本號碼的。
在fdk_aac的configure指令碼裡找到如下的行:
# This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec=‘${libname}${shared_ext} ${libname}${shared_ext} $libname${shared_ext}‘ soname_spec=‘${libname}${shared_ext}‘ finish_cmds=‘PATH="\$PATH:/sbin" ldconfig -n $libdir‘ shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no |
把粗體字部分替換為:
library_names_spec=‘$libname${shared_ext} $libname.a‘ |
1.2.2 建立配置指令碼
在源碼的根目錄下建議一個myconfig.sh指令碼。這是一個SHELL指令碼,要求讀者自己稍微瞭解一些SHELL指令碼的寫法。在這裡只強調一點:SHELL指令碼是以\n結尾的,如果你是在windows下面編輯的,那麼應該用dos2unix來把文本的分行符號轉成unix格式。
為了避免不必要的麻煩,把NDK解壓縮在/opt/ndk/android-ndk-r8e目錄,並將FDK的輸出目錄--prefix設定在/opt/ndk/openlib目錄下。讀者第一遍操作時,不要擅自修改目錄,必須在熟練掌握之後再自行修改目錄。
myconfig.sh裡面內容為:
#!/bin/sh export CC=arm-linux-androideabi-gcc export CXX=arm-linux-androideabi-g++ export AR=arm-linux-androideabi-ar export LD=arm-linux-androideabi-ld export AS=arm-linux-androideabi-gcc export CFLAGS=" --sysroot=/opt/ndk/android-ndk-r8e/platforms/android-14/arch-arm -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 " export CXXFLAGS=" --sysroot=/opt/ndk/android-ndk-r8e/platforms/android-14/arch-arm -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 " export LDFLAGS=" -L/opt/ndk/android-ndk-r8e/platforms/android-14/arch-arm/usr/lib -march=armv7-a -Wl,--fix-cortex-a8 " ./configure --enable-static --prefix=/opt/ndk/openlib --host=arm-linux |
1.2.3 編譯
如果你嚴格按照以上的操作進行,那麼編譯過程會非常簡單。Linux下的開源軟體一般都是分為三步編譯 configure , make,make install,而fdk_aac也不例外。
1) ./myconfig.sh
執行上述配置指令碼,開始配置
2) make
3) make install
一切正常,將在/opt/ndk/openlib下存放輸出庫及標頭檔,注意這個庫是去掉版本號碼的libfdk-aac。顯然,Android/NDK的程式員會更喜歡不帶版本號碼的庫。
1.3 LAME-MP3的編譯
如果你不打算在項目中使用mp3編碼,則略過本節。
本節介紹lame的編譯,但遺憾的是,其動態庫沒有辦法去掉版本號碼。所以本方法產生的目標動態庫是帶版本號碼的。
1.3.1 建立配置指令碼
建立設定檔myconfig.sh,內容如下,
#!/bin/sh export CC=arm-linux-androideabi-gcc export CXX=arm-linux-androideabi-g++ export AR=arm-linux-androideabi-ar export LD=arm-linux-androideabi-ld export CFLAGS=" --sysroot=/opt/ndk/android-ndk-r8e/platforms/android-14/arch-arm " export LDFLAGS=" -L/opt/ndk/android-ndk-r8e/platforms/android-14/arch-arm/usr/lib " ./configure --enable-static --prefix=/opt/ndk/openlib --host=arm-linux |
1.3.2 編譯
1) ./myconfig.sh
執行上述配置指令碼,開始配置
2) make
3) make install
一切正常,將在/opt/ndk/openlib下存放輸出庫及標頭檔。
1.4 x264的編譯
x264屬於VLC的一部分,當前的源碼位置為:
ftp://ftp.videolan.org/pub/videolan/x264/snapshots/
本篇使用的源碼版本為x264-snapshot-20140723-2245-stable.tar.bz2
1.4.1 修改configure指令碼
這一步的目的是使輸出的庫檔案不帶版本號碼。如果你堅持認為版本號碼不影響你的開發,則忽略本小節。
找到這一位置:
else echo "SOSUFFIX=so" >> config.mak echo "SONAME=libx264.so.$API" >> config.mak echo "SOFLAGS=-shared -Wl,-soname,\$(SONAME) $SOFLAGS" >> config.mak fi |
將粗體字修改為:(即去掉$API尾碼)
echo "SONAME=libx264.so" >> config.mak |
1.4.2 建立配置指令碼
建立myconfig.sh配置指令碼,
#!/bin/sh export CC=arm-linux-androideabi-gcc export CXX=arm-linux-androideabi-g++ export AR=arm-linux-androideabi-ar export LD=arm-linux-androideabi-ld export AS=arm-linux-androideabi-gcc export CFLAGS=" --sysroot=/opt/ndk/android-ndk-r8e/platforms/android-14/arch-arm -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16" export LDFLAGS=" -L/opt/ndk/android-ndk-r8e/platforms/android-14/arch-arm/usr/lib -march=armv7-a -Wl,--fix-cortex-a8 " ./configure --enable-static --enable-shared --prefix=/opt/ndk/openlib --host=arm-linux |
1.4.3 編譯
1) ./myconfig.sh
執行上述配置指令碼,開始配置
2) make
3) make install
1.5 FFmpeg的編譯
本樣本使用的是FFmpeg2.1.3版本,可以從其官網ffmpeg.org上下載。如果不需要x264/aac/lame,可以直接進行本節的編譯。
1.5.1 修改configure指令碼
本節的目的是讓輸出的庫不含有版本號碼。
先找到configure的如下位置:
SHFLAGS=‘-shared -Wl,-soname,$$(@F)‘ LIBPREF="lib" LIBSUF=".a" FULLNAME=‘$(NAME)$(BUILDSUF)‘ LIBNAME=‘$(LIBPREF)$(FULLNAME)$(LIBSUF)‘ SLIBPREF="lib" SLIBSUF=".so" SLIBNAME=‘$(SLIBPREF)$(FULLNAME)$(SLIBSUF)‘ SLIBNAME_WITH_VERSION=‘$(SLIBNAME).$(LIBVERSION)‘ 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_VERSION=‘$(SLIBNAME)‘ SLIBNAME_WITH_MAJOR=‘$(SLIBNAME)‘ LIB_INSTALL_EXTRA_CMD=‘$$(RANLIB) "$(LIBDIR)/$(LIBNAME)"‘ SLIB_INSTALL_NAME=‘$(SLIBNAME_WITH_VERSION)‘ SLIB_INSTALL_LINKS= |
1.5.2 修改配置指令碼
建立myconfig.sh
export NDK_ROOT=/opt/ndk export TMPDIR=/tmp ./configure --prefix=$NDK_ROOT/ffmpeg --sysroot=$NDK_ROOT/android-ndk-r8e/platforms/android-14/arch-arm --cross-prefix=arm-linux-androideabi- --target-os=linux --arch=arm --extra-cflags="-I$NDK_ROOT/openlib/include -fPIC " --extra-ldflags=-L$NDK_ROOT/openlib/lib --disable-programs --disable-doc --enable-shared --enable-cross-compile --enable-gpl --enable-nonfree --enable-libx264 --enable-libmp3lame --enable-libfdk-aac |
這個指令碼中是把x264, fdk-aac, lame全部編譯上了,你可以視自己的情況來修改。
1.5.3 編譯
1) ./myconfig.sh
執行上述配置指令碼,開始配置
2) make
3) make install
1.6 在Android.mk中引用FFmpeg庫1.6.1 使用靜態庫
推薦使用靜態庫。需要注意,FFmpeg庫的靜態庫引用順序是不能隨便寫的,為了避免不必要的麻煩,請照抄以下配置。
在你的Android.mk中添加以下幾行(見黑體字部分)
LOCAL_LDLIBS += -llog -lz # ffmpeg FFMPEG=/opt/ndk LOCAL_CFLAGS += -I$(FFMPEG)/include LOCAL_LDLIBS += -l$(FFMPEG)/lib/libavformat.a LOCAL_LDLIBS += -l$(FFMPEG)/lib/libavcodec.a LOCAL_LDLIBS += -l$(FFMPEG)/lib/libavdevice.a LOCAL_LDLIBS += -l$(FFMPEG)/lib/libswresample.a LOCAL_LDLIBS += -l$(FFMPEG)/lib/libavdevice.a LOCAL_LDLIBS += -l$(FFMPEG)/lib/libswscale.a LOCAL_LDLIBS += -l$(FFMPEG)/lib/libpostproc.a LOCAL_LDLIBS += -l$(FFMPEG)/lib/libavutil.a # fdk_aac LOCAL_LDLIBS += -l/opt/ndk/lib/libfdk-aac.a LOCAL_LDLIBS += -l/opt/ndk/lib/libmp3lame.a LOCAL_LDLIBS += -l/opt/ndk/lib/ libx264.a include $(BUILD_SHARED_LIBRARY) |
1.6.2 使用動態庫
不推薦使用動態庫,此過程過於複雜。要注意的有兩點:①編譯的時候,必須去除庫的版本號碼 ② 在Java代碼中載入的時候,要注意載入的順序。
以下可以加在Android.mk中,但是僅供參考,遇到自己問題需要自己調試
# ffmpeg FFMPEG=f:/Ndk/ffmpeg LOCAL_CFLAGS += -I$(FFMPEG)/include LOCAL_LDLIBS += -L$(FFMPEG)/lib LOCAL_LDLIBS += -lavformat -lavcodec -lswresample -lswscale -lavutil -lavfilter include $(BUILD_SHARED_LIBRARY) |
1.6.3 在C++中使用ffmpeg
ffmpeg的庫是按C編譯的,裡面的符號都是C格式,因此,若在C++中使用,還是要使用extern "C"這種技術。相關文法請參考《C/C++文法指南》(邵發,官網http://www.afanihao.cn)。
為了大家的方便,在/opt/ndk/ffmpeg/include下建立一個ffmpeg.h,內容為:
#ifndef _FFMPEG_H #define _FFMPEG_H #ifdef __cplusplus extern "C" { #define INT64_C(val) val##LL #define UINT64_C(val) val##ULL #endif #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libswscale/swscale.h> #include <libavutil/avutil.h> #include <libavutil/opt.h> #include <libavutil/imgutils.h> #include <libswresample/swresample.h> #include <libavutil/error.h> #include <libavfilter/avfiltergraph.h> #include <libavfilter/avcodec.h> #include <libavfilter/buffersink.h> #include <libavfilter/buffersrc.h> #ifdef __cplusplus } #endif #endif |
然後在C++代碼中直接#include "ffmpeg.h"就可以了。
Android/NDK環境下FFmpeg及AAC,MP3,X264的編譯