iOS: FFmpeg编译和使用 学习

ffmpeg是一个多平台多媒体处理工具,处理视频和音频的功能很是强大。目前在网上搜到的iOS上使用FFMPEG的资料都比较陈旧,而FFMPEG更新迭代比较快; 且网上的讲解不够详细,对于初次接触FFMPEG的新手(例如我)来讲确实不太好使用。为了防止忘记,这里对iOS下使用FFMPEG作一个总结。html

1. FFMPEG层次结构的简单理解linux

要使用FFMPEG,首先须要理解FFMPEG的代码结构。根据志哥的提示,ffmpeg的代码是包括两部分的,一部分是library,一部分是tool。api都是在library里面,若是直接调api来操做视频的话,就须要写c或者c++了。另外一部分是tool,使用的是命令行,则不须要本身去编码来实现视频操做的流程。实际上tool只不过把命令行转换为api的操做而已。ios

2. 预热-在mac os下使用ffmpegc++

在mac os下使用ffmpeg比较简单,能够直接使用命令行来操做。首先安装ffmpeg,这里默认系统已经安装好brew,只须要在终端上输入:git

brew install ffmpeggithub

等待安装结束便可。api

安装结束后,尝试如下命令:xcode

ffmpeg -i input.mp4 output.avi网络

若是能顺利转换,代表安装成功架构

3. 编译能在iOS下使用的FFMPEG library库

这一步是编译1所说的library,编译好以后能够调用FFMPEG的api。网上有一些方法,但都要本身手动编译,稍显复杂并且比较陈旧。按照app store的需求,编译出来的包还必须支持arm64。我在万能的github中找到一个可以"一键编译"的脚本,地址以下:

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

并且写这个脚本的歪果仁挺好人,更新很及时,已经更新到了最新的2.5.3版本。下载下来,只有一个build-ffmpeg.sh脚本文件。在终端中转至脚本的目录,执行命令:

./build-ffmpeg.sh

脚本则会自动从github中把ffmpeg源码下到本地并开始编译。

编译结束后,文件目录以下:

【iOS开发】iOS下使用FFMPEG的一些总结

其中,ffmpeg-2.5.3是源码,FFmpeg-iOS是编译出来的库,里面有咱们须要的.a静态库,一共有7个。

执行命令:

lipo -info libavcodec.a

查看.a包支持的架构,这几个包都支持了armv7 armv7s i386 x86_64 arm64这几个架构,这个脚本果然是业界良心啊~~~

4.在xcode中引入FFMPEG library库

新建工程,把上面编译好的FFmpeg-iOS拖到xcode工程中,添加一个头文件引用

#include "avformat.h"

添加一个api语句:

av_register_all();

添加一个空的类,把执行文件.m后缀改成.mm,开启混编模式。

添加相应的framework,包括avfoundation和coremedia。

运行工程,若是没有报错,则代表编译成功。

5.在xcode项目中使用命令行

执行到第4步,已经可使用library库了。可是若是要对视频进行操做,仍是须要手动写不少代码去调用api,工做量较大,天然不如直接写命令行方便。为了命令行可以在xcode工程中使用,还须要作如下工做:

(1)添加源码中的tools,具体文件包括:

【iOS开发】iOS下使用FFMPEG的一些总结

(2)添加Header Search Paths

在target--build setting中搜索Header Search Paths,并在Header Search Paths下面添加源码ffmpeg-2.5.3和scratch的路径。

(3)修改ffmpeg.h和ffmpeg.c源码

若是此时run这个工程,则会报错,缘由是工程里面有2个main函数,此时处理方法为:

在ffmpeg.h中添加一个函数声明:

int ffmpeg_main(int argc, char **argv);

在ffmpeg.c中找到main函数,把main函数改成ffmpeg_main。

(4)调用命令行范例

添加头文件:#import "ffmpeg.h"

调用命令行

int numberOfArgs = 16;

char** arguments = calloc(numberOfArgs, sizeof(char*));

arguments[0] = "ffmpeg";

arguments[1] = "-i";

arguments[2] = inputPath;

arguments[3] = "-ss";

arguments[4] = "0";

arguments[5] = "-t";

arguments[6] = durationChar;

