本文以 ffmpeg-n4.4.1 的版本为准,移植 ffmpeg.c 的工程到 qt creator 里面调试,用 qt msvc 的方式编译调试。
过往的文章调试 ffmpeg.c 都是用的 qt MinGw 方式。今天就来介绍一下如何移植 ffmpeg.c 工程,并且能用 qt msvc 方式调试。
qt msvc 可以断点进去 ffmpeg 的api函数的内部实现,qt mingw不行。
首先按照之前的文章《window10_ffmpeg-msys2-msvc编译》 先在 msys2 环境用 --toolchain=msvc 的方式编译出 ffmpeg 的一些dll 库,如下图所示:
我们 msys2 里面用的是 msvc 的编译方式,qt creator也是用 msvc ,所以编译环境是一样的,config.h 不用修改,可以直接拷贝。参考之前的教程,创建一个qt项目 ffmpeg4-4-msvc-new
,先从 msys2 目录 C:\msys64\home\loken\ffmpeg\FFmpeg-n4.4.1
拷贝需要的文件跟库到 qt项目目录,拷贝完后的结果如下:
ffmpeg4-4-msvc-new 项目下载地址:百度网盘,提取码:5ib4
下载完项目之前,导入qt creator,编译kits 选择 Desktop_Qt_5_15_2_MSVC2019_64bit 。
导入项目之后,运行可以看到以下报错。
可以看到 qt creator的编译流程跑了 两步。
1,qmake.exe -o Makefile ..\ffmpeg4-4-msvc-new\ffmpeg4-4-msvc-new.pro -spec win32-msvc "CONFIG+=debug" "CONFIG+=qml_debug"
执行上面的命令,生成makefile文件,qmake 其实是对 makefile 的再次封装。qmake 可以生成makefile文件,然后再执行make命令编译,qt里面用的是jom.exe,jom 跟 make 可以理解为同一个东西,推荐阅读《qt jom.exe》 。
qmake.exe 之后会生成 3个文件,Makefile,Makefile.Debug,Makefile.Release,如图:
2,D:\Program_Files\qt_new\Tools\QtCreator\bin\jom\jom.exe -f Makefile.Debug
jom.exe 编译 makefile文件。
从上面的信息可以知道,我们只需要分析出 qmake 生成的makefile 跟 ffmpeg 源码里面的makefile的差异。不需要了解全部差异,只需要了解编译 ffmpeg.c 工程的makefile差异,这两个 makefile 有哪些差异,就可以倒推回去修改qmake的规则,就可以顺利完成移植。
qt creator 里面的编译流程是这样,从 kits 里面可以看到, Compiler 的使用的是 Microsoft Visual C++ Compiler 16.4.29613.14 (amd64)
咱们再切换到 Compilers 标签,看下 Microsoft Visual C++ Compiler 16.4.29613.14 (amd64)
是一个什么样的 Compiler,如图:
从上面可以看到启动命令 C:\"Program Files (x86)"\"Microsoft Visual Studio"\2019\Professional\VC\Auxiliary\Build\vcvarsall.bat amd64 amd64
,我们直接启动 cmd 窗口,执行 vcvarsall.bat ,模拟 qt creator 的编译流程,如图:
实际上,vcvarsall.bat 就是让当前窗口继承 vs2019 的一些环境变量,跟之前文章 《window10_ffmpeg-msys2-msvc编译》 ,msys2 继承 vs2019 的环境变量异曲同工。继承之后,在当前窗口就能引入 vs2019 msvc 的一些头文件之类的。
接下来执行以下命令:
cd D:\ffmpeg_data\qt_project\build-ffmpeg4-4-msvc-new-Desktop_Qt_5_15_2_MSVC2019_64bit-Debug
D:\Program_Files\qt_new\Tools\QtCreator\bin\jom\jom.exe -f Makefile.Debug
尝试编译 makefile文件,如下图:
跟在 qt creator 的报错一样,提示找不到 stdatomic.h ,所以我们现在已经在命令行模拟出qt creator的编译流程。下面可以一步一步调试。
Makefile.Debug 的部分代码如下:
CC = cl
CXX = cl
DEFINES = -DUNICODE -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DQT_QML_DEBUG
CFLAGS = -nologo -Zc:wchar_t -FS -Zc:strictStrings -Zi -MDd -W3 -w44456 -w44457 -w44458 /Fddebug\ffmpeg4-4-msvc-new.vc.pdb $(DEFINES)
CXXFLAGS = -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -Zi -MDd -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -EHsc /Fddebug\ffmpeg4-4-msvc-new.vc.pdb $(DEFINES)
INCPATH = -I..\ffmpeg4-4-msvc-new -I. -I..\ffmpeg4-4-msvc-new\build64\ffmepg-4.4-msvc\include -I..\..\..\Program_Files\qt_new\5.15.2\msvc2019_64\mkspecs\win32-msvc
LINKER = link
可以看到,用的编译器 是 cl.exe 跟 在 msys2 里面用的编译器一样。
通过debug Makefile.Debug 文件,可以知道,错误就发生在下面这几行代码里面。
{..\ffmpeg4-4-msvc-new}.c{debug\}.obj::
@echo 999_$<
$(CC) -c $(CFLAGS) $(INCPATH) -Fodebug\ @<<
$<
<<
上面代码的语法比较难以理解,我个人猜测如下:
猜测一:
这里target的规则应该是这样, 涉及到一个隐性的推导,..\ffmpeg4-4-msvc-new 下面的 .c 后缀文件编译出来的 debug 目录下的 obj,就符合这个target。
就是说要找的 obj 文件 是由 ..\ffmpeg4-4-msvc-new 下面的 .c 后缀文件编译出来的,就符合这个target规则。
猜测二:target ,prerequisites 的另一种写法,实际上就是 {debug\}.obj: {..\ffmpeg4-4-msvc-new}.c
,{..\ffmpeg4-4-msvc-new}.c
实际上是一个依赖(prerequisites ),所以下面的命令 $< 获取的是第一个依赖文件,..\ffmpeg4-4-msvc-new\ffmpeg.c
上面这条命令展开之后内容如下:
cl -c -nologo -Zc:wchar_t -FS -Zc:strictStrings -Zi -MDd -W3 -w44456 -w44457 -w44458 /Fddebug\ffmpeg4-4-msvc-new.vc.pdb -DUNICODE -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DQT_QML_DEBUG -I..\ffmpeg4-4-msvc-new -I. -I..\ffmpeg4-4-msvc-new\build64\ffmepg-4.4-msvc\include -I..\..\..\Program_Files\qt_new\5.15.2\msvc2019_64\mkspecs\win32-msvc -Fodebug\ @<<
ffmpeg.c
<<
选项讲解如下:推荐阅读 《cl.exe 的编译选项》,
1,-Fodebug\
,前面的 -Fo 是 创建对象文件,生成 xxx.obj 文件,后面的 debug\
是指定目录。
我也不知道这个 @<< ffmpeg.c << 是什么语法,应该是window bath 的一种命令语法。暂时理解为 输入文件就行,就是用 cl.exe 编译 ffmpeg.c 。
分析到这里,我们就很容易知道 ,qt creator里面的编译规则跟 msys2 里面的有哪些不一样,都是用的 cl.exe ,只是选项有点问题,导致找不到 stdatomic.h 头文件。
实际上我们通过 Everything 搜索可以知道,stdatomic.h 在 compat 目录里面,这是一个跨平台设计,不同的环境用不同的 stdatomic.h。
直接在 msys2 里面执行 make -n > make_cmd.txt 查出 编译 ffmpeg.c 的时候使用的规则,保存进去 make_cmd.txt,如下:
msys2 编译 ffmpeg.c 的规则如下:
# 生成 ffmpeg.d 文件
cl.exe -nologo -I. -I./ -D_ISOC99_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_USE_MATH_DEFINES -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_WARNINGS -D_WIN32_WINNT=0x0600 -DPIC -I./compat/atomics/win32 -I. -I./ -D_ISOC99_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_USE_MATH_DEFINES -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_WARNINGS -D_WIN32_WINNT=0x0600 -DPIC -I./compat/atomics/win32 -nologo -Z7 -W3 -wd4018 -wd4146 -wd4244 -wd4305 -wd4554 -O2 -utf-8 -showIncludes -Zs fftools/ffmpeg.c 2>&1 | awk '/including/ { sub(/^.*file: */, ""); gsub(/\\/, "/"); if (!match($0, / /)) print "fftools/ffmpeg.o:", $0 }' > fftools/ffmpeg.d
# 打印提示
printf "CC\t%s\n" fftools/ffmpeg.o;
# 正式编译 ffmpeg.c 文件
cl.exe -I. -I./ -D_ISOC99_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_USE_MATH_DEFINES -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_WARNINGS -D_WIN32_WINNT=0x0600 -DPIC -I./compat/atomics/win32 -nologo -Z7 -W3 -wd4018 -wd4146 -wd4244 -wd4305 -wd4554 -O2 -utf-8 -c -Fofftools/ffmpeg.o fftools/ffmpeg.c
从上面可以看到 msys2 的编译规则多了一个 -I./compat/atomics/win32
,咱们把这个 编译选项加进去 qt creator项目里面就行,如下:
修改 ffmpeg4-4-msvc-new.pro 文件
stdatomic.h 头文件解决之后继续报以下 错误
cmdutils.obj:-1: error: LNK2019: unresolved external symbol avresample_version referenced in function print_all_libs_info
通过 debug,定位到 命令出错位置在 Makefile.Debug 的以下位置,代码如下:
debug\ffmpeg4-4-msvc-new.exe: $(OBJECTS)
$(LINKER) $(LFLAGS) /MANIFEST:embed /OUT:$(DESTDIR_TARGET) @<<
debug\cmdutils.obj debug\ffmpeg.obj debug\ffmpeg_opt.obj debug\ffmpeg_filter.obj debug\ffmpeg_hw.obj
$(LIBS)
<<
也就是在 link 链接的时候出错了。继续 查之前保存的 make_cmd.txt,比较两种方式 link 的时候的差异。
qt creator Link 展开如下:
link /NOLOGO /DYNAMICBASE /NXCOMPAT /DEBUG /SUBSYSTEM:CONSOLE "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" /MANIFEST:embed /OUT:debug\ffmpeg4-4-msvc-new.exe @<<
debug\cmdutils.obj debug\ffmpeg.obj debug\ffmpeg_opt.obj debug\ffmpeg_filter.obj debug\ffmpeg_hw.obj
$(LIBS)
<<
msys2 Link 展开如下:
#打印提示信息
printf "LD\t%s\n" ffmpeg_g.exe;
#正式开始link
./compat/windows/mslink -libpath:libavcodec -libpath:libavdevice -libpath:libavfilter -libpath:libavformat -libpath:libavresample -libpath:libavutil -libpath:libpostproc -libpath:libswscale -libpath:libswresample -nologo -debug -out:ffmpeg_g.exe fftools/ffmpeg_opt.o fftools/ffmpeg_filter.o fftools/ffmpeg_hw.o fftools/cmdutils.o fftools/ffmpeg.o avdevice.lib avfilter.lib avformat.lib avcodec.lib postproc.lib swresample.lib swscale.lib avutil.lib psapi.lib ole32.lib strmiids.lib uuid.lib oleaut32.lib shlwapi.lib gdi32.lib vfw32.lib secur32.lib ws2_32.lib mfplat.lib mfuuid.lib ole32.lib strmiids.lib ole32.lib user32.lib user32.lib bcrypt.lib ole32.lib psapi.lib shell32.lib
link.exe 的选项请阅读《微软link.exe 手册》
可以看到 link 的链接规则,两者没有差异。这个报错。
unresolved external symbol avresample_version referenced in function print_all_libs_info
翻译一下是 print_all_libs_info 函数里面没有找到 avresample_version() 的实现。
仔细研究发现,avresample_version() 是一个快遗弃的函数,但是 cmd_utils.c 的 print_all_libs_info() 用了这个函数 avresample_version()
static void print_all_libs_info(int flags, int level)
{
//省略代码...
PRINT_LIB_INFO(avresample, AVRESAMPLE, flags, level);
//省略代码...
}
所以,应该是 avresample.lib 没有编译出来,被遗弃了,msys2的环境检测不太严格,所以没有报错。我们只需要修改一下 cmd_utils.c。把 上面那行代码加上一个判断即可。很多地方有加了 CONFIG_AVRESAMPLE 判断,这个地方应该是他们漏了。
#if CONFIG_AVRESAMPLE
PRINT_LIB_INFO(avresample, AVRESAMPLE, flags, level);
#endif
现在只剩最后一个报错。
cmdutils.obj:-1: error: LNK2019: unresolved external symbol __imp_CommandLineToArgvW referenced in function prepare_app_arguments
这个报错是因为 msys2 环境里面的命令行跟 qt creator的命令行不太一样。msys2 有 CommandLineToArgvW 这个系统函数,qt creator 是原生的windows cmd 没有这个系统函数,所以还是需要修改一下 config.h,把 HAVE_COMMANDLINETOARGVW 设置为 0。
修改完毕,移植成功。
完整项目下载:百度网盘,提取码:hbtw
相关阅读:
- 无
版权所属:知识星球:弦外之音,QQ:2338195090。 由于笔者的水平有限, 加之编写的同时还要参与开发工作,文中难免会出现一些错误或者不准确的地方,恳请读者批评指正。如果读者有任何宝贵意见,可以加我微信 Loken1。