在iOS中使用FFmpeg命令
簡介
FFmpeg是一套可以用來記錄、轉換數字音訊、視訊,並能將其轉化為流的開源計算機程式。採用LGPL或GPL許可證。它提供了錄製、轉換以及流化音視訊的完整解決方案,包括了領先的音、視訊編碼庫libavcodec等。
ffmpeg.logo
以下是各個模組功能簡要說明:
libavformat:用於各種音視訊封裝格式的生成和解析;
libavcodec:用於各種型別聲音、影象編解碼;
libavutil:包含一些公共的工具函式;
libswscale:用於視訊場景比例縮放、色彩對映轉換;
libpostproc:用於後期效果處理;
ffmpeg:該專案提供的一個工具,可用於格式轉換、解碼或電視卡即時編碼等;
ffsever
ffplay:是一個簡單的播放器,使用ffmpeg 庫解析和解碼,通過SDL顯示;
實現功能
程式碼地址 https://github.com/QinminiOS/FFmpeg/
- 圖片、聲音合成視訊。
- 視訊編碼轉換。
- 視訊加水印。
- 視訊濾鏡。
庫檔案編譯
1、編譯編解碼庫檔案
由於FFmpeg庫檔案編譯的難度比較大,因此我這裡主要使用了github開源的指令碼檔案去編譯。指令碼地址: FFmpeg-iOS-build-script 這個指令碼更新很及時,已經支援3.0以上的版本了。這裡我使用的是FFmpeg3.0的版本。因此修改了shell指令碼的ffmpeg版本:
SOURCE="ffmpeg-3.0"
修改好後執行命令:
sh build-ffmpeg.sh
指令碼則會自動從github中把ffmpeg原始碼下到本地並開始編譯,編譯好後在當前目錄生成了FFmpeg-iOS資料夾。
注意:
在scratch目錄每個架構都有一個配置檔案 config.h 這個檔案比較重要。它表示當前編譯的庫檔案的配置引數。比如:開啟了哪些解碼器、編碼器。
配置檔案
2、編譯命令列支援庫檔案
當庫檔案編譯好後,我們可以看到在當前目錄生成了FFmpeg-iOS資料夾,這個資料夾就是編譯生成的通用庫。
庫檔案
由於我們在編譯的時候使用了--disable-programs編譯選項,如下所示:
CONFIGURE_FLAGS="--enable-cross-compile --disable-debug --disable-programs --disable-doc --enable-pic"
因此並不會編譯命令列相關的工具。因此,我們需要自己編譯相關檔案來支援FFmpeg命令列的解析。
在編譯命令解析相關的庫檔案的時候,我們主要用到了一下幾個原始檔
ffmpeg_videotoolbox.c
cmdutils.c
ffmpeg_filter.c
ffmpeg_opt.c
ffmpeg.c
ffprobe.c
在編譯的時候我們需要修改ffmpeg.c的main函式,因為一個程式不能有兩個main函式,在這裡我們改成ffmpeg_main,如下所示:
int ffmpeg_main(int argc, char **argv)
{
int ret;
int64_t ti;
register_exit(ffmpeg_cleanup);
setvbuf(stderr,NULL,_IONBF,0); /* win32 runtime needs this */
av_log_set_flags(AV_LOG_SKIP_REPEATED);
parse_loglevel(argc, argv, options);
if(argc>1 && !strcmp(argv[1], "-d")){
run_as_daemon=1;
av_log_set_callback(log_callback_null);
argc--;
argv++;
}
//以下是省略內容
...
}
我們還需要修改cmdutils.c中的exit_program函式,刪掉函式中原來的內容, 新增 return ret;並修改函式的返回型別為int。如果不修改,在FFmpeg命令執行完成後,程式會退出。
int exit_program(int ret);
int exit_program(int ret)
{
//if (program_exit)
// program_exit(ret);
//exit(ret);
return ret;
}
修改完成後,我們用Xcode新建一個靜態庫工程,然後將上面的原始檔拖入專案中。在這裡需要配置標頭檔案搜尋路徑,我們主要是在FFmpeg-iOS資料夾中搜索($(SRCROOT)/../FFmpeg-iOS/include)以及ffmpeg-3.0目錄中搜索($(SRCROOT)/../ffmpeg-3.0)也就是原始檔中搜索。之所以要在原始檔中搜索,是因為編譯出來的FFmpeg-iOS資料夾中並沒有拷貝所有標頭檔案,只有必要的標頭檔案。在這裡我們不需要連結之前編譯的庫檔案,因為靜態庫本來就只是編譯(clang -c)和打包(ar -r)的產物,並不需要連結。
靜態庫工程
編譯好後我們通過lipo -create 命令生成模擬器和真機架構的通用庫。
lipo -create /Users/qinmin/Library/Developer/Xcode/DerivedData/FFmpeg-cvfzxtnwpwznsfclqrttxwgczhjv/Build/Products/Debug-iphonesimulator/libFFmpeg.a /Users/qinmin/Library/Developer/Xcode/DerivedData/FFmpeg-cvfzxtnwpwznsfclqrttxwgczhjv/Build/Products/Debug-iphoneos/libFFmpeg.a -output /Users/qinmin/Desktop/libFFmpeg.a
使用庫檔案
1、視訊切分為圖片。
extern int ffmpeg_main(int argc, char * argv[]);
- (IBAction)sliceBtnClick:(UIButton *)sender
{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
char *movie = (char *)[BundlePath(@"1.mp4") UTF8String];
char *outPic = (char *)[DocumentPath(@"%05d.jpg") UTF8String];
char* a[] = {
"ffmpeg",
"-i",
movie,
"-r",
"10",
outPic
};
ffmpeg_main(sizeof(a)/sizeof(*a), a);
});
}
2、圖片、聲音合成視訊。
extern int ffmpeg_main(int argc, char * argv[]);
- (IBAction)composeBtnClick:(UIButton *)sender
{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
char *outPic = (char *)[DocumentPath(@"%05d.jpg") UTF8String];
char *movie = (char *)[DocumentPath(@"1.mp4") UTF8String];
char* a[] = {
"ffmpeg",
"-i",
outPic,
"-vcodec",
"mpeg4",
movie
};
ffmpeg_main(sizeof(a)/sizeof(*a), a);
});
}
3、視訊編碼轉換。
extern int ffmpeg_main(int argc, char * argv[]);
- (IBAction)transBtnClick:(UIButton *)sender
{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
char *outPic = (char *)[DocumentPath(@"out.avi") UTF8String];
char *movie = (char *)[BundlePath(@"1.mp4") UTF8String];
char* a[] = {
"ffmpeg",
"-i",
movie,
"-vcodec",
"mpeg4",
outPic
};
ffmpeg_main(sizeof(a)/sizeof(*a), a);
});
}
4、視訊加水印
extern int ffmpeg_main(int argc, char * argv[]);
- (IBAction)logoBtnClick:(UIButton *)sender
{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
char *outPic = (char *)[DocumentPath(@"logo.mp4") UTF8String];
char *movie = (char *)[BundlePath(@"1.mp4") UTF8String];
char logo[1024];
// 左上
sprintf(logo, "movie=%s [logo]; [in][logo] overlay=30:10 [out]", [BundlePath(@"ff.jpg") UTF8String]);
// 左下
//sprintf(logo, "movie=%s [logo]; [in][logo] overlay=30:main_h-overlay_h-10 [out]", [BundlePath(@"ff.jpg") UTF8String]);
// 右下
//sprintf(logo, "movie=%s [logo]; [in][logo] overlay=main_w-overlay_w-10:main_h-overlay_h-10 [out]", [BundlePath(@"ff.jpg") UTF8String]);
// 右上
//sprintf(logo, "movie=%s [logo]; [in][logo] overlay=main_w-overlay_w-10:10 [out]", [BundlePath(@"ff.jpg") UTF8String]);
char* a[] = {
"ffmpeg",
"-i",
movie,
"-vf",
logo,
outPic
};
ffmpeg_main(sizeof(a)/sizeof(*a), a);
});
}
5、視訊濾鏡
extern int ffmpeg_main(int argc, char * argv[]);
- (IBAction)filterBtnClick:(id)sender
{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
char *outPic = (char *)[DocumentPath(@"filter.mp4") UTF8String];
char *movie = (char *)[BundlePath(@"1.mp4") UTF8String];
// 畫格子
//char *filter = "drawgrid=w=iw/3:h=ih/3:t=2:[email protected]";
// 畫矩形
char *filter = "drawbox=x=10:y=20:w=200:h=60:[email protected]";
// 裁剪
//char *filter = "crop=in_w/2:in_h/2:(in_w-out_w)/2+((in_w-out_w)/2)*sin(n/10):(in_h-out_h)/2 +((in_h-out_h)/2)*sin(n/7)";
char* a[] = {
"ffmpeg",
"-i",
movie,
"-vf",
filter,
outPic
};
ffmpeg_main(sizeof(a)/sizeof(*a), a);
});
}
效果展示
切分圖片.png
加水印.png
濾鏡.png
總結
在移動裝置上使用FFmpeg編解碼、新增濾鏡等操作的時候還是很費CPU的。因此,在使用的時候需要綜合考慮。
參考
https://ffmpeg.org/ffmpeg-filters.html#crop
作者:秦明Qinmin
連結:https://www.jianshu.com/p/c236287e71ec
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯絡作者獲得授權並註明出處。