arguments[7] = "-vcodec";

arguments[8] = "copy";

arguments[9] = "-acodec";

arguments[10] = "aac";

arguments[11] = "-strict";

arguments[12] = "-2";

arguments[13] = "-b:a";

arguments[14] = "32k";

arguments[15] = outputPath;

int result = ffmpeg_main(numberOfArgs, arguments);

其中inputpath和outputpath是文件路径。经测试,这两个路径不支持asset-library://协议和file:// 协议,因此若是是要用相册的文件,我目前的解决办法是把它拷贝到沙盒里面。

6. 改关闭进程为关闭线程

若是顺利进行到了第5步,在app中是可以用命令行处理视频了,但会出现一个问题,app会退出。经肖大神提醒,发现了命令行执行完毕以后会退出进程。而iOS下只能启动一个进程,所以必须改关闭进程为关闭线程,或者直接把关闭进程的方法给注掉。

在ffmpeg.c中能够看到,执行退出进程的方法是exit_program,定位到了cmdutils.c中执行了c语言的exit方法。这里我将它改成了pthread_exit(须要添加#include 头文件)。在xcode项目中使用时,则能够用NSThread来新开一个线程,执行完毕以后,把线程关闭了便可。再使用NSThreadWillExitNotification通知,便可监听线程退出的状况。

7. 修复ffmpeg.c里面的一个bug

在实际项目中,可能须要屡次调用命令行,但在屡次调用命令行的过程当中,发现ffmpeg.c的代码中会访问空属性致使程序崩溃。逐步debug后发现,不少指针已经置空了,但它们的计数却没有置零,不知道是否是ffmpeg.c的一个bug。修复方法以下:在ffmpeg_cleanup方法下,将各个计数器置零,包括:

nb_filtergraphs

nb_output_files

nb_output_streams

nb_input_files

nb_input_streams

置零以后,重复使用ffmpeg_main方法一切正常。



文/L1先生(简书做者)
原文连接:http://www.jianshu.com/p/52516bdc1eb5
著做权归做者全部,转载请联系做者得到受权,并标注“简书做者”。

1、背景

  网上有不少FFmpeg编译配置的资料,大部分都是关于FFmpeg最新的版本(2.0)的,我一开始也想着编写一个2.0版本的,能够放到接手的那个项目中,发现各类问题(没法快进,没有声音),再看一下代码一堆警告,缘由很简单,使用的FFMpeg库太新了,不少接口变更了。因为手上没有多少信息,不知道那个项目使用的是哪一个版本的FFmpeg库,一点点找,终于知道原来使用的是0.7.x的。找到目标版本的FFmpeg本觉得万事大吉了,后来才发现原来这才是坑的开始,有历经一系列磨难,最后终于把编译问题解决了。

  

2、FFmpeg最新版本的库编译

  FFmpeg最新版本的应该是2.1的,历史版本详见http://www.ffmpeg.org/releases/,在这个网站上咱们能够下到全部历史版本的库。FFmpeg是一个跨平台的用C语言写成的库,包含了编码,解码,色彩空间转换等库。编译须要用到命令行,对于咱们这些没搞事后台或者linux开发的脚本知识欠缺的人来讲的确算是一个挑战。庆幸的是如今网络这么方便,不会作问Google,很快就找到了一个在xcode5下一键编译FFmpeg库的脚本。这个脚本是个老外写的,真心强大,从下载到编译到构建最后的Fat库一鼓作气。

  脚本地址: https://gist.github.com/m1entus/6983547

  运行这个脚本须要依赖一个库Perl写的脚本,搜了一下网上目前编译FFmpeg库的帖子基本都会提到这个脚本,脚本地址以下: https://github.com/mansr/gas-preprocessor

  下载完这两个脚本后,编译FFmpeg库的准备工做就基本完成了,接着依次执行下面几步:

  一、拷贝gas-preprocessor.pl文件到 /usr/bin目录下。

  二、修改gas-preprocessor.pl文件的权限

  注:须要有读,写和执行的权限。具体操做为,首先在命令行下进入/usr/bin目录,而后执行chmod命令,以下图所示:

  三、切换build-ffmpeg.sh脚本的目录下,使用命令sh build-ffmpeg.sh 运行该脚本便可。

 

  注:  1) build-ffmpeg.sh脚本的父目录的名字不能包括空格,不然可能致使构建失败。

      2) build-ffmpeg.sh脚本中能够配置编译的FFMpeg版本,以及使用iOS SDK的版本,以下图所示:

 

  该脚本中默认采用的FFmpeg是2.0版本,使用iOS 7.0的SDK编译,c语言编译器采用clang,应用中能够根据实际项目须要选中不一样的FFmpeg和iOS SDK版本。

  根据上面的步骤看来,编译工做也没有什么复杂的,为何我会说踩了不少坑呢?这个问题我会一点点儿解释。

 

3、编译较早期版本的FFmpeg本库

  第二部分中咱们介绍了一个牛逼的脚本,一键编译,这给咱们形成了一种错觉,FFmpeg编译不过如此吗!若是咱们尝试一下把脚本中的VERSION变成0.7试试,运行脚本,发现编译报错。以下图所示:

  提示位置选项--disable-iconv,根据提示咱们输入./configure查看全部可用选项。命令行下切换到实际的FFmpeg源码目录下,查看帮助以下图:

  咱们能够看到不少选项,英语不难,就是有些选项描述的太简洁了,因此实际使用时若是不肯定的话,咱们能够去问问google。

  好了回过头来看看这个configure文件到底有什么做用呢?

  一、裁剪

  咱们知道FFmpeg库是一个很是庞大的库,包括编码,解码以及流媒体的支持等,若是不作裁剪所有编译进来的话,最后生成的静态库会很大。实际使用中咱们可能只想用到解码(例如播放器),所以咱们可使用相关选项指定编译时禁用编码部分。固然咱们还能够作进一步的裁剪,例如只打开部分经常使用格式的解码,禁用掉其余的解码,这样编译出来的静态库将会更小。

  要想裁剪,咱们的先知道有哪些部分,使用下面的命令能够查看FFMpeg库支持的组件列表。

1
2
3
4
5
6
7
8
9
10
11
--list-decoders          show all available decoders
--list-encoders          show all available encoders
--list-hwaccels          show all available hardware accelerators
--list-muxers            show all available muxers
--list-demuxers          show all available demuxers
--list-parsers           show all available parsers
--list-protocols         show all available protocols
--list-bsfs              show all available bitstream filters
--list-indevs            show all available input devices
--list-outdevs           show all available output devices
--list-filters           show all available filters

  咱们能够根据实际须要把不用的部分都禁用掉,这样编译快,包也会比较小,经常使用的裁剪选项以下:

1
2
3
4
5
6
7
8
9
10
11
12
--disable-doc             do  not build documentation
--disable-ffmpeg         disable ffmpeg build
--disable-ffplay         disable ffplay build
--disable-ffserver       disable ffserver build
--disable-network        disable network support [no]
--disable-encoder=NAME   disable encoder NAME
--enable-encoder=NAME    enable encoder NAME
--disable-encoders       disable all encoders
--disable-decoder=NAME   disable decoder NAME
--enable-decoder=NAME    enable decoder NAME
--disable-decoders       disable all decoders
--disable-hwaccel=NAME   disable hwaccel

  举个例子,若是咱们须要作一款本地视频播放器,那么咱们可使用以下配置:

  

  固然你还能够根据帮助列表进行更细粒度的裁剪,例如只支持哪几种格式的解码等等。

 

  二、指定编译环境

  FFMpeg做为一个跨平台的库,不一样的平台,不一样的人的计算机上编译器的路径均可能不尽相同,因此咱们须要为编译脚本指定编译器的路径。同事咱们还能够指定其余编译选项,如是否交叉编译,目标平台系统,CPU架构,须要依赖的其余库的路径已经指定是否禁用汇编优化等。

1
2
3
4
5
6
7
8
9
10
11
--enable-cross-compile   assume a cross-compiler is used
--sysroot=PATH           root of cross-build tree
--sysinclude=PATH        location of cross-build system headers
--target-os=OS           compiler targets OS []
--cc=CC                  use C compiler CC [gcc]
--extra-cflags=ECFLAGS   add ECFLAGS to CFLAGS []
--extra-ldflags=ELDFLAGS add ELDFLAGS to LDFLAGS []
--arch=ARCH              select architecture []
--cpu=CPU                select the minimum required CPU (affects
                          instruction selection, may crash on older CPUs)
