FFmpeg iOS 集成过程

记录最新FFmpeg-ios集成过程,方便查看。

FFmpeg 官网 : http://ffmpeg.org/download.html
FFmpeg 源码 : https://github.com/FFmpeg/FFmpeg
FFmpeg doc : http://www.ffmpeg.org/documentation.html
FFmpeg wiki : https://trac.ffmpeg.org/wiki
Homebrew 安装网站 : http://brew.sh/index_zh-cn.html
FFmpeg官方安装教程 : https://trac.ffmpeg.org/wiki/CompilationGuide/MacOSX

安装过程Homebrew

ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

如果之前装过Homebrew,查是否装过,命令输入

brew

要是报Version value must be a string; got a NilClass () (TypeError),则执行命令brew update-reset。

Error: homebrew-core is a shallow clone.
删除homebrew-core后更新即可

cd /usr/local/Homebrew/Library/Taps/homebrew
rm -rf homebrew-core
brew upgrade

fatal: unable to access 'https://github.com/homebrew/brew/':
解决方案:

git config --global --unset http.proxy
git config --global --unset https.proxy

安装过程ffmpeg

安装好Homebrew,然后终端执行

brew install ffmpeg

等待完成即可。
执行结束,在终端中输入

ffmpeg

验证是否安装成功。

安装 gas-preprocessor

后面运行 FFmpeg-iOS-build-script 这个自动编译脚本需要 gas-preprocessor,将 gas-preprocessor.pl 文件复制到/usr/local/bin/ 目录下,然后为文件开启可执行权限,删掉下载的文件

sudo git clone https://github.com/bigsen/gas-preprocessor.git /usr/local/bin/gas
sudo cp /usr/local/bin/gas/gas-preprocessor.pl /usr/local/bin/gas-preprocessor.pl
sudo chmod 777 /usr/local/bin/gas-preprocessor.pl
sudo rm -rf /usr/local/bin/gas/

安装 yams

yasm是汇编编译器,因为ffmpeg中为了提高效率用到了汇编指令,所以编译时需要安装

brew install yasm

检测是否已安装 yasm

brew info yasm

配置 FFmpeg 编译脚本

编译FFmpeg可使用一个脚本:FFmpeg-iOS-build-script.sh。
FFmpeg-iOS-build-script 是一个外国人写的自动编译的脚本,脚本则会自动从github中把ffmpeg源码下到本地并开始编译出iOS可用的库,支持各种架构。
脚本下载地址:

git clone https://github.com/kewlbear/FFmpeg-iOS-build-script.git

