1 前言
本文介绍下在macOS上将ffmpeg的fftools目录下的ffmpeg.c程序,也就是ffmpeg的命令行程序,编译成framework的方法。编译成.a或.dylib的方法类似。
编译环境如下:
- xcode15.3;
- ffmpeg release/6.1;
2 编译ffmpeg
首先clone我们的FFmpeg代码仓到本地,并更名为ffmpeg,切换分支为release/6.1:
git clone https://github.com/FFmpeg/FFmpeg.git ffmpeg && git checkout release/6.1
然后在ffmpeg的根目录执行如下命令,由于是学习使用,configure配置的是缺省选项,编译后安装目录在根目录下的mac_build目录下:
mkdir -p mac_build && ./configure --prefix=./mac_build
然后执行如下命令,编译并安装ffmpeg:
make -j8 && make install
configure并编译安装完成之后,会在根目录下生成如下文件:
- configure配置完毕,会在根目录产生config.h和config_components.h文件;
- 生成了ffmpeg/ffplay/ffprobe可执行文件;
- mac_build目录是ffmpeg的bin/include/lib/share文件;
其中,mac_build目录结构如下:
xxxxxx@localhost:~/Applications/workspace/av/ffmpeg/mac_build$tree -L 2
.
├── bin
│ ├── ffmpeg
│ ├── ffplay
│ └── ffprobe
├── include
│ ├── libavcodec
│ ├── libavdevice
│ ├── libavfilter
│ ├── libavformat
│ ├── libavutil
│ ├── libswresample
│ └── libswscale
├── lib
│ ├── libavcodec.a
│ ├── libavdevice.a
│ ├── libavfilter.a
│ ├── libavformat.a
│ ├── libavutil.a
│ ├── libswresample.a
│ ├── libswscale.a
│ └── pkgconfig
└── share
├── ffmpeg
└── man
15 directories, 10 files
编译安装完成之后,就可以拿该目录下的include和lib下的文件引入到工程使用了。
3 编译ffmpeg.c
3.1 新建xcode工程
打开xcode新建一个xcode的app工程:
然后,新建一个target,FFmpegFramework,接着把ffmpeg相关c程序拖进来编译:
然后在FFmpegFramework => Build Settings => Search Paths里加入ffmpeg的include和lib以及config.h的搜索路径:
到此,xcode工程和FFmpegFramework的创建和初始化工作完成,接下来就是将FFmpegFramework编译成framework了。
3.2 改写ffmpeg.c的接口方法
- 在ffmpeg.h文件中,将int main(int argc, char* argv[])方法名改成ffmpeg;
- ffmpeg.c对应文件的main方法名按以上同步修改为ffmpeg;
ffmpeg.h头文件中修改方法名:
// 将main方法名改为ffmpeg名
int ffmpeg(int argc, char* argv[]);
ffmpeg.c同步修改方法名:
// 将main方法名改为ffmpeg
int ffmpeg(int argc, char **argv)
{
int ret, err_rate_exceeded;
BenchmarkTimeStamps ti;
init_dynload();
setvbuf(stderr,NULL,_IONBF,0); /* win32 runtime needs this */
av_log_set_flags(AV_LOG_SKIP_REPEATED);
parse_loglevel(argc, argv, options);
#if CONFIG_AVDEVICE
avdevice_register_all();
#endif
avformat_network_init();
show_banner(argc, argv, options);
/* parse options and open all input/output files */
ret = ffmpeg_parse_options(argc, argv);
if (ret < 0)
goto finish;
if (nb_output_files <= 0 && nb_input_files == 0) {
show_usage();
av_log(NULL, AV_LOG_WARNING, "Use -h to get full help or, even better, run 'man %s'\n", program_name);
ret = 1;
goto finish;
}
if (nb_output_files <= 0) {
av_log(NULL, AV_LOG_FATAL, "At least one output file must be specified\n");
ret = 1;
goto finish;
}
current_time = ti = get_benchmark_time_stamps();
ret = transcode(&err_rate_exceeded);
if (ret >= 0 && do_benchmark) {
int64_t utime, stime, rtime;
current_time = get_benchmark_time_stamps();
utime = current_time.user_usec - ti.user_usec;
stime = current_time.sys_usec - ti.sys_usec;
rtime = current_time.real_usec - ti.real_usec;
av_log(NULL, AV_LOG_INFO,
"bench: utime=%0.3fs stime=%0.3fs rtime=%0.3fs\n",
utime / 1000000.0, stime / 1000000.0, rtime / 1000000.0);
}
ret = received_nb_signals ? 255 :
err_rate_exceeded ? 69 : ret;
finish:
if (ret == AVERROR_EXIT)
ret = 0;
ffmpeg_cleanup(ret);
return ret;
}
3.3 将ffmpeg.c编译成Framework
接下来就是将ffmpeg.c编译成framework了,期间遇到了很多问题,但基本是一类问题,那就是链接时的Undefined symbol。
解决方式就是将ffmpeg.c所依赖的macOS系统framework和基础.a或者.dylib库链接到本framework即可。如下图,总共依赖了39个库,其中SDL2需用dylib库(若使用libSDL2.a则会报错找不到_FFCreateDevice symbol等):
ffmpeg.c所依赖的.a和.dylib库路径:
mingo@localhost:~/Applications/workspace/av/ffmpeg/mac_build/lib$ll
total 292680
drwxr-xr-x 23 mingo staff 736 5 10 23:58 ./
drwxr-xr-x 7 mingo staff 224 5 10 11:38 ../
-rw-r--r--@ 1 mingo staff 6148 5 10 23:51 .DS_Store
-r--r--r-- 1 mingo staff 2514640 5 10 23:33 libSDL2.a
-rw-r--r--@ 1 mingo staff 1561488 5 10 23:58 libSDL2.dylib
-r--r--r-- 1 mingo staff 848 5 10 22:49 libX11-xcb.a
-r--r--r-- 1 mingo staff 1402392 5 10 22:49 libX11.a
-r--r--r-- 1 mingo staff 11656 5 10 23:25 libXau.a
-r--r--r-- 1 mingo staff 20696 5 10 23:27 libXdmcp.a
-rw-r--r-- 1 mingo staff 78648416 5 10 11:35 libavcodec.a
-rw-r--r-- 1 mingo staff 574072 5 10 11:35 libavdevice.a
-rw-r--r-- 1 mingo staff 22947096 5 10 11:35 libavfilter.a
-rw-r--r-- 1 mingo staff 26952064 5 10 11:35 libavformat.a
-rw-r--r-- 1 mingo staff 3054552 5 10 11:35 libavutil.a
-r--r--r-- 1 mingo staff 7380136 5 10 22:27 libcrypto.a
-r--r--r-- 1 mingo staff 1386648 5 10 22:24 libssl.a
-rw-r--r-- 1 mingo staff 411520 5 10 11:35 libswresample.a
-rw-r--r-- 1 mingo staff 2655320 5 10 11:35 libswscale.a
-r--r--r-- 1 mingo staff 11160 5 10 22:47 libxcb-shape.a
-r--r--r-- 1 mingo staff 8864 5 10 22:45 libxcb-shm.a
-r--r--r-- 1 mingo staff 39352 5 10 22:43 libxcb-xfixes.a
-r--r--r-- 1 mingo staff 218768 5 10 21:31 libxcb.a
drwxr-xr-x 9 mingo staff 288 5 10 11:35 pkgconfig/
mingo@localhost:~/Applications/workspace/av/ffmpeg/mac_build/lib$
ffmpeg.c所依赖的macOS系统库及ffmpeg相关库39个:
按以上操作,便可顺利编译FFmpegFramework了。接下来就可以在learn-ffmpeg项目中使用它了。
4 使用framework
贴出main.m调用ffmpeg.c接口的代码:
#include "libavutil/log.h"
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libavutil/opt.h"
#include "ffmpeg.h"
static void test_ffmpeg(int argc, const char* argv[]) {
ffmpeg(argc, argv);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Setup code that might create autoreleased objects goes here.
av_log_set_level(AV_LOG_DEBUG);
av_log(NULL, AV_LOG_INFO, "hello ffmpeg");
// 示例ffmpeg -h命令
const char* args[] = { "ffmpeg", "-h" };
test_ffmpeg(2, args);
}
return NSApplicationMain(argc, argv);
}
然后,选中learn-ffmpeg target,在learn-ffmpeg => Build Settings => Search Paths => Header Search Paths/Library Search Paths中指定ffmpeg及其framework相关的include和lib文件:
include搜索路径:
lib搜索路径:
在learn-ffmpeg target中 Build Settings => Link Binary with Library中加入FFmpegFramework:
接下来就可以顺利编译learn-ffmpeg程序,并开始调试ffmpeg.c程序了: