Sunday 22 December 2013

Building FFmpeg on Windows with in-line asm and the Intel compiler.

Building FFmpeg for Windows is generally done using MinGW compiler in either a MSYS or Cygwin environment. But the resulting libraries don't always play all that well when you try and use them in a native Windows program (i.e. something built with the Microsoft compiler -msvc). For those who work on Windows it is often slightly more convenient to be able to use a native build chain when creating FFmpeg binaries.

Unfortunately FFmpeg (much like most opensource projects) uses C99 code which will not build using the msvc compiler found in Visual Studio. This is because Microsoft in all there wisdom don't support anything past C89. With the standardisation of C++11 newer versions of msvc are slowly starting to support C99 features as the C++11 specification mandates that they must be supported. So supporting C++11 generally also means supporting C99. However this support started to feature in Visual Studio 2013 and it is still far from complete and can not compile many C99 based projects (at time of writing).

Things are made worse as FFmpeg doesnt just use C99 but also has large amounts of in-line assembly. This assembly is written in the AT&T assembly syntax (or more specifically GAS - GNU Assembler) as opposed to Intel assembly syntax. Since msvc only supports Intel syntax then even with C99 support it will never be possible to compile FFmpeg with in-line assembly natively. Of-course FFmpeg has a nice config option that can be used to disable all in-line asm which would mean that it would compile on a C99 compliant compiler without needing AT&T assembly support. But then we don't get all the nicely hand tuned assembly optimisations that were written specifically to maximise performance.

Luckily there is the Intel compiler to the rescue. The Intel compiler for Windows will generate a native windows library that can be easily linked and debugged like any other msvc library. It also has the added benefit of supporting AT&T assembly compilation on Windows.

Unfortunately Intels AT&T support is not fully featured on Windows. On Linux the Intel compiler just uses GAS so it supports everything GAS does. But on Windows Intel had to implement the support themselves and as a result it is not as feature complete as GAS. The main issue is that the Intel compiler on Windows does not support 'Direct symbol references' which are used in various places within FFmpeg. In order to compile on Windows these lines need to be updated in order to remove the symbol references.

An example of the problem and the fix can be found in libavcodec/x86/dsputil_mmx.c:

--- a/libavcodec/x86/dsputil_mmx.c
+++ b/libavcodec/x86/dsputil_mmx.c
@@ -115,13 +115,13 @@ void ff_put_signed_pixels_clamped_mmx(const int16_t *block, uint8_t *pixels,
     x86_reg line_skip3;
 
     __asm__ volatile (
-        "movq "MANGLE(ff_pb_80)", %%mm0     \n\t"
+        "movq %4, %%mm0                     \n\t"
         "lea         (%3, %3, 2), %1        \n\t"
         put_signed_pixels_clamped_mmx_half(0)
         "lea         (%0, %3, 4), %0        \n\t"
         put_signed_pixels_clamped_mmx_half(64)
         : "+&r"(pixels), "=&r"(line_skip3)
-        : "r"(block), "r"(line_skip)
+        : "r"(block), "r"(line_skip), "m"(ff_pb_80)
         : "memory");
 }

Here the symbol reference is replaced with an asm-interface.

There are many occurrences of direct symbol references in FFmpeg (to many to list here) so I will instead direct people to check out the attached patch.

The attached patch includes updates to the in-line asm that allow most of it to compile under the Intel compiler. Unfortunately there are 2 places where the asm uses slightly more complicated features than just symbol references so these cant be easily converted. These instances are in libavcodec/x86/mlpdsp.c where the use of labels (combined with a jump list) is not supported by Intel on Windows and is not easily modified. The second instance is in the BRANCHLESS_GET_CABAC macro in libavcodec/x86/cabac.h. This contains slightly more complex use of symbol references that if just swapped out like previous occurrences still doesn't compile without some additional work.

However despite a couple of problem functions the attached patch will allow for FFmpeg compilation on Windows with the Intel compiler and in-line asm optimisations enabled.

Update 2: New and improved patches have been created and these are now available directly in FFmpeg master (no patching required). See my post for more information (Building FFmpeg on Windows with in-line asm and the Intel compiler (Part 3)).
Update: A new patch is available in my newer post (Building FFmpeg on Windows with in-line asm and the Intel compiler (Part 2)).

Download patch file:
https://github.com/ShiftMediaProject/FFmpeg/commit/a810d0822fd9f894a3750bdb0cbea3014229ae8b.diff

Lastly for those who don't want to apply the modifications themselves and would like a nicer build method than using MSYS/Cygwin then you can check out my git repository which has working Visual Studio build projects. These projects are in the 'SMP' directory and of course require a valid install of the Intel Windows compiler.
https://github.com/ShiftMediaProject/FFmpeg

Saturday 21 December 2013

Building FFmpeg with libssh on Windows

FFmpeg recently added support for SFTP protocol through the use of libssh. However if you have tried to use this feature on Windows you would have discovered that FFmpeg will not build without some changes. These changes are pretty simple and are yet another case of the Windows headers not including things that are commonly found in their Linux counterparts. In this case whats missing is some defines used for flagging permissions and other things on read/write operations. So in order to get them to work we just have to add the appropriate defines to the file.

