FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。它提供了录制、转换以及流化音视频的完整解决方案。同时,FFmpeg是一套跨平台的方案,所以我们可以在iOS开发中使用它来进行一些视频与GIF的开发。

接下来,我们从编译FFmpeg开始,到使用FFmpeg,再到使用中的一些注意事项进行总结。

一、编译FFMpeg

在这个过程中,我们需要以下几个资源:

1.gas-preprocessor

2.yasm

3.FFmpeg-iOS-build-script

1.gas-preprocessor

gas-preprocessor 其实就是我们要编译FFmpeg所需的脚本文件。

1).下载并解压

2).将 gas-preprocessor.pl 文件复制到 /usr/sbin/ 目录下,如果该目录无法修改,那么可将文件复制到 /usr/local/bin/ 目录下。

3).为 gas-preprocessor.pl 文件开启可执行权限,在终端中进行如下命令:

chmod 777 /usr/sbin/gas-preprocessor.pl

chmod 777 /usr/local/bin/gas-preprocessor.pl

2.yasm

yasm 是一个完全重写的 NASM 汇编。目前,它支持 x86 和 AMD64 指令集,接受 NASM 和气体汇编语法,产出二进制,ELF32,ELF64,COFF,Mach-O 的(32和64),RDOFF2的 Win32 和 Win64 对象的格式,并生成 STABS 调试信息的来源,DWARF 2 ,CodeView 8格式。

MAC上可以使用homebrew来安装:

brew install yasm

3.FFMpeg-iOS-build-script

在build-ffmpeg.sh这个文件中,我们可以对要进行编译的FFmpeg进行一系列的设置。

1).设置FFmpeg的版本

FF_VERSION="3.4.2"

2).设置所要支持的架构,查找到 ARCHS 关键字

ARCHS="arm64 armv7"

3).设置所需要的FFmpeg功能配置 
该设置可在 CONFIGURE_FLAGS= 中进行,通过禁用一些不必要的功能,可以有效地减小最终库文件的大小,格式如下:

禁用交叉编译:

--disable-cross-compile

支持交叉编译:

--enable-cross-compile

4).确保该脚本所在路径中不包含有空格

5).需要为该脚本所在文件夹赋予权限
chmod 777 /Users/mdm/Desktop/ffmpeg

6).进入脚本所在文件夹目录,执行脚本
./build-ffmpeg.sh

此过程可能会出现各种问题,大多数问题可以通过前往执行脚本过程中生成的 scratch 文件夹下的 config.log 中查看对应原因。

另外,如果遇到

xcrun -sdk iphoneos clang is unable to create an executable file.
C compiler test failed.

这是由于系统安装了多个Xcode环境或者没有选定指定的Xcode环境所致,可使用下面方法选定一个Xcode环境来解决问题:

sudo xcode-select -s /Applications/Xcode.app

7).脚本执行完毕,生成所需文件

ffmpeg-3.3.6 FFmpeg源文件

scratch 编译过程中生成的文件

thin 对应各个架构下的库文件

FFmpeg-iOS 合并各个架构之后的库文件

 

三.集成FFmpeg命令行功能

我们在使用ffmpeg时,可以直接使用该功能,通过设置命令参数,从而避免编写大量c语言代码来调用ffmpeg库。

1.找到如下文件放入同一个文件夹下,并拷贝至工程目录中:

1).从 ffmpeg-3.3.6 中找到以下文件:

ffmpeg.h

ffmpeg.c

cmdutils.h

cmdutils.c

ffmpeg_filter.c

ffmpeg_opt.c

cmdutils_common_opts.h

2).从 scratch 文件夹下随便一个架构文件夹中找到如下文件:

config.h

2.修改文件

1).前往 cmdutils.c 文件中,注释以下内容:

#include "compat/va_copy.h"
#include "libavdevice/avdevice.h"
#include "libavresample/avresample.h"
#include "libpostproc/postprocess.h"
#include "libavutil/libm.h"
PRINT_LIB_INFO(avdevice, AVDEVICE, flags, level);
PRINT_LIB_INFO(avresample, AVRESAMPLE, flags, level);
PRINT_LIB_INFO(postproc, POSTPROC, flags, level);

