| /* GStreamer |
| * Copyright (C) 2004 Wim Taymans <wim@fluendo.com> |
| * Copyright (C) 2011 Sebastian Dröge <sebastian.droege@collabora.co.uk> |
| * |
| * gstiterator.h: Base class for iterating datastructures. |
| * |
| * 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., 51 Franklin St, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| /** |
| * SECTION:gstiterator |
| * @title: GstIterator |
| * @short_description: Object to retrieve multiple elements in a threadsafe |
| * way. |
| * @see_also: #GstElement, #GstBin |
| * |
| * A GstIterator is used to retrieve multiple objects from another object in |
| * a threadsafe way. |
| * |
| * Various GStreamer objects provide access to their internal structures using |
| * an iterator. |
| * |
| * Note that if calling a GstIterator function results in your code receiving |
| * a refcounted object (with, say, g_value_get_object()), the refcount for that |
| * object will not be increased. Your code is responsible for taking a reference |
| * if it wants to continue using it later. |
| * |
| * The basic use pattern of an iterator is as follows: |
| * |[<!-- language="C" --> |
| * GstIterator *it = _get_iterator(object); |
| * GValue item = G_VALUE_INIT; |
| * done = FALSE; |
| * while (!done) { |
| * switch (gst_iterator_next (it, &item)) { |
| * case GST_ITERATOR_OK: |
| * ...get/use/change item here... |
| * g_value_reset (&item); |
| * break; |
| * case GST_ITERATOR_RESYNC: |
| * ...rollback changes to items... |
| * gst_iterator_resync (it); |
| * break; |
| * case GST_ITERATOR_ERROR: |
| * ...wrong parameters were given... |
| * done = TRUE; |
| * break; |
| * case GST_ITERATOR_DONE: |
| * done = TRUE; |
| * break; |
| * } |
| * } |
| * g_value_unset (&item); |
| * gst_iterator_free (it); |
| * ]| |
| */ |
| |
| #include "gst_private.h" |
| #include <gst/gstiterator.h> |
| |
| /** |
| * gst_iterator_copy: |
| * @it: a #GstIterator |
| * |
| * Copy the iterator and its state. |
| * |
| * Returns: a new copy of @it. |
| */ |
| GstIterator * |
| gst_iterator_copy (const GstIterator * it) |
| { |
| GstIterator *copy; |
| |
| copy = g_slice_copy (it->size, it); |
| if (it->copy) |
| it->copy (it, copy); |
| |
| return copy; |
| } |
| |
| G_DEFINE_BOXED_TYPE (GstIterator, gst_iterator, |
| (GBoxedCopyFunc) gst_iterator_copy, (GBoxedFreeFunc) gst_iterator_free); |
| |
| static void |
| gst_iterator_init (GstIterator * it, |
| guint size, |
| GType type, |
| GMutex * lock, |
| guint32 * master_cookie, |
| GstIteratorCopyFunction copy, |
| GstIteratorNextFunction next, |
| GstIteratorItemFunction item, |
| GstIteratorResyncFunction resync, GstIteratorFreeFunction free) |
| { |
| it->size = size; |
| it->type = type; |
| it->lock = lock; |
| it->master_cookie = master_cookie; |
| it->cookie = *master_cookie; |
| it->copy = copy; |
| it->next = next; |
| it->item = item; |
| it->resync = resync; |
| it->free = free; |
| it->pushed = NULL; |
| } |
| |
| /** |
| * gst_iterator_new: (skip) |
| * @size: the size of the iterator structure |
| * @type: #GType of children |
| * @lock: pointer to a #GMutex. |
| * @master_cookie: pointer to a guint32 that is changed when the items in the |
| * iterator changed. |
| * @copy: copy function |
| * @next: function to get next item |
| * @item: function to call on each item retrieved |
| * @resync: function to resync the iterator |
| * @free: function to free the iterator |
| * |
| * Create a new iterator. This function is mainly used for objects |
| * implementing the next/resync/free function to iterate a data structure. |
| * |
| * For each item retrieved, the @item function is called with the lock |
| * held. The @free function is called when the iterator is freed. |
| * |
| * Returns: the new #GstIterator. |
| * |
| * MT safe. |
| */ |
| GstIterator * |
| gst_iterator_new (guint size, |
| GType type, |
| GMutex * lock, |
| guint32 * master_cookie, |
| GstIteratorCopyFunction copy, |
| GstIteratorNextFunction next, |
| GstIteratorItemFunction item, |
| GstIteratorResyncFunction resync, GstIteratorFreeFunction free) |
| { |
| GstIterator *result; |
| |
| g_return_val_if_fail (size >= sizeof (GstIterator), NULL); |
| g_return_val_if_fail (g_type_qname (type) != 0, NULL); |
| g_return_val_if_fail (master_cookie != NULL, NULL); |
| g_return_val_if_fail (next != NULL, NULL); |
| g_return_val_if_fail (resync != NULL, NULL); |
| g_return_val_if_fail (free != NULL, NULL); |
| |
| result = g_slice_alloc0 (size); |
| gst_iterator_init (result, size, type, lock, master_cookie, copy, next, item, |
| resync, free); |
| |
| return result; |
| } |
| |
| /* |
| * list iterator |
| */ |
| typedef struct _GstListIterator |
| { |
| GstIterator iterator; |
| GObject *owner; |
| GList **orig; |
| GList *list; /* pointer in list */ |
| |
| void (*set_value) (GValue * value, gpointer item); |
| } GstListIterator; |
| |
| static void |
| gst_list_iterator_copy (const GstListIterator * it, GstListIterator * copy) |
| { |
| if (copy->owner) |
| g_object_ref (copy->owner); |
| } |
| |
| static GstIteratorResult |
| gst_list_iterator_next (GstListIterator * it, GValue * elem) |
| { |
| gpointer data; |
| |
| if (it->list == NULL) |
| return GST_ITERATOR_DONE; |
| |
| data = it->list->data; |
| it->list = g_list_next (it->list); |
| |
| it->set_value (elem, data); |
| |
| return GST_ITERATOR_OK; |
| } |
| |
| static void |
| gst_list_iterator_resync (GstListIterator * it) |
| { |
| it->list = *it->orig; |
| } |
| |
| static void |
| gst_list_iterator_free (GstListIterator * it) |
| { |
| if (it->owner) |
| g_object_unref (it->owner); |
| } |
| |
| /** |
| * gst_iterator_new_list: (skip) |
| * @type: #GType of elements |
| * @lock: pointer to a #GMutex protecting the list. |
| * @master_cookie: pointer to a guint32 that is incremented when the list |
| * is changed. |
| * @list: pointer to the list |
| * @owner: object owning the list |
| * @item: function to call on each item retrieved |
| * |
| * Create a new iterator designed for iterating @list. |
| * |
| * The list you iterate is usually part of a data structure @owner and is |
| * protected with @lock. |
| * |
| * The iterator will use @lock to retrieve the next item of the list and it |
| * will then call the @item function before releasing @lock again. |
| * |
| * When a concurrent update to the list is performed, usually by @owner while |
| * holding @lock, @master_cookie will be updated. The iterator implementation |
| * will notice the update of the cookie and will return %GST_ITERATOR_RESYNC to |
| * the user of the iterator in the next call to gst_iterator_next(). |
| * |
| * Returns: the new #GstIterator for @list. |
| * |
| * MT safe. |
| */ |
| GstIterator * |
| gst_iterator_new_list (GType type, |
| GMutex * lock, guint32 * master_cookie, GList ** list, GObject * owner, |
| GstIteratorItemFunction item) |
| { |
| GstListIterator *result; |
| gpointer set_value; |
| |
| if (g_type_is_a (type, G_TYPE_OBJECT)) { |
| set_value = g_value_set_object; |
| } else if (g_type_is_a (type, G_TYPE_BOXED)) { |
| set_value = g_value_set_boxed; |
| } else if (g_type_is_a (type, G_TYPE_POINTER)) { |
| set_value = g_value_set_pointer; |
| } else if (g_type_is_a (type, G_TYPE_STRING)) { |
| set_value = g_value_set_string; |
| } else { |
| g_critical ("List iterators can only be created for lists containing " |
| "instances of GObject, boxed types, pointer types and strings"); |
| return NULL; |
| } |
| |
| /* no need to lock, nothing can change here */ |
| result = (GstListIterator *) gst_iterator_new (sizeof (GstListIterator), |
| type, |
| lock, |
| master_cookie, |
| (GstIteratorCopyFunction) gst_list_iterator_copy, |
| (GstIteratorNextFunction) gst_list_iterator_next, |
| (GstIteratorItemFunction) item, |
| (GstIteratorResyncFunction) gst_list_iterator_resync, |
| (GstIteratorFreeFunction) gst_list_iterator_free); |
| |
| result->owner = owner ? g_object_ref (owner) : NULL; |
| result->orig = list; |
| result->list = *list; |
| result->set_value = set_value; |
| |
| return GST_ITERATOR (result); |
| } |
| |
| static void |
| gst_iterator_pop (GstIterator * it) |
| { |
| if (it->pushed) { |
| gst_iterator_free (it->pushed); |
| it->pushed = NULL; |
| } |
| } |
| |
| /** |
| * gst_iterator_next: |
| * @it: The #GstIterator to iterate |
| * @elem: (out caller-allocates): pointer to hold next element |
| * |
| * Get the next item from the iterator in @elem. |
| * |
| * Only when this function returns %GST_ITERATOR_OK, @elem will contain a valid |
| * value. @elem must have been initialized to the type of the iterator or |
| * initialized to zeroes with g_value_unset(). The caller is responsible for |
| * unsetting or resetting @elem with g_value_unset() or g_value_reset() |
| * after usage. |
| * |
| * When this function returns %GST_ITERATOR_DONE, no more elements can be |
| * retrieved from @it. |
| * |
| * A return value of %GST_ITERATOR_RESYNC indicates that the element list was |
| * concurrently updated. The user of @it should call gst_iterator_resync() to |
| * get the newly updated list. |
| * |
| * A return value of %GST_ITERATOR_ERROR indicates an unrecoverable fatal error. |
| * |
| * Returns: The result of the iteration. Unset @elem after usage. |
| * |
| * MT safe. |
| */ |
| GstIteratorResult |
| gst_iterator_next (GstIterator * it, GValue * elem) |
| { |
| GstIteratorResult result; |
| |
| g_return_val_if_fail (it != NULL, GST_ITERATOR_ERROR); |
| g_return_val_if_fail (elem != NULL, GST_ITERATOR_ERROR); |
| g_return_val_if_fail (G_VALUE_TYPE (elem) == G_TYPE_INVALID |
| || G_VALUE_HOLDS (elem, it->type), GST_ITERATOR_ERROR); |
| |
| if (G_VALUE_TYPE (elem) == G_TYPE_INVALID) |
| g_value_init (elem, it->type); |
| |
| restart: |
| if (it->pushed) { |
| result = gst_iterator_next (it->pushed, elem); |
| if (result == GST_ITERATOR_DONE) { |
| /* we are done with this iterator, pop it and |
| * fallthrough iterating the main iterator again. */ |
| gst_iterator_pop (it); |
| } else { |
| return result; |
| } |
| } |
| |
| if (G_LIKELY (it->lock)) |
| g_mutex_lock (it->lock); |
| |
| if (G_UNLIKELY (*it->master_cookie != it->cookie)) { |
| result = GST_ITERATOR_RESYNC; |
| goto done; |
| } |
| |
| result = it->next (it, elem); |
| if (result == GST_ITERATOR_OK && it->item) { |
| GstIteratorItem itemres; |
| |
| itemres = it->item (it, elem); |
| switch (itemres) { |
| case GST_ITERATOR_ITEM_SKIP: |
| if (G_LIKELY (it->lock)) |
| g_mutex_unlock (it->lock); |
| g_value_reset (elem); |
| goto restart; |
| case GST_ITERATOR_ITEM_END: |
| result = GST_ITERATOR_DONE; |
| g_value_reset (elem); |
| break; |
| case GST_ITERATOR_ITEM_PASS: |
| break; |
| } |
| } |
| |
| done: |
| if (G_LIKELY (it->lock)) |
| g_mutex_unlock (it->lock); |
| |
| return result; |
| } |
| |
| /** |
| * gst_iterator_resync: |
| * @it: The #GstIterator to resync |
| * |
| * Resync the iterator. this function is mostly called |
| * after gst_iterator_next() returned %GST_ITERATOR_RESYNC. |
| * |
| * When an iterator was pushed on @it, it will automatically be popped again |
| * with this function. |
| * |
| * MT safe. |
| */ |
| void |
| gst_iterator_resync (GstIterator * it) |
| { |
| g_return_if_fail (it != NULL); |
| |
| gst_iterator_pop (it); |
| |
| if (G_LIKELY (it->lock)) |
| g_mutex_lock (it->lock); |
| it->resync (it); |
| it->cookie = *it->master_cookie; |
| if (G_LIKELY (it->lock)) |
| g_mutex_unlock (it->lock); |
| } |
| |
| /** |
| * gst_iterator_free: |
| * @it: The #GstIterator to free |
| * |
| * Free the iterator. |
| * |
| * MT safe. |
| */ |
| void |
| gst_iterator_free (GstIterator * it) |
| { |
| g_return_if_fail (it != NULL); |
| |
| gst_iterator_pop (it); |
| |
| it->free (it); |
| |
| g_slice_free1 (it->size, it); |
| } |
| |
| /** |
| * gst_iterator_push: |
| * @it: The #GstIterator to use |
| * @other: The #GstIterator to push |
| * |
| * Pushes @other iterator onto @it. All calls performed on @it are |
| * forwarded to @other. If @other returns %GST_ITERATOR_DONE, it is |
| * popped again and calls are handled by @it again. |
| * |
| * This function is mainly used by objects implementing the iterator |
| * next function to recurse into substructures. |
| * |
| * When gst_iterator_resync() is called on @it, @other will automatically be |
| * popped. |
| * |
| * MT safe. |
| */ |
| void |
| gst_iterator_push (GstIterator * it, GstIterator * other) |
| { |
| g_return_if_fail (it != NULL); |
| g_return_if_fail (other != NULL); |
| |
| it->pushed = other; |
| } |
| |
| typedef struct _GstIteratorFilter |
| { |
| GstIterator iterator; |
| GstIterator *slave; |
| |
| GMutex *master_lock; |
| GCompareFunc func; |
| GValue user_data; |
| gboolean have_user_data; |
| } GstIteratorFilter; |
| |
| static GstIteratorResult |
| filter_next (GstIteratorFilter * it, GValue * elem) |
| { |
| GstIteratorResult result = GST_ITERATOR_ERROR; |
| gboolean done = FALSE; |
| GValue item = { 0, }; |
| |
| while (G_LIKELY (!done)) { |
| result = gst_iterator_next (it->slave, &item); |
| switch (result) { |
| case GST_ITERATOR_OK: |
| if (G_LIKELY (it->master_lock)) |
| g_mutex_unlock (it->master_lock); |
| if (it->func (&item, &it->user_data) == 0) { |
| g_value_copy (&item, elem); |
| done = TRUE; |
| } |
| g_value_reset (&item); |
| if (G_LIKELY (it->master_lock)) |
| g_mutex_lock (it->master_lock); |
| break; |
| case GST_ITERATOR_RESYNC: |
| case GST_ITERATOR_DONE: |
| done = TRUE; |
| break; |
| default: |
| g_assert_not_reached (); |
| break; |
| } |
| } |
| g_value_unset (&item); |
| return result; |
| } |
| |
| static void |
| filter_copy (const GstIteratorFilter * it, GstIteratorFilter * copy) |
| { |
| copy->slave = gst_iterator_copy (it->slave); |
| copy->master_lock = copy->slave->lock ? copy->slave->lock : it->master_lock; |
| copy->slave->lock = NULL; |
| |
| if (it->have_user_data) { |
| memset (©->user_data, 0, sizeof (copy->user_data)); |
| g_value_init (©->user_data, G_VALUE_TYPE (&it->user_data)); |
| g_value_copy (&it->user_data, ©->user_data); |
| } |
| } |
| |
| static void |
| filter_resync (GstIteratorFilter * it) |
| { |
| gst_iterator_resync (it->slave); |
| } |
| |
| static void |
| filter_free (GstIteratorFilter * it) |
| { |
| if (it->have_user_data) |
| g_value_unset (&it->user_data); |
| gst_iterator_free (it->slave); |
| } |
| |
| /** |
| * gst_iterator_filter: |
| * @it: The #GstIterator to filter |
| * @func: (scope call): the compare function to select elements |
| * @user_data: (closure): user data passed to the compare function |
| * |
| * Create a new iterator from an existing iterator. The new iterator |
| * will only return those elements that match the given compare function @func. |
| * The first parameter that is passed to @func is the #GValue of the current |
| * iterator element and the second parameter is @user_data. @func should |
| * return 0 for elements that should be included in the filtered iterator. |
| * |
| * When this iterator is freed, @it will also be freed. |
| * |
| * Returns: (transfer full): a new #GstIterator. |
| * |
| * MT safe. |
| */ |
| GstIterator * |
| gst_iterator_filter (GstIterator * it, GCompareFunc func, |
| const GValue * user_data) |
| { |
| GstIteratorFilter *result; |
| |
| g_return_val_if_fail (it != NULL, NULL); |
| g_return_val_if_fail (func != NULL, NULL); |
| |
| result = (GstIteratorFilter *) gst_iterator_new (sizeof (GstIteratorFilter), |
| it->type, it->lock, it->master_cookie, |
| (GstIteratorCopyFunction) filter_copy, |
| (GstIteratorNextFunction) filter_next, |
| (GstIteratorItemFunction) NULL, |
| (GstIteratorResyncFunction) filter_resync, |
| (GstIteratorFreeFunction) filter_free); |
| |
| result->master_lock = it->lock; |
| it->lock = NULL; |
| result->func = func; |
| if (user_data) { |
| g_value_init (&result->user_data, G_VALUE_TYPE (user_data)); |
| g_value_copy (user_data, &result->user_data); |
| result->have_user_data = TRUE; |
| } else { |
| result->have_user_data = FALSE; |
| } |
| result->slave = it; |
| |
| return GST_ITERATOR (result); |
| } |
| |
| /** |
| * gst_iterator_fold: |
| * @it: The #GstIterator to fold over |
| * @func: (scope call): the fold function |
| * @ret: the seed value passed to the fold function |
| * @user_data: (closure): user data passed to the fold function |
| * |
| * Folds @func over the elements of @iter. That is to say, @func will be called |
| * as @func (object, @ret, @user_data) for each object in @it. The normal use |
| * of this procedure is to accumulate the results of operating on the objects in |
| * @ret. |
| * |
| * This procedure can be used (and is used internally) to implement the |
| * gst_iterator_foreach() and gst_iterator_find_custom() operations. |
| * |
| * The fold will proceed as long as @func returns %TRUE. When the iterator has no |
| * more arguments, %GST_ITERATOR_DONE will be returned. If @func returns %FALSE, |
| * the fold will stop, and %GST_ITERATOR_OK will be returned. Errors or resyncs |
| * will cause fold to return %GST_ITERATOR_ERROR or %GST_ITERATOR_RESYNC as |
| * appropriate. |
| * |
| * The iterator will not be freed. |
| * |
| * Returns: A #GstIteratorResult, as described above. |
| * |
| * MT safe. |
| */ |
| GstIteratorResult |
| gst_iterator_fold (GstIterator * it, GstIteratorFoldFunction func, |
| GValue * ret, gpointer user_data) |
| { |
| GValue item = { 0, }; |
| GstIteratorResult result; |
| |
| g_return_val_if_fail (it != NULL, GST_ITERATOR_ERROR); |
| |
| while (1) { |
| result = gst_iterator_next (it, &item); |
| switch (result) { |
| case GST_ITERATOR_OK: |
| if (!func (&item, ret, user_data)) |
| goto fold_done; |
| |
| g_value_reset (&item); |
| break; |
| case GST_ITERATOR_RESYNC: |
| case GST_ITERATOR_ERROR: |
| goto fold_done; |
| case GST_ITERATOR_DONE: |
| goto fold_done; |
| } |
| } |
| |
| fold_done: |
| |
| #if GLIB_CHECK_VERSION (2, 48, 0) |
| g_value_unset (&item); |
| #else |
| if (item.g_type != 0) |
| g_value_unset (&item); |
| #endif |
| |
| return result; |
| } |
| |
| typedef struct |
| { |
| GstIteratorForeachFunction func; |
| gpointer user_data; |
| } ForeachFoldData; |
| |
| static gboolean |
| foreach_fold_func (const GValue * item, GValue * unused, ForeachFoldData * data) |
| { |
| data->func (item, data->user_data); |
| return TRUE; |
| } |
| |
| /** |
| * gst_iterator_foreach: |
| * @it: The #GstIterator to iterate |
| * @func: (scope call): the function to call for each element. |
| * @user_data: (closure): user data passed to the function |
| * |
| * Iterate over all element of @it and call the given function @func for |
| * each element. |
| * |
| * Returns: the result call to gst_iterator_fold(). The iterator will not be |
| * freed. |
| * |
| * MT safe. |
| */ |
| GstIteratorResult |
| gst_iterator_foreach (GstIterator * it, GstIteratorForeachFunction func, |
| gpointer user_data) |
| { |
| ForeachFoldData data; |
| |
| data.func = func; |
| data.user_data = user_data; |
| |
| return gst_iterator_fold (it, (GstIteratorFoldFunction) foreach_fold_func, |
| NULL, &data); |
| } |
| |
| typedef struct |
| { |
| GCompareFunc func; |
| gpointer user_data; |
| gboolean found; |
| } FindCustomFoldData; |
| |
| static gboolean |
| find_custom_fold_func (const GValue * item, GValue * ret, |
| FindCustomFoldData * data) |
| { |
| if (data->func (item, data->user_data) == 0) { |
| data->found = TRUE; |
| g_value_copy (item, ret); |
| return FALSE; |
| } else { |
| return TRUE; |
| } |
| } |
| |
| /** |
| * gst_iterator_find_custom: |
| * @it: The #GstIterator to iterate |
| * @func: (scope call): the compare function to use |
| * @elem: (out): pointer to a #GValue where to store the result |
| * @user_data: (closure): user data passed to the compare function |
| * |
| * Find the first element in @it that matches the compare function @func. |
| * @func should return 0 when the element is found. The first parameter |
| * to @func will be the current element of the iterator and the |
| * second parameter will be @user_data. |
| * The result will be stored in @elem if a result is found. |
| * |
| * The iterator will not be freed. |
| * |
| * This function will return %FALSE if an error happened to the iterator |
| * or if the element wasn't found. |
| * |
| * Returns: Returns %TRUE if the element was found, else %FALSE. |
| * |
| * MT safe. |
| */ |
| gboolean |
| gst_iterator_find_custom (GstIterator * it, GCompareFunc func, |
| GValue * elem, gpointer user_data) |
| { |
| GstIteratorResult res; |
| FindCustomFoldData data; |
| |
| g_return_val_if_fail (G_VALUE_TYPE (elem) == G_TYPE_INVALID |
| || G_VALUE_HOLDS (elem, it->type), GST_ITERATOR_ERROR); |
| |
| if (G_VALUE_TYPE (elem) == G_TYPE_INVALID) |
| g_value_init (elem, it->type); |
| |
| data.func = func; |
| data.user_data = user_data; |
| data.found = FALSE; |
| |
| do { |
| res = |
| gst_iterator_fold (it, (GstIteratorFoldFunction) find_custom_fold_func, |
| elem, &data); |
| if (res == GST_ITERATOR_RESYNC) |
| gst_iterator_resync (it); |
| } while (res == GST_ITERATOR_RESYNC); |
| |
| if (!data.found) |
| g_value_unset (elem); |
| |
| return data.found; |
| } |
| |
| typedef struct |
| { |
| GstIterator parent; |
| GValue object; |
| gboolean visited; |
| gboolean empty; |
| } GstSingleObjectIterator; |
| |
| static guint32 _single_object_dummy_cookie = 0; |
| |
| static void |
| gst_single_object_iterator_copy (const GstSingleObjectIterator * it, |
| GstSingleObjectIterator * copy) |
| { |
| if (!it->empty) { |
| memset (©->object, 0, sizeof (copy->object)); |
| g_value_init (©->object, it->parent.type); |
| g_value_copy (&it->object, ©->object); |
| } |
| } |
| |
| static GstIteratorResult |
| gst_single_object_iterator_next (GstSingleObjectIterator * it, GValue * result) |
| { |
| if (it->visited || it->empty) |
| return GST_ITERATOR_DONE; |
| |
| g_value_copy (&it->object, result); |
| it->visited = TRUE; |
| |
| return GST_ITERATOR_OK; |
| } |
| |
| static void |
| gst_single_object_iterator_resync (GstSingleObjectIterator * it) |
| { |
| it->visited = FALSE; |
| } |
| |
| static void |
| gst_single_object_iterator_free (GstSingleObjectIterator * it) |
| { |
| if (!it->empty) |
| g_value_unset (&it->object); |
| } |
| |
| /** |
| * gst_iterator_new_single: |
| * @type: #GType of the passed object |
| * @object: object that this iterator should return |
| * |
| * This #GstIterator is a convenient iterator for the common |
| * case where a #GstIterator needs to be returned but only |
| * a single object has to be considered. This happens often |
| * for the #GstPadIterIntLinkFunction. |
| * |
| * Returns: the new #GstIterator for @object. |
| */ |
| GstIterator * |
| gst_iterator_new_single (GType type, const GValue * object) |
| { |
| GstSingleObjectIterator *result; |
| |
| result = (GstSingleObjectIterator *) |
| gst_iterator_new (sizeof (GstSingleObjectIterator), |
| type, NULL, &_single_object_dummy_cookie, |
| (GstIteratorCopyFunction) gst_single_object_iterator_copy, |
| (GstIteratorNextFunction) gst_single_object_iterator_next, |
| (GstIteratorItemFunction) NULL, |
| (GstIteratorResyncFunction) gst_single_object_iterator_resync, |
| (GstIteratorFreeFunction) gst_single_object_iterator_free); |
| |
| if (object) { |
| g_value_init (&result->object, type); |
| g_value_copy (object, &result->object); |
| result->empty = FALSE; |
| } else { |
| result->empty = TRUE; |
| } |
| result->visited = FALSE; |
| |
| return GST_ITERATOR (result); |
| } |