iOS开发ffmpeg SDK 编译和集成

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 = 0;
 nb_output_files = 0;
 nb_output_streams = 0;
 nb_input_files = 0;
 nb_input_streams = 0;

四.使用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 = 1;
int argc = 19;
int i = 0;
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++] = "10";
    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=0:0[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 7 -threads 0 -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 7 -threads 0 -c:v png -i /Users/mdm/Desktop/resources/image%d.png -filter scale=480:480:force_original_aspect_ratio=decrease, pad=480:480:(480-in_w)/2:(480-in_h)/2: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 25 -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 15 -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 15 -t 3.0 -b:v 1024k -y /Users/mdm/Desktop/water.gif

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

5.输出特定全局调色板

ffmpeg -i /Users/mdm/Desktop/water.mp4 -vf palettegen -vframes 1 -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=380:h=380:x=50:y=50 -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/80656262https://www.oschina.net/translate/high-quality-gif-with-ffmpeg
http://ffmpeg.org/documentation.html


原文地址:https://www.cnblogs.com/wjw-blog/p/9522843.html

时间: 2024-08-26 11:59:59

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

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

第一部分: 在使用Intel芯片的Macintosh计算机开发iOS应用程序所需的全部接口.工具以及资源全都包含于iPhone SDK. 苹果公司将大部分系统接口发布在框架这种特殊的数据包.一个框架就是一个目录,它包含一个动态共享库以及使用这个库所需的资源(例如头文件.图像以及帮助应用程序等).如果要使用某个框架,则需要将其链接到应用程序工程,这一点和使用其他共享库相似.另外,您还需要告知开发工具何处可以找到框架头文件以及其他资源. 除了使用框架,苹果公司还通过标准共享库的形式来发布某些技术.

iOS开发你不知道的事-编译&链接

对于平常的应用程序开发,我们很少需要关注编译和链接过程.我们平常Xcode开发就是集成的的开发环境(IDE),这样的IDE一般都将编译和链接的过程一步完成,通常将这种编译和链接合并在一起的过程称为构建,即使使用命令行来编译一个源代码文件,简单的一句gcc hello.c命令就包含了非常复杂的过程! 正是因为集成开发环境的强大,很多系统软件的运行机制与机理被掩盖,其程序的很多莫名其妙的错误让我们无所适从,面对程序运行时种种性能瓶颈我们束手无策.我们看到的是这些问题的现象,但是却很难看清本质,所有这

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

1.融云即时通讯iOS SDK下载地址   http://rongcloud.cn/downloads  选择iOS   SDK下载 2.进行应用开发之前,需要先在融云开发者平台创建应用,如果您已经注册了融云开发者帐号,请前往 融云开发者平台 创建应用:如果您还没有注册融云开发者帐号,请前往 融云官方网站 首先注册开发者帐号,注册后创建应用.注册地址  https://developer.rongcloud.cn/signup 3.登陆融云开发者平台 https://developer.rong

iOS开发笔试面试- 编译原理

C/C++的编译器是集成的,编译一边分为4个过程: 1.预处理阶段 : 从hello.c变成hello.i文件,预处理一般就是将include或者define之类的东西,插入到代码中: 2.编译 : 从hello.i 变成hello.s文件,过程:检查语法.规范,在确定无误之后,将代码翻译成汇编语言. 3.汇编 : 从hello.s 变成hello.o文件,过程:将汇编代码转换成二进制文件. 4.连接 : 根据目标文件以及库文件,完成链接之后,生成可执行文件. 有用的地方:我们在开发的过程中,c

iOS开发讯飞语音的集成

1.进入官网注册账号,登陆,注册,应用. 2,下载sdk  导入系统库. 3,关闭bitcode 4,初始化讯飞语音. NSString * initString = [[NSString alloc] initWithFormat:@"appid=%@",@"56fb34f4"]; [IFlySpeechUtility createUtility:initString]; 5.集成代码 #import <UIKit/UIKit.h> #import &

IOS开发 EaseMobSDK 环信快速集成

一.新建工程,将下载好环信SDK的<EaseMobSDK>文件夹添加到工程中. 二.添加依赖 三.将EaseMobSDK文件夹中include文件夹路径添加到Header Search Paths中. Other Linker Flags 中添加-ObjC 都在Build Settings中. 四.完成后在 AppDelegate.m中 启动文件 中写入代码 [[EaseMob sharedInstance] registerSDKWithAppKey:@"easemob-demo#

极客学院iOS开发工程师初级+中级+高级视频教程

下载地址:百度网盘下载 课程目录: 初级IOS开发工程师 19课程 7小时50分职业介绍1.iOS 开发的前世今生C语言基础 1.二级 C 流程控制 2.二级 C 函数与指针 3.二级 C 数组4.二级 C 字符串5.二级 C 作用域.预处理与存储 6.二级 C 结构体与共用体 7.二级 C 文件Swift语言基础1.Swift 语言基础2.Swift 中的字符串和集合 3.Swift 中的函数和闭包 4.Swift 面向对象基础(上)5.Swift 面向对象基础(中) 6.Swift 面向对象

iOS开发&amp;nbsp;引用第三方库出现duplicat…

在iOS开发中, 难免会集成别人的三方类库, 当集成的三方类库过多时, 难免会出现某些库同时使用了同样的函数库, 导致link的时候报错提示duplicate symbol.详情见图: 引用第三方库出现duplicate symbol时的处理方法" TITLE="iOS开发 引用第三方库出现duplicate symbol时的处理方法" /> 这样的报错让我们觉得很麻烦, 这样的文件重复错误, 不是由自己的程序中的代码造成的, 而是别人的三方类库, 也就是说, 这种情况

iOS开发——高级篇——如何集成支付宝SDK

一.什么是支付宝 第三方支付平台 和内购非常相似内购是用户将钱付款给苹果,之后苹果分成给商户支付宝是用户将钱付款给支付宝,之后支付宝将钱转入我们的账户 使用支付宝前提购买的物品必须是和应用程序无关的.比如:团购卷/衣服/电子产品如果和应用程序有关,必须采用内购(否则不允许上架).比如:会员/游戏道具 二.集成支付宝 现在不少app内都集成了支付宝功能 使用支付宝进行一个完整的支付功能,大致有以下步骤:向支付宝申请, 与支付宝签约,获得商户ID(partner)和账号ID(seller)和私钥(p