iOS開發中整合FFmpeg以及相關注意事項
FFmpeg是一套可以用來記錄、轉換數字音訊、視訊,並能將其轉化為流的開源計算機程式。它提供了錄製、轉換以及流化音視訊的完整解決方案。同時,FFmpeg是一套跨平臺的方案,所以我們可以在iOS開發中使用它來進行一些視訊與GIF的開發。
接下來,我們從編譯FFmpeg開始,到使用FFmpeg,再到使用中的一些注意事項進行總結。
一、編譯FFMpeg
在這個過程中,我們需要以下幾個資源:
2.yasm
1.gas-preprocessor
gas-preprocessor 其實就是我們要編譯FFmpeg所需的指令碼檔案。
1).下載並解壓
2).將 gas-preprocessor.pl 檔案複製到 /usr/sbin/ 目錄下,如果該目錄無法修改,那麼可將檔案複製到 /usr/local/bin/ 目錄下。
3).為 gas-preprocessor.pl 檔案開啟可執行許可權,在終端中進行如下命令:
chmod 777 /usr/sbin/gas-preprocessor.pl
或
chmod 777 /usr/local/bin/gas-preprocessor.pl
2.yasm
yasm 是一個完全重寫的 NASM 彙編。目前,它支援 x86 和 AMD64 指令集,接受 NASM 和氣體彙編語法,產出二進位制,ELF32,ELF64,COFF,Mach-O 的(32和64),RDOFF2的 Win32 和 Win64 物件的格式,並生成 STABS 除錯資訊的來源,DWARF 2 ,CodeView 8格式。
可以使用homebrew來安裝:
brew install yasm
3.FFMpeg-iOS-build-script
在這個檔案中,我們可以對要進行編譯的FFmpeg進行一系列的設定。
1).設定FFmpeg的版本
FF_VERSION="3.3.6"
2).設定所要支援的架構
ARCHS="arm64 armv7"
3).設定所需要的FFmpeg功能配置
該設定可在 CONFIGURE_FLAGS= 中進行,通過禁用一些不必要的功能,可以有效地減小最終庫檔案的大小,格式如下:
禁用交叉編譯:
--disable-cross-compile
支援交叉編譯:
--enable-cross-compile
4).確保該指令碼所在路徑中不包含有空格
5).需要為該指令碼所在資料夾賦予許可權
chmod 777 /Users/mdm/Desktop/ffmpeg
6).進入指令碼所在資料夾目錄,執行指令碼
./build-ffmpeg.sh
此過程可能會出現各種問題,大多數問題可以通過前往執行指令碼過程中生成的 scratch 資料夾下的 config.log 中檢視對應原因。
另外,如果遇到
xcrun -sdk iphoneos clang is unable to create an executable file.
C compiler test failed.
這是由於系統安裝了多個Xcode環境所致,可使用下面方法選定一個Xcode環境來解決問題:
sudo xcode-select -s /Application/Xcode.app
7).指令碼執行完畢,生成所需檔案
ffmpeg-3.3.6 FFmpeg原始檔
scratch 編譯過程中生成的檔案
thin 對應各個架構下的庫檔案
FFmpeg-iOS 合併各個架構之後的庫檔案
二.整合FFmpeg到專案中
1.將生成的FFmpeg-iOS資料夾拷貝至專案中。
2.新增所需依賴的依賴庫,如下:
AudioToolbox.framework
CoreMedia.framework
VideoToolbox.framework
libz.tbd
libbz2.tbd
libiconv.tbd
3.新增 Header Search Paths 設定
$(SRCROOT)/專案名/所在資料夾/FFmpeg-iOS/include
三.整合FFmpeg命令列功能
我們在使用ffmpeg時,可以直接使用該功能,通過設定命令引數,從而避免編寫大量c語言程式碼來呼叫ffmpeg庫。
1.找到如下檔案放入同一個資料夾下,並拷貝至工程目錄中:
1).從 ffmpeg-3.3.6 中找到以下檔案:
ffmpeg.h
ffmpeg.c
cmdutils.h
cmdutils.c
ffmpeg_filter.c
ffmpeg_opt.c
cmdutils_common_opts.h
2).從 scratch 資料夾下隨便一個架構資料夾中找到如下檔案:
config.h
2.修改檔案
1).前往 cmdutils.c 檔案中,註釋以下內容:
#include "compat/va_copy.h"
#include "libavdevice/avdevice.h"
#include "libavresample/avresample.h"
#include "libpostproc/postprocess.h"
#include "libavutil/libm.h"
PRINT_LIB_INFO(avdevice, AVDEVICE, flags, level);
PRINT_LIB_INFO(avresample, AVRESAMPLE, flags, level);
PRINT_LIB_INFO(postproc, POSTPROC, flags, level);
2).前往 ffmpeg_filter.c 檔案中,註釋以下內容:
#include "libavresample/avresample.h"
3).前往 ffmpeg.c 檔案中,註釋以下內容:
#include "libavdevice/avdevice.h"
#include "libavutil/internal.h"
#include "libavutil/libm.h"
#include "libavformat/os_support.h"
ff_dlog(NULL, "force_key_frame: n:%f n_forced:%f prev_forced_n:%f t:%f prev_forced_t:%f -> res:%f\n",
ost->forced_keyframes_expr_const_values[FKF_N],
ost->forced_keyframes_expr_const_values[FKF_N_FORCED],
ost->forced_keyframes_expr_const_values[FKF_PREV_FORCED_N],
ost->forced_keyframes_expr_const_values[FKF_T],
ost->forced_keyframes_expr_const_values[FKF_PREV_FORCED_T],
res);
同時,將 ffmpeg-3.3.6/libavcodec/mathops.h 和 ffmpeg-3.3.6/libavutil/reverse.h 兩個檔案複製至專案對應位置
4).前往 ffmpeg_opt.c 檔案中,註釋掉以下內容:
{ "videotoolbox_pixfmt", HAS_ARG | OPT_STRING | OPT_EXPERT, { &videotoolbox_pixfmt}, "" },
{ "vda", videotoolbox_init, HWACCEL_VDA, AV_PIX_FMT_VDA },
{ "videotoolbox", videotoolbox_init, HWACCEL_VIDEOTOOLBOX, AV_PIX_FMT_VIDEOTOOLBOX },
5).前往 ffmpeg.h 檔案下增加函式宣告:
int ffmpeg_main(int argc, char **argv);
6).修改 ffmpeg.c 檔案中
int main(int argc, char **argv)
為:
int ffmpeg_main(int argc, char **argv)
7).修改 ffmpeg.c 檔案中
#include "libavutil/time.h"
為:
#include "libavutil/ffmpegtime.h"
同時修改 FFmpeg-iOS/include/libavutil/time.h 為 ffmpegtime.h
8).修改執行一次 ffmpeg_main 方法後 App 退出問題
前往 cmdutils.h 中,將
void exit_program(int ret) av_noreturn;
方法宣告改為:
int exit_program(int ret);
並前往 cmdutils.c 中,將對應實現改為:
int exit_program(int ret)
{
if (program_exit)
program_exit(ret);
// exit(ret);
return ret;
}
9).修改多次呼叫 ffmpeg_main 時,訪問空指標的問題
前往 ffmpeg.c 中,在 ffmpeg_cleanup 方法中,增加處理。
在
term_exit();
前增加
nb_filtergraphs = 0;
nb_output_files = 0;
nb_output_streams = 0;
nb_input_files = 0;
nb_input_streams = 0;
四.使用ffmpeg
至此,我們已經整合了 ffmpeg 和 ffmpeg 的命令列工具,接下來我們就可以使用命令列來調起ffmpeg了。
使用ffmpeg命令列的大致格式如下:
ffmpeg [global_options] {[input_file_options] -i input_url} ... {[output_file_options] output_url}
對應於 ffmpeg 工具中,就是如下格式:
當然需要匯入 #import "ffmpeg.h"
int result = 1;
int argc = 19;
int i = 0;
char **arguments = calloc(argc, sizeof(char *));
if(arguments != NULL) {
arguments[i++] = "ffmpeg";
arguments[i++] = "-r";
arguments[i++] = (char *)[fps UTF8String];
arguments[i++] = "-i";
arguments[i++] = (char *)[gifPath UTF8String];
arguments[i++] = "-i";
arguments[i++] = (char *)[globalPalettePath UTF8String];
arguments[i++] = "-lavfi";
arguments[i++] = "paletteuse";
arguments[i++] = "-s";
arguments[i++] = (char *)[[NSString stringWithFormat:@"%dx%d", (int)size.width, (int)size.height] UTF8String];
arguments[i++] = "-t";
arguments[i++] = (char *)[[self p_formatFloat:[gifInfo[kFFmpegToolGifDuration] floatValue]] UTF8String];
arguments[i++] = "-r";
arguments[i++] = "10";
arguments[i++] = "-b:v";
arguments[i++] = "1024k";
arguments[i++] = "-y";
arguments[i++] = (char *)[resizeGifPath UTF8String];
result = ffmpeg_main(argc, arguments);
free(arguments);
}
其中,一些常見的引數配置如下:
-f 強制指定編碼格式
-i 輸出源
-t 指定輸入輸出時長
-r 指定幀率,即1S內的幀數
-threads 指定執行緒數
-c:v 指定視訊的編碼格式
-ss 指定持續時長
-b:v 指定位元率
-s 指定解析度
-y 覆蓋輸出
-filter 指定過濾器
-vf 指定視訊過濾器
-an 指定去除對音訊的影響
五.常見的命令及注意事項
1.GIF轉為同等長度視訊
ffmpeg -t 3.0 -i /Users/mdm/Desktop/water.gif -t 3.0 -b:v 1024k -y /User/mdm/Desktop/water.mp4
1).如果需要新增水印,可以增加 -vf 過濾器
ffmpeg -t 3.0 -i /Users/mdm/Desktop/water.gif -vf movie=/Users/mdm/Desktop/mark.png[watermark];[in][watermark]overlay=0:0[out] -t 3.0 -b:v 1024k -y /User/mdm/Desktop/water.mp4
其中 overlay 指定水印圖片所處的位置
2).如果需要指定輸出的解析度,可為輸出指定 -s 引數,若不指定,則預設輸出為輸入源同等大小解析度
ffmpeg -t 3.0 -i /Users/mdm/Desktop/water.gif -s 480x480 -t 3.0 -b:v 1024k -y /User/mdm/Desktop/water.mp4
注意:-s之後的引數需指定為整數
2.將一組照片生成為視訊
ffmpeg -r 7 -threads 0 -c:v png -i /Users/mdm/Desktop/resources/image%d.png -t 3.0 -b:v 1024k -y /Users/mdm/Desktop/water.mp4
注意:
- 該方法指定的是一個資料夾下的圖片路徑,ffmpeg是支援正則判斷的,所以該路徑下的圖片命名需要滿足 image%d.png 格式。
- 該方法需指定輸入的編碼格式,即 -c:v png ,所以讀取目錄下的所有圖片必須為 png 格式。
1).如果輸入的圖片尺寸不一致,那麼所有的圖片都會從左上角開始繪製,如果需要居中展示,可以進行輸出的邊界設定,提前指定一個輸出尺寸:
ffmpeg -r 7 -threads 0 -c:v png -i /Users/mdm/Desktop/resources/image%d.png -filter scale=480:480:force_original_aspect_ratio=decrease, pad=480:480:(480-in_w)/2:(480-in_h)/2:white -t 3.0 -b:v 1024k -y /Users/mdm/Desktop/water.mp4
其中scale指定繪製畫布大小,pad格式為pad=w:h:x:y:color,其中可以使用in_w,in_h表示輸入寬高,color指定邊界顏色。
3.對視訊進行調速
ffmpeg -t 3.0 -i /Users/mdm/Desktop/water.mp4 -an -r 25 -filter:v setpts=0.5*PTS -t 6.0 -b:v 1024k -y /Users/mdm/Desktop/newWater.mp4
注意:
- 該方法中,假設調為x倍速,則setpts=1.0 / x*PTS。
- 該調速方法,最多支援在[0.25, 4]區間內調整。
4.根據視訊生成GIF
ffmpeg -i /Users/mdm/Desktop/water.mp4 -f gif -r 15 -t 3.0 -b:v 1024k -y /Users/mdm/Desktop/water.gif
該方法可以將視訊轉換為GIF輸出,但是在輸出之後,發現GIF的影象質量不是很高。這是由於ffmpeg預設使用一個通用的全域性調色盤來覆蓋所有的顏色區域,以此來支援含有大量內容的檔案,所以生成的GIF影象質量不是很高。我們可以為視訊提供一個特有的全域性調色盤,這樣該視訊轉換出的GIF影象就有了特定的圖片內容支援,從而可以提高影象質量。
ffmpeg -i /Users/mdm/Desktop/water.mp4 -i /Users/mdm/Desktop/waterGlobalPalette.png -lavfi paletteuse=dither=sierra2:diff_mode=rectangle -f gif -r 15 -t 3.0 -b:v 1024k -y /Users/mdm/Desktop/water.gif
通過制定一個輸入源為全域性調色盤,進而來提高輸出GIF影象質量。
全域性調色盤生成請看下一條。
5.輸出特定全域性調色盤
ffmpeg -i /Users/mdm/Desktop/water.mp4 -vf palettegen -vframes 1 -y /Users/mdm/Desktop/globalPalette.png
6.縮放GIF
ffmpeg -i /Users/mdm/Desktop/water.gif -i /Users/mdm/Desktop/globalPalette.png -lavfi paletteuse -s 480x480 -t 3.0 -y /Users/mdm/Desktop/newWater.gif
注意:
- 在指定新的尺寸時,新尺寸不能大於輸入源舊尺寸。
7.裁剪GIF
ffmpeg -t 3.0 -i /Users/mdm/Desktop/water.png -vf crop=w=380:h=380:x=50:y=50 -t 3.0 -y /Users/mdm/Desktop/newWater.png
注意:
- crop引數有以下幾個可選值:輸入寬 iw,輸入高 ih,輸出寬 ow,輸出高 oh。
- crop引數的x,y預設值為:x = (iw - ow) / 2.0;y = (ih - oh) / 2.0;
- 在裁剪命令中,crop的引數需滿足 w + x 不大於輸入源寬度, h + y 不大於輸入源高度。
- 該命令需要有 crop filter 的支援,所以在FFmpeg的功能配置中,不能有 –disable-filter=crop的出現。
六.其他注意事項
- ffmpeg 是需要對實體檔案進行處理的,所以無論是輸入源還是輸出源,都必須對應實體檔案,同時在 ffmpeg 的命令中,需指定路徑。對於輸出源來說,如果不指定 -y,即覆蓋輸出,那麼如果輸出原始檔已經存在,ffmpeg 命令會執行失敗。
- ffmpeg 命令需要阻塞執行緒來處理,所以為了避免主執行緒的阻塞,建議放入子執行緒進行處理。
- ffmpeg 的輸入和輸出需要知道明確的編碼格式:輸入編碼可以通過解碼來獲取到,但是輸入編碼如果指定的話,就必須與實體檔案編碼一致,否則解碼會出錯;輸出源同樣需要指定編碼格式,如果沒有明確指定輸出的編碼格式,那麼需要在輸出路徑中指定字尾,否則會出現編碼出錯。
- 如果需要對 GIF 進行調速的話,直接通過指定 GIF 的 -r 來生成新的 GIF 是不合適的,因為 GIF 的幀間隔可以不一致,而通過設定 -r,就將所有幀間隔設定為一致,這樣生成的效果與理想效果不一致。可通過將 GIF 轉為視訊,然後調整視訊的速度生成新的視訊,進而再生成新 GIF 來達到目的。
- 由於 ffmpeg 的命令列工具中,有許多引數為全域性變數,所以為了保證使用的正確,我們需要保證在一個時間點,只有一次 ffmpeg_main() 方法的呼叫。
- 在使用全域性調色盤的時候,需要注意與水印的搭配處理。如果全域性調色盤是在新增水印之前就已經生成,那麼新增水印之後,使用該全域性調色盤生成 GIF,水印會被全域性調色盤校正,從而在 GIF 上顯示不出來。
七.參考文獻
相關文章
- ios開發注意事項iOS
- iOS發包前注意事項iOS
- delphi中的bpl開發注意事項
- web移動開發技能圖譜以及注意事項Web移動開發
- JavaScript 中 this 的工作原理以及注意事項JavaScript
- JavaScript中this的工作原理以及注意事項JavaScript
- 開發及上線中的注意事項
- iOS支付寶授權登入整合注意事項iOS
- [Android開發] 注意事項Android
- WatchKit 開發注意事項
- iOS 程式碼注意事項iOS
- iOS開發 iOS整合FFmpeg及視訊格式轉碼iOS
- 微信小程式開發注意事項微信小程式
- uni-app開發注意事項APP
- 介面開發文件及注意事項
- struts2 + urlrewrite 整合注意事項
- React生命週期以及注意事項React
- 安裝python3.5注意事項及相關命令Python
- 【轉】編譯Ogre1.9 IOS Dependencies及Ogre Source步驟及相關注意事項…編譯iOS
- spring cloud開發、部署注意事項SpringCloud
- 軟體開發中專案管理的注意事項(轉)專案管理
- Mysql索引以及使用索引注意事項MySql索引
- cookie的使用方法以及注意事項Cookie
- python相對匯入注意事項Python
- iOS專案開發實戰——storyboard設定介面技巧與注意事項iOS
- Storm介紹&實際開發注意事項ORM
- uni-app 跨端開發注意事項APP跨端
- IDEA Maven專案開發注意事項IdeaMaven
- Spring+SpringMvc+Mybatis整合注意事項SpringMVCMyBatis
- 淺談執行緒池(下):相關試驗及注意事項執行緒
- WebView與JS的互動,以及注意事項WebViewJS
- PureComponent 使用注意事項以及原始碼解析原始碼
- C中memcpy使用注意事項memcpy
- 網站定製開發需要注意的事項網站
- Unity3d遊戲開發注意事項Unity3D遊戲開發
- 資料庫開發程式設計師在開發過程中的注意事項資料庫程式設計師
- [FFMpeg] 非標準解析度視訊Dump YUV注意事項
- 關於GPIO合封引腳以及晶振引腳使用注意事項