Xcode-FFmpeg环境搭建


## 集成FFmpeg

 

主要分为量大块,按需将相关的库导入到工程中,补充缺失的头文件

 

## 编译FFmpeg

 

以iOS为例
需要指定平台,Xcode信息,root sdk路径,编译工具链,支持的arch,输出路径,等参数.这里推荐一份完成的脚本,如果自己编译碰到问题可以参考这个脚本[FFmpeg-iOS-build-script](https://github.com/kewlbear/FFmpeg-iOS-build-script)



执行`./build-ffmpeg.sh`,会自动抓去依赖`yasm:1.2.0`,`FFmpeg 4.3.1`,`gas-preprocessor.pl`然后编译生成`FFmpeg-iOS `文件夹,内容如下:

 

```shell
├── include
│   ├── libavcodec //用于各种类型声音、图像编解码;
│   ├── libavdevice //与系统输入输出设备交互,读取和写入多媒体数据
│   ├── libavfilter //实现滤镜效果,可以用其做一些音视频处理,如音视频倍速、水平翻转、裁剪、加方框、叠加文字等功能。
│   ├── libavformat //用于各种音视频封装格式的生成和解析
│   ├── libavutil //包含一些公共的工具函数
│   ├── libswresample //音频重采样、rematrixing和样本格式转换操作
│   └── libswscale //视频场景比例缩放、色彩映射转换;
└── lib
├── libavcodec.a
├── libavdevice.a
├── libavfilter.a
├── libavformat.a
├── libavutil.a
├── libswresample.a
└── libswscale.a
```

 

## 将FFmpeg-iOS集成到iOS工程中

 

以Swift项目为例子说明

 

创建并设置`bridge-header.h`,设置工程`Objective-Bridge-Header`的路径

 

```shell
#ifndef bridge_h
#define bridge_h

 

#include <libavcodec/avcodec.h>
#include <libavdevice/avdevice.h>
#include <libavfilter/avfilter.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include <libswresample/swresample.h>
#include <libswscale/swscale.h>
#endif /* bridge_h */
```

 

设置`User Search Path`

 

添加依赖库

 

```shell
* libz.dylib
* libbz2.dylib
* libiconv.dylib
```

 

添加测试代码,根据编译结果添加对应的系统库

 

```Swift
class ViewController: UIViewController {

 

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
avdevice_register_all()
avcodec_configuration()
}
}
```

 

错误信息会提示类或者属性无法找到,根据前缀添加系统库,下面是完整的依赖库

 

```shell
AudioToolbox.framework
VideoToolbox.framework
CoreAudio.framework
CoreVideo.framework
AVFoundation.framework
CoreMedia.framework
libz.tbd
ibiconv.tbd
libbz2.tbd
```

 

点击运行,若失败则需检查前面的步骤是否遗漏,亲测有效

 

## 集成FFmpeg命令行工具(可选)

 

此步骤可以将我们在终端命令行的`ffmpeg`命令传入到`ffmepg.h`的`main`函数中,可以同通过封装一套统一的协议api,输入参数到`main`函数中,然后输出文件

 

向工程中添加`ffmpeg`工具链main文件相关的类,并加`.c`文件添加到编译的build phase中

 

```shell
├── cmdutils.c
├── cmdutils.h
├── config.h
├── ffmpeg.c
├── ffmpeg.h
├── ffmpeg_filter.c
├── ffmpeg_opt.c
└── ffmpeg_videotoolbox.c
```

 

## 集成FFmpeg命令行-CONFIGURE_FLAGS设置

 

需要重新编译新的`FFmpeg`库,将`--disable-programs` flags关闭,运行通过命令行参数调用.
为了解决ffmpeg命令行依赖`libpostproc.a`库, 需要添加 `--enable-gpl` flags,网上看了很多博客都没有提到这,最后在全局搜索关键字`LICENSE`文件中发现了这样一段话

 

```shell
None of
these parts are used by default, you have to explicitly pass `--enable-gpl` to
configure to activate them. In this case, FFmpeg's license changes to GPL v2+.
Specifically, the GPL parts of FFmpeg are:
- libpostproc
...
```

 

大致意识就是如果你需要build这个库文件,需要指定`--enable-gpl`,但是FFmpeg的license将会变更成`GPL v2+`




最终确认在`ffmpeg-build.sh`中将`CONFIGURE_FLAGS`设定为如下选项

 

```shell
CONFIGURE_FLAGS="\
--enable-cross-compile \
--disable-debug \
--enable-gpl \
--disable-doc --enable-pic"
#--disable-programs \
```

 

得出的编译产物如下,相比原来多了一个库`libpostproc.a`

 

```shell
├── include
│ ├── libavcodec
│ ├── libavdevice
│ ├── libavfilter
│ ├── libavformat
│ ├── libavresample
│ ├── libavutil
│ ├── libpostproc
│ ├── libswresample
│ └── libswscale
└── lib
├── libavcodec.a
├── libavdevice.a
├── libavfilter.a
├── libavformat.a
├── libavresample.a
├── libavutil.a
├── libpostproc.a
├── libswresample.a
└── libswscale.a
```

 

## 集成FFmpeg命令行-缺失头文件补充

 

集成新的`FFmpeg`之后运行工程,根据错误提示,按路径添加缺失的头文件`.h`
根据如下类似的提示信息在`ffmpeg`库中搜索头文件并添加到工程中

 

```shell
Showing All Messages
/Users/qxq4633/ffmpegdemo/ffmpeg_demo/ffmpeg/ffmpeg.h:22:10: 'config.h' file not found
```

 

最终完整的头文件如下,亲测有效, ffmpeg版本`4.3.1`,`Xcode 12`, `iOS 14.0`

 

```shell
qxq4633@LSCN897187 ffmpeg_demo % tree -L 4
.
├── FFmpeg-iOS
│   ├── include
│   │   ├── libavcodec
│   │   │   ├── ac3_parser.h
│   │   │   ├── adts_parser.h
│   │   │   ├── avcodec.h
│   │   │   ├── avdct.h
│   │   │   ├── avfft.h
│   │   │   ├── bsf.h
│   │   │   ├── codec.h
│   │   │   ├── codec_desc.h
│   │   │   ├── codec_id.h
│   │   │   ├── codec_par.h
│   │   │   ├── d3d11va.h
│   │   │   ├── dirac.h
│   │   │   ├── dv_profile.h
│   │   │   ├── dxva2.h
│   │   │   ├── jni.h
│   │   │   ├── mathops.h
│   │   │   ├── mediacodec.h
│   │   │   ├── packet.h
│   │   │   ├── qsv.h
│   │   │   ├── vaapi.h
│   │   │   ├── vdpau.h
│   │   │   ├── version.h
│   │   │   ├── videotoolbox.h
│   │   │   ├── vorbis_parser.h
│   │   │   ├── x86
│   │   │   └── xvmc.h
│   │   ├── libavdevice
│   │   │   ├── avdevice.h
│   │   │   └── version.h
│   │   ├── libavfilter
│   │   │   ├── avfilter.h
│   │   │   ├── buffersink.h
│   │   │   ├── buffersrc.h
│   │   │   └── version.h
│   │   ├── libavformat
│   │   │   ├── avformat.h
│   │   │   ├── avio.h
│   │   │   ├── os_support.h
│   │   │   └── version.h
│   │   ├── libavresample
│   │   │   ├── avresample.h
│   │   │   └── version.h
│   │   ├── libavutil
│   │   │   ├── adler32.h
│   │   │   ├── aes.h
│   │   │   ├── aes_ctr.h
│   │   │   ├── attributes.h
│   │   │   ├── audio_fifo.h
│   │   │   ├── avassert.h
│   │   │   ├── avconfig.h
│   │   │   ├── avstring.h
│   │   │   ├── avutil.h
│   │   │   ├── base64.h
│   │   │   ├── blowfish.h
│   │   │   ├── bprint.h
│   │   │   ├── bswap.h
│   │   │   ├── buffer.h
│   │   │   ├── camellia.h
│   │   │   ├── cast5.h
│   │   │   ├── channel_layout.h
│   │   │   ├── common.h
│   │   │   ├── cpu.h
│   │   │   ├── crc.h
│   │   │   ├── des.h
│   │   │   ├── dict.h
│   │   │   ├── display.h
│   │   │   ├── dovi_meta.h
│   │   │   ├── downmix_info.h
│   │   │   ├── encryption_info.h
│   │   │   ├── error.h
│   │   │   ├── eval.h
│   │   │   ├── ffversion.h
│   │   │   ├── fifo.h
│   │   │   ├── file.h
│   │   │   ├── frame.h
│   │   │   ├── hash.h
│   │   │   ├── hdr_dynamic_metadata.h
│   │   │   ├── hmac.h
│   │   │   ├── hwcontext.h
│   │   │   ├── hwcontext_cuda.h
│   │   │   ├── hwcontext_d3d11va.h
│   │   │   ├── hwcontext_drm.h
│   │   │   ├── hwcontext_dxva2.h
│   │   │   ├── hwcontext_mediacodec.h
│   │   │   ├── hwcontext_opencl.h
│   │   │   ├── hwcontext_qsv.h
│   │   │   ├── hwcontext_vaapi.h
│   │   │   ├── hwcontext_vdpau.h
│   │   │   ├── hwcontext_videotoolbox.h
│   │   │   ├── hwcontext_vulkan.h
│   │   │   ├── imgutils.h
│   │   │   ├── internal.h
│   │   │   ├── intfloat.h
│   │   │   ├── intreadwrite.h
│   │   │   ├── lfg.h
│   │   │   ├── libm.h
│   │   │   ├── log.h
│   │   │   ├── lzo.h
│   │   │   ├── macros.h
│   │   │   ├── mastering_display_metadata.h
│   │   │   ├── mathematics.h
│   │   │   ├── md5.h
│   │   │   ├── mem.h
│   │   │   ├── motion_vector.h
│   │   │   ├── murmur3.h
│   │   │   ├── opt.h
│   │   │   ├── parseutils.h
│   │   │   ├── pixdesc.h
│   │   │   ├── pixelutils.h
│   │   │   ├── pixfmt.h
│   │   │   ├── random_seed.h
│   │   │   ├── rational.h
│   │   │   ├── rc4.h
│   │   │   ├── replaygain.h
│   │   │   ├── reverse.h
│   │   │   ├── ripemd.h
│   │   │   ├── samplefmt.h
│   │   │   ├── sha.h
│   │   │   ├── sha512.h
│   │   │   ├── spherical.h
│   │   │   ├── stereo3d.h
│   │   │   ├── tea.h
│   │   │   ├── thread.h
│   │   │   ├── threadmessage.h
│   │   │   ├── time.h
│   │   │   ├── timecode.h
│   │   │   ├── timestamp.h
│   │   │   ├── tree.h
│   │   │   ├── twofish.h
│   │   │   ├── tx.h
│   │   │   ├── version.h
│   │   │   ├── video_enc_params.h
│   │   │   ├── x86
│   │   │   └── xtea.h
│   │   ├── libpostproc
│   │   │   ├── postprocess.h
│   │   │   └── version.h
│   │   ├── libswresample
│   │   │   ├── swresample.h
│   │   │   └── version.h
│   │   └── libswscale
│   │   ├── swscale.h
│   │   └── version.h
│   └── lib
│   ├── libavcodec.a
│   ├── libavdevice.a
│   ├── libavfilter.a
│   ├── libavformat.a
│   ├── libavresample.a
│   ├── libavutil.a
│   ├── libpostproc.a
│   ├── libswresample.a
│   └── libswscale.a
├── LICENSE
├── README.en.md
├── README.md
├── compat
│   └── va_copy.h
├── config.h
├── ffmpeg
│   ├── cmdutils.c
│   ├── cmdutils.h
│   ├── ffmpeg.c
│   ├── ffmpeg.h
│   ├── ffmpeg_filter.c
│   ├── ffmpeg_hw.c
│   ├── ffmpeg_opt.c
│   └── ffmpeg_videotoolbox.c
```

 

## 集成FFmpeg命令行-验证ffmpeg输入指令



```swift
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
 
// self.navigationController?.pushViewController(SwiftPointViewController(), animated: true)
let inputFile = "/Users/qxq4633/ffmpegdemo/ffmpeg_demo/ffmpeg_demo/resoures/input.mov";
 
let outputFilePath = "/Users/qxq4633/ffmpegdemo/ffmpeg_demo/ffmpeg_demo/resoures/output2.webm";
 
let arguments = [ "ffmpeg","-i",inputFile]
let arr = UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>.allocate(capacity: arguments.count)
 
for index in 0..<arguments.count {
arr[index] = UnsafeMutablePointer<Int8>.init(mutating: (arguments[index] as NSString).utf8String)
}
print("======== start call ffmpeg async ========")
DispatchQueue.global().async {
print("======== ffmpeg_main start ========")
ffmpeg_main(Int32(arguments.count), arr)
print("======== ffmpeg_main end and call back result ========")
}
print("======== handler other logic ========")
 
}
```

 

## 集成FFmpeg命令行-exit_program相关bug修复

 

在运行中碰到诸多错误,由于`exit_program`函数推出时会卡住当前线程,所以需要将此函数设置返回值

 

```shell
int exit_program(int ret)
{
// if (program_exit)
// program_exit(ret);

 

//exit(ret);
return ret; //TODO: fix thread not exit
}
```

 

退出时需要释放野指针

 

```shell
ffmpeg_exited = 1;
//TODO: fix unsafe pointee
ffmpeg_exited = 1;
nb_filtergraphs = 0;
nb_output_files = 0;
nb_output_streams = 0;
nb_input_files = 0;
nb_input_streams = 0;
```

 

我这个例子是打印文件信息的,没有指定output,默认的print函数会检查输出文件,需要修改此处崩溃
```shell
static void print_report(int is_last_report, int64_t timer_start, int64_t cur_time) { ...
//TODO: Fix out put files not assiged
if (output_files == NULL){
return;
}
```

 

由于我们强制修改退出条件,在overrite文件时需要提前做清除

 

```shell
static void assert_file_overwrite(const char *filename)
{
 
avpriv_io_delete(filename); //TODO: fix frozen
```

 

这里也设置到返回值没有设定,需要增加返回值

 

```shell
static int opt_init_hw_device(void *optctx, const char *opt, const char *arg)
{
if (!strcmp(arg, "list")) {
enum AVHWDeviceType type = AV_HWDEVICE_TYPE_NONE;
printf("Supported hardware device types:\n");
while ((type = av_hwdevice_iterate_types(type)) !=
AV_HWDEVICE_TYPE_NONE)
printf("%s\n", av_hwdevice_get_type_name(type));
printf("\n");
return exit_program(0); //TODO: fix none return
} else {
return hw_device_init_from_string(arg, NULL);
}
}
```

 

以上就是通过Xcode执行`ffmpeg -i input.mov` 查看文件信息的全过程

 

## 集成FFmpeg命令行-执行顺序

 

控制台打印结果如下,在同一个线程中可以看到`ffmpeg_main`函数是同步执行的. 所以基于此方法我们可以封装一套ffmpeg的命令行接口
流程如下:

 

`prepare arguments` -> `call ffmpeg_main async` -> `return ffmpeg result`

 

实际的打印结果也验证了这一点
```shell
======== start call ffmpeg async ========
======== handler other logic ========
======== ffmpeg_main start ========
ffmpeg version 4.3.1 Copyright (c) 2000-2020 the FFmpeg developers
built with Apple clang version 12.0.0 (clang-1200.0.31.1)
configuration: --prefix=FFmpeg --disable-shared --enable-static --disable-x86asm
WARNING: library configuration mismatch
avutil configuration: --target-os=darwin --arch=x86_64 --cc='xcrun -sdk iphonesimulator clang' --as='gas-preprocessor.pl -- xcrun -sdk iphonesimulator clang' --enable-cross-compile --disable-debug --enable-gpl --disable-doc --enable-pic --extra-cflags='-arch x86_64 -mios-simulator-version-min=8.0' --extra-ldflags='-arch x86_64 -mios-simulator-version-min=8.0' --prefix=/Users/qxq4633/ffmpegdemo/FFmpeg-iOS-build-script/thin/x86_64
avcodec configuration: --target-os=darwin --arch=x86_64 --cc='xcrun -sdk iphonesimulator clang' --as='gas-preprocessor.pl -- xcrun -sdk iphonesimulator clang' --enable-cross-compile --disable-debug --enable-gpl --disable-doc --enable-pic --extra-cflags='-arch x86_64 -mios-simulator-version-min=8.0' --extra-ldflags='-arch x86_64 -mios-simulator-version-min=8.0' --prefix=/Users/qxq4633/ffmpegdemo/FFmpeg-iOS-build-script/thin/x86_64
avformat configuration: --target-os=darwin --arch=x86_64 --cc='xcrun -sdk iphonesimulator clang' --as='gas-preprocessor.pl -- xcrun -sdk iphonesimulator clang' --enable-cross-compile --disable-debug --enable-gpl --disable-doc --enable-pic --extra-cflags='-arch x86_64 -mios-simulator-version-min=8.0' --extra-ldflags='-arch x86_64 -mios-simulator-version-min=8.0' --prefix=/Users/qxq4633/ffmpegdemo/FFmpeg-iOS-build-script/thin/x86_64
avdevice configuration: --target-os=darwin --arch=x86_64 --cc='xcrun -sdk iphonesimulator clang' --as='gas-preprocessor.pl -- xcrun -sdk iphonesimulator clang' --enable-cross-compile --disable-debug --enable-gpl --disable-doc --enable-pic --extra-cflags='-arch x86_64 -mios-simulator-version-min=8.0' --extra-ldflags='-arch x86_64 -mios-simulator-version-min=8.0' --prefix=/Users/qxq4633/ffmpegdemo/FFmpeg-iOS-build-script/thin/x86_64
avfilter configuration: --target-os=darwin --arch=x86_64 --cc='xcrun -sdk iphonesimulator clang' --as='gas-preprocessor.pl -- xcrun -sdk iphonesimulator clang' --enable-cross-compile --disable-debug --enable-gpl --disable-doc --enable-pic --extra-cflags='-arch x86_64 -mios-simulator-version-min=8.0' --extra-ldflags='-arch x86_64 -mios-simulator-version-min=8.0' --prefix=/Users/qxq4633/ffmpegdemo/FFmpeg-iOS-build-script/thin/x86_64
swscale configuration: --target-os=darwin --arch=x86_64 --cc='xcrun -sdk iphonesimulator clang' --as='gas-preprocessor.pl -- xcrun -sdk iphonesimulator clang' --enable-cross-compile --disable-debug --enable-gpl --disable-doc --enable-pic --extra-cflags='-arch x86_64 -mios-simulator-version-min=8.0' --extra-ldflags='-arch x86_64 -mios-simulator-version-min=8.0' --prefix=/Users/qxq4633/ffmpegdemo/FFmpeg-iOS-build-script/thin/x86_64
swresample configuration: --target-os=darwin --arch=x86_64 --cc='xcrun -sdk iphonesimulator clang' --as='gas-preprocessor.pl -- xcrun -sdk iphonesimulator clang' --enable-cross-compile --disable-debug --enable-gpl --disable-doc --enable-pic --extra-cflags='-arch x86_64 -mios-simulator-version-min=8.0' --extra-ldflags='-arch x86_64 -mios-simulator-version-min=8.0' --prefix=/Users/qxq4633/ffmpegdemo/FFmpeg-iOS-build-script/thin/x86_64
libavutil 56. 51.100 / 56. 51.100
libavcodec 58. 91.100 / 58. 91.100
libavformat 58. 45.100 / 58. 45.100
libavdevice 58. 10.100 / 58. 10.100
libavfilter 7. 85.100 / 7. 85.100
libswscale 5. 7.100 / 5. 7.100
libswresample 3. 7.100 / 3. 7.100
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '/Users/qxq4633/ffmpegdemo/ffmpeg_demo/ffmpeg_demo/resoures/input.mov':
Metadata:
major_brand : qt
minor_version : 0
compatible_brands: qt
creation_time : 2021-02-05T02:51:34.000000Z
com.apple.quicktime.make: Apple
com.apple.quicktime.model: MacBookPro15,1
com.apple.quicktime.software: Mac OS X 10.15.6 (19G2021)
com.apple.quicktime.creationdate: 2021-02-05T10:50:55+0800
Duration: 00:00:26.63, start: 0.000000, bitrate: 2028 kb/s
Stream #0:0(und): Video: h264 (Main) (avc1 / 0x31637661), yuv420p(tv, bt709), 766x1334 [SAR 1:1 DAR 383:667], 2015 kb/s, 59.93 fps, 60 tbr, 6k tbn, 12k tbc (default)
Metadata:
creation_time : 2021-02-05T02:51:34.000000Z
handler_name : Core Media Video
encoder : H.264
At least one output file must be specified
Stream mapping:
Press [q] to stop, [?] for help
======== ffmpeg_main end and call back result ========
```

 

## 代码地址

 

- repo: https://gitee.com/jiodg45/ffmpeg_demo.git

 

- feature/add-ffmpeg-command-tool: 包含ffmpeg命令行工具集成
- master: normal版本,不包含ffmpeg命令行工具集成



## 参考文章

 

[FFmpeg](https://github.com/FFmpeg/FFmpeg.git)
[FFmpeg-flutter](https://github.com/tanersener/flutter-ffmpeg.git)
[FFmpeg-iOS-Scripts](https://github.com/kewlbear/FFmpeg-iOS-build-script.git)
[在iOS中使用FFmpeg命令](https://blog.csdn.net/wenzfcsdn/article/details/84847534)
posted @ 2021-03-15 20:41  阿甘左  阅读(905)  评论(0编辑  收藏  举报