--disable-asm            disable all assembler optimizations

  sysroot即iOS SDK的路径,注意编译真机版本的库时须要使用iPhoneOS.platform中SDK的路径,编译模拟器版本的库使用iPhoneSimulator.platform中SDK的路径。target-os填写darwin(苹果系统的内核),arch能够根据具体的状况添加i386(模拟器),armv6,armv7等。cpu根据具体类型可填写cortex-a8,cortox-a9,i386等。   

 

  三、指定静态库的安装路径

  指定执行make install命令时编译好的静态库和相关头文件拷贝到的位置,即FFmpeg库编译后输出的路径。一般咱们只须要设置“--prefix=PREFIX”选项便可。例如咱们须要将最后生成静态库的路径指向“build/armv7”下,则设置--prefix="build/armv7";

   

4、FFmpeg0.7版本库一键编译脚本

  经过第三部分的介绍,相信咱们应该对FFmpeg的配置都有了一个初步的认识,咱们再回到第三部分开始时咱们运行build-ffmpeg.sh的碰到的问题,通过查看configure的帮助,咱们发现0.7这个版本的FFmpeg库倒是没有"--disable-iconv"选项。这个牛逼的脚本是针对当前较新的FFmpeg库写的,在低版本中没有一些配置选项也是正常。

  下面给出通过修改后的脚本,脚本中对原先的脚本进行了精简,去掉了下载部分的代码。

  build-ffmpeg0.7

  注:因为FFmpeg库比较陈旧,该脚本使用xcode4.6下,编译器为GCC,采用6.1的SDK进行编译。若是你的机器上装的同事安装了xcode4.x和xcode5的话,能够在命令行下使用以下命令切换当前的默认编译环境为xcode4.6便可:

  设置好xcode的编译环境之后,只须要将该脚本拷贝到FFMpeg源文件路径下运行便可一键生成armv7,armv7s,i386以及合成后的全平台库。

 

