diff options
author | Olivier CrĂȘte <olivier.crete@collabora.com> | 2011-10-11 16:08:49 -0400 |
---|---|---|
committer | Olivier CrĂȘte <olivier.crete@collabora.com> | 2011-10-11 16:14:10 -0400 |
commit | 52a59229d200a8d74d66e02126c290434d3157d8 (patch) | |
tree | 1adf3344ad47dbed6c35c42511362b232c703683 /farstream/fs-element-added-notifier.c | |
parent | 7098f40db04b6a32c311b802a2c12f0f450ee7b7 (diff) | |
download | farstream-52a59229d200a8d74d66e02126c290434d3157d8.tar.gz |
Move the lib out of gst-libs
Diffstat (limited to 'farstream/fs-element-added-notifier.c')
-rw-r--r-- | farstream/fs-element-added-notifier.c | 448 |
1 files changed, 448 insertions, 0 deletions
diff --git a/farstream/fs-element-added-notifier.c b/farstream/fs-element-added-notifier.c new file mode 100644 index 00000000..beac34e9 --- /dev/null +++ b/farstream/fs-element-added-notifier.c @@ -0,0 +1,448 @@ +/* + * Farstream - Recursive element addition notifier + * + * Copyright 2007-2008 Collabora Ltd. + * @author: Olivier Crete <olivier.crete@collabora.co.uk> + * Copyright 2007-2008 Nokia Corp. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +/** + * SECTION:fs-element-added-notifier + * @short_description: Recursive element addition notifier + * + * This object can be attach to any #GstBin and will emit a the + * #FsElementAddedNotifier::element-added signal for every element inside the + * #GstBin or any sub-bin and any element added in the future to the bin or + * its sub-bins. There is also a utility method to have it used to + * set the properties of elements based on a GKeyfile. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "fs-element-added-notifier.h" + +#include <stdlib.h> + +#include "fs-utils.h" + +#include "fs-marshal.h" + + +/* Signals */ +enum +{ + ELEMENT_ADDED, + LAST_SIGNAL +}; + +#define FS_ELEMENT_ADDED_NOTIFIER_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), FS_TYPE_ELEMENT_ADDED_NOTIFIER, \ + FsElementAddedNotifierPrivate)) + +struct _FsElementAddedNotifierPrivate { + GList *keyfiles; +}; + +static void _element_added_callback (GstBin *parent, GstElement *element, + gpointer user_data); + +static void fs_element_added_notifier_finalize (GObject *object); + + +G_DEFINE_TYPE(FsElementAddedNotifier, fs_element_added_notifier, G_TYPE_OBJECT); + +static guint signals[LAST_SIGNAL] = { 0 }; + +static void +fs_element_added_notifier_class_init (FsElementAddedNotifierClass *klass) +{ + GObjectClass *gobject_class; + + gobject_class = (GObjectClass *) klass; + + gobject_class->finalize = fs_element_added_notifier_finalize; + + /** + * FsElementAddedNotifier::element-added: + * @self: #FsElementAddedNotifier that emitted the signal + * @bin: The #GstBin to which this object was added + * @element: The #GstElement that was added + * + * This signal is emitted when an element is added to a #GstBin that was added + * to this object or one of its sub-bins. + * Be careful, there is no guarantee that this will be emitted on your + * main thread, it will be emitted in the thread that added the element. + * The bin may be %NULL if this is the top-level bin. + */ + signals[ELEMENT_ADDED] = g_signal_new ("element-added", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + _fs_marshal_VOID__OBJECT_OBJECT, + G_TYPE_NONE, 2, GST_TYPE_BIN, GST_TYPE_ELEMENT); + + g_type_class_add_private (klass, sizeof (FsElementAddedNotifierPrivate)); +} + + +static void +fs_element_added_notifier_init (FsElementAddedNotifier *notifier) +{ + notifier->priv = FS_ELEMENT_ADDED_NOTIFIER_GET_PRIVATE(notifier); +} + + + +static void +fs_element_added_notifier_finalize (GObject *object) +{ + FsElementAddedNotifier *self = FS_ELEMENT_ADDED_NOTIFIER (object); + + g_list_foreach (self->priv->keyfiles, (GFunc) g_key_file_free, NULL); + g_list_free (self->priv->keyfiles); + self->priv->keyfiles = NULL; +} + +/** + * fs_element_added_notifier_new: + * + * Creates a new #FsElementAddedNotifier object + * + * Returns: the newly-created #FsElementAddedNotifier + */ + +FsElementAddedNotifier * +fs_element_added_notifier_new (void) +{ + return (FsElementAddedNotifier *) + g_object_new (FS_TYPE_ELEMENT_ADDED_NOTIFIER, NULL); +} + +/** + * fs_element_added_notifier_add: + * @notifier: a #FsElementAddedNotifier + * @bin: A #GstBin to watch to added elements + * + * Add a #GstBin to on which the #FsElementAddedNotifier::element-added signal + * will be called on every element and sub-element present and added in the + * future. + */ + +void +fs_element_added_notifier_add (FsElementAddedNotifier *notifier, + GstBin *bin) +{ + g_return_if_fail (notifier && FS_IS_ELEMENT_ADDED_NOTIFIER (notifier)); + g_return_if_fail (bin && GST_IS_BIN (bin)); + + _element_added_callback (NULL, GST_ELEMENT_CAST (bin), notifier); +} + + +static void +_bin_unparented_cb (GstObject *object, GstObject *parent, gpointer user_data) +{ + GstIterator *iter = NULL; + gboolean done; + + /* Return if there was no handler connected */ + if (g_signal_handlers_disconnect_by_func (object, _element_added_callback, + user_data) == 0) + return; + + iter = gst_bin_iterate_elements (GST_BIN (object)); + + done = FALSE; + while (!done) + { + gpointer item; + + switch (gst_iterator_next (iter, &item)) { + case GST_ITERATOR_OK: + if (GST_IS_BIN (item)) + _bin_unparented_cb (GST_OBJECT (item), object, user_data); + gst_object_unref (item); + break; + case GST_ITERATOR_RESYNC: + // We don't rollback anything, we just ignore already processed ones + gst_iterator_resync (iter); + break; + case GST_ITERATOR_ERROR: + g_error ("Wrong parameters were given?"); + done = TRUE; + break; + case GST_ITERATOR_DONE: + done = TRUE; + break; + } + } + + gst_iterator_free (iter); +} + + +/** + * fs_element_added_notifier_remove: + * @notifier: a #FsElementAddedNotifier + * @bin: A #GstBin to stop watching + * + * Stop watching the passed bin and its subbins. + * + * Returns: %TRUE if the #GstBin was being watched, %FALSE otherwise + */ + +gboolean +fs_element_added_notifier_remove (FsElementAddedNotifier *notifier, + GstBin *bin) +{ + g_return_val_if_fail (FS_IS_ELEMENT_ADDED_NOTIFIER (notifier), FALSE); + g_return_val_if_fail (GST_IS_BIN (bin), FALSE); + + if (g_signal_handler_find (bin, + G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, + 0, 0, NULL, /* id, detail, closure */ + _element_added_callback, notifier) != 0) + { + _bin_unparented_cb (GST_OBJECT (bin), NULL, notifier); + return TRUE; + } + else + { + return FALSE; + } +} + + +#if 1 +# define DEBUG(...) do {} while (0) +#else +# define DEBUG g_debug +#endif + +static void +_bin_added_from_keyfile (FsElementAddedNotifier *notifier, GstBin *bin, + GstElement *element, gpointer user_data) +{ + GKeyFile *keyfile = user_data; + const gchar *name = NULL; + gchar *free_name = NULL; + gchar **keys; + gint i; + GstElementFactory *factory = gst_element_get_factory (element); + + if (factory) + { + name = gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)); + if (name && !g_key_file_has_group (keyfile, name)) + name = NULL; + } + + if (!name) + { + GST_OBJECT_LOCK (element); + if (GST_OBJECT_NAME (element) && + g_key_file_has_group (keyfile, GST_OBJECT_NAME (element))) + name = free_name = g_strdup (GST_OBJECT_NAME (element)); + GST_OBJECT_UNLOCK (element); + } + + if (!name) + return; + + DEBUG ("Found config for %s", name); + keys = g_key_file_get_keys (keyfile, name, NULL, NULL); + + for (i = 0; keys[i]; i++) + { + GParamSpec *param_spec; + GValue prop_value = { 0 }; + gchar *str_value; + + DEBUG ("getting %s", keys[i]); + param_spec = g_object_class_find_property (G_OBJECT_GET_CLASS(element), + keys[i]); + + if (!param_spec) + { + DEBUG ("Property %s does not exist in element %s, ignoring", + keys[i], name); + continue; + } + + g_value_init (&prop_value, param_spec->value_type); + + str_value = g_key_file_get_value (keyfile, name, keys[i], NULL); + if (str_value && gst_value_deserialize (&prop_value, str_value)) + { + DEBUG ("Setting %s to on %s", keys[i], name); + g_object_set_property (G_OBJECT (element), keys[i], &prop_value); + } + else + { + DEBUG ("Could not read value for property %s", keys[i]); + } + g_free (str_value); + g_value_unset (&prop_value); + } + + g_strfreev (keys); + g_free (free_name); +} + + +/** + * fs_element_added_notifier_set_properties_from_keyfile: + * @notifier: a #FsElementAddedNotifier + * @keyfile: a #GKeyFile + * + * Using a #GKeyFile where the groups are the element's type or name + * and the key=value are the property and its value, this function + * will set the properties on the elements added to this object after + * this function has been called. It will take ownership of the + * GKeyFile structure. It will first try the group as the element type, if that + * does not match, it will check its name. + */ +void +fs_element_added_notifier_set_properties_from_keyfile ( + FsElementAddedNotifier *notifier, + GKeyFile *keyfile) +{ + g_return_if_fail (FS_IS_ELEMENT_ADDED_NOTIFIER (notifier)); + g_return_if_fail (keyfile); + + g_signal_connect (notifier, "element-added", + G_CALLBACK (_bin_added_from_keyfile), keyfile); + + notifier->priv->keyfiles = + g_list_prepend (notifier->priv->keyfiles, keyfile); +} + + +/** + * fs_element_added_notifier_set_properties_from_file: + * @notifier: a #FsElementAddedNotifier + * @filename: The name of the keyfile to use + * @error: location of a #GError, or %NULL if no error occured + * + * Same as fs_element_added_notifier_set_properties_from_keyfile() but using + * the name of the file to load instead of the #GKeyFile directly. + * + * Returns: %TRUE if the file was successfully loaded, %FALSE otherwise + */ +gboolean +fs_element_added_notifier_set_properties_from_file ( + FsElementAddedNotifier *notifier, + const gchar *filename, + GError **error) +{ + GKeyFile *keyfile = g_key_file_new (); + + if (!g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, error)) + { + g_key_file_free (keyfile); + return FALSE; + } + + fs_element_added_notifier_set_properties_from_keyfile(notifier, keyfile); + + return TRUE; +} + +static void +_element_added_callback (GstBin *parent, GstElement *element, + gpointer user_data) +{ + FsElementAddedNotifier *notifier = FS_ELEMENT_ADDED_NOTIFIER (user_data); + + if (GST_IS_BIN (element)) { + GstIterator *iter = NULL; + gboolean done; + + g_signal_connect_object (element, "element-added", + G_CALLBACK (_element_added_callback), notifier, 0); + + if (parent) + g_signal_connect_object (element, "parent-unset", + G_CALLBACK (_bin_unparented_cb), notifier, 0); + + iter = gst_bin_iterate_elements (GST_BIN (element)); + + done = FALSE; + while (!done) + { + gpointer item = NULL; + + switch (gst_iterator_next (iter, &item)) { + case GST_ITERATOR_OK: + /* We make sure the callback has not already been added */ + if (g_signal_handler_find (item, + G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, + 0, 0, NULL, /* id, detail, closure */ + _element_added_callback, notifier) == 0) + _element_added_callback (GST_BIN_CAST (element), item, notifier); + gst_object_unref (item); + break; + case GST_ITERATOR_RESYNC: + // We don't rollback anything, we just ignore already processed ones + gst_iterator_resync (iter); + break; + case GST_ITERATOR_ERROR: + g_error ("Wrong parameters were given?"); + done = TRUE; + break; + case GST_ITERATOR_DONE: + done = TRUE; + break; + } + } + + gst_iterator_free (iter); + } + + g_signal_emit (notifier, signals[ELEMENT_ADDED], 0, parent, element); +} + + +/** + * fs_element_added_notifier_set_default_properties: + * @notifier: a #FsElementAddedNotifier + * @element: Element for which to set the default codec + * preferences + * + * Same as first calling fs_utils_get_default_element_properties() and using + * the result with + * fs_element_added_notifier_set_properties_from_keyfile() . + * + * This is binding friendly (since GKeyFile doesn't have a boxed type). + */ +void +fs_element_added_notifier_set_default_properties ( + FsElementAddedNotifier *notifier, + GstElement *element) +{ + GKeyFile *keyfile = fs_utils_get_default_element_properties (element); + + if (!keyfile) + return; + + fs_element_added_notifier_set_properties_from_keyfile(notifier, keyfile); +} |