文章目录
前言
在上一篇博客《FFmpeg源码编译—支持x264编码》中,通过简单的配置、编译、安装等几个步骤,就完成了FFmepg源码的编译。本文总结下Makefile是怎么组织整个编译过程的,也加深下自己的印象。
一、Makefile基础
这方面资料比较多,简单罗列下我感觉还不错的资料。
1、陈皓老师的《跟我一起写Makefile》
Github上有大佬重新进行了排版,看起来比较舒服,详见:跟我一起写Makefile (PDF重制版)
2、GNU官网资料
这是第一手资料,就是看英文比较劝退,新手别轻易尝试,详见:GNU make
3、某未来大佬博客
高度模拟了FFmpeg源码Makefile编译的过程,比如动态库、静态库生成的方式等,详见:Makefile学习笔记:静态库和动态库的生成和使用
二、FFmpeg源码makefile几个关键点
查看ffmpeg_build中的编译产物,可见有很多东东,下面节选了一些关键部分,bin目录中的可执行文件,以及lib目录中的动态库.so文件。还有个很重要的include目录,内容太多就不列出来了。
├── bin
│ ├── ffmpeg
│ └── ffprobe
├── lib
│ ├── libavcodec.so -> libavcodec.so.60.31.102
│ ├── libavcodec.so.60 -> libavcodec.so.60.31.102
│ ├── libavcodec.so.60.31.102
│ ├── libavdevice.so -> libavdevice.so.60.3.100
│ ├── libavdevice.so.60 -> libavdevice.so.60.3.100
│ ├── libavdevice.so.60.3.100
│ ├── libavfilter.so -> libavfilter.so.9.12.100
│ ├── libavfilter.so.9 -> libavfilter.so.9.12.100
│ ├── libavfilter.so.9.12.100
│ ├── libavformat.so -> libavformat.so.60.16.100
│ ├── libavformat.so.60 -> libavformat.so.60.16.100
│ ├── libavformat.so.60.16.100
│ ├── libavutil.so -> libavutil.so.58.29.100
│ ├── libavutil.so.58 -> libavutil.so.58.29.100
│ ├── libavutil.so.58.29.100
│ ├── libpostproc.so -> libpostproc.so.57.3.100
│ ├── libpostproc.so.57 -> libpostproc.so.57.3.100
│ ├── libpostproc.so.57.3.100
│ ├── libswresample.so -> libswresample.so.4.12.100
│ ├── libswresample.so.4 -> libswresample.so.4.12.100
│ ├── libswresample.so.4.12.100
│ ├── libswscale.so -> libswscale.so.7.5.100
│ ├── libswscale.so.7 -> libswscale.so.7.5.100
│ ├── libswscale.so.7.5.100
结合前面分享的大佬博客,这里本着自顶向下、逐步细化的原则,尝试找下源码Makefile中几个对应的关键点。
1.源码.c文件是怎么变成.o文件的
代码ffbuild/common.mak文件节选:
define COMPILE
$(call $(1)DEP,$(1))
$($(1)) $($(1)FLAGS) $($(2)) $($(1)_DEPFLAGS) $($(1)_C) $($(1)_O) $(patsubst $(SRC_PATH)/%,$(SRC_LINK)/%,$<)
endef
COMPILE_C = $(call COMPILE,CC)
%.o: %.c
$(COMPILE_C)
毕竟是行业大佬写的,小白看了真的很劝退有木有?但是静下心来分析下,还是能看到一些端倪的。
$(1)是通过call给COMPILE后传入的参数CC,通过ffbuild/config.mak可知,
$($(1)) #CC=gcc
$($(1)_C) #CC_C=-c
$($(1)_O) #CC_O=-o $@
2.源码.o文件是怎么变成动态库.so或静态库.a文件的
代码ffbuild/library.mak文件节选:
$(SUBDIR)$(LIBNAME): $(OBJS) $(STLIBOBJS)
$(RM) $@
$(AR) $(ARFLAGS) $(AR_O) $^
$(RANLIB) $@
其中,
目标$(SUBDIR)$(LIBNAME),就是最终的静态库/动态库的名字,如libavcodec.so等
#在ffbuild/config.mak可见库名字是拼接起来的,其中NAME在各库中定义,如libavcodec/Makefile中定义NAME = avcodec
FULLNAME=$(NAME)$(BUILDSUF)
LIBPREF=lib
LIBSUF=.a
LIBNAME=$(LIBPREF)$(FULLNAME)$(LIBSUF)
SLIBPREF=lib
SLIBSUF=.so
SLIBNAME=$(SLIBPREF)$(FULLNAME)$(SLIBSUF)
依赖 $(OBJS)
#在各库如libavcodec/Makefile中定义依赖的.o文件,节选如下
OBJS = ac3_parser.o \
adts_parser.o \
allcodecs.o \
avcodec.o \
avdct.o \
规则$(AR) $(ARFLAGS) $(AR_O) $^
#通过ffbuild/config.mak可知以下信息
$(AR) #AR=ar
$(ARFLAGS) #ARFLAGS=rcD
$(AR_O) #AR_O=$@,其中$@是自动化变量,表示目标
$^ #自动化变量,所有的依赖目标的集合
3.源码ffmpeg可执行文件是怎么生成的
代码根目录Makefile文件节选:
#LD=gcc,先编译、链接产生ffmpeg_g文件
%$(PROGSSUF)_g$(EXESUF): $(FF_DEP_LIBS)
$(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $(OBJS-$*) $(FF_EXTRALIBS)
#对可执行文件如ffmpeg_g进行strip,生成ffmpeg可执行文件
$(PROGS): %$(PROGSSUF)$(EXESUF): %$(PROGSSUF)_g$(EXESUF)
ifeq ($(STRIPTYPE),direct)
$(STRIP) -o $@ $<
else
$(RM) $@
$(CP) $< $@
$(STRIP) $@
endif
找到这3个关键点,是不是就跟已知知识联系起来了?
总结
本文只是粗浅的列出了FFmpeg源码Makefile中几个关键的位置,通过对比的方式加深理解。后续还有很多工作要做,比如:
1、编译时具体是怎么决定生成动态库or静态库;
2、几个库如avcodec、avformat等是怎么逐个产生。
当然还有一些细节,等有机会再深究吧。