5、如何使用以及编译连接中可能遇到的问题

  第四部分中咱们对build-ffmpeg.sh的脚本进行了修改和精简后获得了build-ffmpeg0.7.sh,咱们只须要运行该脚本就能够一键完成FFmpeg 0.7版本库的编译工做了。编译后咱们获得的是lib目录(包含全部生成的静态库)以及include目录(包含相应的头文件),使用时咱们只须要将这些文件添加到工程中便可。

  问题到这里彷佛就所有解决了,若是顺利的话,恭喜你,你能够直接使用了。

  若是你跟我同样的"不幸"的话,可能还会遇到一些其余问题。下面是我遇到的问题及解决办法:

  一、time.h重复问题

  咱们知道通常静态库都是搭配头文件使用的,要在项目里面使用FFmpeg库,咱们出了须要在xcode的build phases中添加静态库之外,还须要导入该库对应的头文件。FFmpeg库对应的头文件有不少,一般会采用设置header search path的方式来导入头文件,这样作有两个好处: 第一能够避免对咱们的工程结构形成干扰。第二能够在必定程序上下降头文件冲突。

  time.h冲突的问题就是属于头文件冲突,系统的标准库中有time.h文件,FFmpeg应该是在1.1以后也加入了一个time.h文件,路径为libavutil/time.h。因此若是你使用的是FFmpeg1.1以后的版本,那么在使用中就可能会碰到头文件冲突的问题。解决这个问题,网上流传一个方法是修改FFmpeg库中time.h文件的名字,我以为这太麻烦了,并且也容易出错。后来查看FFmpeg源码的时候偶然发现它自身内部引用这个time.h的时候都有带一层父目录,如#include "libavutil/time.h"。所以想是否是经过指定头文件搜索路径就能够解决这个问题。

  打开工程设置页面,搜索header search path以下图所示:

  若是你的FFmpeg库正好是放在当前的路径下,且为了偷懒设置了递归包含头文件的话,那么你极可能就会遇到time.h冲突的问题。由于xcode工程默认的设置是优先查找用户路径,编译时FFmpeg中libavutil下的time.h就会优先被连接,从而致使不会再连接系统time.h文件,最终致使编译失败。

  解决这个问题有两个办法:

  a、取消掉Header Search Paths中的递归引用。

  b、设置Always Search User Paths为NO。

 

  二、gcc c compiletest error问题
  xcode5下面编译FFmpeg都采用clang,一样也会遇到相似问题。这个问题一般出如今配置文件错误的状况下,通常都是gcc路径错误,固然也多是其余编译参数错误问题。

  出现这个问题咱们应该首先检查gcc的路径是否正确,若是确认了指定路径上存在gcc程序,可是仍是报错的,咱们再去检查当前要编译的平台和指定的gcc路径是否一致,若是你使用iPhoneOS.platform下面的gcc去编译i386平台的库那确定是不会测试经过的。

 

  三、C compiler test failed问题
  编译i386版本的FFmpeg库和armv版本库可能用到的参数不尽相同,例如我遇到这个问题,个人编译选项中有一项以下:

  --extra-cflags='-arch i386 -mfloat-abi=softfp -miphoneos-version-min=5.0'

  在我确认其余参数(如cpu,arch)都正确的状况下,依然提示咱们“C compiler test failed.” 后面紧跟着一句查看config.log你能够获得更详细的信息,因而打开该文件,你能够在最开始的地方看到你的配置语句,若是是用脚本,这块儿会显示最终解释后(替换参数为真实值)的配置语句。而后紧跟着一堆具体的配置,一般哭啼的错误信息会在该文件的最末尾。我遇到的问题的信息以下:

  

  看到标红的这个区域了没有,提示“-mfloat-abi=softfp”选项不支持,删掉该选项后,在运行时配置就经过了。其余配置问题,均可以经过查看config.log来获取更详细的错误信息。

 

  四、因为未导入libbz动态库的问题

  若是导入FFmpeg库了,而且配置了头文件搜索路径,遇到"Undefined symbols for architecture armv7s: _BZ2_bzDecompressInit",以下图所示:

 

  这个问题是因为没有导入“libbz2.dylib”库的缘由,导入库便可解决该问题。

 

  五、libavcodec/audioconvert.h头文件缺失问题

  不知道为何执行make install的时候libavcodec中的audioconvert.h怎么没有拷贝到include目录下的libavcodec中去,查看发现原来libavutil目录下已经有一个audioconvert.h了。解决这个问题只须要从FFmpeg库的libavcodec中拷贝audioconvert.h头文件到include的libavcodec目录中便可解决。

 

6、杂谈

  感谢我所遭遇的"不幸",若是当时接受的项目使用的最新版本的FFmpeg库,我可能就直接运行一下那个牛逼的脚本,而后一切就能够顺顺利利。若是真是那样的话,我可能也就不会花时间去学习基本的脚本知识,去了解FFmpeg库的相关配置,这样的结果就是下次当我中奖遇到FFmpeg库编译连接等问题时,只能一筹莫展。

  说了这么多,当咱们使用一个技术的时候,不该该仅仅停留在会用的层次,花点儿时间了解一下背后的原理会更让你对该技术有个更深的理解,多学,多看,多思考,最终会有有所收获的。

 

7、编译脚本及参考资料

  一、编译脚本  

  gas-preprocessor脚本地址https://github.com/mansr/gas-preprocessor  

  FFmpeg 2.x一键化编译脚本: https://gist.github.com/m1entus/6983547

  FFmpeg0.7一键化编译脚本: https://gist.github.com/smileEvday/7565260

 

  二、参考资料

  模拟器与真机下ffmpeg的编译方法(总结版)

  http://www.cocoachina.com/iphonedev/toolthain/2011/1020/3395.html

  编译在ios4.3中使用的ffmpeg库(转)

  http://www.cocoachina.com/bbs/simple/?t70887.html

  Installing ffmpeg ios libraries armv7, armv7s, i386 and universal on Mac with 10.8 

  http://stackoverflow.com/questions/18003034/installing-ffmpeg-ios-libraries-armv7-armv7s-i386-and-universal-on-mac-with-10/19370679#19370679

相关文章
相关标签/搜索