2).前往 ffmpeg_filter.c 文件中,注释以下内容:
#include "libavresample/avresample.h"

3).前往 ffmpeg.c 文件中,注释以下内容:

#include "libavdevice/avdevice.h"
#include "libavutil/internal.h"
#include "libavutil/libm.h"
#include "libavformat/os_support.h" ff_dlog(NULL, "force_key_frame: n:%f n_forced:%f prev_forced_n:%f t:%f prev_forced_t:%f -> res:%f\n", ost->forced_keyframes_expr_const_values[FKF_N], ost->forced_keyframes_expr_const_values[FKF_N_FORCED], ost->forced_keyframes_expr_const_values[FKF_PREV_FORCED_N], ost->forced_keyframes_expr_const_values[FKF_T], ost->forced_keyframes_expr_const_values[FKF_PREV_FORCED_T], res); 
同时,将 ffmpeg-3.3.6/libavcodec/mathops.h 和 ffmpeg-3.3.6/libavutil/reverse.h 两个文件复制至项目对应位置

4).前往 ffmpeg_opt.c 文件中,注释掉以下内容:

{ "videotoolbox_pixfmt", HAS_ARG | OPT_STRING | OPT_EXPERT, { &videotoolbox_pixfmt}, "" },
{ "vda", videotoolbox_init, HWACCEL_VDA, AV_PIX_FMT_VDA },
{ "videotoolbox", videotoolbox_init, HWACCEL_VIDEOTOOLBOX, AV_PIX_FMT_VIDEOTOOLBOX },

5).前往 ffmpeg.h 文件下增加函数声明:

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

6).修改 ffmpeg.c 文件中

int main(int argc, char **argv)
改为
int ffmpeg_main(int argc, char **argv)

7).修改 ffmpeg.c 文件中
#include "libavutil/time.h" 为 #include "libavutil/ffmpegtime.h"

同时修改 FFmpeg-iOS/include/libavutil/time.h 为 ffmpegtime.h

8).修改执行一次 ffmpeg_main 方法后 App 退出问题 
前往 cmdutils.h 中,将

void exit_program(int ret) av_noreturn; 
方法声明为
int exit_program(int ret);
并前往 cmdutils.c 中,将对应实现改为:
int exit_program(int ret) {
  if (program_exit)
   program_exit(ret);
  // exit(ret);
  return ret; }

9).修改多次调用 ffmpeg_main 时,访问空指针的问题 
前往 ffmpeg.c 中,在 ffmpeg_cleanup 方法中,增加处理。


term_exit();
之前 增加
 nb_filtergraphs = ;
nb_output_files = ;
nb_output_streams = ;
nb_input_files = ;
nb_input_streams = ;

四.使用ffmpeg

至此,我们已经集成了 ffmpeg 和 ffmpeg 的命令行工具,接下来我们就可以使用命令行来调起ffmpeg了。

使用ffmpeg命令行的大致格式如下:

ffmpeg [global_options] {[input_file_options] -i input_url} ... {[output_file_options] output_url}



对应于 ffmpeg 工具中,就是如下格式: 

当然需要导入 #import "ffmpeg.h"

int result = ;
int argc = ;
int i = ;
char **arguments = calloc(argc, sizeof(char *));
if(arguments != NULL) {
arguments[i++] = "ffmpeg";
arguments[i++] = "-r";
arguments[i++] = (char *)[fps UTF8String];
arguments[i++] = "-i";
arguments[i++] = (char *)[gifPath UTF8String];
arguments[i++] = "-i";
arguments[i++] = (char *)[globalPalettePath UTF8String];
arguments[i++] = "-lavfi";
arguments[i++] = "paletteuse";
arguments[i++] = "-s";
arguments[i++] = (char *)[[NSString stringWithFormat:@"%dx%d", (int)size.width, (int)size.height] UTF8String];
arguments[i++] = "-t";
arguments[i++] = (char *)[[self p_formatFloat:[gifInfo[kFFmpegToolGifDuration] floatValue]] UTF8String];
arguments[i++] = "-r";
arguments[i++] = "";
arguments[i++] = "-b:v";
arguments[i++] = "1024k";
arguments[i++] = "-y";
arguments[i++] = (char *)[resizeGifPath UTF8String]; result = ffmpeg_main(argc, arguments);
free(arguments);
}

