From e291642d1767c75300f510db0e5ca562e2574dc0 Mon Sep 17 00:00:00 2001 From: Marc-Andre Lureau (elmarco) Date: Wed, 23 May 2007 12:09:21 +0300 Subject: [PATCH] Monitor ALSA fds with a GstTask To poll ALSA fds, I decided to drop the custom GSource and use poll directly inside the task. I am not confident how to handle errors in the task. I don't know if RecMutex are necessary (it depends if ALSA plugins could call cb from different threads - ideally not) I am unsure about the (un)locking places (I am not an expert in MT environnement) To sum up... this is a draft patch :) --- ext/alsa/gstalsamixer.c | 261 +++++++++++++++++++---------------------------- ext/alsa/gstalsamixer.h | 5 +- 2 files changed, 108 insertions(+), 158 deletions(-) diff --git a/ext/alsa/gstalsamixer.c b/ext/alsa/gstalsamixer.c index b6a1f09..0c06c42 100644 --- a/ext/alsa/gstalsamixer.c +++ b/ext/alsa/gstalsamixer.c @@ -46,78 +46,8 @@ static void gst_alsa_mixer_update_option (GstAlsaMixer * mixer, GstAlsaMixerOptions * alsa_opts); static void gst_alsa_mixer_update_track (GstAlsaMixer * mixer, GstAlsaMixerTrack * alsa_track); - -static gboolean gst_alsa_mixer_prepare (GSource * source, gint * timeout); -static gboolean gst_alsa_mixer_check (GSource * source); -static gboolean gst_alsa_mixer_dispatch (GSource * source, GSourceFunc callback, - gpointer user_data); -static void gst_alsa_mixer_finalize (GSource * source); - -static GMainContext *main_context; - -typedef struct _GstAlsaMixerWatch GstAlsaMixerWatch; -struct _GstAlsaMixerWatch -{ - GSource source; - gint n_poll_fds; - GPollFD *poll_fds; -}; - -GSourceFuncs gst_alsa_mixer_watch_funcs = { - gst_alsa_mixer_prepare, - gst_alsa_mixer_check, - gst_alsa_mixer_dispatch, - gst_alsa_mixer_finalize -}; - -static gboolean -gst_alsa_mixer_prepare (GSource * source, gint * timeout) -{ - *timeout = -1; - return FALSE; -} - -static gboolean -gst_alsa_mixer_check (GSource * source) -{ - GstAlsaMixerWatch *watch = (GstAlsaMixerWatch *) source; - gint i; - gushort revents; - - /* Code based on snd_mixer_poll_descriptors_revents() adapted for glib */ - revents = 0; - for (i = 0; i < watch->n_poll_fds; ++i) { - revents |= watch->poll_fds[i].revents & (G_IO_IN | G_IO_ERR | G_IO_HUP); - } - - if (revents & G_IO_ERR || revents & G_IO_HUP) { - GST_DEBUG ("ALSA poll fds returned ERR or HUP"); - return FALSE; - } - - return (revents & G_IO_IN); -} - -static gboolean -gst_alsa_mixer_dispatch (GSource * source, GSourceFunc callback, - gpointer user_data) -{ - g_return_val_if_fail (callback, FALSE); - return (*callback) (user_data); -} - -static void -gst_alsa_mixer_finalize (GSource * source) -{ - GstAlsaMixerWatch *watch = (GstAlsaMixerWatch *) source; - - if (watch->poll_fds != NULL) { - g_free (watch->poll_fds); - watch->poll_fds = NULL; - } - - watch->n_poll_fds = 0; -} +static int gst_alsa_mixer_handle_callback (snd_mixer_t * handle, unsigned int mask, + snd_mixer_elem_t * elem); /* First some utils, then the mixer implementation */ static gboolean @@ -150,6 +80,8 @@ gst_alsa_mixer_open (GstAlsaMixer * mixer) goto error; } + snd_mixer_set_callback_private (mixer->handle, mixer); + snd_mixer_set_callback (mixer->handle, gst_alsa_mixer_handle_callback); /* now get the device name, any of this is not fatal */ g_free (mixer->cardname); @@ -245,65 +177,63 @@ gst_alsa_mixer_find_master_mixer (GstAlsaMixer * mixer, snd_mixer_t * handle) return NULL; } -static gboolean -gst_alsa_mixer_handle_source_callback (gpointer data) -{ - GstAlsaMixer *mixer = (GstAlsaMixer *) data; - - GST_LOG ("Source cb"); - snd_mixer_handle_events (mixer->handle); - - return TRUE; -} -static int -gst_alsa_mixer_handle_callback (snd_mixer_t * handle, unsigned int mask, - snd_mixer_elem_t * elem) +static void +gst_alsa_mixer_update (GstAlsaMixer *mixer, snd_mixer_elem_t * elem) { - GstAlsaMixer *mixer = - (GstAlsaMixer *) snd_mixer_get_callback_private (handle); GList *item; - GST_LOG ("ALSA cb"); - snd_mixer_handle_events (mixer->handle); + g_return_if_fail (mixer != NULL); + + g_static_rec_mutex_lock (mixer->rec_mutex); for (item = mixer->tracklist; item != NULL; item = item->next) { if (GST_IS_ALSA_MIXER_TRACK (item->data)) { - GstAlsaMixerTrack *item_alsa_track = GST_ALSA_MIXER_TRACK (item->data); + if (elem && (GST_ALSA_MIXER_TRACK (item->data)->element != elem)) + continue; - gst_alsa_mixer_update_track (mixer, item_alsa_track); - } else { - GstAlsaMixerOptions *item_alsa_options = - GST_ALSA_MIXER_OPTIONS (item->data); - gst_alsa_mixer_update_option (mixer, item_alsa_options); + gst_alsa_mixer_update_track (mixer, GST_ALSA_MIXER_TRACK (item->data)); + } else if (GST_IS_ALSA_MIXER_OPTIONS (item->data)) { + if (elem && (GST_ALSA_MIXER_OPTIONS (item->data)->element != elem)) + continue; + + gst_alsa_mixer_update_option (mixer, GST_ALSA_MIXER_OPTIONS (item->data)); } } - return 0; + g_static_rec_mutex_unlock (mixer->rec_mutex); } -int +static int gst_alsa_mixer_elem_handle_callback (snd_mixer_elem_t * elem, unsigned int mask) { GstAlsaMixer *mixer = (GstAlsaMixer *) snd_mixer_elem_get_callback_private (elem); - GList *item; GST_LOG ("ALSA elem cb"); - for (item = mixer->tracklist; item != NULL; item = item->next) { - if (GST_IS_ALSA_MIXER_TRACK (item->data)) { - if (GST_ALSA_MIXER_TRACK (item->data)->element != elem) - continue; + g_return_val_if_fail (mixer != NULL, 1); - gst_alsa_mixer_update_track (mixer, GST_ALSA_MIXER_TRACK (item->data)); - } else if (GST_IS_ALSA_MIXER_OPTIONS (item->data)) { - if (GST_ALSA_MIXER_OPTIONS (item->data)->element != elem) - continue; + gst_alsa_mixer_update (mixer, elem); - gst_alsa_mixer_update_option (mixer, GST_ALSA_MIXER_OPTIONS (item->data)); - } - } + return 0; +} + +static int +gst_alsa_mixer_handle_callback (snd_mixer_t * handle, unsigned int mask, + snd_mixer_elem_t * elem) +{ + GstAlsaMixer *mixer = + (GstAlsaMixer *) snd_mixer_get_callback_private (handle); + + GST_LOG ("ALSA cb"); + + g_return_val_if_fail (mixer != NULL, 1); + + /* Hopefully won't be call recursively and will handle pending elem events */ + snd_mixer_handle_events (mixer->handle); + + gst_alsa_mixer_update (mixer, elem); return 0; } @@ -313,8 +243,6 @@ gst_alsa_mixer_ensure_track_list (GstAlsaMixer * mixer) { gint i, count; snd_mixer_elem_t *element, *master; - struct pollfd *pfds; - GstAlsaMixerWatch *watch; GList *item; g_return_if_fail (mixer->handle != NULL); @@ -322,6 +250,8 @@ gst_alsa_mixer_ensure_track_list (GstAlsaMixer * mixer) if (mixer->tracklist) return; + g_static_rec_mutex_lock (mixer->rec_mutex); + master = gst_alsa_mixer_find_master_mixer (mixer, mixer->handle); count = snd_mixer_get_count (mixer->handle); @@ -428,64 +358,56 @@ gst_alsa_mixer_ensure_track_list (GstAlsaMixer * mixer) element = snd_mixer_elem_next (element); } - if (mixer->handle_source != NULL) - return; + for (item = mixer->tracklist; item != NULL; item = item->next) { + snd_mixer_elem_t *temp; - snd_mixer_set_callback_private (mixer->handle, mixer); - snd_mixer_set_callback (mixer->handle, gst_alsa_mixer_handle_callback); + if (GST_IS_ALSA_MIXER_OPTIONS (item->data)) + temp = GST_ALSA_MIXER_OPTIONS (item->data)->element; + else + temp = GST_ALSA_MIXER_TRACK (item->data)->element; - main_context = g_main_context_default (); + snd_mixer_elem_set_callback (temp, gst_alsa_mixer_elem_handle_callback); + snd_mixer_elem_set_callback_private (temp, mixer); + } - mixer->handle_source = - g_source_new (&gst_alsa_mixer_watch_funcs, sizeof (GstAlsaMixerWatch)); - g_return_if_fail (main_context != NULL && mixer->handle_source != NULL); + g_static_rec_mutex_unlock (mixer->rec_mutex); +} - count = snd_mixer_poll_descriptors_count (mixer->handle); - if (count < 1) { - GST_WARNING ("Poll descriptors count < 0: %d", count); - g_source_destroy (mixer->handle_source); - mixer->handle_source = NULL; +static void +task_monitor_alsa (gpointer data) +{ + struct pollfd *pfds; + unsigned int nfds; + unsigned short revents; + GstAlsaMixer *mixer = (GstAlsaMixer *)data; + + g_static_rec_mutex_lock (mixer->rec_mutex); + + nfds = snd_mixer_poll_descriptors_count (mixer->handle); + if (nfds <= 0) { + GST_ERROR ("snd_mixer_poll_descriptors_count <= 0: %d", nfds); + /* FIXME: sleep ? stop monitoring ? */ return; } - pfds = g_newa (struct pollfd, count); + pfds = g_newa (struct pollfd, nfds); + nfds = snd_mixer_poll_descriptors (mixer->handle, pfds, nfds); - watch = (GstAlsaMixerWatch *) mixer->handle_source; - watch->n_poll_fds = snd_mixer_poll_descriptors (mixer->handle, pfds, count);; - watch->poll_fds = g_new0 (GPollFD, count); + g_static_rec_mutex_unlock (mixer->rec_mutex); - for (i = 0; i < watch->n_poll_fds; ++i) { - watch->poll_fds[i].fd = pfds[i].fd; + GST_LOG ("task loop"); + poll (pfds, nfds, -1); - if (pfds[i].events & POLLIN) - watch->poll_fds[i].events |= G_IO_IN; - if (pfds[i].events & POLLOUT) - watch->poll_fds[i].events |= G_IO_OUT; - if (pfds[i].events & POLLPRI) - watch->poll_fds[i].events |= G_IO_PRI; + g_static_rec_mutex_lock (mixer->rec_mutex); - g_source_add_poll (mixer->handle_source, &watch->poll_fds[i]); + snd_mixer_poll_descriptors_revents (mixer->handle, pfds, nfds, &revents); + if (revents & POLLIN || revents & POLLPRI) { + snd_mixer_handle_events (mixer->handle); } - /* FIXME mixer has to be protected, since cb could be made from different thread ? */ - g_source_set_callback (mixer->handle_source, - gst_alsa_mixer_handle_source_callback, mixer, NULL); - g_source_attach (mixer->handle_source, main_context); - - for (item = mixer->tracklist; item != NULL; item = item->next) { - snd_mixer_elem_t *temp; - - if (GST_IS_ALSA_MIXER_OPTIONS (item->data)) - temp = GST_ALSA_MIXER_OPTIONS (item->data)->element; - else - temp = GST_ALSA_MIXER_TRACK (item->data)->element; - - snd_mixer_elem_set_callback (temp, gst_alsa_mixer_elem_handle_callback); - snd_mixer_elem_set_callback_private (temp, mixer); - } + g_static_rec_mutex_unlock (mixer->rec_mutex); } - /* API */ GstAlsaMixer * @@ -497,12 +419,25 @@ gst_alsa_mixer_new (const char *device, GstAlsaMixerDirection dir) ret = g_new0 (GstAlsaMixer, 1); + ret->rec_mutex = g_new (GStaticRecMutex, 1); + g_static_rec_mutex_init (ret->rec_mutex); + + ret->task_mutex = g_new (GStaticRecMutex, 1); + g_static_rec_mutex_init (ret->task_mutex); + + ret->task = gst_task_create (task_monitor_alsa, ret); + gst_task_set_lock (ret->task, ret->task_mutex); + ret->device = g_strdup (device); ret->dir = dir; if (!gst_alsa_mixer_open (ret)) goto error; + if (gst_task_start (ret->task) == FALSE) { + GST_WARNING ("Could not start alsamixer task"); + } + return ret; /* ERRORS */ @@ -523,11 +458,23 @@ gst_alsa_mixer_free (GstAlsaMixer * mixer) mixer->interface = NULL; } - if (mixer->handle_source) { - g_source_destroy (mixer->handle_source); - mixer->handle_source = NULL; + if (mixer->task) { + if (gst_task_join (mixer->task) == FALSE) { + GST_ERROR ("Cannot join alsamixer task"); + } + + gst_object_unref (mixer->task); + mixer->task = NULL; } + g_static_rec_mutex_free (mixer->task_mutex); + g_free (mixer->task_mutex); + mixer->task_mutex = NULL; + + g_static_rec_mutex_free (mixer->rec_mutex); + g_free (mixer->rec_mutex); + mixer->rec_mutex = NULL; + if (mixer->device) { g_free (mixer->device); mixer->device = NULL; diff --git a/ext/alsa/gstalsamixer.h b/ext/alsa/gstalsamixer.h index bf6457a..c319b2e 100644 --- a/ext/alsa/gstalsamixer.h +++ b/ext/alsa/gstalsamixer.h @@ -51,7 +51,10 @@ struct _GstAlsaMixer GList * tracklist; /* list of available tracks */ snd_mixer_t * handle; - GSource * handle_source; + + GstTask * task; + GStaticRecMutex * task_mutex; + GStaticRecMutex * rec_mutex; GstMixer * interface; gchar * device; -- 1.5.2.rc0.71.g4342-dirty