From c161e2930750c60715640a2c5a784890bd9a0a7c Mon Sep 17 00:00:00 2001 From: Marc-Andre Lureau Date: Sat, 21 Jul 2007 09:21:12 +0000 Subject: [PATCH] Fixes: #152864 Original commit message from CVS: * docs/libs/gst-plugins-base-libs-sections.txt: * ext/alsa/gstalsamixer.c: * ext/alsa/gstalsamixer.h: * ext/alsa/gstalsamixerelement.c: * ext/alsa/gstalsamixertrack.c: * gst-libs/gst/interfaces/mixer.c: * gst-libs/gst/interfaces/mixer.h: * gst-libs/gst/interfaces/mixeroptions.c: * gst-libs/gst/interfaces/mixeroptions.h: * gst-libs/gst/interfaces/mixertrack.c: * gst-libs/gst/interfaces/mixertrack.h: * tests/check/Makefile.am: * tests/check/libs/mixer.c: Patch By: Marc-Andre Lureau Fixes: #152864 Add support for notifying mixer changes on the message bus, and implement it in alsamixer. API: gst_mixer_get_mixer_flags API: gst_mixer_message_parse_mute_toggled API: gst_mixer_message_parse_record_toggled API: gst_mixer_message_parse_volume_changed API: gst_mixer_message_parse_option_changed API: GstMixerMessageType API: GstMixerFlags --- ChangeLog | 30 ++ docs/libs/gst-plugins-base-libs-sections.txt | 12 + ext/alsa/gstalsamixer.c | 288 +++++++++++++- ext/alsa/gstalsamixer.h | 23 +- ext/alsa/gstalsamixerelement.c | 17 +- ext/alsa/gstalsamixertrack.c | 21 + gst-libs/gst/interfaces/mixer.c | 394 ++++++++++++++++++- gst-libs/gst/interfaces/mixer.h | 71 +++- gst-libs/gst/interfaces/mixeroptions.c | 7 +- gst-libs/gst/interfaces/mixeroptions.h | 2 + gst-libs/gst/interfaces/mixertrack.c | 7 +- gst-libs/gst/interfaces/mixertrack.h | 4 +- tests/check/Makefile.am | 11 + tests/check/libs/mixer.c | 222 +++++++++++ 14 files changed, 1057 insertions(+), 52 deletions(-) create mode 100644 tests/check/libs/mixer.c diff --git a/ChangeLog b/ChangeLog index ac88c6e42d..4a109a68d0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,33 @@ +2007-07-20 Jan Schmidt + + * docs/libs/gst-plugins-base-libs-sections.txt: + * ext/alsa/gstalsamixer.c: + * ext/alsa/gstalsamixer.h: + * ext/alsa/gstalsamixerelement.c: + * ext/alsa/gstalsamixertrack.c: + * gst-libs/gst/interfaces/mixer.c: + * gst-libs/gst/interfaces/mixer.h: + * gst-libs/gst/interfaces/mixeroptions.c: + * gst-libs/gst/interfaces/mixeroptions.h: + * gst-libs/gst/interfaces/mixertrack.c: + * gst-libs/gst/interfaces/mixertrack.h: + * tests/check/Makefile.am: + * tests/check/libs/mixer.c: + + Patch By: Marc-Andre Lureau + Fixes: #152864 + + Add support for notifying mixer changes on the message bus, and + implement it in alsamixer. + + API: gst_mixer_get_mixer_flags + API: gst_mixer_message_parse_mute_toggled + API: gst_mixer_message_parse_record_toggled + API: gst_mixer_message_parse_volume_changed + API: gst_mixer_message_parse_option_changed + API: GstMixerMessageType + API: GstMixerFlags + 2007-07-20 Michael Smith * sys/xvimage/xvimagesink.c: (gst_xvimagesink_xvimage_new), diff --git a/docs/libs/gst-plugins-base-libs-sections.txt b/docs/libs/gst-plugins-base-libs-sections.txt index eb86c3bd77..4909b54844 100644 --- a/docs/libs/gst-plugins-base-libs-sections.txt +++ b/docs/libs/gst-plugins-base-libs-sections.txt @@ -288,6 +288,8 @@ gst_color_balance_channel_get_type gst/interfaces/mixer.h GstMixer GstMixerType +GstMixerFlags +GstMixerMessageType gst_mixer_list_tracks gst_mixer_get_volume @@ -303,6 +305,14 @@ gst_mixer_volume_changed gst_mixer_option_changed gst_mixer_get_option +gst_mixer_get_mixer_flags + +gst_mixer_message_get_type +gst_mixer_message_parse_mute_toggled +gst_mixer_message_parse_option_changed +gst_mixer_message_parse_record_toggled +gst_mixer_message_parse_volume_changed + GstMixerClass GST_TYPE_MIXER @@ -315,6 +325,8 @@ GST_IS_MIXER GST_IS_MIXER_CLASS gst_mixer_get_type gst_mixer_type_get_type +gst_mixer_flags_get_type +gst_mixer_message_type_get_type
diff --git a/ext/alsa/gstalsamixer.c b/ext/alsa/gstalsamixer.c index 931d98f346..16fee7b708 100644 --- a/ext/alsa/gstalsamixer.c +++ b/ext/alsa/gstalsamixer.c @@ -42,6 +42,12 @@ #include "gstalsamixer.h" +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 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 @@ -74,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); @@ -169,17 +177,80 @@ gst_alsa_mixer_find_master_mixer (GstAlsaMixer * mixer, snd_mixer_t * handle) return NULL; } +static void +gst_alsa_mixer_update (GstAlsaMixer * mixer, snd_mixer_elem_t * elem) +{ + GList *item; + + 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)) { + if (elem && (GST_ALSA_MIXER_TRACK (item->data)->element != elem)) + continue; + + 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)); + } + } + + g_static_rec_mutex_unlock (mixer->rec_mutex); +} + +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); + + GST_LOG ("ALSA elem cb"); + + g_return_val_if_fail (mixer != NULL, 1); + + gst_alsa_mixer_update (mixer, elem); + + 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; +} + static void gst_alsa_mixer_ensure_track_list (GstAlsaMixer * mixer) { gint i, count; snd_mixer_elem_t *element, *master; + GList *item; g_return_if_fail (mixer->handle != NULL); 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); @@ -287,8 +358,62 @@ gst_alsa_mixer_ensure_track_list (GstAlsaMixer * mixer) element = snd_mixer_elem_next (element); } + + 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); } +static void +task_monitor_alsa (gpointer data) +{ + struct pollfd *pfds; + unsigned int nfds, rnfds; + 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, nfds + 1); + rnfds = snd_mixer_poll_descriptors (mixer->handle, pfds, nfds); + g_assert (rnfds <= nfds); + + pfds[rnfds].fd = mixer->pfd[0]; + pfds[rnfds].events = POLLIN | POLLPRI | POLLHUP | POLLERR; + pfds[rnfds].revents = 0; + + g_static_rec_mutex_unlock (mixer->rec_mutex); + + GST_LOG ("task loop"); + poll (pfds, rnfds + 1, -1); + + g_static_rec_mutex_lock (mixer->rec_mutex); + + snd_mixer_poll_descriptors_revents (mixer->handle, pfds, nfds, &revents); + if (revents & POLLIN || revents & POLLPRI) { + GST_DEBUG ("Handling events"); + snd_mixer_handle_events (mixer->handle); + } + + g_static_rec_mutex_unlock (mixer->rec_mutex); +} /* API */ @@ -301,12 +426,28 @@ gst_alsa_mixer_new (const char *device, GstAlsaMixerDirection dir) ret = g_new0 (GstAlsaMixer, 1); + if (pipe (ret->pfd) == -1) + goto error; + + 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 */ @@ -322,6 +463,40 @@ gst_alsa_mixer_free (GstAlsaMixer * mixer) { g_return_if_fail (mixer != NULL); + if (mixer->task) { + if (write (mixer->pfd[1], "stop", 5) <= 0) { + GST_ERROR ("Cannot send " "stop" " to alsamixer task"); + close (mixer->pfd[1]); + mixer->pfd[1] = -1; + } + + 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; + + if (mixer->pfd[0] > 0) { + close (mixer->pfd[0]); + mixer->pfd[0] = -1; + } + + if (mixer->pfd[1] > 0) { + close (mixer->pfd[1]); + mixer->pfd[1] = -1; + } + + if (mixer->interface) { + g_object_unref (G_OBJECT (mixer->interface)); + mixer->interface = NULL; + } + if (mixer->device) { g_free (mixer->device); mixer->device = NULL; @@ -343,6 +518,10 @@ gst_alsa_mixer_free (GstAlsaMixer * mixer) mixer->handle = NULL; } + g_static_rec_mutex_free (mixer->rec_mutex); + g_free (mixer->rec_mutex); + mixer->rec_mutex = NULL; + g_free (mixer); } @@ -356,16 +535,6 @@ gst_alsa_mixer_list_tracks (GstAlsaMixer * mixer) return (const GList *) mixer->tracklist; } -static void -gst_alsa_mixer_update (GstAlsaMixer * mixer, GstAlsaMixerTrack * alsa_track) -{ - snd_mixer_handle_events (mixer->handle); - - if (alsa_track) - gst_alsa_mixer_track_update (alsa_track); -} - - void gst_alsa_mixer_get_volume (GstAlsaMixer * mixer, GstMixerTrack * track, gint * volumes) @@ -375,7 +544,7 @@ gst_alsa_mixer_get_volume (GstAlsaMixer * mixer, GstMixerTrack * track, g_return_if_fail (mixer->handle != NULL); - gst_alsa_mixer_update (mixer, alsa_track); + gst_alsa_mixer_track_update (alsa_track); if (track->flags & GST_MIXER_TRACK_OUTPUT) { /* return playback volume */ @@ -420,7 +589,7 @@ gst_alsa_mixer_set_volume (GstAlsaMixer * mixer, GstMixerTrack * track, g_return_if_fail (mixer->handle != NULL); - gst_alsa_mixer_update (mixer, alsa_track); + gst_alsa_mixer_track_update (alsa_track); if (track->flags & GST_MIXER_TRACK_OUTPUT) { @@ -461,7 +630,7 @@ gst_alsa_mixer_set_mute (GstAlsaMixer * mixer, GstMixerTrack * track, g_return_if_fail (mixer->handle != NULL); - gst_alsa_mixer_update (mixer, alsa_track); + gst_alsa_mixer_track_update (alsa_track); if (!!(mute) == !!(track->flags & GST_MIXER_TRACK_MUTE)) return; @@ -509,7 +678,7 @@ gst_alsa_mixer_set_record (GstAlsaMixer * mixer, g_return_if_fail (mixer->handle != NULL); - gst_alsa_mixer_update (mixer, alsa_track); + gst_alsa_mixer_track_update (alsa_track); if (!!(record) == !!(track->flags & GST_MIXER_TRACK_RECORD)) return; @@ -536,7 +705,7 @@ gst_alsa_mixer_set_record (GstAlsaMixer * mixer, if (item_alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_CSWITCH_EXCL && item_alsa_track->capture_group == alsa_track->capture_group) { - gst_alsa_mixer_update (mixer, item_alsa_track); + gst_alsa_mixer_track_update (item_alsa_track); } } } @@ -562,8 +731,6 @@ gst_alsa_mixer_set_option (GstAlsaMixer * mixer, g_return_if_fail (mixer->handle != NULL); - gst_alsa_mixer_update (mixer, NULL); - for (item = opts->values; item != NULL; item = item->next, n++) { if (!strcmp (item->data, value)) { idx = n; @@ -585,11 +752,94 @@ gst_alsa_mixer_get_option (GstAlsaMixer * mixer, GstMixerOptions * opts) g_return_val_if_fail (mixer->handle != NULL, NULL); - gst_alsa_mixer_update (mixer, NULL); - ret = snd_mixer_selem_get_enum_item (alsa_opts->element, 0, &idx); if (ret == 0) return g_list_nth_data (opts->values, idx); else return snd_strerror (ret); /* feeble attempt at error handling */ } + +GstMixerFlags +gst_alsa_mixer_get_mixer_flags (GstAlsaMixer * mixer) +{ + g_return_val_if_fail (mixer != NULL, GST_MIXER_FLAG_NONE); + + return GST_MIXER_FLAG_AUTO_NOTIFICATIONS; +} + +static void +gst_alsa_mixer_update_option (GstAlsaMixer * mixer, + GstAlsaMixerOptions * alsa_opts) +{ + gint ret; + guint idx; + /* const */ gchar *option; + + if (mixer->interface == NULL) { + GST_WARNING ("Cannot send update notifications, no GstMixer * given"); + return; + } + + ret = snd_mixer_selem_get_enum_item (alsa_opts->element, 0, &idx); + if (ret == 0) { + option = g_list_nth_data (GST_MIXER_OPTIONS (alsa_opts)->values, idx); + gst_mixer_option_changed (mixer->interface, GST_MIXER_OPTIONS (alsa_opts), + option); + } +} + +static void +gst_alsa_mixer_update_track (GstAlsaMixer * mixer, + GstAlsaMixerTrack * alsa_track) +{ + GstMixerTrack *track = (GstMixerTrack *) alsa_track; + gboolean old_mute; + gboolean old_record; + gint i, n_channels; + gint *old_volumes; + + GST_DEBUG ("Updating track %" GST_PTR_FORMAT, alsa_track); + + if (mixer->interface == NULL) { + GST_WARNING ("Cannot send update notifications, no GstMixer * given"); + return; + } + + old_mute = !!(GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_MUTE)); + old_record = !!(GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_RECORD)); + old_volumes = g_new (gint, track->num_channels); + n_channels = track->num_channels; + memcpy (old_volumes, alsa_track->volumes, + sizeof (gint) * track->num_channels); + + gst_alsa_mixer_track_update (alsa_track); + + if (old_record != + !!(GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_RECORD))) { + gst_mixer_record_toggled (mixer->interface, track, + !!GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_RECORD)); + } + if (old_mute != !!(GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_MUTE))) { + gst_mixer_mute_toggled (mixer->interface, track, + !!GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_MUTE)); + } + + n_channels = MIN (n_channels, track->num_channels); + for (i = 0; i < n_channels; i++) { + if (old_volumes[i] != alsa_track->volumes[i]) { + gst_mixer_volume_changed (mixer->interface, track, alsa_track->volumes); + break; + } + } + g_free (old_volumes); +} + +/* utility function for gstalsamixerelement to set the interface */ +void +_gst_alsa_mixer_set_interface (GstAlsaMixer * mixer, GstMixer * interface) +{ + g_return_if_fail (mixer != NULL && mixer->interface == NULL); + g_return_if_fail (interface != NULL); + + mixer->interface = g_object_ref (G_OBJECT (interface)); +} diff --git a/ext/alsa/gstalsamixer.h b/ext/alsa/gstalsamixer.h index 6c9eb1e7ca..ccc3784574 100644 --- a/ext/alsa/gstalsamixer.h +++ b/ext/alsa/gstalsamixer.h @@ -52,6 +52,13 @@ struct _GstAlsaMixer snd_mixer_t * handle; + GstTask * task; + GStaticRecMutex * task_mutex; + GStaticRecMutex * rec_mutex; + + int pfd[2]; + + GstMixer * interface; gchar * device; gchar * cardname; @@ -81,7 +88,9 @@ void gst_alsa_mixer_set_option (GstAlsaMixer * mixer, gchar * value); const gchar* gst_alsa_mixer_get_option (GstAlsaMixer * mixer, GstMixerOptions * opts); - +void _gst_alsa_mixer_set_interface (GstAlsaMixer * mixer, + GstMixer * interface); +GstMixerFlags gst_alsa_mixer_get_mixer_flags (GstAlsaMixer *mixer); #define GST_IMPLEMENT_ALSA_MIXER_METHODS(Type, interface_as_function) \ static gboolean \ @@ -174,6 +183,17 @@ interface_as_function ## _get_option (GstMixer * mixer, GstMixerOptions * opts) return gst_alsa_mixer_get_option (this->mixer, opts); \ } \ \ +static GstMixerFlags \ +interface_as_function ## _get_mixer_flags (GstMixer * mixer) \ +{ \ + Type *this = (Type*) mixer; \ + \ + g_return_val_if_fail (this != NULL, GST_MIXER_FLAG_NONE); \ + g_return_val_if_fail (this->mixer != NULL, GST_MIXER_FLAG_NONE); \ + \ + return gst_alsa_mixer_get_mixer_flags (this->mixer); \ +} \ + \ static void \ interface_as_function ## _interface_init (GstMixerClass * klass) \ { \ @@ -187,6 +207,7 @@ interface_as_function ## _interface_init (GstMixerClass * klass) klass->set_record = interface_as_function ## _set_record; \ klass->set_option = interface_as_function ## _set_option; \ klass->get_option = interface_as_function ## _get_option; \ + klass->get_mixer_flags = interface_as_function ## _get_mixer_flags; \ } diff --git a/ext/alsa/gstalsamixerelement.c b/ext/alsa/gstalsamixerelement.c index 75d5b4a49c..f96e0aba52 100644 --- a/ext/alsa/gstalsamixerelement.c +++ b/ext/alsa/gstalsamixerelement.c @@ -44,22 +44,22 @@ GST_ELEMENT_DETAILS ("Alsa mixer", static void gst_alsa_mixer_element_init_interfaces (GType type); GST_BOILERPLATE_FULL (GstAlsaMixerElement, gst_alsa_mixer_element, - GstElement, GST_TYPE_ELEMENT, gst_alsa_mixer_element_init_interfaces) + GstElement, GST_TYPE_ELEMENT, gst_alsa_mixer_element_init_interfaces); /* massive macro that takes care of all the GstMixer stuff */ - GST_IMPLEMENT_ALSA_MIXER_METHODS (GstAlsaMixerElement, gst_alsa_mixer_element); +GST_IMPLEMENT_ALSA_MIXER_METHODS (GstAlsaMixerElement, gst_alsa_mixer_element); - static void gst_alsa_mixer_element_get_property (GObject * object, +static void gst_alsa_mixer_element_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); - static void gst_alsa_mixer_element_set_property (GObject * object, +static void gst_alsa_mixer_element_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); - static void gst_alsa_mixer_element_finalize (GObject * object); +static void gst_alsa_mixer_element_finalize (GObject * object); - static GstStateChangeReturn gst_alsa_mixer_element_change_state (GstElement +static GstStateChangeReturn gst_alsa_mixer_element_change_state (GstElement * element, GstStateChange transition); - static gboolean - gst_alsa_mixer_element_interface_supported (GstAlsaMixerElement * this, +static gboolean +gst_alsa_mixer_element_interface_supported (GstAlsaMixerElement * this, GType interface_type) { if (interface_type == GST_TYPE_MIXER) { @@ -214,6 +214,7 @@ gst_alsa_mixer_element_change_state (GstElement * element, this->mixer = gst_alsa_mixer_new (this->device, GST_ALSA_MIXER_ALL); if (!this->mixer) goto open_failed; + _gst_alsa_mixer_set_interface (this->mixer, GST_MIXER (element)); } break; default: diff --git a/ext/alsa/gstalsamixertrack.c b/ext/alsa/gstalsamixertrack.c index d7b8a931b9..90f3fee504 100644 --- a/ext/alsa/gstalsamixertrack.c +++ b/ext/alsa/gstalsamixertrack.c @@ -47,6 +47,7 @@ gst_alsa_mixer_track_get_type (void) sizeof (GstAlsaMixerTrack), 0, (GInstanceInitFunc) gst_alsa_mixer_track_init, + 0 }; track_type = @@ -239,6 +240,26 @@ gst_alsa_mixer_track_update (GstAlsaMixerTrack * alsa_track) gint i; gint audible = !(track->flags & GST_MIXER_TRACK_MUTE); + if (alsa_track_has_cap (alsa_track, GST_ALSA_MIXER_TRACK_PVOLUME)) { + /* update playback volume */ + for (i = 0; i < track->num_channels; i++) { + long vol = 0; + + snd_mixer_selem_get_playback_volume (alsa_track->element, i, &vol); + alsa_track->volumes[i] = (gint) vol; + } + } + + if (alsa_track_has_cap (alsa_track, GST_ALSA_MIXER_TRACK_CVOLUME)) { + /* update capture volume */ + for (i = 0; i < track->num_channels; i++) { + long vol = 0; + + snd_mixer_selem_get_capture_volume (alsa_track->element, i, &vol); + alsa_track->volumes[i] = (gint) vol; + } + } + /* Any updates in flags? */ if (alsa_track_has_cap (alsa_track, GST_ALSA_MIXER_TRACK_PSWITCH)) { int v = 0; diff --git a/gst-libs/gst/interfaces/mixer.c b/gst-libs/gst/interfaces/mixer.c index 116bab23bb..246b04e2c8 100644 --- a/gst-libs/gst/interfaces/mixer.c +++ b/gst-libs/gst/interfaces/mixer.c @@ -26,11 +26,14 @@ #include "mixer.h" #include "interfaces-marshal.h" +#define GST_MIXER_MESSAGE_NAME "gst-mixer-message" + /** * SECTION:gstmixer * @short_description: Interface for elements that provide mixer operations */ +#ifndef GST_DISABLE_DEPRECATED enum { SIGNAL_MUTE_TOGGLED, @@ -40,10 +43,12 @@ enum LAST_SIGNAL }; -static void gst_mixer_class_init (GstMixerClass * klass); - static guint gst_mixer_signals[LAST_SIGNAL] = { 0 }; +#endif + +static void gst_mixer_class_init (GstMixerClass * klass); + GType gst_mixer_get_type (void) { @@ -74,8 +79,10 @@ gst_mixer_get_type (void) static void gst_mixer_class_init (GstMixerClass * klass) { +#ifndef GST_DISABLE_DEPRECATED static gboolean initialized = FALSE; + /* signals (deprecated) */ if (!initialized) { gst_mixer_signals[SIGNAL_RECORD_TOGGLED] = g_signal_new ("record-toggled", @@ -108,6 +115,7 @@ gst_mixer_class_init (GstMixerClass * klass) initialized = TRUE; } +#endif klass->mixer_type = GST_MIXER_SOFTWARE; @@ -313,53 +321,411 @@ gst_mixer_get_option (GstMixer * mixer, GstMixerOptions * opts) return NULL; } +/** + * gst_mixer_get_mixer_flags: + * @mixer: The #GstMixer implementation + * + * Get the set of supported flags for this mixer implementation. + * + * Returns: A set of or-ed GstMixerFlags for supported features. + */ +GstMixerFlags +gst_mixer_get_mixer_flags (GstMixer * mixer) +{ + GstMixerClass *klass; + + g_return_val_if_fail (mixer != NULL, FALSE); + klass = GST_MIXER_GET_CLASS (mixer); + + if (klass->get_mixer_flags) { + return klass->get_mixer_flags (mixer); + } + return GST_MIXER_FLAG_NONE; +} + +/** + * gst_mixer_mute_toggled: + * + * @mixer: the #GstMixer (a #GstElement) that owns the track + * @track: the GstMixerTrack that has change mute state. + * @mute: the new state of the mute flag on the track + * + * This function is called by the mixer implementation to produce + * a notification message on the bus indicating that the given track + * has changed mute state. + * + * This function only works for GstElements that are implementing the + * GstMixer interface, and the element needs to have been provided a bus. + */ void gst_mixer_mute_toggled (GstMixer * mixer, GstMixerTrack * track, gboolean mute) { + GstStructure *s; + GstMessage *m; + g_return_if_fail (mixer != NULL); + g_return_if_fail (GST_IS_ELEMENT (mixer)); g_return_if_fail (track != NULL); - g_signal_emit (G_OBJECT (mixer), - gst_mixer_signals[SIGNAL_MUTE_TOGGLED], 0, track, mute); + s = gst_structure_new (GST_MIXER_MESSAGE_NAME, + "type", G_TYPE_STRING, "mute-toggled", + "track", GST_TYPE_MIXER_TRACK, track, "mute", G_TYPE_BOOLEAN, mute, NULL); - g_signal_emit_by_name (G_OBJECT (track), "mute_toggled", mute); + m = gst_message_new_element (GST_OBJECT (mixer), s); + if (gst_element_post_message (GST_ELEMENT (mixer), m) == FALSE) { + GST_WARNING ("This element has no bus, therefore no message sent!"); + } } +/** + * gst_mixer_record_toggled: + * + * @mixer: the #GstMixer (a #GstElement) that owns the track + * @track: the GstMixerTrack that has changed recording state. + * @record: the new state of the record flag on the track + * + * This function is called by the mixer implementation to produce + * a notification message on the bus indicating that the given track + * has changed recording state. + * + * This function only works for GstElements that are implementing the + * GstMixer interface, and the element needs to have been provided a bus. + */ void gst_mixer_record_toggled (GstMixer * mixer, GstMixerTrack * track, gboolean record) { + GstStructure *s; + GstMessage *m; + g_return_if_fail (mixer != NULL); + g_return_if_fail (GST_IS_ELEMENT (mixer)); g_return_if_fail (track != NULL); - g_signal_emit (G_OBJECT (mixer), - gst_mixer_signals[SIGNAL_RECORD_TOGGLED], 0, track, record); + s = gst_structure_new (GST_MIXER_MESSAGE_NAME, + "type", G_TYPE_STRING, "record-toggled", + "track", GST_TYPE_MIXER_TRACK, track, + "record", G_TYPE_BOOLEAN, record, NULL); - g_signal_emit_by_name (G_OBJECT (track), "record_toggled", record); + m = gst_message_new_element (GST_OBJECT (mixer), s); + if (gst_element_post_message (GST_ELEMENT (mixer), m) == FALSE) { + GST_WARNING ("This element has no bus, therefore no message sent!"); + } } +/** + * gst_mixer_volume_changed: + * + * @mixer: the #GstMixer (a #GstElement) that owns the track + * @track: the GstMixerTrack that has changed. + * @volumes: Array of volume values, one per channel on the mixer track. + * + * This function is called by the mixer implementation to produce + * a notification message on the bus indicating that the volume(s) for the + * given track have changed. + * + * This function only works for GstElements that are implementing the + * GstMixer interface, and the element needs to have been provided a bus. + */ void gst_mixer_volume_changed (GstMixer * mixer, GstMixerTrack * track, gint * volumes) { + GstStructure *s; + GstMessage *m; + GValue l = { 0, }; + GValue v = { 0, }; + gint i; + g_return_if_fail (mixer != NULL); + g_return_if_fail (GST_IS_ELEMENT (mixer)); g_return_if_fail (track != NULL); - g_signal_emit (G_OBJECT (mixer), - gst_mixer_signals[SIGNAL_VOLUME_CHANGED], 0, track, volumes); + s = gst_structure_new (GST_MIXER_MESSAGE_NAME, + "type", G_TYPE_STRING, "volume-changed", + "track", GST_TYPE_MIXER_TRACK, track, NULL); - g_signal_emit_by_name (G_OBJECT (track), "volume_changed", volumes); + g_value_init (&l, GST_TYPE_ARRAY); + + g_value_init (&v, G_TYPE_INT); + + /* FIXME 0.11: pass track->num_channels to the function */ + for (i = 0; i < track->num_channels; ++i) { + g_value_set_int (&v, volumes[i]); + gst_value_array_append_value (&l, &v); + } + g_value_unset (&v); + + gst_structure_set_value (s, "volumes", &l); + g_value_unset (&l); + + m = gst_message_new_element (GST_OBJECT (mixer), s); + if (gst_element_post_message (GST_ELEMENT (mixer), m) == FALSE) { + GST_WARNING ("This element has no bus, therefore no message sent!"); + } } +/** + * gst_mixer_option_changed: + * + * @mixer: the #GstMixer (a #GstElement) that owns the options + * @opts: the GstMixerOptions that has changed value. + * @value: the new value of the GstMixerOptions. + * + * This function is called by the mixer implementation to produce + * a notification message on the bus indicating that the given options + * object has changed state. + * + * This function only works for GstElements that are implementing the + * GstMixer interface, and the element needs to have been provided a bus. + */ void gst_mixer_option_changed (GstMixer * mixer, GstMixerOptions * opts, gchar * value) { + GstStructure *s; + GstMessage *m; + g_return_if_fail (mixer != NULL); + g_return_if_fail (GST_IS_ELEMENT (mixer)); g_return_if_fail (opts != NULL); - g_signal_emit (G_OBJECT (mixer), - gst_mixer_signals[SIGNAL_OPTION_CHANGED], 0, opts, value); + s = gst_structure_new (GST_MIXER_MESSAGE_NAME, + "type", G_TYPE_STRING, "option-changed", + "options", GST_TYPE_MIXER_OPTIONS, opts, + "value", G_TYPE_STRING, value, NULL); - g_signal_emit_by_name (G_OBJECT (opts), "value_changed", value); + m = gst_message_new_element (GST_OBJECT (mixer), s); + if (gst_element_post_message (GST_ELEMENT (mixer), m) == FALSE) { + GST_WARNING ("This element has no bus, therefore no message sent!"); + } +} + +static gboolean +gst_mixer_message_is_mixer_message (GstMessage * message) +{ + const GstStructure *s; + + if (message == NULL) + return FALSE; + if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT) + return FALSE; + + s = gst_message_get_structure (message); + return gst_structure_has_name (s, GST_MIXER_MESSAGE_NAME); +} + +/** + * gst_mixer_message_get_type: + * @message: A GstMessage to inspect. + * + * Check a bus message to see if it is a GstMixer notification + * message and return the GstMixerMessageType identifying which + * type of notification it is. + * + * Returns: The type of the GstMixerMessage, or GST_MIXER_MESSAGE_NONE + * if the message is not a GstMixer notification. + * + * Since: 0.10.14 + */ +GstMixerMessageType +gst_mixer_message_get_type (GstMessage * message) +{ + const GstStructure *s; + const gchar *m_type; + + if (!gst_mixer_message_is_mixer_message (message)) + return GST_MIXER_MESSAGE_INVALID; + + s = gst_message_get_structure (message); + m_type = gst_structure_get_string (s, "type"); + g_return_val_if_fail (m_type != NULL, GST_MIXER_MESSAGE_INVALID); + + if (g_str_equal (m_type, "mute-toggled")) + return GST_MIXER_MESSAGE_MUTE_TOGGLED; + else if (g_str_equal (m_type, "record-toggled")) + return GST_MIXER_MESSAGE_RECORD_TOGGLED; + else if (g_str_equal (m_type, "volume-changed")) + return GST_MIXER_MESSAGE_VOLUME_CHANGED; + else if (g_str_equal (m_type, "option-changed")) + return GST_MIXER_MESSAGE_OPTION_CHANGED; + + return GST_MIXER_MESSAGE_INVALID; +} + +/** + * gst_mixer_message_parse_mute_toggled: + * @message: A mute-toggled change notification message. + * @track: Pointer to hold a GstMixerTrack object, or NULL. + * @mute: A pointer to a gboolean variable, or NULL. + * + * Extracts the contents of a mute-toggled bus message. Reads + * the GstMixerTrack that has changed, and the new value of the mute + * flag. + * + * The GstMixerTrack remains valid until the message is freed. + * + * Since: 0.10.14 + */ +void +gst_mixer_message_parse_mute_toggled (GstMessage * message, + GstMixerTrack ** track, gboolean * mute) +{ + const GstStructure *s; + const gchar *m_type; + + g_return_if_fail (gst_mixer_message_is_mixer_message (message)); + s = gst_message_get_structure (message); + + m_type = gst_structure_get_string (s, "type"); + g_return_if_fail (m_type == NULL || g_str_equal (m_type, "mute-toggled")); + + if (track) { + const GValue *v = gst_structure_get_value (s, "track"); + + g_return_if_fail (v != NULL); + *track = (GstMixerTrack *) g_value_get_object (v); + g_return_if_fail (GST_IS_MIXER_TRACK (*track)); + } + + if (mute) + g_return_if_fail (gst_structure_get_boolean (s, "mute", mute)); +} + +/** + * gst_mixer_message_parse_record_toggled: + * @message: A record-toggled change notification message. + * @track: Pointer to hold a GstMixerTrack object, or NULL. + * @record: A pointer to a gboolean variable, or NULL. + * + * Extracts the contents of a record-toggled bus message. Reads + * the GstMixerTrack that has changed, and the new value of the + * recording flag. + * + * The GstMixerTrack remains valid until the message is freed. + * + * Since: 0.10.14 + */ +void +gst_mixer_message_parse_record_toggled (GstMessage * message, + GstMixerTrack ** track, gboolean * record) +{ + const GstStructure *s; + const gchar *m_type; + + g_return_if_fail (gst_mixer_message_is_mixer_message (message)); + s = gst_message_get_structure (message); + + m_type = gst_structure_get_string (s, "type"); + g_return_if_fail (m_type == NULL || g_str_equal (m_type, "record-toggled")); + + if (track) { + const GValue *v = gst_structure_get_value (s, "track"); + + g_return_if_fail (v != NULL); + *track = (GstMixerTrack *) g_value_get_object (v); + g_return_if_fail (GST_IS_MIXER_TRACK (*track)); + } + + if (record) + g_return_if_fail (gst_structure_get_boolean (s, "record", record)); +} + +/** + * gst_mixer_message_parse_volume_changed: + * @message: A volume-changed change notification message. + * @track: Pointer to hold a GstMixerTrack object, or NULL. + * @volumes: A pointer to receive an array of gint values, or NULL. + * @num_channels: Result location to receive the number of channels, or NULL. + * + * Parses a volume-changed notification message and extracts the track object + * it refers to, as well as an array of volumes and the size of the volumes array. + * + * The track object remains valid until the message is freed. + * + * The caller must free the array returned in the volumes parameter using g_free + * when they are done with it. + * + * Since: 0.10.14 + */ +void +gst_mixer_message_parse_volume_changed (GstMessage * message, + GstMixerTrack ** track, gint ** volumes, gint * num_channels) +{ + const GstStructure *s; + const gchar *m_type; + + g_return_if_fail (gst_mixer_message_is_mixer_message (message)); + s = gst_message_get_structure (message); + + m_type = gst_structure_get_string (s, "type"); + g_return_if_fail (m_type == NULL || g_str_equal (m_type, "volume-changed")); + + if (track) { + const GValue *v = gst_structure_get_value (s, "track"); + + g_return_if_fail (v != NULL); + *track = (GstMixerTrack *) g_value_get_object (v); + g_return_if_fail (GST_IS_MIXER_TRACK (*track)); + } + + if (volumes || num_channels) { + gint n_chans, i; + const GValue *v = gst_structure_get_value (s, "volumes"); + + g_return_if_fail (v != NULL); + g_return_if_fail (GST_VALUE_HOLDS_ARRAY (v)); + + n_chans = gst_value_array_get_size (v); + if (num_channels) + *num_channels = n_chans; + + if (volumes) { + *volumes = g_new (gint, n_chans); + for (i = 0; i < n_chans; i++) { + const GValue *e = gst_value_array_get_value (v, i); + + g_return_if_fail (e != NULL && G_VALUE_HOLDS_INT (e)); + (*volumes)[i] = g_value_get_int (e); + } + } + } +} + +/** + * gst_mixer_message_parse_option_changed: + * @message: A volume-changed change notification message. + * @options: Pointer to hold a GstMixerOptions object, or NULL. + * @value: Result location to receive the new options value, or NULL. + * + * Extracts the GstMixerOptions and new value from a option-changed bus notification + * message. + * + * The options and value returned remain valid until the message is freed. + * + * Since: 0.10.14 + */ +void +gst_mixer_message_parse_option_changed (GstMessage * message, + GstMixerOptions ** options, const gchar ** value) +{ + const GstStructure *s; + const gchar *m_type; + + g_return_if_fail (gst_mixer_message_is_mixer_message (message)); + s = gst_message_get_structure (message); + + m_type = gst_structure_get_string (s, "type"); + g_return_if_fail (m_type == NULL || g_str_equal (m_type, "option-changed")); + + if (options) { + const GValue *v = gst_structure_get_value (s, "options"); + + g_return_if_fail (v != NULL); + *options = (GstMixerOptions *) g_value_get_object (v); + g_return_if_fail (GST_IS_MIXER_OPTIONS (*options)); + } + + if (value) + *value = gst_structure_get_string (s, "value"); } diff --git a/gst-libs/gst/interfaces/mixer.h b/gst-libs/gst/interfaces/mixer.h index dde73ab485..7bc77b25c9 100644 --- a/gst-libs/gst/interfaces/mixer.h +++ b/gst-libs/gst/interfaces/mixer.h @@ -53,6 +53,44 @@ typedef enum GST_MIXER_SOFTWARE } GstMixerType; +/** + * GstMixerMessageType: + * @GST_MIXER_MESSAGE_INVALID: Not a GstMixer message + * @GST_MIXER_MESSAGE_MUTE_TOGGLED: A mute-toggled GstMixer message + * @GST_MIXER_MESSAGE_RECORD_TOGGLED: A record-toggled GstMixer message + * @GST_MIXER_MESSAGE_VOLUME_CHANGED: A volume-changed GstMixer message + * @GST_MIXER_MESSAGE_OPTION_CHANGED: An option-changed GstMixer message + * + * An enumeration for the type of a GstMixer message received on the bus + * + * Since: 0.10.14 + */ +typedef enum +{ + GST_MIXER_MESSAGE_INVALID, + GST_MIXER_MESSAGE_MUTE_TOGGLED, + GST_MIXER_MESSAGE_RECORD_TOGGLED, + GST_MIXER_MESSAGE_VOLUME_CHANGED, + GST_MIXER_MESSAGE_OPTION_CHANGED +} GstMixerMessageType; + +/** + * GstMixerFlags: + * @GST_MIXER_FLAG_NONE: No flags + * @GST_MIXER_AUTO_NOTIFICATIONS: The mixer implementation automatically sends + * notification messages. + * + * Flags for supported features. Whether the element automatically sends + * notifications on the bus is the only one for now. + * + * Since: 0.10.14 + */ +typedef enum +{ + GST_MIXER_FLAG_NONE = 0, + GST_MIXER_FLAG_AUTO_NOTIFICATIONS = (1<<0) +} GstMixerFlags; + struct _GstMixerClass { GTypeInterface klass; @@ -74,8 +112,8 @@ struct _GstMixerClass { void (* set_record) (GstMixer *mixer, GstMixerTrack *track, gboolean record); - - /* signals */ +#ifndef GST_DISABLE_DEPRECATED + /* signals (deprecated) */ void (* mute_toggled) (GstMixer *mixer, GstMixerTrack *channel, gboolean mute); @@ -85,6 +123,7 @@ struct _GstMixerClass { void (* volume_changed) (GstMixer *mixer, GstMixerTrack *channel, gint *volumes); +#endif /* not GST_DISABLE_DEPRECATED */ void (* set_option) (GstMixer *mixer, GstMixerOptions *opts, @@ -92,12 +131,16 @@ struct _GstMixerClass { const gchar * (* get_option) (GstMixer *mixer, GstMixerOptions *opts); +#ifndef GST_DISABLE_DEPRECATED void (* option_changed) (GstMixer *mixer, GstMixerOptions *opts, - gchar *option); + gchar *option); +#endif /* not GST_DISABLE_DEPRECATED */ + + GstMixerFlags (* get_mixer_flags) (GstMixer *mixer); /*< private >*/ - gpointer _gst_reserved[GST_PADDING]; + gpointer _gst_reserved[GST_PADDING-1]; }; GType gst_mixer_get_type (void); @@ -122,7 +165,7 @@ void gst_mixer_set_option (GstMixer *mixer, const gchar * gst_mixer_get_option (GstMixer *mixer, GstMixerOptions *opts); -/* trigger signals */ +/* trigger bus messages */ void gst_mixer_mute_toggled (GstMixer *mixer, GstMixerTrack *track, gboolean mute); @@ -136,6 +179,24 @@ void gst_mixer_option_changed (GstMixer *mixer, GstMixerOptions *opts, gchar *value); +GstMixerFlags gst_mixer_get_mixer_flags (GstMixer *mixer); + +/* Functions for recognising and parsing GstMixerMessages on the bus */ +GstMixerMessageType gst_mixer_message_get_type (GstMessage *message); +void gst_mixer_message_parse_mute_toggled (GstMessage *message, + GstMixerTrack **track, + gboolean *mute); +void gst_mixer_message_parse_record_toggled (GstMessage *message, + GstMixerTrack **track, + gboolean *record); +void gst_mixer_message_parse_volume_changed (GstMessage *message, + GstMixerTrack **track, + gint **volumes, + gint *num_channels); +void gst_mixer_message_parse_option_changed (GstMessage *message, + GstMixerOptions **options, + const gchar **value); + G_END_DECLS #endif /* __GST_MIXER_H__ */ diff --git a/gst-libs/gst/interfaces/mixeroptions.c b/gst-libs/gst/interfaces/mixeroptions.c index 7c5c836b43..a0715ae8d0 100644 --- a/gst-libs/gst/interfaces/mixeroptions.c +++ b/gst-libs/gst/interfaces/mixeroptions.c @@ -25,19 +25,21 @@ #include "mixeroptions.h" +#ifndef GST_DISABLE_DEPRECATED enum { /* FILL ME */ SIGNAL_OPTION_CHANGED, LAST_SIGNAL }; +static guint signals[LAST_SIGNAL] = { 0 }; +#endif static void gst_mixer_options_class_init (GstMixerOptionsClass * klass); static void gst_mixer_options_init (GstMixerOptions * mixer); static void gst_mixer_options_dispose (GObject * object); static GObjectClass *parent_class = NULL; -static guint signals[LAST_SIGNAL] = { 0 }; GType gst_mixer_options_get_type (void) @@ -72,13 +74,14 @@ gst_mixer_options_class_init (GstMixerOptionsClass * klass) GObjectClass *object_klass = (GObjectClass *) klass; parent_class = g_type_class_peek_parent (klass); - +#ifndef GST_DISABLE_DEPRECATED signals[SIGNAL_OPTION_CHANGED] = g_signal_new ("option_changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstMixerOptionsClass, option_changed), NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); +#endif object_klass->dispose = gst_mixer_options_dispose; } diff --git a/gst-libs/gst/interfaces/mixeroptions.h b/gst-libs/gst/interfaces/mixeroptions.h index 1b85b05547..e59a7e54e6 100644 --- a/gst-libs/gst/interfaces/mixeroptions.h +++ b/gst-libs/gst/interfaces/mixeroptions.h @@ -57,9 +57,11 @@ struct _GstMixerOptions { struct _GstMixerOptionsClass { GstMixerTrackClass parent; +#ifndef GST_DISABLE_DEPRECATED /* signals */ void (* option_changed) (GstMixerOptions *opts, gchar *value); +#endif /* not GST_DISABLE_DEPRECATED */ gpointer _gst_reserved[GST_PADDING]; }; diff --git a/gst-libs/gst/interfaces/mixertrack.c b/gst-libs/gst/interfaces/mixertrack.c index d5c24870e0..638de509d5 100644 --- a/gst-libs/gst/interfaces/mixertrack.c +++ b/gst-libs/gst/interfaces/mixertrack.c @@ -24,7 +24,7 @@ #endif #include "mixertrack.h" - +#ifndef GST_DISABLE_DEPRECATED enum { /* FILL ME */ @@ -33,6 +33,8 @@ enum SIGNAL_MUTE_TOGGLED, LAST_SIGNAL }; +static guint signals[LAST_SIGNAL] = { 0 }; +#endif enum { @@ -55,7 +57,6 @@ static void gst_mixer_track_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static GObjectClass *parent_class = NULL; -static guint signals[LAST_SIGNAL] = { 0 }; GType gst_mixer_track_get_type (void) @@ -135,6 +136,7 @@ gst_mixer_track_class_init (GstMixerTrackClass * klass) "The number of channels contained within the track", 0, G_MAXINT, 0, G_PARAM_READABLE)); +#ifndef GST_DISABLE_DEPRECATED signals[SIGNAL_RECORD_TOGGLED] = g_signal_new ("record_toggled", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, @@ -156,6 +158,7 @@ gst_mixer_track_class_init (GstMixerTrackClass * klass) volume_changed), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); +#endif object_klass->dispose = gst_mixer_track_dispose; } diff --git a/gst-libs/gst/interfaces/mixertrack.h b/gst-libs/gst/interfaces/mixertrack.h index 7c87e00a62..655e8d028d 100644 --- a/gst-libs/gst/interfaces/mixertrack.h +++ b/gst-libs/gst/interfaces/mixertrack.h @@ -88,13 +88,15 @@ struct _GstMixerTrack { struct _GstMixerTrackClass { GObjectClass parent; - /* signals */ +#ifndef GST_DISABLE_DEPRECATED + /* signals (deprecated) */ void (* mute_toggled) (GstMixerTrack *channel, gboolean mute); void (* record_toggled) (GstMixerTrack *channel, gboolean record); void (* volume_changed) (GstMixerTrack *channel, gint *volumes); +#endif /* not GST_DISABLE_DEPRECATED */ gpointer _gst_reserved[GST_PADDING]; }; diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am index 5de12992bf..2b457deebe 100644 --- a/tests/check/Makefile.am +++ b/tests/check/Makefile.am @@ -89,6 +89,7 @@ check_PROGRAMS = \ gst/typefindfunctions \ libs/audio \ libs/cddabasesrc \ + libs/mixer \ libs/netbuffer \ libs/pbutils \ libs/tag \ @@ -138,6 +139,16 @@ libs_cddabasesrc_LDADD = \ $(GST_BASE_LIBS) \ $(LDADD) +libs_mixer_CFLAGS = \ + $(GST_PLUGINS_BASE_CFLAGS) \ + $(GST_BASE_CFLAGS) \ + $(AM_CFLAGS) + +libs_mixer_LDADD = \ + $(top_builddir)/gst-libs/gst/interfaces/libgstinterfaces-@GST_MAJORMINOR@.la \ + $(GST_BASE_LIBS) \ + $(LDADD) + libs_netbuffer_CFLAGS = \ $(GST_PLUGINS_BASE_CFLAGS) \ $(AM_CFLAGS) diff --git a/tests/check/libs/mixer.c b/tests/check/libs/mixer.c new file mode 100644 index 0000000000..f94efb51f9 --- /dev/null +++ b/tests/check/libs/mixer.c @@ -0,0 +1,222 @@ +/* GStreamer + * + * unit tests for audio support library + * + * Copyright (C) 2007 Jan Schmidt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include + +#include + +#define TEST_ELEMENT_TYPE (test_element_get_type()) + +typedef struct TestElement TestElement; +typedef struct TestElementClass TestElementClass; + +struct TestElement +{ + GstElement parent; +}; + +struct TestElementClass +{ + GstElementClass parent_class; +}; + +static void init_interface (GType type); +static void gst_implements_interface_init (GstImplementsInterfaceClass * klass); + +GST_BOILERPLATE_FULL (TestElement, test_element, GstElement, GST_TYPE_ELEMENT, + init_interface); + +static void +test_element_mixer_interface_init (GstMixerClass * klass) +{ + /* Not actually implementing any interfaces for this test atm */ +} + +static void +init_interface (GType type) +{ + static const GInterfaceInfo mixer_iface_info = { + (GInterfaceInitFunc) test_element_mixer_interface_init, + NULL, + NULL, + }; + static const GInterfaceInfo implements_iface_info = { + (GInterfaceInitFunc) gst_implements_interface_init, + NULL, + NULL, + }; + + g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE, + &implements_iface_info); + g_type_add_interface_static (type, GST_TYPE_MIXER, &mixer_iface_info); +} + +static void +test_element_base_init (gpointer klass) +{ +} + +static void +test_element_class_init (TestElementClass * klass) +{ +} + +static gboolean +test_element_interface_supported (TestElement * this, GType interface_type) +{ + if (interface_type == GST_TYPE_MIXER) + return TRUE; + + return FALSE; +} + +static void +gst_implements_interface_init (GstImplementsInterfaceClass * klass) +{ + klass->supported = (gpointer) test_element_interface_supported; +} + +static void +test_element_init (TestElement * this, TestElementClass * klass) +{ +} + +GST_START_TEST (test_messages) +{ + /* Create an empty GstElement that has a GstMixer interface and then + * send some notifications and validate them */ + GstElement *test_element = + (GstElement *) g_object_new (TEST_ELEMENT_TYPE, NULL); + GstBus *bus = gst_bus_new (); + GstMixerTrack *mtrack = g_object_new (GST_TYPE_MIXER_TRACK, NULL); + GstMixerOptions *mopts = g_object_new (GST_TYPE_MIXER_OPTIONS, NULL); + GstMixerTrack *t; + GstMixerOptions *o; + gint vols_in[2] = { 50, 75 }; + gboolean mute, record; + gint *vols_out; + gint n_chans, i; + const gchar *val; + GstMessage *message; + + mtrack->num_channels = 2; + mtrack->flags = GST_MIXER_TRACK_MUTE | GST_MIXER_TRACK_RECORD; + + gst_element_set_bus (test_element, bus); + + /* Test mute-toggled */ + gst_mixer_mute_toggled (GST_MIXER (test_element), mtrack, TRUE); + message = gst_bus_poll (bus, GST_MESSAGE_ELEMENT, GST_CLOCK_TIME_NONE); + fail_if (message == NULL); + fail_unless (gst_mixer_message_get_type (message) == + GST_MIXER_MESSAGE_MUTE_TOGGLED); + /* Test that we can pass NULL args */ + gst_mixer_message_parse_mute_toggled (message, NULL, NULL); + /* Test the parsing */ + gst_mixer_message_parse_mute_toggled (message, &t, &mute); + fail_unless (t == mtrack); + fail_unless (mute == TRUE); + gst_message_unref (message); + + /* Test record-toggled */ + gst_mixer_record_toggled (GST_MIXER (test_element), mtrack, TRUE); + message = gst_bus_poll (bus, GST_MESSAGE_ELEMENT, GST_CLOCK_TIME_NONE); + fail_if (message == NULL); + fail_unless (gst_mixer_message_get_type (message) == + GST_MIXER_MESSAGE_RECORD_TOGGLED); + gst_mixer_message_parse_record_toggled (message, NULL, NULL); + gst_mixer_message_parse_record_toggled (message, &t, &record); + fail_unless (t == mtrack); + fail_unless (record == TRUE); + gst_message_unref (message); + + /* Test volume-changed */ + gst_mixer_volume_changed (GST_MIXER (test_element), mtrack, vols_in); + message = gst_bus_poll (bus, GST_MESSAGE_ELEMENT, GST_CLOCK_TIME_NONE); + fail_if (message == NULL); + fail_unless (gst_mixer_message_get_type (message) == + GST_MIXER_MESSAGE_VOLUME_CHANGED); + gst_mixer_message_parse_volume_changed (message, NULL, NULL, NULL); + gst_mixer_message_parse_volume_changed (message, NULL, NULL, &n_chans); + fail_unless (n_chans == 2); + gst_mixer_message_parse_volume_changed (message, &t, &vols_out, &n_chans); + fail_unless (mtrack == t); + for (i = 0; i < n_chans; i++) + fail_unless (vols_out[i] == vols_in[i]); + + gst_message_unref (message); + g_free (vols_out); + + /* Test options-changed */ + gst_mixer_option_changed (GST_MIXER (test_element), mopts, "TESTING"); + message = gst_bus_poll (bus, GST_MESSAGE_ELEMENT, GST_CLOCK_TIME_NONE); + fail_if (message == NULL); + fail_unless (gst_mixer_message_get_type (message) == + GST_MIXER_MESSAGE_OPTION_CHANGED); + gst_mixer_message_parse_option_changed (message, NULL, NULL); + gst_mixer_message_parse_option_changed (message, &o, &val); + fail_unless (o == mopts); + fail_unless (g_str_equal (val, "TESTING")); + gst_message_unref (message); + + gst_object_unref (mtrack); + gst_object_unref (mopts); + gst_object_unref (bus); + gst_object_unref (test_element); +} + +GST_END_TEST; + +static Suite * +mixer_suite (void) +{ + Suite *s = suite_create ("mixer interface"); + TCase *tc_chain = tcase_create ("notifications"); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_messages); + + return s; +} + +int +main (int argc, char **argv) +{ + int nf; + + Suite *s = mixer_suite (); + SRunner *sr = srunner_create (s); + + gst_check_init (&argc, &argv); + + srunner_run_all (sr, CK_NORMAL); + nf = srunner_ntests_failed (sr); + srunner_free (sr); + + return nf; +}