其中,一些常见的参数配置如下:

-f 强制指定编码格式

-i 输出源

-t 指定输入输出时长

-r 指定帧率,即1S内的帧数

-threads 指定线程数

-c:v 指定视频的编码格式

-ss 指定持续时长

-b:v 指定比特率

-s 指定分辨率

-y 覆盖输出

-filter 指定过滤器

-vf 指定视频过滤器

-an 指定去除对音频的影响

五.常见的命令及注意事项

1.GIF转为同等长度视频

ffmpeg -t 3.0 -i /Users/mdm/Desktop/water.gif -t 3.0 -b:v 1024k -y /User/mdm/Desktop/water.mp4

1).如果需要添加水印,可以增加 -vf 过滤器

ffmpeg -t 3.0 -i /Users/mdm/Desktop/water.gif -vf movie=/Users/mdm/Desktop/mark.png[watermark];[in][watermark]overlay=:[out] -t 3.0 -b:v 1024k -y /User/mdm/Desktop/water.mp4

其中 overlay 指定水印图片所处的位置

2).如果需要指定输出的分辨率,可为输出指定 -s 参数,若不指定,则默认输出为输入源同等大小分辨率

ffmpeg -t 3.0 -i /Users/mdm/Desktop/water.gif -s 480x480 -t 3.0 -b:v 1024k -y /User/mdm/Desktop/water.mp4

注意:-s之后的参数需指定为整数

2.将一组照片生成为视频

ffmpeg -r  -threads  -c:v png -i /Users/mdm/Desktop/resources/image%d.png -t 3.0 -b:v 1024k -y /Users/mdm/Desktop/water.mp4

注意:

  • 该方法指定的是一个文件夹下的图片路径,ffmpeg是支持正则判断的,所以该路径下的图片命名需要满足 image%d.png 格式。
  • 该方法需指定输入的编码格式,即 -c:v png ,所以读取目录下的所有图片必须为 png 格式。

1).如果输入的图片尺寸不一致,那么所有的图片都会从左上角开始绘制,如果需要居中展示,可以进行输出的边界设置,提前指定一个输出尺寸:

ffmpeg -r  -threads  -c:v png -i /Users/mdm/Desktop/resources/image%d.png -filter scale=::force_original_aspect_ratio=decrease, pad=::(-in_w)/:(-in_h)/:white -t 3.0 -b:v 1024k -y /Users/mdm/Desktop/water.mp4

其中scale指定绘制画布大小,pad格式为pad=w:h:x:y:color,其中可以使用in_w,in_h表示输入宽高,color指定边界颜色。

3.对视频进行调速

ffmpeg -t 3.0 -i /Users/mdm/Desktop/water.mp4 -an -r  -filter:v setpts=0.5*PTS -t 6.0 -b:v 1024k -y /Users/mdm/Desktop/newWater.mp4

注意:

  • 该方法中,假设调为x倍速,则setpts=1.0 / x*PTS。
  • 该调速方法,最多支持在[0.25, 4]区间内调整。

4.根据视频生成GIF

ffmpeg -i /Users/mdm/Desktop/water.mp4 -f gif -r  -t 3.0 -b:v 1024k -y /Users/mdm/Desktop/water.gif

该方法可以将视频转换为GIF输出,但是在输出之后,发现GIF的图像质量不是很高。这是由于ffmpeg默认使用一个通用的全局调色板来覆盖所有的颜色区域,以此来支持含有大量内容的文件,所以生成的GIF图像质量不是很高。我们可以为视频提供一个特有的全局调色板,这样该视频转换出的GIF图像就有了特定的图片内容支持,从而可以提高图像质量。

ffmpeg -i /Users/mdm/Desktop/water.mp4 -i /Users/mdm/Desktop/waterGlobalPalette.png -lavfi paletteuse=dither=sierra2:diff_mode=rectangle -f gif -r  -t 3.0 -b:v 1024k -y /Users/mdm/Desktop/water.gif

