How to make Skype work with PulseAudio on 64 bits Linux systems
Par Schnouki le samedi 16 août 2008, 02:45 - Geek inside - Lien permanent
Some people like to hurt themselves: their computers run Linux, they have a 64 bits system, they use PulseAudio to have more fun with their sound cards, and they use Skype to call their geeky friends.
That's what I do. My laptop has an Athlon64 CPU, runs ArchLinux, and I'm a PulseAudio addict (exporting display and sound through the network when connecting to my remote PC with SSH is just awesome). And since my girlfriend will soon go and spend one semester in a university in Sweden, I need to have Skype working so I can call her without ruining myself. [1]
The problem is that the Linux version of Skype is designed to use Alsa only. This shouldn't be a problem since there is an Alsa plugin to use PulseAudio as a backend: with this library (in its 32 bits version), everything should work properly. Except...
Except that Skype developers are some kind of morons who didn't realize that everybody isn't using the very same system as them. You can't change the directory where Skype will look for the Alsa libs: it will search for them in /usr/lib/alsa-lib and that's all. No chance to change it using the environment variable ALSA_LIBS. And therefore this 32 bits executable tries to load the 64 bits libraries in /usr/lib/alsa-lib instead of the nice 32 bits libraries in /opt/lib32/usr/lib/alsa-lib.
One solution to this problem would be to set up a 32 bits chroot and launch Skype from there. But this is quite complicated for the end-user [2] and it isn't really comfortable to chroot and then to launch Skype (what if I want it to start when I log on KDE?).
Another solution would be to disassemble the executable, change the hard-coded path, and assemble it again. Hehe, just kidding, I'm not a great SM fan 
A last solution is to do some clever hacking including library preloading. This is, of course, the solution I have chosen.
Library preloading allows one to "replace" any libc function with your own one, without having to recompile the entire libc (glibc is just huge). It works by loading the libraries specified in the environment variable LD_PRELOAD against an app before loading any other libraries (such as the ones you can see when running ldd my_app), and it can therefore replace just any system call. Since it uses a simple environment variable, you can use it to modify a single app too: just run it with LD_PRELOAD=./mylib.so myapp and you're done!
This is interesting in our case because Skype use the standard dlopen() system call to load the Alsa libraries. When it tries to open the 64 bit libraries, you can see on the console the following messages:
/* Copyright (c) 2008, Schnouki <schnouki _at_ schnouki _dot_ net> Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include <string> #include <iostream> #include <dlfcn.h> // Thanks to Stephen Done! // http://sources.redhat.com/ml/libc-alpha/2001-05/msg00321.html #if defined(RTLD_NEXT) #define REAL_LIBC RTLD_NEXT #else #define REAL_LIBC ((void *) -1L) #endif void* dlopen(const char *file, int mode) { // Pointer to the original dlopen() static void* (*o_dlopen) (const char *file, int mode) = NULL; // string-ified file name std::string filename = file; // Get the original dlopen() if (o_dlopen == NULL) { o_dlopen = (void*(*)(const char *file, int mode)) dlsym(REAL_LIBC, "dlopen"); if (o_dlopen == NULL) { std::cerr << "libskypepulse: can't get a pointer to the original dlopen()" << std::endl; return NULL; } } // Do we need to do something? if (filename.find("/usr/lib/alsa-lib/") == 0) { filename = "/opt/lib32" + filename; return o_dlopen(filename.c_str(), mode); } else { return o_dlopen(file, mode); } }
So, if we just "patch" dlopen() so that Skype loads the libs in /opt/lib32/usr/lib/alsa-lib instead of the ones in /usr/lib/alsa-lib, it should be able to load the libs properly and then to use PulseAudio. Well, let's do it!
The code is actually pretty simple. We get a pointer to the "original" dlopen() from glibc and we test if the provided file name begins with /usr/lib/alsa-lib. If so we add /opt/lib32 at the beginning of this path, otherwise we don't touch it. And then we call the "original" dlopen() with this new file name.
This code was inspired by a piece of code I found thanks to Google; many thanks to its original author. I just adapted it to this situation and moved to C++ because I didn't want to do nasty char* hacks when the class string is so easy to use.
To use it: in a 32 bits environment [3], run g++ -fPIC -shared -o libskypepulse.so libskypepulse.cpp. Then run Skype with the following command: LD_PRELOAD=./libskypepulse.so skype. Open pavucontrol and test Skype (for example calling the Echo test): Skype should appear in the playback streams list. If it does, congratulations! your Skype is now PulseAudio-ready; if it doesn't, check the messages in the console and only blame yourself 
If you don't have access to a 32 bits environment or if you don't want to compile it yourself, my compiled library (as well as its source code) is available (included at the end of this post). I also made a little launcher (don't forget to adjust the library path), and ArchLinux users may use the package I put on the AUR (bin32-skype-pulse) which will install everything (all the needed 32 bits libraries) properly [4].
After you installed this library, you should modify your PulseAudio config: in /etc/pulseaudio/daemon.conf, add the following lines:
; For Skype, thanks to http://ubuntuforums.org/showthread.php?t=789578&highlight=skype+Pulse default-fragments = 8 default-fragment-size-msec = 5
This will improve the sound quality quite dramatically.
Voilà, that's enough for now. Please leave a comment if this post helped you or if you had any problem with the library 
Notes
[1] I know we could use Ekiga or any other SIP clients, but I don't want to deal with messy SIP configurations, NAT traversal, settings on both Windows and Linux, etc., when Skype is so easy to set up...
[2] Yeah, I care about others 
[3] A 32 bits chroot or another computer...
[4] And to install packages from AUR, yaourt is your friend!
Non disponible
Derniers commentaires