• FFmpeg之Makefile流程分析


    1. 目标依赖关系

    在这里插入图片描述
    FFMpeg源码根目录下下执行make,会找到第一个目标all,那么整个编译最终目标就是要完成All目标。我们先找一下all目标的所有依赖

    依赖一:all-yes
    根目录下的Makefile

    # first so "all" becomes default target
    all: all-yes
    
    include $(SRC_PATH)/fftools/Makefile
    
    • 1
    • 2
    • 3
    • 4

    依赖二:AVPROGS
    fftools/Makefile

    # first so "all" becomes default target
    all: $(AVPROGS)
    
    • 1
    • 2

    这个AVPROGS=ffmpeg ffprobe,两个可执行程序

    2. all-yes依赖

    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))))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2.1 子模块Makefile

    这里遍历每个模块(FFLIBS),然后include模块中的Makefile和ffbuild/library.mak。我们以codec模块为例,看下:

    NAME = avcodec
    DESC = FFmpeg codec library
    
    HEADERS = ac3_parser.h                                                  \
              adts_parser.h                                                 \
              avcodec.h                                                     \
              avdct.h                                                       \
              
    # 后面省略...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    每次include一个模块的Makefile这个NAME变量都会发生更新。

    2.2 library.mak

    这个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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    2.3 common.mak

    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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    大致编译流程如下:

    1. foreach循环确定了all-yes所有依赖,也就是avcodec, avutil, avscale, avformat等这些模块。
    2. 然后进入这些模块,将.c编译出所有的.o
    3. 然后通过 library.mak中,$(AR) $(ARFLAGS) $(AR_O) $^将所有的.o打包成.a库文件
    4. 最后每个模块都会产出各自的.a 文件,比如:libavformat.a, libavcodec.a, libavutil.a …

    3. PROGS依赖

    所有模块的库文件制作完成之后,接下来,编译生成两个可执行程序: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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    直接看注释即可,不做过多赘述。

  • 相关阅读:
    结尾:编程指南
    Python面试知识点
    建造者模式
    AI大模型-流式处理 (以百度接口为例)
    上位机工作感想-从C#到Qt的转变-2
    计算机图形编程中的这几本经典书你读过吗?
    【luogu P5320】勘破神机(数学)(数列特征方程)(第一类斯特林数)
    手机穿戴设备能力共享,提升丰富交互体验
    前端相关题目随笔
    Ansible概述以及模块
  • 原文地址:https://blog.csdn.net/H_Zhang/article/details/127839822