FFmpeg系列之编译

"FFmpeg在macOS和iOS下的编译详解"

Posted by SNLO on January 4, 2019

简介

跳过简介

FFmpeg介绍

FFmpeg创始人Fabrice Bellard

FFmpeg是领先的多媒体框架,能够解码,编码, 转码,复用,解复用流式传输过滤和播放人类和机器创建的任何内容。它支持最晦涩难懂的古代格式,直至最前沿。无论它们是由某些标准委员会,社区还是公司设计的。它还具有高度可移植性:FFmpeg 在各种构建环境、机器架构和配置下,跨Linux、Mac OS X、Microsoft Windows、BSD、Solaris等编译。

FFmpeg项目结构:

它包含可供应用程序使用的libavcodec,libavutil,libavformat,libavfilter,libavdevice,libswscale和libswresample。以及最终用户可用于转码和播放的ffmpeg,ffplay和ffprobe。

Library:
  • libavutil是一个包含简化编程功能的库,包括随机数生成器,数据结构,数学例程,核心多媒体实用程序等等。
  • libavcodec是一个包含用于音频/视频编解码器的解码器和编码器的库。
  • libavformat是一个包含多媒体容器格式的解复用器和复用器的库。
  • libavdevice是一个包含输入和输出设备的库,用于从许多常见的多媒体输入/输出软件框架中获取和呈现,包括Video4Linux,Video4Linux2,VfW和ALSA。
  • libavfilter是一个包含媒体过滤器的库。
  • libswscale是一个执行高度优化的图像缩放和色彩空间/像素格式转换操作的库。
  • libswresample是一个执行高度优化的音频重采样,重新矩阵化和样本格式转换操作的库。
Tool:
  • ffmpeg一个命令行工具,用来多媒体之间进行格式转换。
  • ffplay一个基于SDL和FFmpeg库的简单媒体播放器。
  • ffprobe简单的多媒体流分析器。

FFmpeg的小插曲

FFmpeg还有个分身Libav,他们最开始其实是一个团队的成员,由于FFmpeg为了迎合开发商,导致的代码凌乱和ABI不稳定而使得团队分裂了,Libav是FFmpeg镜像过来的,每当Libav有更新的时候FFmpeg会去拉取最新的修改。可以说FFmpeg是Libav的超集,并且FFmpeg似乎更受欢迎。

编译

macOS - FFmpeg

官方编译指南

编译环境:
安装HomeBrew:

mac自带brew,不过版本有可能比较老了,需要更新

1
2
3
4
5
$ xcode-select --install #Xcode提供编译环境
$ brew update #更新,有可能会很慢,慢到以为没反应,可以先卸载再安装,也可以继续等待
$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/uninstall)" #卸载
$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" #安装
$ brew cleanup # 清理所有包的旧版本
安装FFmpeg:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
$ brew info ffmpeg #获取安装选项帮助
$ brew install ffmpeg #安装FFmpeg
#安装FFmpeg完全版,所有的可选依赖项都选中。这可能需要花费上不少的时间,有可能你的网络状态不佳中断了,那还得从新执行一次安装命令,好的是之前下载成功过的不会再次下载了
$ brew install ffmpeg \
--with-aom \
--with-chromaprint \
--with-fdk-aac \
--with-fontconfig \
--with-freetype \
--with-frei0r \
--with-game-music-emu \
--with-libass \
--with-libbluray \
--with-libbs2b \
--with-libcaca \
--with-libgsm \
--with-libmodplug \
--with-librsvg \
--with-libsoxr \
--with-libssh \
--with-libvidstab \
--with-libvmaf \
--with-opencore-amr \
--with-openh264 \
--with-openjpeg \
--with-openssl \
--with-rtmpdump \
--with-rubberband \
--with-speex \
--with-srt \
--with-tesseract \
--with-two-lame \
--with-wavpack \
--with-webp \
--with-zeromq \
--with-zimg

如果出现Empty installation如下所示,可参考这个issues

