在FFMpeg源码根目录下下执行make,会找到第一个目标all,那么整个编译最终目标就是要完成All目标。我们先找一下all目标的所有依赖
依赖一:all-yes
根目录下的Makefile
# first so "all" becomes default target
all: all-yes
include $(SRC_PATH)/fftools/Makefile
依赖二:AVPROGS
fftools/Makefile
# first so "all" becomes default target
all: $(AVPROGS)
这个AVPROGS=ffmpeg ffprobe,两个可执行程序
all-yes表示所有的lib依赖,就是ffmpeg里面核心的几个模块:avcodec, avutil, avformat avresample…
all-yes是通过foreach循环完成添加。核心代码在根目录下的Makefile:
define DOSUBDIR
$(foreach V,$(SUBDIR_VARS),$(eval $(call RESET,$(V))))
SUBDIR := $(1)/
include $(SRC_PATH)/$(1)/Makefile
-include $(SRC_PATH)/$(1)/$(ARCH)/Makefile
-include $(SRC_PATH)/$(1)/$(INTRINSICS)/Makefile
include $(SRC_PATH)/ffbuild/library.mak
endef
$(foreach D,$(FFLIBS),$(eval $(call DOSUBDIR,lib$(D))))
这里遍历每个模块(FFLIBS),然后include模块中的Makefile和ffbuild/library.mak。我们以codec模块为例,看下:
NAME = avcodec
DESC = FFmpeg codec library
HEADERS = ac3_parser.h \
adts_parser.h \
avcodec.h \
avdct.h \
# 后面省略...
每次include一个模块的Makefile这个NAME变量都会发生更新。
这个mak文件内,负责处理每个模块编译通用逻辑。看一下核心逻辑:
# common.mak描述了所有模块中文件编译的通用规则
include $(SRC_PATH)/ffbuild/common.mak
# 模块的all-yes目标和对应依赖
all-$(CONFIG_STATIC): $(SUBDIR)$(LIBNAME) $(SUBDIR)lib$(FULLNAME).pc
all-$(CONFIG_SHARED): $(SUBDIR)$(SLIBNAME) $(SUBDIR)lib$(FULLNAME).pc
# 静态压缩
# LIBNAME = libavcodec.a (不同模块的 LIBNAME 会更新)
# OBJS 是每个模块内部Makefile定义,模块编译出来的所有.o文件
$(SUBDIR)$(LIBNAME): $(OBJS) $(STLIBOBJS)
$(RM) $@
$(AR) $(ARFLAGS) $(AR_O) $^
$(RANLIB) $@
# 动态库
$(SUBDIR)$(SLIBNAME_WITH_MAJOR): $(OBJS) $(SHLIBOBJS) $(SLIBOBJS) $(SUBDIR)lib$(NAME).ver
$(warning lorien SLIBNAME TARGET = $@ DEPS = $^)
$(SLIB_CREATE_DEF_CMD)
$$(LD) $(SHFLAGS) $(LDFLAGS) $(LDSOFLAGS) $$(LD_O) $$(filter %.o,$$^) $(FFEXTRALIBS)
$(SLIB_EXTRA_CMD)
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)
COMPILE_CXX = $(call COMPILE,CXX)
%.o: %.c
$(COMPILE_C)
%.o: %.cpp
$(COMPILE_CXX)
大致编译流程如下:
$(AR) $(ARFLAGS) $(AR_O) $^
将所有的.o打包成.a库文件所有模块的库文件制作完成之后,接下来,编译生成两个可执行程序:ffmpeg 和 ffprobe
我们看下根目录的Makefile
# 这个include,会给all新增一个依赖:PROGS
include $(SRC_PATH)/fftools/Makefile
include $(SRC_PATH)/doc/Makefile
include $(SRC_PATH)/doc/examples/Makefile
# ffmpeg: ffmpeg_g
# 使用strip命令将ffmpeg_g生成ffmpeg
$(PROGS): %$(PROGSSUF)$(EXESUF): %$(PROGSSUF)_g$(EXESUF)
ifeq ($(STRIPTYPE),direct)
$(STRIP) -o $@ $<
else
$(warning lorien PROGS dep = $(PROGS))
$(RM) $@
$(CP) $< $@
$(STRIP) $@
endif
# ffmpeg_g: libavdevice/libavdevice.a libavfilter/libavfilter.a ... fftools/ffmpeg.o
%$(PROGSSUF)_g$(EXESUF): $(FF_DEP_LIBS)
$(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $(OBJS-$*) $(FF_EXTRALIBS)
直接看注释即可,不做过多赘述。