通过制定一个输入源为全局调色板,进而来提高输出GIF图像质量。 
全局调色板生成请看下一条。

5.输出特定全局调色板

ffmpeg -i /Users/mdm/Desktop/water.mp4 -vf palettegen -vframes  -y /Users/mdm/Desktop/globalPalette.png

6.缩放GIF

ffmpeg -i /Users/mdm/Desktop/water.gif -i /Users/mdm/Desktop/globalPalette.png -lavfi paletteuse -s 480x480 -t 3.0 -y /Users/mdm/Desktop/newWater.gif

注意:

  • 在指定新的尺寸时,新尺寸不能大于输入源旧尺寸。

7.裁剪GIF

ffmpeg -t 3.0 -i /Users/mdm/Desktop/water.png -vf crop=w=:h=:x=:y= -t 3.0 -y /Users/mdm/Desktop/newWater.png

注意:

  • crop参数有以下几个可选值:输入宽 iw,输入高 ih,输出宽 ow,输出高 oh。
  • crop参数的x,y默认值为:x = (iw - ow) / 2.0;y = (ih - oh) / 2.0;
  • 在裁剪命令中,crop的参数需满足 w + x 不大于输入源宽度, h + y 不大于输入源高度。
  • 该命令需要有 crop filter 的支持,所以在FFmpeg的功能配置中,不能有 –disable-filter=crop的出现。

六.其他注意事项

  • ffmpeg 是需要对实体文件进行处理的,所以无论是输入源还是输出源,都必须对应实体文件,同时在 ffmpeg 的命令中,需指定路径。对于输出源来说,如果不指定 -y,即覆盖输出,那么如果输出源文件已经存在,ffmpeg 命令会执行失败。
  • ffmpeg 命令需要阻塞线程来处理,所以为了避免主线程的阻塞,建议放入子线程进行处理。
  • ffmpeg 的输入和输出需要知道明确的编码格式:输入编码可以通过解码来获取到,但是输入编码如果指定的话,就必须与实体文件编码一致,否则解码会出错;输出源同样需要指定编码格式,如果没有明确指定输出的编码格式,那么需要在输出路径中指定后缀,否则会出现编码出错。
  • 如果需要对 GIF 进行调速的话,直接通过指定 GIF 的 -r 来生成新的 GIF 是不合适的,因为 GIF 的帧间隔可以不一致,而通过设置 -r,就将所有帧间隔设置为一致,这样生成的效果与理想效果不一致。可通过将 GIF 转为视频,然后调整视频的速度生成新的视频,进而再生成新 GIF 来达到目的。
  • 由于 ffmpeg 的命令行工具中,有许多参数为全局变量,所以为了保证使用的正确,我们需要保证在一个时间点,只有一次 ffmpeg_main() 方法的调用。
  • 在使用全局调色板的时候,需要注意与水印的搭配处理。如果全局调色板是在添加水印之前就已经生成,那么添加水印之后,使用该全局调色板生成 GIF,水印会被全局调色板校正,从而在 GIF 上显示不出来。
https://blog.csdn.net/TuGeLe/article/details/80656262
https://www.oschina.net/translate/high-quality-gif-with-ffmpeg
http://ffmpeg.org/documentation.html
												