1
2
3
4
5
Type `exit' to return and finalize the installation
Install to this prefix: /usr/local/Cellar/ffmpeg/4.1_1......
bash-3.2$ exit
exit
Error: Empty installation

安装成功后查看版本会得到以下信息,我是安装的完整版的FFmpeg

1
2
3
4
5
6
7
8
9
10
11
12
13
$ ffmpeg -version #检测是否安装成功
ffmpeg version 4.1 Copyright (c) 2000-2018 the FFmpeg developers
built with Apple LLVM version 10.0.0 (clang-1000.11.45.5)
configuration: --prefix=/usr/local/Cellar/ffmpeg/4.1_1 --enable-shared --enable-pthreads --enable-version3 --enable-hardcoded-tables --enable-avresample --cc=clang --host-cflags=-I/System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers/ --host-ldflags= --enable-ffplay --enable-gpl --enable-libmp3lame --enable-libopus --enable-libsnappy --enable-libtheora --enable-libvorbis --enable-libvpx --enable-libx264 --enable-libx265 --enable-libxvid --enable-lzma --enable-chromaprint --enable-frei0r --enable-libaom --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libfdk-aac --enable-libfontconfig --enable-libfreetype --enable-libgme --enable-libgsm --enable-libmodplug --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenh264 --enable-librsvg --enable-librtmp --enable-librubberband --enable-libsoxr --enable-libspeex --enable-libsrt --enable-libssh --enable-libtesseract --enable-libtwolame --enable-libvidstab --enable-libvmaf --enable-libwavpack --enable-libwebp --enable-libzimg --enable-libzmq --enable-opencl --enable-openssl --enable-videotoolbox --enable-libopenjpeg --disable-decoder=jpeg2000 --extra-cflags=-I/usr/local/Cellar/openjpeg/2.3.0/include/openjpeg-2.3 --enable-nonfree
libavutil      56. 22.100 / 56. 22.100
libavcodec     58. 35.100 / 58. 35.100
libavformat    58. 20.100 / 58. 20.100
libavdevice    58.  5.100 / 58.  5.100
libavfilter     7. 40.101 /  7. 40.101
libavresample   4.  0.  0 /  4.  0.  0
libswscale      5.  3.100 /  5.  3.100
libswresample   3.  3.100 /  3.  3.100
libpostproc    55.  3.100 / 55.  3.100

此时就可以在终端执行ffmpeg命令进行多媒体处理了。

iOS - FFmpeg

编译环境:
安装yasm:
1
2
3
4
5
6
$ brew install yasm
$ yasm --version
yasm 1.3.0
Compiled on Oct 14 2018.
Copyright (c) 2001-2014 Peter Johnson and other Yasm developers.
Run yasm --license for licensing overview and summary.
使用FFmpeg-iOS-build-script

在Github上下载下来找到build-ffmpeg.sh,感谢这个国外的朋友,可以给他star。

build-ffmpeg.sh文件中修改FF_VERSIONCONFIGURE_FLAGS两个参数,我只修改了版本信息为4.1,没有修改编译的配置参数。

1

CONFIGURE_FLAGS 部分配置选项:

1
2
$ cd ~/ffmpeg-4.1 
$ ./configure --help
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Configuration options:
  --disable-static         do not build static libraries [no]
  --enable-shared          build shared libraries [no]
  --enable-small           optimize for size instead of speed
  --disable-runtime-cpudetect disable detecting CPU capabilities at runtime (smaller binary)
  --enable-gray            enable full grayscale support (slower color)
  --disable-swscale-alpha  disable alpha channel support in swscale
  --disable-all            disable building components, libraries and programs
  --disable-autodetect     disable automatically detected external libraries [no]

Program options:
  --disable-programs       do not build command line programs
  --disable-ffmpeg         disable ffmpeg build
  --disable-ffplay         disable ffplay build
  --disable-ffprobe        disable ffprobe build
 .
 .
 .
  

需要精简和优化FFmpeg的话,可以在build-ffmpeg.sh脚本文件中修改CONFIGURE_FLAGS参数。比如添加参数:

1
2
3
4
5
6
7
8
9
--disable-muxers         disable all muxers
--disable-avdevice       disable libavdevice build
--enable-small           optimize for size instead of speed
--disable-sdl2           disable sdl2 [autodetect]
Program options:
  --disable-programs       do not build command line programs
  --disable-ffmpeg         disable ffmpeg build
  --disable-ffplay         disable ffplay build
  --disable-ffprobe        disable ffprobe build

此时CONFIGURE_FLAGS值为:

1
2
3
CONFIGURE_FLAGS="--enable-cross-compile --disable-debug --disable-programs \
                 --disable-doc --enable-pic \
                 --enable-gpl --enable-small --disable-sdl2 --disable-ffmpeg --disable-ffplay --disable-ffprobe --disable-avdevice --disable-network --disable-postproc"

在终端中运行build-ffmpeg.sh文件,等待一段时间,得到4个文件夹:

1
2
3
4
ffmpeg-4.1
FFmpeg-iOS
scratch
thin

集成

iOS集成FFmpeg静态库

FFmpeg-iOS中的文件全部拖入到项目工程里面,再到TARGETS->Build Phases->Link Binary With Libraries中添加下面几个依赖库:

2

iOS集成FFmpeg Tool

  • 找到./ffmpeg-4.1中的fftools拖入工程,当然也可以自己编写C/C++去调用FFmpeg-Tool注意如果是C++写的Tool话,上面的Libraries还需再添加一个libc++.tbd
  • 在TARGETS->Build Setting中搜索header search选项添加$(PROJECT_DIR)/.../include、$(PROJECT_DIR)/fftools
  • 报错的代码块和not found#include该注释掉的就直接注释掉好了,因为根本就没用到。
  • 项目中只允许一个main函数,所以在ffmpeg.c文件中修改main -> ffmpeg_main并在头文件中声明。
  • ffmpeg.c文件中导入:#include <pthread/pthread.h>头文件

优化

ffmpeg.c文件中 exit_program是退出的功能,我们不需要,直接全局改名为ffmpeg_cleanup。再在ffmpeg_cleanup函数中结尾处添加一下代码:

1
2
3
4
5
nb_filtergraphs = 0;
nb_output_files = 0;
nb_output_streams = 0;
nb_input_files = 0;
nb_input_streams = 0;

当输出文件已经存在时,APP会闪退。为防止这种情况闪退,在static void assert_file_overwrite(const char *filename)函数开头添加avpriv_io_delete(filename);移除旧文件。

更多的优化请自行查找,因为不同版本的优化大多有差异,而且几乎每3个月就有一次版本更新。如果嫌麻烦的话,那就放弃定制化编译吧,这个开源项目MobileFFmpeg可以了解下。

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#import "ViewController.h"
#import "ffmpeg.h"

@interface ViewController ()
@end

@implementation ViewController

- (void)viewDidLoad {
	[super viewDidLoad];
	// Do any additional setup after loading the view, typically from a nib.
	NSArray * array = @[@"ffmpeg",
						@"-i",
						@"/Users/snloveydus/Desktop/GitHub/NEW/NEW/video.mov",
						@"-f",
						@"wav",
						@"-ar",
						@"16000",
						@"/Users/snloveydus/Desktop/GitHub/NEW/NEW/video.wav"
						];
	int argc = (int)array.count;
	char **argv = calloc(argc, sizeof(char *));
	for (int i = 0; i < array.count; i ++) {
		argv[i] = (char *)[array[i] UTF8String];
	}
	if (!ffmpeg_main(argc, argv)) {
		NSLog(@"👌");
	}
}


@end

本文项目:DEMO。我会及时更新项目中FFmpeg的版本,并且会陆续开发一个iOS APP来作FFmpeg的使用教程。

参考

FFmpeg Wiki

FFmpeg-iOS-build-script

MobileFFmpeg