So if your curious as to how to get libssh support in FFmpeg on Windows then all you need to do is add the following lines to libavformat/libssh.c:

@@ -20,12 +20,34 @@
 
 #include <fcntl.h>
 #include <libssh/sftp.h>
+#include <sys/stat.h>
 #include "libavutil/avstring.h"
 #include "libavutil/opt.h"
 #include "avformat.h"
 #include "internal.h"
 #include "url.h"
 
+#if defined(_WIN32)
+#ifndef S_IRUSR
+#define S_IRUSR S_IREAD
+#endif
+#ifndef S_IWUSR
+#define S_IWUSR S_IWRITE
+#endif
+#ifndef S_IRGRP
+#define S_IRGRP 0
+#endif
+#ifndef S_IWGRP
+#define S_IWGRP 0
+#endif
+#ifndef S_IROTH
+#define S_IROTH 0
+#endif
+#ifndef S_IWOTH
+#define S_IWOTH 0
+#endif
+#endif
+
 typedef struct {
     const AVClass *class;
     ssh_session session;


And thats it. After adding those lines then assuming you have a built libssh library then everything should work fine.

Wednesday 11 December 2013

Compiling LibVPX 1.3.0 using MinGW

Recently Ive been building a script to compile ffmpeg (in interesting ways). As part of that I have been generating ffmpeg libraries using MinGW from within MSYS. As anyone who has tried to do this before will know that this is not exactly the quickest and simplest of tasks. Part of my ffmpeg build uses the libvpx library for VP8/VP9 encoding. Libvpx has a pretty good set of build scripts but I found that I had to change a few things in order to get it to work in my MinGW build chain.

Firstly I was getting errors during compilation related to the 'strip' command. The error I was getting was from strip complaining that passed parameters where to long. While other people have run into a similar error where strip was failing due to a "Bad file number" error. Luckily the fix is the same for both problems (and is a rather simple one). Just disable strip from running. This can be done by modifying the Makefile after running configure.

The offending line is in 'libs-x86-win32-gcc.mk' (or libs-x86_64-**** depending on whether your building 32/64 bit).

HAVE_GNU_STRIP=yes

Just change the 'yes' to a 'no' and your golden. For those who dont want to have to do this manually here is a bash script that will do it for you.

#There is a bug that causes compilation to fail due to an error in strip
if !( sed -i -e 's/HAVE_GNU_STRIP=yes/HAVE_GNU_STRIP=no/g' $VPXFOLDER/libs-$LOCALTARGET.mk ); then
    echo "    Error: libvpx make update failed"
    exit 1
fi

Where '$VPXFOLDER' is the location of your vpx source and '$LOCALTARGET' is either 'x86-win32-gcc' or 'x86_64-win64-gcc' depending on if you are compiling 32 or 64 bit respectively.

This fixes most common MinGW errors associated with libVPX but I was also getting a linker error about missing 'strtok_r'. This was due to an inconsistency in my MinGW build with the use of 'MINGW_HAS_SECURE_API'. 
The fix was just a simple edit in the file 'svc_encodeframe.c':

@@ -23,7 +23,7 @@
 #include "vpx/vp8cx.h"
 #include "vpx/vpx_encoder.h"
-#if defined(__MINGW32__) && !defined(MINGW_HAS_SECURE_API)
+#if defined(__MINGW32__)
 #define strtok_r strtok_s
 // proto from /usr/x86_64-w64-mingw32/include/sec_api/string_s.h
 _CRTIMP char *__cdecl strtok_s(char *str, const char *delim, char **context);

Lastly there was an error when generating a 64bit version using experimental mingw-w64. This was due to an incompatible pthread implementation that caused errors when building the libvpx tests. This error is easiest to avoid by just turning of the generation of the tests.

For those interested the final configure parameters looked like this:
./configure --disable-shared --enable-static --target=$LOCALTARGET --disable-unit-tests --disable-install-bins --disable-examples --disable-docs --enable-vp8 --enable-vp9

Just like before the '$LOCALTARGET' variable should be set to either 'x86-win32-gcc' or 'x86_64-win64-gcc' for 32 or 64 bit compilation respectively. The above builds static libraries, if you want to generate a dll just swap the 'enable'/'disable' bits for the shared and static parameters (i.e. --enable-shared --disable-static)

Tuesday 10 December 2013

A New Blog

As part of my day to day coding work I am constantly fixing (often after creating) new problems. After each fix I often find myself thinking that someone else out there may have had the same problem and may be interested in how to solve it. Of course previously this has been nothing more than a passing thought and on the odd occasion it is more than that by the time I get around to doing anything about I have often forgotten what the fix process was myself.

So I finally made the decision to try and put some of those little fixes and insights into the public domain where maybe, just maybe they may be of use to someone. I for one have found a few solutions from other peoples blogs and decided it was probably about time I contributed a little something back.

That said I cant guarantee any future fixes/insights will be any good (or exist at all) but if they are then, well, here goes...