或者进入 gitHub 网站,下载 FFmpeg-iOS-build-script (https://github.com/kewlbear/FFmpeg-iOS-build-script)。
下载好压缩包,编译脚本,打包出我们需要的 iOS 的 ffmpeg 库

To build everything:
  ./build-ffmpeg.sh

To build arm64 libraries:
  ./build-ffmpeg.sh arm64

To build fat libraries for armv7 and x86_64 (64-bit simulator):
  ./build-ffmpeg.sh armv7 x86_64

To build fat libraries from separately built thin libraries:
  ./build-ffmpeg.sh lipo

此时我们选择 ./build-ffmpeg.sh 编译arm64 armv7 x86_64 i386

iOS 下 集成 FFmpeg

新建一个空项目,在Link Binary With Libraries 里添加

libz.tbd
libbz2.tbd
libiconv.tbd
CoreMedia.framework
VideoToolbox.framework
AudioToolbox.framework
AVFoundation.framework

将目录下的 include 和 lib文件夹 拖拽进项目中,设置 Header Search Paths 路径,指向 项目中include目录(如

"$(SRCROOT)/FFmpegTest/FFmpeg-iOS/include"

没加双引号,路径中间有空格会就成两个路径,没空格加不加引号都一样) 。
然后导入

 #import "avformat.h"

在代码中 写

av_register_all();

然后进行编译,如果没有报错,代表编译成功( av_register_all()已经过时了,但是还可以验证是不是引用ffmpeg对了)。
到这一步其实已经可以使用library库了,如果要对音视频进行操作,需要手动写C++代码去调用 API 使用FFmpeg。

iOS 运行 FFmpeg Tool

如果想要使用Tool工具来调用 FFmpg 的话,就是直接通过调用传参的方式执行ffmpeg 命令的话,就需要:
把剩下的 ffmpeg.h ffmpeg.c 等依赖的文件拖进项目中,并导入

#import "ffmpeg.h"

文件。
然后进行调用 ffmpeg_main 函数传递参数,执行 ffmpeg 命令即可。
FFmpeg Tool 相关文件:
cmdutils.c
cmdutils.h
config.h
ffmpeg_filter.c
ffmpeg_hw.c
ffmpeg_opt.c
ffmpeg_videotoolbox.c
ffmpeg.c
ffmpeg.h
9个文件
config.h 文件 (在scratch目录下四个文件都有 )
把这几个文件拖入对应文件到工程


image.jpeg

如果把相关其他文件导入后,编译的时候会发现有一些头文件找不到,如

#include "libavutil/thread.h"
#include "libavutil/reverse.h"
#include "libavutil/libm.h"
#include "libpostproc/postprocess.h"
#include "libpostproc/version.h"
#include "libavformat/os_support.h"
#include "libavcodec/mathops.h"

去相应的ffmpeg目录找,还有一些比如

#include "libavresample/avresample.h"
#include "compat/va_copy.h" 
#include "libavutil/internal.h"

注释掉就可以了。

解决main函数重复问题
FFmpeg也有个main函数,如果不改名就会冲突报错。
打开 ffmpeg.c 文件,找到main函数,修改为 ffmpeg_main。
并在 ffmpeg.h 中声明。

最终引入的如下图


image.jpeg

优化解决集成后问题

编译成功了,但是还有一些小问题,比如重复执行ffmpeg报的bug,需要修改下。

  1. 计数器置零问题 (ffmpeg.c的代码中会访问空属性导致程序崩溃)
    解决方法:
    在 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;
  1. 命令执行结束崩溃问题
    1)第一种方案:
    改关闭进程为关闭线程
    网上流传的方法的方法都是找到 exit_program 函数,然后注释掉结束进程的代码,然后调用 pthread_exit 结束线程来代替结束进程,进行解决。
    这种方法的缺点:
    执行完 ffmpeg 的 main 函数后会回调一个code,这个回调是用于判断命令指定过程中是否执行错误的回调。但是我们如果在退出的时候调用了pthread_exit 这样线程就结束了,然后也不会走执行是否成功的回调了。
    并且这样的话,想要监听到命令结束,必须要注册一个通知,进行监听线程结束。也可以用下面的方式去掉av_noreturn,会执行回调。
    cmdutils.h
void exit_program(int ret);//后面的av_noreturn去掉,这个是给编译器的注解。

cmdutils.c

void exit_program(int ret)
{
    if (program_exit)
        program_exit(ret);

    //通知OC
    //dosomething();
    
    //把下面退出进程的注释
//    exit(ret);
    //只结束线种
    //关闭线程需要导入#include 
//    pthread_exit("all thread");
}

结束主线程会把主线程关掉了,所以想再次执行FFmpeg时就不会执行了,所以都注释掉不要关闭进程线程了。

第二种方案:
在命令执行完不进行结束线程和进程,只进行 cleanup。
具体做法:
在 ffmpeg.c 的 ffmpeg_main 函数中,把所有调用 exit_program 函数 ,改为调用 ffmpeg_cleanup 函数就可以了,

exit_program(1);

替换为

ffmpeg_cleanup(1);

测试视频转gif

导入头文件

#import "ffmpeg.h"
    NSString *fromFile = [[NSBundle mainBundle]pathForResource:@"abc.mp4" ofType:nil];
    
    NSString *toFile = @"/Users/limuyi/MyWork/aa/video.gif";
    
    NSFileManager *fileManager = [NSFileManager defaultManager];
    if ([fileManager fileExistsAtPath:toFile]) {
        [fileManager removeItemAtPath:toFile error:nil];
    }
    
    
//    int argc = 4;
//    char **arguments = calloc(argc, sizeof(char*));
//    if(arguments != NULL)
//    {
//        arguments[0] = "ffmpeg";
//        arguments[1] = "-i";
//        arguments[2] = (char *)[fromFile UTF8String];
//        arguments[3] = (char *)[toFile UTF8String];
//        if (!ffmpeg_main(argc, arguments)) {
//            NSLog(@"生成成功");
//        }
//    }
    
    char *a[] = {
        "ffmpeg", "-i", (char *)[fromFile UTF8String], (char *)[toFile UTF8String]
    };

    int result = ffmpeg_main(sizeof(a)/sizeof(*a), a);
    NSLog(@"生成结果 %d",result);

最后输出路径就有gif图啦


图片.png

DEMO
链接: https://pan.baidu.com/s/1hio5oa78HrrJPBFEmp0Tpw 提取码: 5i7z 复制这段内容后打开百度网盘手机App,操作更方便哦

你可能感兴趣的:(FFmpeg iOS 集成过程)