An often-requested feature for FFmpeg is to compile it using Microsoft Visual Studio’s C compiler (MSVC). The default (quite arrogant) answer used to be that this is not possible, because the godly FFmpeg code is too good for MSVC. Usually this will be followed by some list of C language features/extensions that GCC supports, but MSVC doesn’t (e.g. compound literals, designated initializers, GCC-style inline assembly). There are complete patches and forks related to this one single feature.
Reality is, many of these C language features are cosmetic extensions introduced in C99 that are trivially emulated using classic C89 syntax. Consider designated initializers:
struct { int a, b; } var = { .b = 1, };
This can be trivially emulated in C89 by using the following syntax:
struct { int a, b; } var = { 0, 1 };
For unions, you can change the initialization (as long as the size of the first field is large enough to hold the contents of any other field in the union) to do a binary translation of the initialized field type to the first field type:
union { unsigned int a; float b; } var = { .b = 1.0, };
becomes:
union { unsigned int a; float b; } var = { 0x3f800000, };
Here, 0x3f800000 is the binary representation of the floating point number 1.0. If the value to be converted is not static, the assignment can simply become a statement on its own:
union { unsigned int a; float b; } var; var.b = 1.0;
Other C99 language features (e.g. compound literals) can be translated in a similar manner:
struct { int *list; } var = { (int *) { 0, 1 } };
becomes:
int *list = { 0, 1 }; struct { int *list; } var = { list };
Two other Libav developers (Derek Buitenhuis and Martin Storsjo) and I wrote a conversion tool that automatically translates these C99 language features to C89-compatible equivalents. With this tool, the FFmpeg and Libav source trees can be translated and subsequently compiled with MSVC. A wrapper is provided so that you can tell the FFmpeg build script to use that as compiler. The wrapper will then (internally) call the conversion utility to convert the source file from C99 to C89, and then it calls the MSVC build tools to compile the resulting “C89’ified source file”. In the end, this effectively means FFmpeg and Libav can be compiled with MSVC, and the resulting binaries are capable of decoding all media types covered by the test suite (32bit, 64bit) and can be debugged using the Visual Studio debugger.
For the adventurous, here’s a quick guide (this is being added to the official Windows build documentation as-we-speak):
Requirements:
- Microsoft Visual Studio 2010 or above (2008 may work, but is untested; 2005 won’t work);
- msys (part of mingw or mingw-64);
- yasm;
- zlib, compiled with MSVC;
- a recent version (e.g. current git master) of Libav or FFmpeg.
Build instructions:
- from the Start menu, open a “Visual Studio Command Prompt” for whatever version of Visual Studio you want to use to compile FFmpeg/Libav;
- from this DOS shell, open a msys shell;
- first-time-only – build c99-to-c89 (this may be tricky for beginners):
- you’ll need clang, compiled with MSVC, for this step;
- check out the c99-to-c89 repository;
- compile it with clang (this probably requires some manual Makefile hackery; good luck!);
- at some point in the near future, we will provide pre-compiled static binaries to make this easier (then, you won’t need clang anymore);
- get the C99 header file inttypes.h from code.google.com and place it in the root folder of your source tree;
- use the configure option “–toolchain=msvc” to tell it to use the MSVC tools (rather than the default mingw tools) to compile FFmpeg/Libav. Ensure that the c99-to-c89 conversion tools (c99wrap.exe and c99conv.exe, generated two steps up) are in your $PATH;
- now, “make” will generate the libraries and binaries for you.
If you want to run tests (“fate”), use the “–samples=/path/to/dir” configure option to tell it where the test suite files are located. You need bc.exe (not included in default msys install) in your $PATH to run the testsuite.
It’s probably possible to generate Visual Studio solutions (.sln files) to import this project in the actual Visual Studio user interface (e.g. libvpx does that) so you no longer need the msys shell for compilation (just for configure). Although we haven’t done that yet, we’re very interested in such a feature.
Fantastic work, this sounds like it could be useful tool in the buildchain of all sorts of open source projects.
Wait, you’re converting C99 statements to C89? Does this mean that VC 2010 doesn’t support C99 — 11 years after its release? What the hell is Microsoft spending its development time on?
I can understand if this would be needed to help people build on older version than that (and thus this effort is commendable), but if it’s really needed even now, then I find it hard to take VC as a serious compiler.
This is an interesting twist on an old problem!
I wonder if this one day GLib could adopt this mechanism to provide MSVC support, because I’m pretty sure it’s the only supported target left that can’t cope with C99
@Sam: the converter is written in a very generic way, the idea being that it could indeed be useful to other projects also. We’re currently testing it on VLC, I’d actually like to test it on x264, and yes, it would be really exciting to try this on Glib as well.
What about intermingled declarations and code or stuff like
for(int i = low; i <= high; ++i)
A quick look at the code suggests that this is not supported
@Christian: great question! Indeed, that kind of construct currently doesn’t work. The reason we didn’t hook it up (code to initialize a new declaration context, e.g. do { .. } while (0);) is actually there already, part of compound literals support), is simply because FFmpeg/Libav don’t use it, so it wasn’t a priority. VLC _does_ mixed statements and declarations, and that’s our next target, so I’d expect that to work sometime over the next couple of weeks.
Very cool! Does it support compound literals?
Actually scratch that, I didn’t read closely enough, looks like it does support compound literals! How pedantic is the translation in terms of only relying on what is defined in the standard? For example, is it actually legal to read out of a union member other than the one you initialized? And is the integer representation of a float well-defined? Is it guaranteed that floats and integers have the same endianness? etc.
@josh: my understanding is that C89 does not define packing of unions or structs in any specific way, so you’re right that we’re relying on compiler-specific behaviour to get this to work correctly. We’re just lucky that it happens to work correctly with MSVC. Other C89 compilers may barf on our translations.
Floating point representations are defined in IEEE-754, so that’s generally not an issue.
Pingback: Compiling FFMPEG via Visual Studio « On Various Things
I would like to add two LGPL-related issues for commercial developers:
1) The libav documentation mentions, that currently only static builds are possible. Is there some progress in dll creation?
2) The wrapper changes source code (in-place?). Will this violate the LGPL (I’m not a lawyer)
@Michael: DLL support is in progress, see http://thread.gmane.org/gmane.comp.video.ffmpeg.devel/152033/focus=152085
As for source code changing, I don’t see how this could possibly violate the LGPL – you have the right to view, modify and run the software, and this program enables exactly that.
This is really highly appreciated. Many thanks for taking this effort.
I’m just curious: From my experience playing around with the configure script many years ago, the script only “knows” gcc compiler switches, Did you change the configure script (I guess so) to support msvc compiler switches (and ‘/’ instead og ‘-‘)? I guess that’s what’s done by the -toolchain parameter.
Would it be possible to use icl with -toolchain=msvc (without the C99 to C89 conversion)?
@aholzinger: – instead of / actually works for me (maybe msys translates them?). Yes, part of the patch was to make configure aware of the different commandline switches, so with some minor work (somebody else is currently doing that – with great performance results so far compared to original MSVC or GCC) it’s possible to use ICL.
It looks like CMake can generate .SLN files. Also, any word on when static binaries will be available? Thanks for all your hard work.
@Elyscape: Derek is working on static binaries on github. For now, you can use the binary used by Chrome: https://gerrit.chromium.org/gerrit/#/c/34728/
And thanks for the cmake reference, might be useful in the future.
Hi,
I followed the instructions and was able to build c99conv.exe and c99wrap.exe using clang (and also using cl with the Makefile.w32). I added their location to my PATH and also copied them to VC\bin folder.
However, when I run configure –toolchain=msvc, I get an error saying that c99wrap cl is not able to create an executable file.
c99wrap is found correctly in the path, as I can call it from the msys prompt.
I am using Visual Studio 9.0
Has anyone experienced this issue?
Thanks for any help you can provide.
In Visual Studio 2010, configure does not stop, but I still get the error message:
configure: line 346: pr: command not found.
Help…. I can’t build c99conv anymore. I am getting the following error:
/usr/bin/link: invalid option — o
linker command failed with exit code 1.
make: *** [c99conv.exe] Error 1
What is causing that error?
Thanks in advance for your help.
@Christophe: Visual Studio 9 (2008) is untested and may thus not work – we don’t know. I think it can be made to work but it may need minor work – don’t use it unless you like experimenting.
To build (either c99wrap.exe or FFmpeg/Libav itself), make sure that you’re not using the msys linker, but instead the MSVS linker. For now, temporarily backup /usr/bin/link to someplace so it’s not in your $PATH.
As for pr, it’s a shell utility that configure needs but you don’t have it, simply download it from anywhere, put it anywhere in $PATH and try configure again. See e.g. http://creekcodes.blogspot.com/2011/02/msys-pr-not-found.html or related sites.
Thanks so much for your answer. That fixes the issues with the converter and pr.
Unfortunately, I still have difficulties to build:
It looks like the library.mak file might be corrupted: first, I had an error when I tried to build using make on line 97 (missing separator).
$(eval $(RULES))
After searching the web, I replace CR/LF in this file by just LF, and added a tab in front on that instruction. That fixes that error, but the build later fails with the error
AR libavcodec/libavcodec.a
LINK : fatal error LNK1104: cannot open file ‘libavcodc/’
make: *** [libavcodec/libavcodec.a] Error 80
Have you run into this when you tried it out? I followed all the instructions to setup my system to the letter, as described in this link:
http://ffmpeg.org/trac/ffmpeg/wiki/MingwCompilationGuide
Thanks so much for any help you can provide.
@Christophe: I wouldn’t recommend editing the Makefile or .mak files, that’s a recipe for disaster. Undo all those changes, and try upgrading to a later version of GNU make in your msys shell?
@rbultje, thanks a lot for your help. I got everything to work using VS2010.
You mentioned in your response to @aholzinger that someone is working on support of the Intel icl compiler and is already getting good results. Would that be possible to provide the instructions to do it? Do you have a timeline for when the instructions will be made public?
Thanks.
@Christophe: can’t say much about ICL yet, sorry. “It’s done when it’s done” is about as much as I can realistically promise, I hate to be vague but there isn’t really any real timeline for this. You can join IRC (#libav-devel on freenode) to see where things stand if you’re really curious.
I managed to get make running with msvc toolchain. The tutorial and comments here helped me very much so far.
The remaining problem is that make stops with an error:
libavcodec/aacenc.c(824) : error C2099: Initializer is not a constant
Michael
@Michael: no idea, would need more details – can you share the output of make V=1 (i.e. the cl.exe commandline that leads to that error) and cl.exe (to get the version information of your compiler)?
I have one last question. I am able to compile it for 32-bits, but am running into some issues trying to compile for 64-bits. Is 32-bit the only platform supported so far?
I tried to launch the MSYS console from MSVC 2010 x64 Win64 command prompt, and also from the x64 Cross Tools. In both cases, I get warnings such as ‘rsp’ is a register in 64-bit mode, then make *** [libavcodec/x86/deinterlace.o] Error 1.
Is there anything special we need to ensure to build in 64-bits?
@Christophe: http://fate.libav.org/x86_64-msvc10-windows-native/20121105152044 and http://fate.ffmpeg.org/report.cgi?time=20121105192417&slot=x86_64-msvc10-windows-native suggest it works. Can you submit a full bug report (configure commandline, config.h, complete make output) to the mailinglist?
I have compiled the ffmpeg successfully, thanks for the article and comments. But I cannot find debug symbols (pdb) at the result so still cannot debug ffmpeg in Visual Studio. Could you advice how to get it?
@Aleksey: I actually use the configure options –extra-cflags=”-Z7″ and –extra-ldflags=”-Z7″ (or manually add those to CFLAGS/LDFLAGS in config.mak), that did the trick for me.
@Aleksey: actually it may be –extra-cflags=”-Z7″ and –extra-ldflags=”-DEBUG”, I don’t remember exactly. If you know the commandline syntax of cl.exe and link.exe, you may know better than me. :-).
Thanks. The “-Z7” and “-DEBUG” arguments seems to be right for VS compiler and linker, but make stops with error in aacpsy.c with them. I tried to disable aac (don’t need it) but there is another error:
audioconvert.o_converted.c
libavcodec/audioconvert.c(108) : error C4013: 'lrint' undefined; assuming extern
returning int
make: *** [libavcodec/audioconvert.o] Error 1
My configure string:
./configure --toolchain=msvc --prefix=build --extra-cflags="-Z7" --extra-ldflags="-DEBUG" --disable-encoder=aac
Without extra-cflags and extra-ldflags make runs successfully.
Thank you for your information.
I have some questions.
I checked some path of cl.exe and link.exe on the msys since I am using VS2008 and VS2010.
$ which cl
/Program Files /Microsoft Visual Studio 10.0/VC/BIN/cl.exe
$ which link
/Program Files /Microsoft Visual Studio 10.0/VC/BIN/link.exe
$which c99wrap
/Program Files /Microsoft Visual Studio 10.0/VC/BIN/c99wrap.exe
$which c99conv
/Program Files /Microsoft Visual Studio 10.0/VC/BIN/c99wrap.exe
With the ffmpeg-1.0 and configure option is “./configure –toolchain=msvc”
error message is like this
“c99wrap cl is unable to create an executable file.”
If c99wrap cl is a cross-compiler, use the –enable-cross-compile option
@Aleksey: this sounds like a bug, let’s try to get that fixed. Not sure what’s going on.
@Jo: please check config.log why the compile test is failing.
config.log message is like this
c99wrap link -nologo -out /tmp/ffconf..JO-PC.500.5436.exe /tmp/ffconf..JO-PC.500.5436.o
LINK : fatal error LNK1561: entry point must be defined
C compiler test failed.
Thank you for your interest
@Jo: how did you get c99conv.exe? Did you get a pre-built binary, or build it yourself? If yourself, which version of clang is it compiled against?
You can actually help debug further to see where the issue is here; if you look a little up in config.log, you’ll see the code it tried to compile, probably something like int main(){return 0;} or so. Then, it gives you the commands it runs to compile that. What I’d be interested in is the output of those commands, and possibly prepend the option “-keep” to the “c99wrap cl” compile command (e.g. c99wrap.exe -keep cl.exe -c -Fofile.o file.c). Then, it’ll keep a set of intermediate files called file.o_{converted,preprocessed}.c which contain intermediate states of the process, and I’d like to see if any of these is broken (e.g. diff -u file.o_{preprocessed,converted}.c should give no or very little output). If that gives significant output, my guess is that your version of clang that you linked c99conv.exe against is somehow not right?
I used VMware software to install Windows 7 and visual studio 2010. Because I thought that some errors which occurred before came from the compile environment of my personal computer. All the processes have been done normally due to your manual and conversion software(c99conv.exe). I got bin, include, lib, shared folder finally !!
However I don’t know that how can I debug the ffmpeg source code (*.c) with visual studio 2010. Could you advice how to do it ?
as the post says we need to make manual Makefile hackery, so it will compile agains clang. I did everything but didn’t managed. Please someone, what changes I should do to makefile in order get it work?
I will repost it in norm format (please remove two last my posts):
1) Download 2 headers (stdint.h/inttypes.h) from http://code.google.com/p/msinttypes/ (to be exactly from http://code.google.com/p/msinttypes/downloads/list)
2a) May be we can use static version of c99conv.exe/c99wrap.exe, it can be downloaded from https://github.com/libav/c99-to-c89/downloads
But when I tried to use it, these binaries did not work, I think I did something wrong.
If you will try to use binaries from https://github.com/libav/c99-to-c89/downloads, we can skip next 2-11 steps. Begin from #12.
2) Download Python, http://www.python.org/download/, I have downloaded “Python 2.7.3 Windows Installer (Windows binary — does not include source)” and installed it
3) Download CMake msi installer for Windows from http://www.cmake.org/cmake/resources/software.html and install it
4) download llvm (e.g. to c:\projects\llvm):
from repo http://llvm.org/svn/llvm-project/llvm/trunk
using console: svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm
5) download Clang (to c:\projects\llvm\tools, i.e. c:\projects\llvm\tools\clang):
from repo http://llvm.org/svn/llvm-project/cfe/trunk
using console: svn co http://llvm.org/svn/llvm-project/cfe/trunk clang
6) Run CMake to generate the Visual Studio solution and project files:
a) cd ..\.. (back to where you started)
b) mkdir build (for building without polluting the source dir)
c) cd build
d1) If you are using Visual Studio 2008: cmake -G “Visual Studio 9 2008” ..\llvm
d2) Or if you are using Visual Studio 2010: cmake -G “Visual Studio 10” ..\llvm
e) Open LLVM.sln in Visual Studio.
f) Build the “clang” and “libclang” project
7) download small project of c99-to-c89 from https://github.com/libav/c99-to-c89/ usign git
7a) I expect that clang.exe is ready on your side from 3 step
8 ) Run Visual Studio command prompt 2010 (2008 not works!!! anyway for me)
9) Run msys.bat from “Visual Studio command prompt 2010”
10) small refactoring of Makefile
a) Makefile
path to clang.exe/clang.lib/libclang.lib on my PC C:/Projects/build/bin/Release
It is Makefile:
———————————-
EXT =.exe
all: c99conv$(EXT) c99wrap$(EXT)
CC=/C/Projects/build/bin/Release/clang.exe
LD=$(CC)
CFLAGS=-I/C/Projects/llvm/include -I/C/Projects/llvm/tools/clang/include -I/C/Projects/c99-to-c89 -g
LDFLAGS=-l/C/Projects/build/lib/Release/clang.lib -l/C/Projects/build/lib/Release/libclang.lib
LIBS=
clean:
rm -f c99conv$(EXT) c99wrap$(EXT) convert.o compilewrap.o
rm -f unit.c.c unit2.c.c
c99conv$(EXT): convert.o
$(CC) -v -o $@ $< $(LDFLAGS) $(LIBS)
c99wrap$(EXT): compilewrap.o
$(CC) -v -o $@ $< $(LDFLAGS)
%.o: %.c
$(CC) $(CFLAGS) -o $@ -c $<
———————————-
b) small fix for convert.c, put it after all #include
#if _MSC_VER
#define snprintf _snprintf
#define strdup _strdup
#endif
c) small fix for compilewrap.c, put it after all #include
#if _MSC_VER
#define unlink _unlink
#endif
d) put stdint.h/inttypes.h near by convert.c (to c99-to-c89 folder)
e) Last think: I renamed link.exe (C:\MSYS\bin), else clang.exe is not used as linker for building of c99conv/c99wrap.
11) Now we can type "make" in "c99-to-c89" folder (you must type "make" in the MSYS launched from VS2010 command prompt)
12) Add c99conv.exe/c99wrap.exe/makedef to folder from PATH or add your folder (with these files) to PATH.
If c99conv.exe was built on your side (not from site) we must copy libclang.dll near by c99conv.exe. libclang.dll is placed on C:/Projects/build/lib/Release/ (on my PC).
13) Get recent ffmpeg, for building I used latest release version – 1.0.1 (first my mistake – I tried to use old version of ffmpeg)
a1) NOTE: Do not put –extra-cflags in your options for "configure", these flags the "configure" puts for MS compiler and it fails the building.
a2) NOTE: also, when I put –host or –target-os options for building of x86_64, the "configure" is interrupted.
b1) For 32 bit: ./configure –toolchain=msvc –arch=i686
b2) For 64 bit: ./configure –toolchain=msvc –arch=x86_64
c) Copy stdint.h/inttypes.h to ffmpeg folder (where you will type ./configure…)
d) go ./configure –toolchain=msvc (with your additional options)
e) if "configure" is failed, please check config.log, I had two problems, first issue is c99conv/c99wrap is not placed in PATH and second issue c99conv.exe not worked (e.g. libclang.dll was missed)
f) my first attempts to use
g) "make" and "make install" (you must type "make" in the MSYS launched from VS2010 command prompt)
Thanks for all your comments. I reached the last step: “make” and “make install”. When I type “make” here is the error message:
common.mak:40: target `-I.’ given more than once in the same rule.
common.mak:40: target `-nologo’ given more than once in the same rule.
common.mak:40: target `-I.’ given more than once in the same rule.
common.mak:40: target `-I.’ given more than once in the same rule.
common.mak:40: target `-D_ISOC99_SOURCE’ given more than once in the same rule.
common.mak:40: target `-nologo’ given more than once in the same rule.
common.mak:40: target `-D_USE_MATH_DEFINES’ given more than once in the same rul
e.
common.mak:40: target `-Dinline=__inline’ given more than once in the same rule.
common.mak:40: target `-FIstdlib.h’ given more than once in the same rule.
common.mak:40: target `-Dstrtoll=_strtoi64′ given more than once in the same rul
e.
common.mak:40: target `-W4′ given more than once in the same rule.
common.mak:40: target `-wd4244′ given more than once in the same rule.
common.mak:40: target `-wd4127′ given more than once in the same rule.
common.mak:40: target `-wd4018′ given more than once in the same rule.
common.mak:40: target `-wd4389′ given more than once in the same rule.
common.mak:40: target `-wd4146′ given more than once in the same rule.
common.mak:40: target `-wd4057′ given more than once in the same rule.
common.mak:40: target `-wd4204′ given more than once in the same rule.
common.mak:40: target `-wd4706′ given more than once in the same rule.
common.mak:40: target `-wd4305′ given more than once in the same rule.
common.mak:40: target `-wd4152′ given more than once in the same rule.
common.mak:40: target `-wd4324′ given more than once in the same rule.
common.mak:40: target `-we4013′ given more than once in the same rule.
common.mak:40: target `-wd4100′ given more than once in the same rule.
common.mak:40: target `-wd4214′ given more than once in the same rule.
common.mak:40: target `-wd4996′ given more than once in the same rule.
common.mak:40: target `-wd4273′ given more than once in the same rule.
common.mak:43: *** missing separator. Stop.
May anybody help me? thanks!
Hi
Requirements:
Microsoft Visual Studio 2010 or above (2008 may work, but is untested; 2005 won’t work);
I can’s understand, why 2005 won’t work…
now..I know why 2005,2008 won’t work…
compilewrap.c(c99wrap.exe) :
if (msvc) {
sprintf(arg_buffer, “-Fi%s”, temp_file_1);
cpp_argv[cpp_argc++] = arg_buffer;
} else {
cpp_argv[cpp_argc++] = “-o”;
cpp_argv[cpp_argc++] = temp_file_1;
}
-Fi http://msdn.microsoft.com/en-us/library/ee207218(v=vs.100).aspx
only support from Visual Studio 2010
We require stdint.h, which is included in 2010 but not 2008 or earlier. We also require variadic macros, which 2008 added. I don’t think it’s possible to workaround variadic macros, so that won’t ever work. You could probably hack it up to work with 2008, but I doubt that it’s worth the effort.
I give up work with 2008 or 2005…unworthy
only because my PC is so poor…it can’t ran 2010 smoothly orz…
libavcodec.a 43.7 MB
libavformat.a 24.3 MB
……
i have make the lib successfull!
but how can i use the the ‘a lib’ in vs2010?
@Aleksey: I also use the “-Z7″ and “-DEBUG” arguments and make stops with error in aacpsy.c with them…did you solved? I need to buld shared librareis with debug options for MSVC…someone has done it?