在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:c=white@0.5";
        
        // 画矩形
         char *filter = "drawbox=x=10:y=20:w=200:h=60:color=red@0.5";
        
        // 裁剪
        //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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,056评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,842评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,938评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,296评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,292评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,413评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,824评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,493评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,686评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,502评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,553评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,281评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,820评论 3 305
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,873评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,109评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,699评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,257评论 2 341

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,006评论 4 61
  • Swift版本点击这里欢迎加入QQ群交流: 594119878最新更新日期:18-09-17 About A cu...
    ylgwhyh阅读 25,248评论 7 249
  • 晴空万里 愿有人陪你颠沛流离 二人渐行渐远模糊着视线 车来人往 你我挥手 呐喊 追赶 最后错过 树底下 蚊子亲吻着...
    陳捞头阅读 416评论 0 5
  • 他观察她很久了 她关注他很久了 两个人都没有表白 直到有一天 他们不小心 看到了彼此的眼睛 那种爱的感觉 如触电般...
    照亮Br阅读 242评论 2 3
  • 我见过很多人在朋友圈晒书单,或者有些人天天找我要书单,让我推荐书籍,貌似很多人看了很多书。 但是当某天聊起其中一本...
    张涔汐阅读 871评论 7 22