This time I want to record what feels like a rather hardcore problem. Due to the title length limit, it’s impossible to accurately express all the issues. For example, when debugging FFmpeg code with breakpoints and single‑stepping, the code does not execute in order, jumps around unpredictably, and when checking local variables near a breakpoint, their values cannot be displayed correctly, such as this:

The local variable “offset” cannot show its current value and instead displays “Variable is optimized away and not available.” From the literal meaning, we can guess that the compiler optimized the code, such as inlining and removing intermediate variables, so they can no longer be seen.
However, FFmpeg’s configure parameters for building the debug version do include:
–disable-optimizations –enable-debug –disable-stripping
These officially documented debug build parameters. So why do we still encounter all the debugging obstacles described above, which look like the result of compiler optimizations?
First, let me explain my Windows FFmpeg build method. There are many ways to do this. If I remember correctly, the official recommendation is to use mingw or cygwin, which are Windows ports of *nix environments, allowing FFmpeg’s build scripts to run smoothly and complete compilation and linking. Some people on GitHub even go to great lengths to create Visual Studio project files for certain FFmpeg versions (this method theoretically would not have the issues described in this article). As for me, I use Windows WSL, which feels cleaner than mingw/cygwin, and unlike the Visual Studio solution approach, it does not lock you into a specific VS version. There are many tutorials online about how to do this, so I won’t discuss it here.
Back to the main topic. The reason why enabling debug in configure still triggers compiler optimizations lies here in the FFmpeg configure script:
elif $_cc -nologo- 2>&1 | grep -q Microsoft || { $_cc -v 2>&1 | grep -q clang && $_cc -? > /dev/null 2>&1; }; then
_type=msvc
if $_cc -nologo- 2>&1 | grep -q Microsoft; then
_ident=$($_cc 2>&1 | head -n1 | tr -d '\r')
else
_ident=$($_cc --version 2>/dev/null | head -n1 | tr -d '\r')
fi
_DEPCMD='$(DEP$(1)) $(DEP$(1)FLAGS) $($(1)DEP_FLAGS) $< 2>&1 | awk '\''/including/ { sub(/^.*file: */, ""); gsub(/\\/, "/"); if (!match($$0, / /)) print "$@:", $$0 }'\'' > $(@:.o=.d)'
_DEPFLAGS='$(CPPFLAGS) $(CFLAGS) -showIncludes -Zs'
_cflags_speed="-O2"
_cflags_size="-O1"
_cflags_noopt="-O1"
if $_cc -nologo- 2>&1 | grep -q Linker; then
_ld_o='-out:$@'
else
_ld_o='-Fe$@'
fi
_cc_o='-Fo$@'
_cc_e='-P -Fi$@'
_flags_filter=msvc_flags
_ld_lib='lib%.a'
_ld_path='-libpath:'
_flags='-nologo'
disable stripping
As you can see, in the MSVC compiler configuration, the cflags passed for noopt (i.e., debug mode) use -O1 instead of the proper debug flag -Od!
Before locating this, I had already tried passing -Od through “–extra-cflags” during configure, but because the configure script places this parameter before _cflags_noopt, the final cl.exe invocation receives -Od first, and then another -O1 afterward, resulting in the compilation warning:
warning D9025 : overriding ‘/Od’ with ‘/O1’
In the end, -O1 still takes effect. So, wouldn’t it be enough to simply change the O1 in the MSVC section of the configure script to Od? That was my initial thought as well. But after making the change, linking the avcodec dynamic library produced the following errors:
LD libavcodec/avcodec-60.dll
Creating library libavcodec/avcodec.lib and object libavcodec/avcodec.exp
fdctdsp_init.o : error LNK2019: unresolved external symbol ff_fdct_sse2 referenced in function ff_fdctdsp_init_x86
vc1dsp_init.o : error LNK2019: unresolved external symbol ff_vc1dsp_init_mmx referenced in function ff_vc1dsp_init_x86
vc1dsp_init.o : error LNK2019: unresolved external symbol ff_vc1dsp_init_mmxext referenced in function ff_vc1dsp_init_x86
libavcodec\avcodec-60.dll : fatal error LNK1120: 3 unresolved externals
ffbuild/library.mak:118: recipe for target 'libavcodec/avcodec-60.dll' failed
make: *** [libavcodec/avcodec-60.dll] Error 96
The errors refer to several x86 SIMD‑optimized DSP algorithm implementations built into FFmpeg. Normally, configure should correctly detect whether these features are supported: if supported, they are compiled and linked; if not, init_x86 would not call them. The current error indicates that the init_x86 code believes the feature is supported and attempts to initialize the SIMD‑optimized DSP implementations, but the corresponding x86 optimized DSP code believes the feature is not supported and therefore does not compile or export the relevant functions. I later checked the relevant .o files using objdump -t and confirmed that the missing symbols were indeed not exported.
After reverting the -Od modification in configure and rebuilding from a clean state, the issue disappeared. So it seems FFmpeg’s build scripts cannot be compiled with -Od directly, or some parts of the code will miscompile. This is consistent with a comment in the configure script under the icl section:
_cflags_size="-O1 -Oi" # -O1 without -Oi miscompiles stuff
If the parameter combination is wrong, it will miscompile. Well… fine. So I gave up on this approach and continued using -O1.
So, is there no way to properly debug FFmpeg on Windows? After further experimentation, I found a not‑so‑smart but effective method: use MSVC’s #pragma compile‑time configuration inside the source code. The specific method is as follows:
At the very beginning of the FFmpeg source file where you need to disable compiler optimizations, add #pragma optimize(“”, off), and at the very end add #pragma optimize(“”, on). Using this somewhat crude method, all compiler optimizations for that source file are completely disabled.
With this, the final compiled library or executable behaves normally when debugging in Visual Studio, and the issues described at the beginning of this article no longer occur. It feels just like debugging a native Visual Studio solution or a native CMake debug build. The only annoying part is that whichever source file you want to debug must have the #pragma added manually. But since compiling with -Od causes errors, having this workaround is better than nothing. If a better method appears later, then great.
博主友情提示:
如您在评论中需要提及如QQ号、电子邮件地址或其他隐私敏感信息,欢迎使用>>博主专用加密工具v3<<处理后发布,原文只有博主可以看到。