I reinstalled PulseAudio to try and resolve the plethora of problems I've been having with it (that and openSUSE 11 doesn't actually ship th esound daemon anymore, so if I want full audio support I have no choice but to get the pulse-esound-compat package working short of forking the distro and maintaining my own set of esound/gnome/etc packages which is not my idea of fun).
Unfortunately, this has resulted in countless hours of frustration. After having read and followed the instructions at http://www.pulseaudio.org/wiki/PerfectSetup, it turns out that is exactly how my system was already configured. So no help there.
I don't know what the deal is, but I constantly have problems with PulseAudio.
For example, earlier today I was having an IM conversation with a friend in Pidgin and it locked up. I restarted Pidgin, but I wasn't getting audio events anymore. *shrug* Oh well.
At some point later I navigated my firefox window over to youtube to link a friend to some video. No surprise here, I get no sound and in fact, the flash video pauses as soon as the sound was supposed to start (which is a few seconds into the video).
At this point I close firefox and decide to reload it, thinking maybe that will solve it. Nope, I was wrong. Instead, firefox hangs before any windows pop up. Great.
Since I got flamed last time I blogged about PulseAudio for issuing a `killall -9 pulseaudio` command (btw, pulseaudio appears to have a --kill command-line option), I figured I'd log out and log back in again instead... but this was not to be. Instead, my desktop locks up (presumably because of the logout.wav).
Ok... so I Ctrl+Alt+F1 to the console, login as root, `init 3; init 5`. Since my system is configured to auto-login, when X came back up it tried to log me in. Instead, I get a hang.
Good grief, I guess I have no option but to reboot so that's what I do.
In the interest of saving other people from this catastrophe, I grabbed libgnome out of svn and wrote up the following patch to prevent it from hanging whenever PulseAudio decides to misbehave (which it seems quite prone to do).
Index: libgnome/gnome-sound.c
===================================================================
--- libgnome/gnome-sound.c (revision 3750)
+++ libgnome/gnome-sound.c (working copy)
@@ -30,8 +30,12 @@
#include <stdio.h>
#include <stdlib.h>
+#include <sys/types.h>
#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
#include <time.h>
+#include <poll.h>
#ifdef HAVE_ESD
#include <esd.h>
@@ -413,6 +417,55 @@
}
#endif
+#ifdef HAVE_ESD
+static int
+send_all (int fd, const char *buf, size_t buflen)
+{
+ struct pollfd pfd[1];
+ size_t nwritten = 0;
+ int flags, rv;
+ ssize_t n;
+
+ if ((flags = fcntl (fd, F_GETFL)) == -1)
+ return -1;
+
+ fcntl (fd, F_SETFL, flags | O_NONBLOCK);
+
+ pfd[0].events = POLLOUT;
+ pfd[0].fd = fd;
+
+ do {
+ do {
+ pfd[0].revents = 0;
+ rv = poll (pfd, 1, 100);
+ } while (rv == -1 && errno == EINTR);
+
+ if (pfd[0].revents & POLLOUT) {
+ /* socket is ready for writing */
+ do {
+ n = write (fd, buf + nwritten, buflen - nwritten);
+ } while (n == -1 && errno == EINTR);
+
+ if (n > 0)
+ nwritten += n;
+ } else if (pfd[0].revents & (POLLERR | POLLHUP)) {
+ /* we /just/ lost the esd connection */
+ esd_close (fd);
+ fd = -1;
+ break;
+ } else if (rv == -1 && errno == EBADF) {
+ /* socket is bad */
+ fd = -1;
+ break;
+ }
+ } while (nwritten < buflen);
+
+ if (fd != -1 && flags != -1)
+ fcntl (fd, F_SETFL, flags);
+
+ return fd;
+}
+#endif
/**
* gnome_sound_sample_load:
@@ -469,21 +522,23 @@
* file, or event type, for later identification */
s->id = esd_sample_cache (gnome_sound_connection, s->format, s->rate,
size, (char *)sample_name);
- write (gnome_sound_connection, s->data, size);
- confirm = esd_confirm_sample_cache (gnome_sound_connection);
+
+ gnome_sound_connection = send_all (gnome_sound_connection, (const char *) s->data, size);
+ if (gnome_sound_connection != -1)
+ confirm = esd_confirm_sample_cache (gnome_sound_connection);
+
if (s->id <= 0 || confirm != s->id)
{
g_warning ("error caching sample <%d>!\n", s->id);
s->id = 0;
}
- g_free (s->data);
- s->data = NULL;
}
}
sample_id = s->id;
- g_free(s->data); g_free(s);
+ g_free(s->data);
+ g_free(s);
return sample_id;
#else
@@ -521,9 +576,12 @@
sample = gnome_sound_sample_load (buf, filename);
- esd_sample_play(gnome_sound_connection, sample);
- fsync (gnome_sound_connection);
- esd_sample_free(gnome_sound_connection, sample);
+ if (gnome_sound_connection != -1 && sample > 0)
+ {
+ esd_sample_play(gnome_sound_connection, sample);
+ fsync (gnome_sound_connection);
+ esd_sample_free(gnome_sound_connection, sample);
+ }
#endif
}
This patch should hopefully help prevent the typical gnome apps from hanging when trying to play audio, but I don't really have any surefire way of testing that it actually works.
Update 2008-07-10 10:42am I've now also written the following patch for libesd so that it doesn't hang for god knows how long when trying to open a connection to the sound server (in my case, pulse-esound-compat):
diff -up esound-0.2.38.orig/esdlib.c esound-0.2.38/esdlib.c
--- esound-0.2.38.orig/esdlib.c 2008-07-10 10:10:31.000000000 -0400
+++ esound-0.2.38/esdlib.c 2008-07-10 10:32:23.000000000 -0400
@@ -20,9 +20,11 @@
#include <arpa/inet.h>
#include <errno.h>
#include <sys/wait.h>
+#include <poll.h>
#include <sys/un.h>
+
#ifndef SUN_LEN
#define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \
+ strlen ((ptr)->sun_path))
@@ -408,6 +410,43 @@ int esd_resume( int esd )
return ok;
}
+static int
+connect_timeout (int sockfd, const struct sockaddr *serv_addr, size_t addrlen, int timeout)
+{
+ struct pollfd pfd[1];
+ int flags, rv;
+
+ if ((flags = fcntl (sockfd, F_GETFL)) == -1)
+ return -1;
+
+ fcntl (sockfd, F_SETFL, flags | O_NONBLOCK);
+
+ if (connect (sockfd, serv_addr, addrlen) == 0) {
+ fcntl (sockfd, F_SETFL, flags);
+ return 0;
+ }
+
+ if (errno != EINPROGRESS)
+ return -1;
+
+ pfd[0].fd = sockfd;
+ pfd[0].events = POLLIN | POLLOUT;
+
+ do {
+ pfd[0].revents = 0;
+ rv = poll (pfd, 1, timeout);
+ } while (rv == -1 && errno == EINTR);
+
+ if (pfd[0].revents & (POLLIN | POLLOUT)) {
+ /* success, we are now connected */
+ fcntl (sockfd, F_SETFL, flags);
+ return 0;
+ }
+
+ /* took too long / error connecting, either way: epic fail */
+ return -1;
+}
+
/**
* esd_connect_tcpip: make a TCPIP connection to ESD
* @host: specifies hostname and port to connect to as "hostname:port"
@@ -512,7 +551,7 @@ esd_connect_tcpip(const char *host)
goto error_out;
}
- if ( connect( socket_out, res->ai_addr, res->ai_addrlen ) != -1 )
+ if ( connect_timeout ( socket_out, res->ai_addr, res->ai_addrlen, 1000 ) != -1 )
break;
close ( socket_out );
@@ -596,9 +635,9 @@ esd_connect_tcpip(const char *host)
socket_addr.sin_family = AF_INET;
socket_addr.sin_port = htons( port );
- if ( connect( socket_out,
- (struct sockaddr *) &socket_addr,
- sizeof(struct sockaddr_in) ) < 0 )
+ if ( connect_timeout ( socket_out,
+ (struct sockaddr *) &socket_addr,
+ sizeof(struct sockaddr_in), 1000 ) < 0 )
goto error_out;
}
@@ -650,8 +689,7 @@ esd_connect_unix(void)
socket_unix.sun_family = AF_UNIX;
strncpy(socket_unix.sun_path, ESD_UNIX_SOCKET_NAME, sizeof(socket_unix.sun_path));
- if ( connect( socket_out,
- (struct sockaddr *) &socket_unix, SUN_LEN(&socket_unix) ) < 0 )
+ if ( connect_timeout ( socket_out, (struct sockaddr *) &socket_unix, SUN_LEN(&socket_unix), 100 ) < 0 )
goto error_out;
return socket_out;
Hopefully after these 2 patches get applied, my system at least won't become unusably hung when pulseaudio decides to crap out on me, but these patches doesn't solve the root of the problem :(