在iOS中使用FFmpeg命令

作者: 秦明Qinmin | 来源:发表于2017-03-28 11:00 被阅读3197次

    简介

    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

    相关文章

      网友评论

      • Y_Swordsman:ffmpeg.c,大佬,我怎么没找到这个文件啊
      • afdb2956b049:大神,求demo 我是实在搞不定了
        bf19831f34ee:你好 搞定了吗 求帮助
      • afdb2956b049:从这里开始我就懵逼了“修改完成后,我们用Xcode新建一个静态库工程,然后将上面的源文件拖入项目中。在这里需要配置头文件搜索路径,我们主要是在FFmpeg-iOS文件夹中搜索....“什么意思? 有没有demo 啊?大神
      • David龘:大神,我在iOS上用"delogo"这个命令提示我"No such filter:delogo",但是我在Mac上用终端又能行,这是为什么?
      • Pony风:这个计数器清零根本做不到解决重复调用出现闪退的问题。这个还是得停止推流
      • 坑爹的疯狂:调用了ffmpeg_main的类,调不到dealloc方法,不知道是什么原因?
      • 实干家朔子:第二次执行会挂掉
        坑爹的疯狂:@打草篓兔子 参考下https://www.jianshu.com/p/dfc708bbacd5
        Pony风:@坑爹的疯狂 重复调用命令还是崩溃,你解决了么?能一起讨论下?
        坑爹的疯狂:@实干家朔子 遇到同样问题
      • 沃小沃:请问下,将图片转yuv要怎么写命令行
      • betterton:求问我如何知道转码完成了?
      • 庄msia:作者你好,请问一下,跟着你的步奏做,编译完ffmpeg静态库放进项目后出现这个错误
        duplicate symbol _program_birth_year in:
        ffmpeg.a(ffmpeg.o)
        ffmpeg.a(ffprobe.o)
        duplicate symbol _main in:
        AppDelegate.o
        ffmpeg.a(ffprobe.o)
        duplicate symbol _program_name in:
        ffmpeg.a(ffmpeg.o)
        ffmpeg.a(ffprobe.o)
        duplicate symbol _show_help_default in:
        ffmpeg.a(ffprobe.o)
        ffmpeg.a(ffmpeg_opt.o)

        请勿怎么解决呢
        庄msia:我用的是ffmpeg3.3.4
      • b6891a5278bd:你好 请问一下为什么在执行 ffmpeg_main 这一句命令的时候应用会卡死
      • jimmyken:Unrecognized option 'crf'.
        怎样让ffmpeg支持crf参数?xcode哪里加编译参数--enable-libx264?
      • 09e2c75d3b50:config.h共有4个丫,请问博主大神,打包静态库的时候用的是哪个?
      • 屋顶花园:请问,静态库里的config.h是在哪里获取的?
      • feng55:请问下。我现在需要再ffmpeg命令加两个滤镜。一个是旋转视频方向 -vf
        transpose=1 。第二个是需要裁剪一下这个视频的一部分(获取的视频有点问题)char *filter = "crop=w=1260:h=720:x=20:y=0"; 但是这两个滤镜无法同时使用啊,要么只有其中一个生效。要么直接错。请问这种一个命令里面加两个滤镜的情况要怎么处理呢
      • 焦糖大c:ffmpeg_clearup里面计数器没有置零所以会导致第二次点击崩溃吧?
        074a8e4ed8bf:/if (program_exit)
        // program_exit(ret);

        //exit(ret);

        是这里注释了的原因吧,我看源码里的这个函数调用就是做内存释放的,很多的free函数调用,注释了肯定内存泄漏,应该只注释//exit(ret);?
        秦明Qinmin:@ZAB_f035 是有这个问题
      • ttdiOS:修改完成后,我们用Xcode新建一个静态库工程,然后将上面的源文件拖入项目中。在这里需要配置头文件搜索路径,我们主要是在FFmpeg-iOS文件夹中搜索($(SRCROOT)/../FFmpeg-iOS/include)以及ffmpeg-3.0目录中搜索($(SRCROOT)/../ffmpeg-3.0)也就是源文件中搜索【是什么意思?将原文件拖入就会有其他的头文件导致comm+b(运行)出错,编译不出静态库】将上面的6个源文件拖入项目中,必须拖入其它的文件啊,博主
        ttdiOS:@bf19831f34ee 搞好了,demo看我的博客里
        bf19831f34ee:我也是这一步 不知道要不要吧文件都拖进去呀 搞定了吗?
        ttdiOS:静态库没有做出来
      • 465bba79f380:大神,你的demo我转码成功后,再次点击转码或点滤镜、水印,就崩掉了??麻烦还能看下什么原因,我找了好久没找到。。。
      • pingxhcn:大神,你写的例子不能重复使用ffmpeg,写的步骤到静态库那一步就走不过去了,静态库做出来有问题,能不能把静态库需要的文件写的详细一点?
        秦明Qinmin:@智者不愚 看一下你的头文件搜索路径,需要在ffmpeg源码中以及生成的静态库附带的头文件中搜索,生成的静态库附带的头文件并不会拷贝所有头文件。
        pingxhcn:@秦明Qinmin 我是把源文件拖到静态库里,然后把c 文件拉到静态库里,然后运行没有问题后,生成.a文件,把.a文件拖到工程中,会报compat/va_copy.h找不到
        秦明Qinmin:哪个地方走不过去
      • 465bba79f380:按照上面的步骤做的,为什么我的编码转换成功后,老是崩在ffmpeg.c的方法ffmpeg_cleanup里的这一行avfilter_graph_free(&fg->graph); 哪位大神还知道原因啊
        465bba79f380:添加水印不能添加png格式,各位大神还有解决办法?
        465bba79f380:@智者不愚 静态库按照步骤做的,可以编码转换成功,只是转换结束后崩掉,
        报的错是Thread 2:EXC_BAD_ACCESS(code=EXC_I386_GPFLT)
        pingxhcn:你的静态库做出来没有问题吗?
      • 5288f1f35982:挺详细的

      本文标题:在iOS中使用FFmpeg命令

      本文链接:https://www.haomeiwen.com/subject/yserottx.html