1. 程式人生 > >在iOS中使用FFmpeg命令

在iOS中使用FFmpeg命令

簡介

FFmpeg是一套可以用來記錄、轉換數字音訊、視訊,並能將其轉化為流的開源計算機程式。採用LGPL或GPL許可證。它提供了錄製、轉換以及流化音視訊的完整解決方案,包括了領先的音、視訊編碼庫libavcodec等。

ffmpeg.logo

以下是各個模組功能簡要說明:

libavformat:用於各種音視訊封裝格式的生成和解析;
libavcodec:用於各種型別聲音、影象編解碼;
libavutil:包含一些公共的工具函式;
libswscale:用於視訊場景比例縮放、色彩對映轉換;
libpostproc:用於後期效果處理;
ffmpeg:該專案提供的一個工具,可用於格式轉換、解碼或電視卡即時編碼等;
ffsever

:一個 HTTP 多媒體即時廣播串流伺服器;
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

FFmpeg-iOS-build-script

 



作者:秦明Qinmin
連結:https://www.jianshu.com/p/c236287e71ec
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯絡作者獲得授權並註明出處。