Log in

No account? Create an account
Working around a compile time change at runtime, aka how to run programs compiled against FAAD2.0 wi - Nick [entries|archive|friends|userinfo]

[ website | gagravarr.org ]
[ userinfo | livejournal userinfo ]
[ archive | journal archive ]

Working around a compile time change at runtime, aka how to run programs compiled against FAAD2.0 wi [Sep. 15th, 2007|07:17 pm]
As many of you will know, FAAD2 is part of the FAAC project. It allows you to decode/play mpeg 4 audio (mp4) files, and its library is used by a great many open source media players.

Between FAAD2.0 and FAAD2.5, there were a few API changes made. The most common changes were to rename a bunch of the functions. In order to avoid problems for older programs, they provided compile time compatibility for these changes. So, if you compiled a program written for FAAD2.0 against the new 2.5 library and headers, everything would be fine.

The snag is that if you tried to run a program compiled against FAAD2.0 on a system with FAAD2.5, it wouldn't work. Your program would try to load symbols that had been renamed, and you'll get errors like symbol lookup error: mplayer: undefined symbol: faacDecOpen

The normal suggestion at this point would be to re-compile your programs against the new version. The snag is that most of my media programs come pre-build as rpms, from various repositories. Annoyingly, quite a few of these repositories aren't upgrading to FAAD2.5, because they're being a bit funny about the licence. So, my rpms of things like mplayer and xine are build against FAAD2.0, and this shows no sign of changing. However, I do need FAAD2.5, so that I can play my iTunes Plus files in xmms, which leaves me in a bit of a spot.

So, I decided to write a pre-load library to introduce the compatibility for the API changes at run time, rather than at compile time. Once I'd written this, I'd be able to use all my pre-build media players with the newer FAAD2 libraries, just by pre-loading another library.

What may surprise people is just how easy a prospect this is. You basically just need to clone the header file, make a few tweaks, and add in the stub calls. So, first up, we need to find the faad2 header file with all the definitions in it. This turns out not to be faad.h, but neaacdec.h

Armed with this file, we can start building our shim. In the middle are a whole bunch of #defines. These, at compile time, rename the calls in your program from the old function names to the new ones. We need to replace those #defines with a definition, and some code to call the new ones.

So, for each #define in turn, see what the new function name is (that's on the right). Then, copy and paste in the definition for that function below the #define. Then, in place of the #define, put in a definition of the old function with the same arguments as the new one. Finally, turn that definition into a call to the new one.

Let's look at a fairly simple example:
#define faacDecPostSeekReset           NeAACDecPostSeekReset
void NEAACDECAPI NeAACDecPostSeekReset(NeAACDecHandle hDecoder, long frame);

This becomes:
void faacDecPostSeekReset(NeAACDecHandle hDecoder, long frame) {
    return NeAACDecPostSeekReset(hDecoder,frame); }
void NEAACDECAPI NeAACDecPostSeekReset(NeAACDecHandle hDecoder, long frame);

After you've done this for each of the #defines, we now have a function with the old name, which calls the new function name with its arguments, and a definition of the new function.

We're now in a position to compile this. We can't just include the new header file, as the #defines in there will rename our functions with the old name to the new one, and we'd be no better off! That's why we had to keep in the definitions of the new function names, as we won't have the header included. To compile, we need to tell gcc to link against the new faad2, and to build it as a shared library:
gcc -Wall -O2 -fpic -shared -ldl -o faad-shim.so faad-shim.c -lfaad

Finally, we can run our old program, loading in our shim to provide the functions that've been renamed:
LD_PRELOAD=/usr/local/lib/faad-shim.so mplayer MyMusic.mp4

To save you time, the shim is available to download from http://gagravarr.org/code/faad-shim.c. Just download, compile, install and pre-load. The above method ought to work for other similar situations too.