如何生成ffmpeg.dll—FFmpeg魔改教程
在前文《FFmpeg的API库介绍》里面知道,FFmpeg 一共提供了 8 个 dll 库给外部使用,但是有时候为了软件的安装目录的文件更加简洁一点,会把这 8 个 dll 动态库全部合并进去一个 ffmpeg.dll
里面,挺多软件都这样做的,例如:Typora,draw.io,百度翻译,等等。
下面就来介绍如何生成 ffmpeg.dll
,在阅读本文之前,推荐阅读《编译链接基础知识》一章的以下文章。
1,《MSVC编译多个C程序文件》
2,《MSVC编译动态库》
我个人不太喜欢只写 步骤型 的文章,例如教你改 makefile
的哪个位置,添加哪些代码,就能生成 ffmpeg.dll
,这种步骤型的知识只能治标,不能治本。
我喜欢写的是 dll 动态库的生成过程,调了那些命令,命令的选项参数是什么的。我喜欢写的是,我是怎么学会这个技能的,这样可以举一反三,掌握了这些知识,即使后面遇到其他的 C/C++ 项目,也能轻松解决。
回顾《编译链接基础知识》一章的知识,动态库也是由多个 .obj
文件 生成的,如下:
link.exe /DLL /DEBUG /EXPORT:sun_rotate /EXPORT:moon_rotate /EXPORT:earth_rotate /OUT:star.dll earth.obj moon.obj sun.obj
上面的 /EXPORT
选项是导出 .obj
文件里面的哪些函数给外部使用,在 MSVC 环境上,是需要使用 /EXPORT
选项或者 def 文件来导出函数给外部使用的。Linux 的 gcc 环境,好像不需要这样做,好像默认是暴露 .o
文件里面的全部函数,可能 gcc
也可以设置只导出一部分,这个我不太清楚,后面补充。
提示:在 msys2 命令行下,虽然用的是 msvc 编译器,但是生成的目标文件的后缀是 .o
,而不是 .obj
,但是他们的内容格式是一样的。链接器不是以后缀名判断格式的。msys2 只是为了通用,所以把 .obj
后缀换成了 .o
后缀。
既然 dll 是通过 .o
文件生成的,我们只需要找到 FFmpeg 的 makefile
是在哪里生成 dll 的,然后照葫芦画瓢操作一下就可以了。
在《makefile逻辑分析》里知道,生成 8 个动态库的 makefile
代码在 library.mak
里面,如下:
这条命令很多变量,会不太容易看懂,大家可以使用 make -n > test.txt
命令,把 makefile 真正执行的命令转存到 test.txt
里面,就能看到最终是哪条命令生成 dll 动态库的了,如下:
提示:如果之前已经生成了动态库,需要先 make clean 删除掉,要不 make -n
没有内容输出的。
上图中的 两行命令,实际上就对应 library.mak
里面的两行代码,如下:
$(SLIB_CREATE_DEF_CMD)
$$(LD) $(SHFLAGS) $(LDFLAGS) $(LDSOFLAGS) $$(LD_O) $$(filter %.o,$$^) $(FFEXTRALIBS)
$(SLIB_EXTRA_CMD)
$(SLIB_CREATE_DEF_CMD)
这句代码是执行 ./compat/windows/makedef
,makedef
是一个 shell 脚本,这个脚本是生成 .def
文件的,例如 avdevice-58.def
,avdevice-58.def
是控制 dll
里面的哪些函数暴露给外部使用的。
$$(LD)
这句代码就是生成 dll
的代码,./compat/windows/mslink
也是一个 shell 脚本,它最终调用的就是 link.exe
,mslink
脚本会优选使用跟 cl.exe
同目录下的 link.exe
,这样是为了防止用错了了 mingw
的 link.exe
。
上图中可以看到,avdevice-58.def
会传递给 link.exe
链接器
$(SLIB_EXTRA_CMD)
这行代码在 windows 环境下是 空,所以什么都没做。
从上图的命令可以看到,为了生成 avcodec-58.dll
,传递了很多的 .o
文件给 link.exe
。因此只要我们把 8 个 dll 需要的 .o
文件找出来,然后全部都传递给 link.exe
就能 生成一个 ffmpeg.dll
了,这样就把 8 个 动态库合并成一个了。
但是这些 .o
文件非常多的,我们并不需要一个一个找出来。有个快捷技巧,那就是直接把 静态库 拿来用,libavcodec.a
这些静态库就是 .o
文件的集合,静态库的原理就是把多个 .o
文件打包在一起,所以我们可以直接使用静态库来生成动态库。
不过由于后面需要用到一些 def 文件,而 def 文件是随着 DLL 动态库一起生成的,我们需要先参考《用msys2与msvc编译FFmpeg》编译出动态库,编译完成之后,就可以在 build64\ffmepg-4.4-msvc\lib
目录看到 avcodec-58.def
等文件,如下:
提醒:我们不需要 bin 目录下的 avcodec-58.dll
等 dll 文件,只是要用一下这些 def 文件。
然后再执行一下 make clean,把生成的文件删掉,再执行以下命令生成 静态库,如下:
./configure \
--prefix=/home/loken/ffmpeg/build64/ffmepg-4.4-msvc-static \
--enable-gpl \
--enable-nonfree \
--disable-sdl2 \
--disable-optimizations \
--disable-asm \
--disable-stripping \
--extra-cflags="-MDd" \
--toolchain=msvc
上面的命令把 --enable-shared
删除,所以会生成静态库,然后再执行下面的命令。
make -j12
make install
运行完毕之后,就可以在 ffmepg-4.4-msvc-static/lib
目录看到静态库,如下:
然后,我们把 之前编译动态库的时候生成的 avcodec-58.def
等 复制到跟 静态库同级的目录,如下:
由于 link.exe
链接器 -def
选项好像不能支持多个 def
文件输入,所以我把 这 8 个 def 文件的内容全部复制到 ffmpeg.def
里面了,如下:
注意:def 文件只能有一个 EXPORTS
,不要把 EXPORTS
也复制 8 个进去,这里提供一个 ffmpeg.def 文件下载,供读者参考。
然后就可以执行下面的命令把这 8 个静态库合成一个 ffmpeg.dll 了,如下:
cd /home/loken/ffmpeg/build64/ffmepg-4.4-msvc-static/lib
../../../FFmpeg-n4.4.1/compat/windows/mslink -dll -def:ffmpeg.def -implib:ffmpeg.lib -nologo -debug -out:ffmpeg.dll secur32.lib Ws2_32.lib mfplat.lib mfuuid.lib ole32.lib strmiids.lib ole32.lib user32.lib bcrypt.lib oleaut32.lib ole32.lib shlwapi.lib gdi32.lib vfw32.lib libavcodec.a libavdevice.a libavfilter.a libavformat.a libavutil.a libpostproc.a libswresample.a libswscale.a
上面的命令,有一些 lib 导入库是 Windows 系统的库,例如 gdi 库,我是怎么知道需要这些库的呢?是通过之前的 make -n
生成的 test.txt
里面的选项 参数的。
编译完成之后,就可以看到 ffmpeg.dll
了,如下:
下面我们就用一个示例 input_2 测试一下 这个 ffmpeg.dll
好不好用,编译环境是 Qt 5.15.2 跟 MSVC2019_64bit 。运行效果如下:
提示:这个 input_2 项目不知道为什么 mp4 文件 copy 不过去,所以需要手动复制到调试目录,还有 ffmpeg.dll 也 copy 失败了,要手动拷贝到 exe 统计目录,这是个 bug,我后面解决。
ffmpeg.dll
是可以正常使用的,再通过以下命令查看一下 input_2.exe
的 DLL 依赖,如下:
dumpbin.exe /DEPENDENTS input_2.exe
可以看到, input_2.exe
确实只依赖 ffmpeg.dll
其实把 8 个 动态库合并成一个 ffmpeg.dll 还有一个好处,可以节省一些空间,如下:
原来 8 个库是 25.3M,合并成一个 ffmpeg.dll
之后,变成了 19.1M 了。
不过这个可能是因为我编译动态库的时候,有些选项没设置。一般情况合并成一个 dll ,空间会有所减少,不过可能不会少 6 M 这么多。
虽然本文讲解的是在 windows 下生成 ffmpeg.dll
,但是在 Linux 下合成 ffmpeg.so
也是类似的原理。