前言
FFmpeg是一個很強大的垮平臺的音視頻處理庫,它可用于Mac/windows/ios/android/linux等各個平臺,FFmpeg庫自身native代碼實現了很多功能,也引入了其它的很多外部庫,比如x264、fdk_aac、lamemp3等等,然后對這些外部庫做封裝,對外則提供了統一的接口。如下提供了盡量簡單的方法編譯用于這些平臺的ffmpeg庫(引入常用的x264、fdk_aac、lamemp3外部庫)。
編譯基礎知識
編譯一般分為本地編譯和交叉編譯,本地編譯是相對于交叉編譯而言的。本地編譯一般指在PC上編譯用于當前PC平臺系統使用的程序或者庫,交叉編譯一般指在本地PC平臺上用指定的交叉編譯器編譯用于其它嵌入式平臺用的程序或者庫,如在Mac平臺上編譯iOS平臺用的FFmpeg庫。每一個嵌入式平臺都會提供PC平臺的交叉編譯工具鏈,他們都會有如下幾個工具:
CC:C語言編譯器
CXX:C++編譯器
AS:匯編語言編譯器
AR:打包器,將.o文件打包(CC/CXX/AS編譯器生成的為.o文件)
LD:連接器,將庫文件和.o文件連接成可執行程序(如.out文件)
NM:查看靜態庫文件中的符號表
GDB:調試工具
STRIP:通過優化減小可執行文件或者庫文件體積
Objdump:查看靜態庫或者動態庫的方法簽名
如:
gcc -c main.cpp -I ./include
ar libmedia.a media.o
gcc -o main main.o -L media
make編譯工具
通常我們通過命令編譯的步驟如下:
// 生成中間文件
gcc -c main.c
// 生成可執行文件(可執行程序 main;生成庫的步驟也一樣)
gcc -o main main.o
那如果工程有成千上萬個文件,那么我們這樣的命令就需要寫成千上萬變,顯然是非常不方便的。make是一個非常強大的工具,它可以極大簡化如上的編譯命令,生成想要的庫或者程序。基本如下:
- make語法由configure文件(它為可執行文件)來配置
- --prefix 指定編譯生成的庫或者可執行文件的路徑
- --host 指定最終要運行的平臺
- CC 指定交叉編譯工具的路徑
- CFFLAGS 指定編譯時所帶的參數
- LDFLAGS 指定連接過程中的參數
- --disable-frontend 不編譯可執行程序
執行過程如下:
當調用./configure .....命令時將會配置make的相關參數
當調用make ..... 或者make install 命令時,將會調用make工具進行編譯連接并生成可執行程序或者庫文件
編譯mac平臺下使用的ffmpeg庫
1、homebrew(推薦)
2、make工具手動編譯
IOS和android平臺交叉編譯工具
每個操作系統(MAC osx/windows/linux)都有提供ios和android平臺的交叉編譯工具鏈,這里以mac 系統為例,ios和android的交叉編譯工具如下:
1、ios交叉編譯工具路徑
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
在這個路徑下就有上面說的那些 cc,ar等工具,這里的cc實際上是指向了clang編譯器
ios編譯中bitcode
開啟之后app性能會增加,關閉則有可能降低性能
xcrun -sdk iphoneos clang -arch arm64 意思就是調用clang編譯器編譯64位的源代碼
2、android交叉編譯工具路徑
<安卓sdk/ndk-bundle路徑>/toolchains/[aarch64-linux-android-4.9(編譯64位arm,具體版本號根據安裝的ndk版本確定)][arm-linux-androideabi-4.9(編譯32位)]/prebuilt/darwin-x86_64/bin
在這個路徑下就有上面那些cc,ar等工具
編譯環境
- ios&&mac
Xcode:Version 11.4 (11E146) - android
android-ndk-r20 - 操作系統
Mac os X:10.15.4 (19E266)
開始編譯
這里的腳本是基于ijkplayer腳本經過改寫而成,默認會將x264庫,fdkaac庫,lame庫全部編譯到ffmpeg中,ijkplayer是一個很優秀的國內開源庫,github地址:ijkplayer開源地址
編譯腳本地址:腳本地址
- 下載腳本
git clone https://github.com/nldzsz/ffmpeg-build-scripts.git
一、 編譯 ffmpeg for iOS
cd ffmpeg-build-scripts
./compile-ffmpeg-ios.sh all
執行此腳本會先檢查系統中是否安裝 brew yasm gas-preprocessor.pl工具,如果沒有則會自動安裝;
接著會自動下載ffmpeg/x264/mp3lame/fdk-aac源碼;如果不想下載x264/mp3lame/fdk-aac三個庫,只需要將腳本./compile-init.sh中如下對應代碼注釋即可:
# 拉取 x264源碼
echo "== pull x264 base =="
sh $TOOLS/pull-repo-base.sh $X264_UPSTREAM $X264_LOCAL_REPO
# 拉取 fdkaac源碼
echo "== pull fdkaac base =="
sh $TOOLS/curl-repo-base.sh $FDKAAC_UPSTREAM $FDKAAC_LOCAL_REPO $FDKAAC_VERSION
# 拉取 mp3lame源碼
echo "== pull mp3lame base =="
sh $TOOLS/curl-repo-base.sh $MP3LAME_UPSTREAM $MP3LAME_LOCAL_REPO $MP3LAME_VERSION
# 拉取 ffmpeg源碼
echo "== pull ffmpeg base =="
sh $TOOLS/pull-repo-base.sh $FFMPEG_UPSTREAM $FFMPEG_LOCAL_REPO
默認會編譯 arm64 x86_64兩個平臺的靜態庫
編譯后的結果如下:
[圖片上傳中...(image.png-b02ffa-1593251833927-0)]
這里開啟了x264/mp3lame/fdk-aac三個外部庫,libxrzffmpeg.a是將lib中所有的庫合并成一個庫(推薦使用),如果不做任何裁剪,可以看到默認的.a庫還是挺大的,以上是編譯的默認配置的庫
- 開啟指定的選項
// 開啟rtsp解析協議
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=rtp"
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-demuxer=rtsp"
在ios項目中使用ffmpeg的.a庫
1、直接將universal/lib目錄拖進工程(或者將lib目錄中的庫替換成libxrzffmpeg.a一個庫)
2、配置頭文件和庫文件搜索路徑,如下
3、添加外部庫
libiconv.2.4.0.tbd
libz.1.2.5.tbd
如下:
二、編譯ffmpeg for android
- 1、編譯so動態庫
cd ffmpeg-build-scripts
./compile-ffmpeg-android.sh reset
./compile-ffmpeg-android.sh all
-
備注:
如果NDK是直接從瀏覽器下載的,編譯的時候會彈出如下的提示:
image.png
點擊"仍然允許"即可。從android studio下載的則不會(來自Google的說法,未嘗試過)
最后編譯結果如下:
導入android工程,通過cmake方式導入
CMakeLists.txt文件導入ffmpeg動態庫的關鍵代碼如下:
設置ffmpeg頭文件路徑
set(SRC_DIR ${PROJECT_SOURCE_DIR}/src/main/cpp)
set(FFMPEG_INC ${SRC_DIR}/ffmpeg)
include_directories(${FFMPEG_INC})
設置ffmpeg.so動態庫文件路徑
# import ffmpeg library
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/src/main/jniLibs/${CMAKE_ANDROID_ARCH_ABI}")
# ding yi jiang gai ku da bao jin app de lei xing
add_library(adMedia SHARED
${src_cpp}
${ffmpeg_demo}
${ffmpeg_common}
${com_cpp}
${opensles_cpp}
)
target_link_libraries(adMedia android log
OpenSLES
mediandk
avformat avcodec avfilter avutil swresample swscale
${log-lib}
)
tips:編譯動態庫時沒有加入fdk_aac庫,因為還沒找到去掉libfdk-aac.so.2.0.0后綴的辦法,如果沒有去掉后綴,引入安卓時會提示找不到"fdk-aac.so.2"的錯誤
- 2、編譯.a動態庫
動態庫引入安卓如果有后綴,比如libfdk-aac.so.2.0.0就無法被安卓識別,目前也還沒找到辦法去掉這個后綴,不過現在ffmpeg已經內置了aac的編解碼器,可以不用fdkaac了,不過也了解一下安卓對于靜態庫的引用方式
找到腳本compile-ffmpeg-android.sh,做如下修改
export FF_COMPILE_SHARED=FALSE
export FF_COMPILE_STATIC=TRUE
然后執行
./compile-ffmpeg-android.sh all
最終生成的靜態庫如下:
靜態ffmpeg庫的引入方式
1、將相關庫拖入對應目錄
2、配置CMakeLists.txt
設置ffmpeg頭文件路徑和動態庫一樣
set(SRC_DIR {SRC_DIR}/ffmpeg)
include_directories(${FFMPEG_INC})
設置ffmpeg 相關靜態庫文件路徑
# import ffmpeg library
# import ffmpeg library
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/src/main/jniLibs/${CMAKE_ANDROID_ARCH_ABI} -lm -lz")
# ding yi jiang gai ku da bao jin app de lei xing
add_library(adMedia SHARED
${src_cpp}
${ffmpeg_demo}
${ffmpeg_common}
${com_cpp}
${opensles_cpp}
)
target_link_libraries(adMedia android log
OpenSLES
mediandk
# for dynamic
# avformat avcodec avfilter avutil swresample swscale
# for static
libavformat.a libavcodec.a libavfilter.a libavutil.a libswresample.a libswscale.a libfdk-aac.a libx264.a libmp3lame.a
${log-lib}
)
- 注意:這里和動態庫不一樣的就是多加了-lm -lz的標記和寫入完整的庫名包括后綴
三、編譯ffmpeg for mac - homebrew編譯
1、homebrew安裝ffmpeg for mac
brew install ffmpeg
會自動安裝最新的ffmpeg,如果沒有安裝homebrew,則使用如下命令安裝
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
homebrew卸載ffmpeg
brew uninstall ffmpeg
- 手動編譯
homebrew安裝ffmpeg的好處是簡單,不足是無法自定義ffmpeg的配置,如果修改了ffmpeg的源碼,則必須要通過手動方式安裝ffmpeg
cd ffmpeg-build-scripts
./compile-ffmpeg-mac.sh all
會生成ffmpeg.a庫,如下:
-
將ffmpeg導入xcode命令行工程
這里是一個簡單的command line工程,如下方式設置ffmpeg
2.jpg
注意第二張圖,需要添加如下額外的庫,不然會出現錯誤。
FFmpeg許可協議
在使用開源代碼時要清除開源代碼所使用的的許可協議,因為可能涉及到一定的法律風險。常見的開源許可協議:
- BSD/MIT/APPACHE
可以為所欲為的許可協議 - GPL
全稱(GNU General Public License)。產品中要有一份文檔說明使用了GPL協,只要是使用到它的代碼自身的代碼也要全部開源 - LGPL
全稱(GNU Lesser General Public License)。產品中要有一份文檔說明使用了LGPL協議,使用它的代碼自身代碼不需要開源。但如果修改了它的代碼,則修改的部分要開源出來
ffmpeg關于協議的configure參數為
Licensing options:
--enable-gpl allow use of GPL code, the resulting libs
and binaries will be under GPL [no]
--enable-version3 upgrade (L)GPL to version 3 [no]
--enable-nonfree allow use of nonfree code, the resulting libs
and binaries will be unredistributable [no]
1、ffmpeg協議默認為LGPL v2.1+
2、如果ffmpeg引入了x264庫或者使用GPL的模塊,那么ffmpeg將變成GPL許可協議,需要開啟--enable-gpl
3、ffmpeg使用了gmp、libaribb24等基于LGPL v3的模塊,則需要開啟--enable-version3
4、如果ffmpeg引入了fdk-aac等和GPL不兼容的外部庫,則需要開啟--enable-nonfree,編譯后ffmpeg變成unredistributable
遇到問題
- ios&&mac遇到問題
1、x264庫相關函數找不到
ld: warning: could not create compact unwind for _ff_init_vlc_sparse: stack subq instruction is too different from dwarf stack size
ld: warning: could not create compact unwind for _ff_init_2d_vlc_rl: stack subq instruction is too different from dwarf stack size
ld: warning: could not create compact unwind for _ff_rl_init_vlc: stack subq instruction is too different from dwarf stack size
Undefined symbols for architecture x86_64:
"_x264_encoder_close", referenced from:
_X264_close in libavcodec.a(libx264.o)
"_x264_picture_init", referenced from:
_X264_frame in libavcodec.a(libx264.o)
"_x264_encoder_reconfig", referenced from:
_X264_frame in libavcodec.a(libx264.o)
"_x264_encoder_open_157", referenced from:
_X264_init in libavcodec.a(libx264.o)
"_x264_encoder_encode", referenced from:
_X264_frame in libavcodec.a(libx264.o)
"_x264_param_default", referenced from:
_X264_init in libavcodec.a(libx264.o)
"_x264_param_apply_fastfirstpass", referenced from:
_X264_init in libavcodec.a(libx264.o)
"_x264_levels", referenced from:
_X264_init in libavcodec.a(libx264.o)
"_x264_param_default_preset", referenced from:
_X264_init in libavcodec.a(libx264.o)
"_x264_param_apply_profile", referenced from:
_X264_init in libavcodec.a(libx264.o)
"_x264_param_parse", referenced from:
_X264_init in libavcodec.a(libx264.o)
"_x264_encoder_delayed_frames", referenced from:
_X264_frame in libavcodec.a(libx264.o)
"_x264_encoder_headers", referenced from:
_X264_init in libavcodec.a(libx264.o)
"_x264_encoder_maximum_delayed_frames", referenced from:
_X264_init in libavcodec.a(libx264.o)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (us
編譯的libx264.a也要一并添加進去(如果是用的libxrzffmpeg.a則不需要加)
2、ffmpeg 4.0編譯fdk-aac時遇到的錯誤
libavcodec/libfdk-aacenc.c:292:34: error: ‘AACENC_InfoStruct {aka struct <anonymous>}’ has no member named ‘encoderDelay’ #93
換成4.2,解決了此問題
3、xcode環境不全時編譯ffmpeg 提示 No working C compiler found.
執行如下命令:
xcode-select --install # Install Command Line Tools if you haven't already.
sudo xcode-select --switch /Library/Developer/CommandLineTools # Enable command line tools
# Change the path if you installed Xcode somewhere else.
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
4、編譯x264時,如果提示Found no assembler Minimum version is nasm-2.13
說明系統的nasm版本過低,重新安裝即可
brew reinstall nasm
5、ffmpeg引用錯誤
必須要添加如下對應的庫,否則可能會出現對應的錯誤,如下:
libiconv.2.4.0.tbd,libz.1.2.5.tbd
Undefined symbols for architecture xxx
"deflateInit2", referenced from:
_encode_frame in libavcodec.a(pngenc.o)
"_crc32", referenced from:
_encode_frame in libavcodec.a(pngenc.o)
"_deflateReset", referenced from:
_encode_frame in libavcodec.a(lclenc.o)
_encode_frame in libavcodec.a(zmbvenc.o)
"_compress", referenced from:
.......
Security.framework
Undefined symbols for architecture x86_64:
"_SSLClose", referenced from:
_tls_open in libavformat.a(tls_securetransport.o)
_tls_close in libavformat.a(tls_securetransport.o)
"_SSLCopyPeerTrust", referenced from:
......
VideoDecodeAcceleration.framework
Undefined symbols for architecture x86_64:
"_VDADecoderCreate", referenced from:
_ff_vda_create_decoder in libavcodec.a(vda_h264.o)
_ff_vda_default_init in libavcodec.a(vda_h264.o)
"_VDADecoderDecode", referenced from:
.....
AudioToolbox.framework
Undefined symbols for architecture x86_64:
"_AudioConverterDispose", referenced from:
_ffat_close_decoder in libavcodec.a(audiotoolboxdec.o)
_ffat_close_encoder in libavcodec.a(audiotoolboxenc.o)
"_AudioConverterFillComplexBuffer", referenced from:
....
CoreMedia.framework
Undefined symbols for architecture x86_64:
"_CMBlockBufferCopyDataBytes", referenced from:
_vtenc_frame in libavcodec.a(videotoolboxenc.o)
"_CMBlockBufferCreateWithMemoryBlock", referenced from:
_videotoolbox_common_end_frame in libavcodec.a(videotoolbox.o)
"_CMSampleBufferCreate", referenced from:
.....
CoreFoundation.framework
Undefined symbols for architecture x86_64:
"_CFArrayCreateMutableCopy", referenced from:
_tls_open in libavformat.a(tls_securetransport.o)
"_CFArrayGetCount", referenced from:
_vtenc_frame in libavcodec.a(videotoolboxenc.o)
......
CoreVideo.framework
Undefined symbols for architecture x86_64:
"_CVBufferSetAttachments", referenced from:
_vtenc_send_frame in libavcodec.a(videotoolboxenc.o)
"_CVPixelBufferCreateWithPlanarBytes", referenced from:
_vtenc_send_frame in libavcodec.a(videotoolboxenc.o)
....
VideoToolbox.framework
Undefined symbols for architecture x86_64:
"_VTCompressionSessionCompleteFrames", referenced from:
_vtenc_init in libavcodec.a(videotoolboxenc.o)
_vtenc_frame in libavcodec.a(videotoolboxenc.o)
"_VTCompressionSessionCreate", referenced from:
.....
libz.tbd
Undefined symbols for architecture x86_64:
"_compress", referenced from:
_encode_strip in libavcodec.a(tiffenc.o)
"_compress2", referenced from:
_flashsv2_encode_frame in libavcodec.a(flashsv2enc.o)
_flashsv_encode_frame in libavcodec.a(flashsvenc.o)
"_deflate", referenced from:
....
liblzma.tbd
Undefined symbols for architecture x86_64:
"_lzma_code", referenced from:
_decode_frame in libavcodec.a(tiff.o)
"_lzma_end", referenced from:
_decode_frame in libavcodec.a(tiff.o)
....
libbz2.tbd
Undefined symbols for architecture x86_64:
"_BZ2_bzDecompress", referenced from:
_matroska_decode_buffer in libavformat.a(matroskadec.o)
"_BZ2_bzDecompressEnd", referenced from:
....
libiconv.tbd
Undefined symbols for architecture x86_64:
"_iconv", referenced from:
_avcodec_decode_subtitle2 in libavcodec.a(utils.o)
"_iconv_close", referenced from:
_avcodec_open2 in libavcodec.a(utils.o)
....
libz.tbd
Undefined symbols for architecture x86_64:
"_compress", referenced from:
_encode_strip in libavcodec.a(tiffenc.o)
"_compress2", referenced from:
_flashsv2_encode_frame in libavcodec.a(flashsv2enc.o)
_flashsv_encode_frame in libavcodec.a(flashsvenc.o)
"_deflate", referenced from:
.....
6、"stack_not_16_byte_aligned_error"錯誤
此錯誤為mac osX 10.15.4 (19E266)和Version 11.4 (11E146)編譯環境下新產生問題。在編譯CFlags=添加 "-fno-stack-check" 即可,如下:
# fixbug:mac osX 10.15.4 (19E266)和Version 11.4 (11E146)編譯時產生錯誤"stack_not_16_byte_aligned_error"
# 添加 "-fno-stack-check" 即可
FF_EXTRA_CFLAGS="-fno-stack-check"
- android 問題
待補充。。。。。
源代碼地址
編譯腳本地址:
https://github.com/nldzsz/ffmpeg-build-scripts.git
ffmpeg地址:
https://github.com/FFmpeg/FFmpeg.git
http://ffmpeg.org/releases/ffmpeg-4.2.tar.bz2
x264地址:
https://git.videolan.org/git/x264.git
http://download.videolan.org/x264/snapshots/x264-snapshot-20190809-2245-stable.tar.bz2(備注:該地址每天都會按照日期20190809增加一份拷貝,其它不變)
lame地址:
https://sourceforge.net/projects/lame/files/lame/3.100/lame-3.100.tar.gz
https://jaist.dl.sourceforge.net/project/lame/lame/3.100/lame-3.100.tar.gz
fdk-aac地址:
https://github.com/mstorsjo/fdk-aac
https://sourceforge.net/p/opencore-amr/fdk-aac/ci/v2.0.0/tarball
https://jaist.dl.sourceforge.net/project/opencore-amr/fdk-aac/fdk-aac-2.0.0.tar.gz
參考文章
1、[FFmpeg零基礎(2)-FFmpeg的編譯]https://juejin.im/post/5af3bef5f265da0b9d783ae3
2、xrun命令介紹
3、https://github.com/kewlbear
4、ijkplayer
5、gas-preprocessor
6、https://github.com/kewlbear/FFmpeg-iOS-build-script