iOS开发ffmpeg SDK 编译和集成的更多相关文章

  1. 李洪强iOS开发之-环信03_集成 SDK 基础功能

    李洪强iOS开发之-环信03_集成 SDK 基础功能 集成 SDK 基础功能 在您阅读此文档时,我们假定您已经具备了基础的 iOS 应用开发经验,并能够理解相关基础概念. SDK 同步/异步方法区分 ...

  2. iOS开发---iPhone SDK 包含哪些东西?

    第一部分: 在使用Intel芯片的Macintosh计算机开发iOS应用程序所需的全部接口.工具以及资源全都包含于iPhone SDK. 苹果公司将大部分系统接口发布在框架这种特殊的数据包.一个框架就 ...

  3. iOS开发 支付之银联支付集成

    iOS开发之银联支付集成 最近在做支付这一块的东西,就记录下来以便以后参考和各位交流学习,这里是银联支付 银联官网在这里,这里能下载SDK或者是看文档.文档嘛,对银联来说,还是不要看的太仔细的好,以前 ...

  4. 【Unity游戏开发】SDK接入与集成——小白入门篇

    一.简介 通常一款游戏开发到后期,一般都会涉及到第三方SDK的接入与集成,对于不熟悉SDK接入的同学来说,接SDK每次都是云里雾里,而熟悉SDK接入的同学又觉得不断地重复做接入SDK工作这样没有成就感 ...

  5. Delphi 10.3 Rio + iOS 12.1 SDK 编译错误 "libcharset.1.dylib"

    环境版本: Delphi 10.3 Rio iOS 12.1 SDK Xcode 10.1 (10B61) 错误讯息:[DCC Error] E2597 ld: file not found: /us ...

  6. iOS开发融云即时通讯集成详细步骤

    1.融云即时通讯iOS SDK下载地址   http://rongcloud.cn/downloads  选择iOS   SDK下载 2.进行应用开发之前,需要先在融云开发者平台创建应用,如果您已经注 ...

  7. iOS开发笔记:编译时出现的错误和解决办法

    1."std::ios_base::Init::~Init()", referenced from 出现这样的编译问题,是需要再加进libstdc++.dylib和libstdc+ ...

  8. iOS开发讯飞语音的集成

    1.进入官网注册账号,登陆,注册,应用. 2,下载sdk  导入系统库. 3,关闭bitcode 4,初始化讯飞语音. NSString * initString = [[NSString alloc ...

  9. iOS开发-FFmpeg深入分析

    FFmpeg是相当强大的多媒体编解码框架,在深入分析其源代码之前必须要有基本的多媒体基础知识,否则其源代码会非常晦涩难懂.本文将从介绍一些基本的多媒体只是,主要是为研读ffmpeg源代码做准备,比如一 ...

随机推荐

  1. Spark初识

    一.简介 1.什么是Spark 官网地址:http://spark.apache.org/ Apache Spark™是用于大规模数据处理的统一分析引擎. 从右侧最后一条新闻看,Spark也用于AI人 ...

  2. 设置Nginx以列表方式显示网站内容

    服务器目录内容: 访问该页面时,将所有文件和目录按列表方式显示 nginx配置文件

  3. 【经验分享】我经历的几门MOOC

    这半年来,从1月初到6月底,在coursera上注册了4们有关数据分析/挖掘的课程.这些课程都是利用业余时间学习,每周基本上花5个小时左右.其中通过了3门,注销了一门.感觉还是学到了一些东西. 第一门 ...

  4. 1.揭开消息中间件RabbitMQ的神秘面纱

    当你看到这篇博文的时候,相信你至少已经知道RabbitMQ 是一个非常优秀的消息中间件,它使用专门处理高并发的Erlang 语言编写而成的消息中间件产品. 当然如果你不知道也没关系,读完本篇你将Get ...

  5. Atitit 医学之道 attilax总结

    Atitit 医学之道 attilax总结 1. 相关的学科3 1.1. 口腔医学  ok3 1.2. 人体解剖学  ok3 1.3. 生理学  ok3 1.4. 病理学  ok3 1.5. 骨伤科学 ...

  6. java8学习的一点总结

    最近研究了一下java8 弄了几个例子学习了一下用法: 创建了一个实体类: @Data public class Apple { private Integer id; private String ...

  7. mac中安装wxpython

    一.简介 wxPython是Python语言的一套优秀的GUI图形库,允许Python程序员很方便的创建完整的.功能键全的GUI用户界面. wxPython是作为优秀的跨平台GUI库wxWidgets ...

  8. TCP连接

    https://www.cnblogs.com/dj0325/p/8490293.html

  9. Java知多少(11)数据类型转换

    数据类型的转换,分为自动转换和强制转换.自动转换是程序在执行过程中“悄然”进行的转换,不需要用户提前声明,一般是从位数低的类型向位数高的类型转换:强制类型转换则必须在代码中声明,转换顺序不受限制. 自 ...

  10. linux mysql卸载

    卸载mysql 1.查找以前是否装有mysql 命令:rpm -qa|grep -i mysql 可以看到mysql的包: mysql-libs-5.1.71-1.el6.x86_64 2.删除mys ...