记录最新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目录下四个文件都有 )
把这几个文件拖入对应文件到工程
如果把相关其他文件导入后,编译的时候会发现有一些头文件找不到,如
#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 中声明。
最终引入的如下图
优化解决集成后问题
编译成功了,但是还有一些小问题,比如重复执行ffmpeg报的bug,需要修改下。
- 计数器置零问题 (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)第一种方案:
改关闭进程为关闭线程
网上流传的方法的方法都是找到 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图啦
DEMO
链接: https://pan.baidu.com/s/1hio5oa78HrrJPBFEmp0Tpw 提取码: 5i7z 复制这段内容后打开百度网盘手机App,操作更方便哦