X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gst%2Fgstobject.c;h=3aa20c44351ad0924ba840c6028c6b94009b8358;hb=dac5966da6a0f53d0443dfa1ac239289028c415d;hp=f5a7769d00f5be710db506c6a88e606fc3abb138;hpb=df099959fd2a12ed09b1d070fdab796c7acc6084;p=platform%2Fupstream%2Fgstreamer.git diff --git a/gst/gstobject.c b/gst/gstobject.c index f5a7769..3aa20c4 100644 --- a/gst/gstobject.c +++ b/gst/gstobject.c @@ -17,106 +17,92 @@ * * 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. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ /** * SECTION:gstobject + * @title: GstObject * @short_description: Base class for the GStreamer object hierarchy * * #GstObject provides a root for the object hierarchy tree filed in by the * GStreamer library. It is currently a thin wrapper on top of - * #GObject. It is an abstract class that is not very usable on its own. + * #GInitiallyUnowned. It is an abstract class that is not very usable on its own. * * #GstObject gives us basic refcounting, parenting functionality and locking. - * Most of the function are just extended for special GStreamer needs and can be + * Most of the functions are just extended for special GStreamer needs and can be * found under the same name in the base class of #GstObject which is #GObject * (e.g. g_object_ref() becomes gst_object_ref()). * - * The most interesting difference between #GstObject and #GObject is the - * "floating" reference count. A #GObject is created with a reference count of - * 1, owned by the creator of the #GObject. (The owner of a reference is the - * code section that has the right to call gst_object_unref() in order to - * remove that reference.) A #GstObject is created with a reference count of 1 - * also, but it isn't owned by anyone; Instead, the initial reference count - * of a #GstObject is "floating". The floating reference can be removed by - * anyone at any time, by calling gst_object_sink(). gst_object_sink() does - * nothing if an object is already sunk (has no floating reference). - * - * When you add a #GstElement to its parent container, the parent container will - * do this: - * - * - * gst_object_ref (GST_OBJECT (child_element)); - * gst_object_sink (GST_OBJECT (child_element)); - * - * - * This means that the container now owns a reference to the child element - * (since it called gst_object_ref()), and the child element has no floating - * reference. - * - * The purpose of the floating reference is to keep the child element alive - * until you add it to a parent container, which then manages the lifetime of - * the object itself: - * - * - * element = gst_element_factory_make (factoryname, name); - * // element has one floating reference to keep it alive - * gst_bin_add (GST_BIN (bin), element); - * // element has one non-floating reference owned by the container - * - * - * - * Another effect of this is, that calling gst_object_unref() on a bin object, - * will also destoy all the #GstElement objects in it. The same is true for - * calling gst_bin_remove(). - * - * Special care has to be taken for all methods that gst_object_sink() an object - * since if the caller of those functions had a floating reference to the object, - * the object reference is now invalid. + * Since #GstObject derives from #GInitiallyUnowned, it also inherits the + * floating reference. Be aware that functions such as gst_bin_add() and + * gst_element_add_pad() take ownership of the floating reference. * * In contrast to #GObject instances, #GstObject adds a name property. The functions * gst_object_set_name() and gst_object_get_name() are used to set/get the name * of the object. * - * Last reviewed on 2005-11-09 (0.9.4) + * ## controlled properties + * + * Controlled properties offers a lightweight way to adjust gobject properties + * over stream-time. It works by using time-stamped value pairs that are queued + * for element-properties. At run-time the elements continuously pull value + * changes for the current stream-time. + * + * What needs to be changed in a #GstElement? + * Very little - it is just two steps to make a plugin controllable! + * + * * mark gobject-properties paramspecs that make sense to be controlled, + * by GST_PARAM_CONTROLLABLE. + * + * * when processing data (get, chain, loop function) at the beginning call + * gst_object_sync_values(element,timestamp). + * This will make the controller update all GObject properties that are + * under its control with the current values based on the timestamp. + * + * What needs to be done in applications? Again it's not a lot to change. + * + * * create a #GstControlSource. + * csource = gst_interpolation_control_source_new (); + * g_object_set (csource, "mode", GST_INTERPOLATION_MODE_LINEAR, NULL); + * + * * Attach the #GstControlSource on the controller to a property. + * gst_object_add_control_binding (object, gst_direct_control_binding_new (object, "prop1", csource)); + * + * * Set the control values + * gst_timed_value_control_source_set ((GstTimedValueControlSource *)csource,0 * GST_SECOND, value1); + * gst_timed_value_control_source_set ((GstTimedValueControlSource *)csource,1 * GST_SECOND, value2); + * + * * start your pipeline */ #include "gst_private.h" #include "glib-compat-private.h" #include "gstobject.h" -#include "gstmarshal.h" +#include "gstclock.h" +#include "gstcontrolbinding.h" +#include "gstcontrolsource.h" #include "gstinfo.h" +#include "gstparamspecs.h" #include "gstutils.h" -#ifndef GST_DISABLE_TRACE -#include "gsttrace.h" -static GstAllocTrace *_gst_object_trace; -#endif - #define DEBUG_REFCOUNT /* Object signals and args */ -/* FIXME-0.11: have a read-only parent property instead of the two signals - * then we get notify::parent for free */ enum { - PARENT_SET, - PARENT_UNSET, -#if !defined(GST_DISABLE_LOADSAVE) && !defined(GST_REMOVE_DEPRECATED) - OBJECT_SAVED, -#endif DEEP_NOTIFY, LAST_SIGNAL }; enum { - ARG_0, - ARG_NAME - /* FILL ME */ + PROP_0, + PROP_NAME, + PROP_PARENT, + PROP_LAST }; enum @@ -130,19 +116,11 @@ static GData *object_name_counts = NULL; G_LOCK_DEFINE_STATIC (object_name_mutex); -typedef struct _GstSignalObject GstSignalObject; -typedef struct _GstSignalObjectClass GstSignalObjectClass; - -static GType gst_signal_object_get_type (void); - -#if !defined(GST_DISABLE_LOADSAVE) && !defined(GST_REMOVE_DEPRECATED) -static guint gst_signal_object_signals[SO_LAST_SIGNAL] = { 0 }; -#endif - static void gst_object_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_object_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); + static void gst_object_dispatch_properties_changed (GObject * object, guint n_pspecs, GParamSpec ** pspecs); @@ -151,93 +129,46 @@ static void gst_object_finalize (GObject * object); static gboolean gst_object_set_name_default (GstObject * object); -#ifdef GST_DISABLE_DEPRECATED -#if !defined(GST_DISABLE_LOADSAVE) && !defined(GST_REMOVE_DEPRECATED) -#undef GstXmlNodePtr -#define GstXmlNodePtr xmlNodePtr -#include -GstXmlNodePtr gst_object_save_thyself (GstObject * object, - GstXmlNodePtr parent); -void gst_object_restore_thyself (GstObject * object, GstXmlNodePtr parent); -void gst_class_signal_emit_by_name (GstObject * object, const gchar * name, - GstXmlNodePtr self); -#endif -#endif +static guint gst_object_signals[LAST_SIGNAL] = { 0 }; -#if !defined(GST_DISABLE_LOADSAVE) && !defined(GST_REMOVE_DEPRECATED) -static void gst_object_real_restore_thyself (GstObject * object, - GstXmlNodePtr self); -#endif +static GParamSpec *properties[PROP_LAST]; -static GObjectClass *parent_class = NULL; -static guint gst_object_signals[LAST_SIGNAL] = { 0 }; +G_DEFINE_ABSTRACT_TYPE (GstObject, gst_object, G_TYPE_INITIALLY_UNOWNED); + +static void +gst_object_constructed (GObject * object) +{ + GST_TRACER_OBJECT_CREATED (GST_OBJECT_CAST (object)); -G_DEFINE_ABSTRACT_TYPE (GstObject, gst_object, G_TYPE_OBJECT); + ((GObjectClass *) gst_object_parent_class)->constructed (object); +} static void gst_object_class_init (GstObjectClass * klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - parent_class = g_type_class_peek_parent (klass); - -#ifndef GST_DISABLE_TRACE - _gst_object_trace = gst_alloc_trace_register (g_type_name (GST_TYPE_OBJECT)); -#endif - gobject_class->set_property = gst_object_set_property; gobject_class->get_property = gst_object_get_property; - g_object_class_install_property (gobject_class, ARG_NAME, - g_param_spec_string ("name", "Name", "The name of the object", - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); - - /** - * GstObject::parent-set: - * @gstobject: a #GstObject - * @parent: the new parent - * - * Emitted when the parent of an object is set. - */ - gst_object_signals[PARENT_SET] = - g_signal_new ("parent-set", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GstObjectClass, parent_set), NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_OBJECT); + properties[PROP_NAME] = + g_param_spec_string ("name", "Name", "The name of the object", NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); /** - * GstObject::parent-unset: - * @gstobject: a #GstObject - * @parent: the old parent + * GstObject:parent: * - * Emitted when the parent of an object is unset. + * The parent of the object. Please note, that when changing the 'parent' + * property, we don't emit #GObject::notify and #GstObject::deep-notify + * signals due to locking issues. In some cases one can use + * #GstBin::element-added or #GstBin::element-removed signals on the parent to + * achieve a similar effect. */ - gst_object_signals[PARENT_UNSET] = - g_signal_new ("parent-unset", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstObjectClass, parent_unset), NULL, - NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_OBJECT); + properties[PROP_PARENT] = + g_param_spec_object ("parent", "Parent", "The parent of the object", + GST_TYPE_OBJECT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); -#if !defined(GST_DISABLE_LOADSAVE) && !defined(GST_REMOVE_DEPRECATED) - /** - * GstObject::object-saved: - * @gstobject: a #GstObject - * @xml_node: the xmlNodePtr of the parent node - * - * Trigered whenever a new object is saved to XML. You can connect to this - * signal to insert custom XML tags into the core XML. - */ - /* FIXME This should be the GType of xmlNodePtr instead of G_TYPE_POINTER - * (if libxml would use GObject) - */ - gst_object_signals[OBJECT_SAVED] = - g_signal_new ("object-saved", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstObjectClass, object_saved), NULL, - NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); - - klass->restore_thyself = - ((void (*)(GstObject * object, - gpointer self)) *gst_object_real_restore_thyself); -#endif + g_object_class_install_properties (gobject_class, PROP_LAST, properties); /** * GstObject::deep-notify: @@ -253,20 +184,16 @@ gst_object_class_init (GstObjectClass * klass) g_signal_new ("deep-notify", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_DETAILED | G_SIGNAL_NO_HOOKS, G_STRUCT_OFFSET (GstObjectClass, deep_notify), NULL, - NULL, gst_marshal_VOID__OBJECT_PARAM, G_TYPE_NONE, 2, GST_TYPE_OBJECT, + NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, GST_TYPE_OBJECT, G_TYPE_PARAM); klass->path_string_separator = "/"; - /* FIXME 0.11: Store this directly in the class struct */ - klass->lock = g_slice_new (GStaticRecMutex); - g_static_rec_mutex_init (klass->lock); - - klass->signal_object = g_object_newv (gst_signal_object_get_type (), 0, NULL); /* see the comments at gst_object_dispatch_properties_changed */ gobject_class->dispatch_properties_changed = GST_DEBUG_FUNCPTR (gst_object_dispatch_properties_changed); + gobject_class->constructed = gst_object_constructed; gobject_class->dispose = gst_object_dispose; gobject_class->finalize = gst_object_finalize; } @@ -274,22 +201,20 @@ gst_object_class_init (GstObjectClass * klass) static void gst_object_init (GstObject * object) { - object->lock = g_mutex_new (); + g_mutex_init (&object->lock); object->parent = NULL; object->name = NULL; GST_CAT_TRACE_OBJECT (GST_CAT_REFCOUNTING, object, "%p new", object); -#ifndef GST_DISABLE_TRACE - gst_alloc_trace_new (_gst_object_trace, object); -#endif - object->flags = 0; - GST_OBJECT_FLAG_SET (object, GST_OBJECT_FLOATING); + + object->control_rate = 100 * GST_MSECOND; + object->last_sync = GST_CLOCK_TIME_NONE; } /** * gst_object_ref: - * @object: a #GstObject to reference + * @object: (type Gst.Object): a #GstObject to reference * * Increments the reference count on @object. This function * does not take the lock on @object because it relies on @@ -299,13 +224,14 @@ gst_object_init (GstObject * object) * constructs like : * result = gst_object_ref (object->parent); * - * Returns: (transfer full): A pointer to @object + * Returns: (transfer full) (type Gst.Object): A pointer to @object */ gpointer gst_object_ref (gpointer object) { g_return_val_if_fail (object != NULL, NULL); + GST_TRACER_OBJECT_REFFED (object, ((GObject *) object)->ref_count + 1); #ifdef DEBUG_REFCOUNT GST_CAT_TRACE_OBJECT (GST_CAT_REFCOUNTING, object, "%p ref %d->%d", object, ((GObject *) object)->ref_count, ((GObject *) object)->ref_count + 1); @@ -317,7 +243,7 @@ gst_object_ref (gpointer object) /** * gst_object_unref: - * @object: a #GstObject to unreference + * @object: (type Gst.Object): a #GstObject to unreference * * Decrements the reference count on @object. If reference count hits * zero, destroy @object. This function does not take the lock @@ -332,6 +258,7 @@ gst_object_unref (gpointer object) g_return_if_fail (object != NULL); g_return_if_fail (((GObject *) object)->ref_count > 0); + GST_TRACER_OBJECT_UNREFFED (object, ((GObject *) object)->ref_count - 1); #ifdef DEBUG_REFCOUNT GST_CAT_TRACE_OBJECT (GST_CAT_REFCOUNTING, object, "%p unref %d->%d", object, ((GObject *) object)->ref_count, ((GObject *) object)->ref_count - 1); @@ -340,7 +267,7 @@ gst_object_unref (gpointer object) } /** - * gst_object_ref_sink: + * gst_object_ref_sink: (skip) * @object: a #GstObject to sink * * Increase the reference count of @object, and possibly remove the floating @@ -352,87 +279,66 @@ gst_object_unref (gpointer object) * is not floating, then this call adds a new normal reference increasing the * reference count by one. * - * MT safe. This function grabs and releases @object lock. - * - * Since: 0.10.24 + * For more background on "floating references" please see the #GObject + * documentation. */ -void +gpointer gst_object_ref_sink (gpointer object) { - g_return_if_fail (GST_IS_OBJECT (object)); + g_return_val_if_fail (object != NULL, NULL); - GST_OBJECT_LOCK (object); - if (G_LIKELY (GST_OBJECT_IS_FLOATING (object))) { - GST_CAT_TRACE_OBJECT (GST_CAT_REFCOUNTING, object, - "unsetting floating flag"); - GST_OBJECT_FLAG_UNSET (object, GST_OBJECT_FLOATING); - GST_OBJECT_UNLOCK (object); - } else { - GST_OBJECT_UNLOCK (object); - gst_object_ref (object); - } +#ifdef DEBUG_REFCOUNT + GST_CAT_TRACE_OBJECT (GST_CAT_REFCOUNTING, object, "%p ref_sink %d->%d", + object, ((GObject *) object)->ref_count, + ((GObject *) object)->ref_count + 1); +#endif + return g_object_ref_sink (object); } /** - * gst_object_sink: - * @object: a #GstObject to sink + * gst_clear_object: (skip) + * @object_ptr: a pointer to a #GstObject reference * - * If @object was floating, the #GST_OBJECT_FLOATING flag is removed - * and @object is unreffed. When @object was not floating, - * this function does nothing. + * Clears a reference to a #GstObject. * - * Any newly created object has a refcount of 1 and is floating. - * This function should be used when creating a new object to - * symbolically 'take ownership' of @object. This done by first doing a - * gst_object_ref() to keep a reference to @object and then gst_object_sink() - * to remove and unref any floating references to @object. - * Use gst_object_set_parent() to have this done for you. + * @object_ptr must not be %NULL. * - * MT safe. This function grabs and releases @object lock. - */ + * If the reference is %NULL then this function does nothing. + * Otherwise, the reference count of the object is decreased using + * gst_object_unref() and the pointer is set to %NULL. + * + * A macro is also included that allows this function to be used without + * pointer casts. + * + * Since: 1.16 + **/ +#undef gst_clear_object void -gst_object_sink (gpointer object) +gst_clear_object (GstObject ** object_ptr) { - g_return_if_fail (GST_IS_OBJECT (object)); - - GST_CAT_TRACE_OBJECT (GST_CAT_REFCOUNTING, object, "sink"); - - GST_OBJECT_LOCK (object); - if (G_LIKELY (GST_OBJECT_IS_FLOATING (object))) { - GST_CAT_TRACE_OBJECT (GST_CAT_REFCOUNTING, object, "clear floating flag"); - GST_OBJECT_FLAG_UNSET (object, GST_OBJECT_FLOATING); - GST_OBJECT_UNLOCK (object); - gst_object_unref (object); - } else { - GST_OBJECT_UNLOCK (object); - } + g_clear_pointer (object_ptr, gst_object_unref); } /** * gst_object_replace: - * @oldobj: (inout) (transfer full): pointer to a place of a #GstObject to - * replace - * @newobj: (transfer none): a new #GstObject + * @oldobj: (inout) (transfer full) (nullable): pointer to a place of + * a #GstObject to replace + * @newobj: (transfer none) (allow-none): a new #GstObject * - * Unrefs the #GstObject pointed to by @oldobj, refs @newobj and - * puts @newobj in *@oldobj. Be carefull when calling this - * function, it does not take any locks. You might want to lock - * the object owning @oldobj pointer before calling this - * function. + * Atomically modifies a pointer to point to a new object. + * The reference count of @oldobj is decreased and the reference count of + * @newobj is increased. * - * Make sure not to LOCK @oldobj because it might be unreffed - * which could cause a deadlock when it is disposed. + * Either @newobj and the value pointed to by @oldobj may be %NULL. * - * Since 0.10.36, this function operates atomically. + * Returns: %TRUE if @newobj was different from @oldobj */ -void +gboolean gst_object_replace (GstObject ** oldobj, GstObject * newobj) { GstObject *oldptr; - g_return_if_fail (oldobj != NULL); - g_return_if_fail (*oldobj == NULL || GST_IS_OBJECT (*oldobj)); - g_return_if_fail (newobj == NULL || GST_IS_OBJECT (newobj)); + g_return_val_if_fail (oldobj != NULL, FALSE); #ifdef DEBUG_REFCOUNT GST_CAT_TRACE (GST_CAT_REFCOUNTING, "replace %p %s (%d) with %p %s (%d)", @@ -442,14 +348,25 @@ gst_object_replace (GstObject ** oldobj, GstObject * newobj) newobj ? G_OBJECT (newobj)->ref_count : 0); #endif + oldptr = g_atomic_pointer_get ((gpointer *) oldobj); + + if (G_UNLIKELY (oldptr == newobj)) + return FALSE; + if (newobj) - g_object_ref (newobj); - do { - oldptr = *oldobj; - } while (!G_ATOMIC_POINTER_COMPARE_AND_EXCHANGE ((void *) oldobj, - oldptr, newobj)); + gst_object_ref (newobj); + + while (G_UNLIKELY (!g_atomic_pointer_compare_and_exchange ((gpointer *) + oldobj, oldptr, newobj))) { + oldptr = g_atomic_pointer_get ((gpointer *) oldobj); + if (G_UNLIKELY (oldptr == newobj)) + break; + } + if (oldptr) - g_object_unref (oldptr); + gst_object_unref (oldptr); + + return oldptr != newobj; } /* dispose is called when the object has to release all links @@ -457,9 +374,10 @@ gst_object_replace (GstObject ** oldobj, GstObject * newobj) static void gst_object_dispose (GObject * object) { + GstObject *self = (GstObject *) object; GstObject *parent; - GST_CAT_TRACE_OBJECT (GST_CAT_REFCOUNTING, object, "dispose"); + GST_CAT_TRACE_OBJECT (GST_CAT_REFCOUNTING, object, "%p dispose", object); GST_OBJECT_LOCK (object); if ((parent = GST_OBJECT_PARENT (object))) @@ -467,7 +385,17 @@ gst_object_dispose (GObject * object) GST_OBJECT_PARENT (object) = NULL; GST_OBJECT_UNLOCK (object); - parent_class->dispose (object); + if (self->control_bindings) { + GList *node; + + for (node = self->control_bindings; node; node = g_list_next (node)) { + gst_object_unparent (node->data); + } + g_list_free (self->control_bindings); + self->control_bindings = NULL; + } + + ((GObjectClass *) gst_object_parent_class)->dispose (object); return; @@ -491,18 +419,16 @@ gst_object_finalize (GObject * object) { GstObject *gstobject = GST_OBJECT_CAST (object); - GST_CAT_TRACE_OBJECT (GST_CAT_REFCOUNTING, object, "finalize"); + GST_CAT_TRACE_OBJECT (GST_CAT_REFCOUNTING, object, "%p finalize", object); g_signal_handlers_destroy (object); g_free (gstobject->name); - g_mutex_free (gstobject->lock); + g_mutex_clear (&gstobject->lock); -#ifndef GST_DISABLE_TRACE - gst_alloc_trace_free (_gst_object_trace, object); -#endif + GST_TRACER_OBJECT_DESTROYED (gstobject); - parent_class->finalize (object); + ((GObjectClass *) gst_object_parent_class)->finalize (object); } /* Changing a GObject property of a GstObject will result in "deep-notify" @@ -511,9 +437,6 @@ gst_object_finalize (GObject * object) * top-level bin to catch property-change notifications for all contained * elements. * - * This function is not MT safe in glib < 2.8 so we need to lock it with a - * classwide mutex in that case. - * * MT safe. */ static void @@ -528,11 +451,13 @@ gst_object_dispatch_properties_changed (GObject * object, #endif /* do the standard dispatching */ - parent_class->dispatch_properties_changed (object, n_pspecs, pspecs); + ((GObjectClass *) + gst_object_parent_class)->dispatch_properties_changed (object, n_pspecs, + pspecs); gst_object = GST_OBJECT_CAST (object); #ifndef GST_DISABLE_GST_DEBUG - if (G_UNLIKELY (__gst_debug_min >= GST_LEVEL_LOG)) { + if (G_UNLIKELY (_gst_debug_min >= GST_LEVEL_LOG)) { name = gst_object_get_name (gst_object); debug_name = GST_STR_NULL (name); } else @@ -564,9 +489,9 @@ gst_object_dispatch_properties_changed (GObject * object, * @object: the #GObject that signalled the notify. * @orig: a #GstObject that initiated the notify. * @pspec: a #GParamSpec of the property. - * @excluded_props: (array zero-terminated=1) (element-type gchar*) - * (allow-none):a set of user-specified properties to exclude or - * NULL to show all changes. + * @excluded_props: (array zero-terminated=1) (element-type gchar*) (allow-none): + * a set of user-specified properties to exclude or %NULL to show + * all changes. * * A default deep_notify signal callback for an object. The user data * should contain a pointer to an array of strings that should be excluded @@ -594,19 +519,10 @@ gst_object_default_deep_notify (GObject * object, GstObject * orig, g_value_init (&value, pspec->value_type); g_object_get_property (G_OBJECT (orig), pspec->name, &value); - /* FIXME: handle flags */ - if (G_IS_PARAM_SPEC_ENUM (pspec)) { - GEnumValue *enum_value; - GEnumClass *klass = G_ENUM_CLASS (g_type_class_ref (pspec->value_type)); - - enum_value = g_enum_get_value (klass, g_value_get_enum (&value)); - - str = g_strdup_printf ("%s (%d)", enum_value->value_nick, - enum_value->value); - g_type_class_unref (klass); - } else { - str = g_strdup_value_contents (&value); - } + if (G_VALUE_HOLDS_STRING (&value)) + str = g_value_dup_string (&value); + else + str = gst_value_serialize (&value); name = gst_object_get_path_string (orig); g_print ("%s: %s = %s\n", name, pspec->name, str); g_free (name); @@ -646,7 +562,14 @@ gst_object_set_name_default (GstObject * object) type_name = g_quark_to_string (q); if (strncmp (type_name, "Gst", 3) == 0) type_name += 3; - name = g_strdup_printf ("%s%d", type_name, count); + /* give the 20th "queue" element and the first "queue2" different names */ + l = strlen (type_name); + if (l > 0 && g_ascii_isdigit (type_name[l - 1])) { + name = g_strdup_printf ("%s-%d", type_name, count); + } else { + name = g_strdup_printf ("%s%d", type_name, count); + } + l = strlen (name); for (i = 0; i < l; i++) name[i] = g_ascii_tolower (name[i]); @@ -674,15 +597,15 @@ had_parent: /** * gst_object_set_name: * @object: a #GstObject - * @name: new name of object + * @name: (allow-none): new name of object * * Sets the name of @object, or gives @object a guaranteed unique - * name (if @name is NULL). + * name (if @name is %NULL). * This function makes a copy of the provided name, so the caller * retains ownership of the name it sent. * - * Returns: TRUE if the name could be set. Since Objects that have - * a parent cannot be renamed, this function returns FALSE in those + * Returns: %TRUE if the name could be set. Since Objects that have + * a parent cannot be renamed, this function returns %FALSE in those * cases. * * MT safe. This function grabs and releases @object's LOCK. @@ -709,10 +632,8 @@ gst_object_set_name (GstObject * object, const gchar * name) GST_OBJECT_UNLOCK (object); result = gst_object_set_name_default (object); } - /* FIXME-0.11: this misses a g_object_notify (object, "name"); unless called - * from gst_object_set_property. - * Ideally remove such custom setters (or make it static). - */ + + g_object_notify (G_OBJECT (object), "name"); return result; /* error */ @@ -730,12 +651,13 @@ had_parent: * * Returns a copy of the name of @object. * Caller should g_free() the return value after usage. - * For a nameless object, this returns NULL, which you can safely g_free() + * For a nameless object, this returns %NULL, which you can safely g_free() * as well. * * Free-function: g_free * - * Returns: (transfer full): the name of @object. g_free() after usage. + * Returns: (transfer full) (nullable): the name of @object. g_free() + * after usage. * * MT safe. This function grabs and releases @object's LOCK. */ @@ -754,82 +676,14 @@ gst_object_get_name (GstObject * object) } /** - * gst_object_set_name_prefix: - * @object: a #GstObject - * @name_prefix: new name prefix of @object - * - * Sets the name prefix of @object to @name_prefix. - * This function makes a copy of the provided name prefix, so the caller - * retains ownership of the name prefix it sent. - * - * MT safe. This function grabs and releases @object's LOCK. - * - * Deprecated: deprecated because the name prefix has never actually been used - * for anything. - */ -#ifndef GST_REMOVE_DEPRECATED -#ifdef GST_DISABLE_DEPRECATED -void gst_object_set_name_prefix (GstObject * object, const gchar * name_prefix); -#endif -void -gst_object_set_name_prefix (GstObject * object, const gchar * name_prefix) -{ - g_return_if_fail (GST_IS_OBJECT (object)); - - GST_OBJECT_LOCK (object); - g_free (object->name_prefix); - object->name_prefix = g_strdup (name_prefix); /* NULL gives NULL */ - GST_OBJECT_UNLOCK (object); -} -#endif /* GST_REMOVE_DEPRECATED */ - -/** - * gst_object_get_name_prefix: - * @object: a #GstObject - * - * Returns a copy of the name prefix of @object. - * Caller should g_free() the return value after usage. - * For a prefixless object, this returns NULL, which you can safely g_free() - * as well. - * - * Returns: the name prefix of @object. g_free() after usage. - * - * MT safe. This function grabs and releases @object's LOCK. - * - * Deprecated: deprecated because the name prefix has never actually been used - * for anything. - */ -#ifndef GST_REMOVE_DEPRECATED -#ifdef GST_DISABLE_DEPRECATED -gchar *gst_object_get_name_prefix (GstObject * object); -#endif -gchar * -gst_object_get_name_prefix (GstObject * object) -{ - gchar *result = NULL; - - g_return_val_if_fail (GST_IS_OBJECT (object), NULL); - - GST_OBJECT_LOCK (object); - result = g_strdup (object->name_prefix); - GST_OBJECT_UNLOCK (object); - - return result; -} -#endif /* GST_REMOVE_DEPRECATED */ - -/** * gst_object_set_parent: - * @object: a #GstObject + * @object: (transfer floating): a #GstObject * @parent: new parent of object * * Sets the parent of @object to @parent. The object's reference count will - * be incremented, and any floating reference will be removed (see gst_object_sink()). - * - * This function causes the parent-set signal to be emitted when the parent - * was successfully set. + * be incremented, and any floating reference will be removed (see gst_object_ref_sink()). * - * Returns: TRUE if @parent could be set or FALSE when @object + * Returns: %TRUE if @parent could be set or %FALSE when @object * already had a parent or @object and @parent are the same. * * MT safe. Grabs and releases @object's LOCK. @@ -848,21 +702,16 @@ gst_object_set_parent (GstObject * object, GstObject * parent) if (G_UNLIKELY (object->parent != NULL)) goto had_parent; - /* sink object, we don't call our own function because we don't - * need to release/acquire the lock needlessly or touch the refcount - * in the floating case. */ object->parent = parent; - if (G_LIKELY (GST_OBJECT_IS_FLOATING (object))) { - GST_CAT_TRACE_OBJECT (GST_CAT_REFCOUNTING, object, - "unsetting floating flag"); - GST_OBJECT_FLAG_UNSET (object, GST_OBJECT_FLOATING); - GST_OBJECT_UNLOCK (object); - } else { - GST_OBJECT_UNLOCK (object); - gst_object_ref (object); - } + gst_object_ref_sink (object); + GST_OBJECT_UNLOCK (object); - g_signal_emit (object, gst_object_signals[PARENT_SET], 0, parent); + /* FIXME-2.0: this does not work, the deep notify takes the lock from the + * parent object and deadlocks when the parent holds its lock when calling + * this function (like _element_add_pad()), we need to use a GRecMutex + * for locking the parent instead. + */ + /* g_object_notify_by_pspec ((GObject *)object, properties[PROP_PARENT]); */ return TRUE; @@ -871,6 +720,8 @@ had_parent: { GST_CAT_DEBUG_OBJECT (GST_CAT_REFCOUNTING, object, "set parent failed, object already had a parent"); + gst_object_ref_sink (object); + gst_object_unref (object); GST_OBJECT_UNLOCK (object); return FALSE; } @@ -883,8 +734,8 @@ had_parent: * Returns the parent of @object. This function increases the refcount * of the parent object so you should gst_object_unref() it after usage. * - * Returns: (transfer full): parent of @object, this can be NULL if @object - * has no parent. unref after usage. + * Returns: (transfer full) (nullable): parent of @object, this can be + * %NULL if @object has no parent. unref after usage. * * MT safe. Grabs and releases @object's LOCK. */ @@ -928,7 +779,7 @@ gst_object_unparent (GstObject * object) object->parent = NULL; GST_OBJECT_UNLOCK (object); - g_signal_emit (object, gst_object_signals[PARENT_UNSET], 0, parent); + /* g_object_notify_by_pspec ((GObject *)object, properties[PROP_PARENT]); */ gst_object_unref (object); } else { @@ -937,19 +788,47 @@ gst_object_unparent (GstObject * object) } /** - * gst_object_has_ancestor: + * gst_object_has_as_parent: + * @object: a #GstObject to check + * @parent: a #GstObject to check as parent + * + * Check if @parent is the parent of @object. + * E.g. a #GstElement can check if it owns a given #GstPad. + * + * Returns: %FALSE if either @object or @parent is %NULL. %TRUE if @parent is + * the parent of @object. Otherwise %FALSE. + * + * MT safe. Grabs and releases @object's locks. + * Since: 1.6 + */ +gboolean +gst_object_has_as_parent (GstObject * object, GstObject * parent) +{ + gboolean result = FALSE; + + if (G_LIKELY (GST_IS_OBJECT (object) && GST_IS_OBJECT (parent))) { + GST_OBJECT_LOCK (object); + result = GST_OBJECT_PARENT (object) == parent; + GST_OBJECT_UNLOCK (object); + } + + return result; +} + +/** + * gst_object_has_as_ancestor: * @object: a #GstObject to check * @ancestor: a #GstObject to check as ancestor * * Check if @object has an ancestor @ancestor somewhere up in * the hierarchy. One can e.g. check if a #GstElement is inside a #GstPipeline. * - * Returns: TRUE if @ancestor is an ancestor of @object. + * Returns: %TRUE if @ancestor is an ancestor of @object. * * MT safe. Grabs and releases @object's locks. */ gboolean -gst_object_has_ancestor (GstObject * object, GstObject * ancestor) +gst_object_has_as_ancestor (GstObject * object, GstObject * ancestor) { GstObject *parent, *tmp; @@ -972,6 +851,28 @@ gst_object_has_ancestor (GstObject * object, GstObject * ancestor) } /** + * gst_object_has_ancestor: + * @object: a #GstObject to check + * @ancestor: a #GstObject to check as ancestor + * + * Check if @object has an ancestor @ancestor somewhere up in + * the hierarchy. One can e.g. check if a #GstElement is inside a #GstPipeline. + * + * Returns: %TRUE if @ancestor is an ancestor of @object. + * + * Deprecated: Use gst_object_has_as_ancestor() instead. + * + * MT safe. Grabs and releases @object's locks. + */ +#ifndef GST_REMOVE_DEPRECATED +gboolean +gst_object_has_ancestor (GstObject * object, GstObject * ancestor) +{ + return gst_object_has_as_ancestor (object, ancestor); +} +#endif + +/** * gst_object_check_uniqueness: * @list: (transfer none) (element-type Gst.Object): a list of #GstObject to * check through @@ -981,10 +882,10 @@ gst_object_has_ancestor (GstObject * object, GstObject * ancestor) * does not do any locking of any kind. You might want to protect the * provided list with the lock of the owner of the list. This function * will lock each #GstObject in the list to compare the name, so be - * carefull when passing a list with a locked object. + * careful when passing a list with a locked object. * - * Returns: TRUE if a #GstObject named @name does not appear in @list, - * FALSE if it does. + * Returns: %TRUE if a #GstObject named @name does not appear in @list, + * %FALSE if it does. * * MT safe. Grabs and releases the LOCK of each object in the list. */ @@ -1014,65 +915,6 @@ gst_object_check_uniqueness (GList * list, const gchar * name) } -#if !defined(GST_DISABLE_LOADSAVE) && !defined(GST_REMOVE_DEPRECATED) -/** - * gst_object_save_thyself: - * @object: a #GstObject to save - * @parent: The parent XML node to save @object into - * - * Saves @object into the parent XML node. - * - * Returns: the new xmlNodePtr with the saved object - */ -GstXmlNodePtr -gst_object_save_thyself (GstObject * object, GstXmlNodePtr parent) -{ - GstObjectClass *oclass; - - g_return_val_if_fail (GST_IS_OBJECT (object), parent); - g_return_val_if_fail (parent != NULL, parent); - - oclass = GST_OBJECT_GET_CLASS (object); - - if (oclass->save_thyself) - oclass->save_thyself (object, parent); - - g_signal_emit (object, gst_object_signals[OBJECT_SAVED], 0, parent); - - return parent; -} - -/** - * gst_object_restore_thyself: - * @object: a #GstObject to load into - * @self: The XML node to load @object from - * - * Restores @object with the data from the parent XML node. - */ -void -gst_object_restore_thyself (GstObject * object, GstXmlNodePtr self) -{ - GstObjectClass *oclass; - - g_return_if_fail (GST_IS_OBJECT (object)); - g_return_if_fail (self != NULL); - - oclass = GST_OBJECT_GET_CLASS (object); - - if (oclass->restore_thyself) - oclass->restore_thyself (object, self); -} - -static void -gst_object_real_restore_thyself (GstObject * object, GstXmlNodePtr self) -{ - g_return_if_fail (GST_IS_OBJECT (object)); - g_return_if_fail (self != NULL); - - gst_class_signal_emit_by_name (object, "object_loaded", self); -} -#endif /* GST_DISABLE_LOADSAVE */ - static void gst_object_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) @@ -1082,9 +924,12 @@ gst_object_set_property (GObject * object, guint prop_id, gstobject = GST_OBJECT_CAST (object); switch (prop_id) { - case ARG_NAME: + case PROP_NAME: gst_object_set_name (gstobject, g_value_get_string (value)); break; + case PROP_PARENT: + gst_object_set_parent (gstobject, g_value_get_object (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1100,9 +945,12 @@ gst_object_get_property (GObject * object, guint prop_id, gstobject = GST_OBJECT_CAST (object); switch (prop_id) { - case ARG_NAME: + case PROP_NAME: g_value_take_string (value, gst_object_get_name (gstobject)); break; + case PROP_PARENT: + g_value_take_object (value, gst_object_get_parent (gstobject)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1142,7 +990,7 @@ gst_object_get_path_string (GstObject * object) path = g_strdup (""); /* first walk the object hierarchy to build a list of the parents, - * be carefull here with refcounting. */ + * be careful here with refcounting. */ do { if (GST_IS_OBJECT (object)) { parent = gst_object_get_parent (object); @@ -1195,83 +1043,447 @@ gst_object_get_path_string (GstObject * object) return path; } +/* controller helper functions */ -struct _GstSignalObject +/* + * gst_object_find_control_binding: + * @self: the gobject to search for a property in + * @name: the gobject property name to look for + * + * Searches the list of properties under control. + * + * Returns: (nullable): a #GstControlBinding or %NULL if the property + * is not being controlled. + */ +static GstControlBinding * +gst_object_find_control_binding (GstObject * self, const gchar * name) { - GObject object; -}; + GstControlBinding *binding; + GList *node; + + for (node = self->control_bindings; node; node = g_list_next (node)) { + binding = node->data; + /* FIXME: eventually use GQuark to speed it up */ + if (!strcmp (binding->name, name)) { + GST_DEBUG_OBJECT (self, "found control binding for property '%s'", name); + return binding; + } + } + GST_DEBUG_OBJECT (self, "controller does not manage property '%s'", name); + + return NULL; +} -struct _GstSignalObjectClass +/* controller functions */ + +/** + * gst_object_suggest_next_sync: + * @object: the object that has controlled properties + * + * Returns a suggestion for timestamps where buffers should be split + * to get best controller results. + * + * Returns: Returns the suggested timestamp or %GST_CLOCK_TIME_NONE + * if no control-rate was set. + */ +GstClockTime +gst_object_suggest_next_sync (GstObject * object) { - GObjectClass parent_class; + GstClockTime ret; - /* signals */ -#if !defined(GST_DISABLE_LOADSAVE) && !defined(GST_REMOVE_DEPRECATED) - void (*object_loaded) (GstSignalObject * object, GstObject * new, - GstXmlNodePtr self); -#endif -}; + g_return_val_if_fail (GST_IS_OBJECT (object), GST_CLOCK_TIME_NONE); + g_return_val_if_fail (object->control_rate != GST_CLOCK_TIME_NONE, + GST_CLOCK_TIME_NONE); -G_DEFINE_TYPE (GstSignalObject, gst_signal_object, G_TYPE_OBJECT); + GST_OBJECT_LOCK (object); -static void -gst_signal_object_class_init (GstSignalObjectClass * klass) -{ - parent_class = g_type_class_peek_parent (klass); - -#if !defined(GST_DISABLE_LOADSAVE) && !defined(GST_REMOVE_DEPRECATED) - gst_signal_object_signals[SO_OBJECT_LOADED] = - g_signal_new ("object-loaded", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstSignalObjectClass, object_loaded), - NULL, NULL, gst_marshal_VOID__OBJECT_POINTER, G_TYPE_NONE, 2, - G_TYPE_OBJECT, G_TYPE_POINTER); -#endif + /* TODO: Implement more logic, depending on interpolation mode and control + * points + * FIXME: we need playback direction + */ + ret = object->last_sync + object->control_rate; + + GST_OBJECT_UNLOCK (object); + + return ret; } -static void -gst_signal_object_init (GstSignalObject * object) +/** + * gst_object_sync_values: + * @object: the object that has controlled properties + * @timestamp: the time that should be processed + * + * Sets the properties of the object, according to the #GstControlSources that + * (maybe) handle them and for the given timestamp. + * + * If this function fails, it is most likely the application developers fault. + * Most probably the control sources are not setup correctly. + * + * Returns: %TRUE if the controller values could be applied to the object + * properties, %FALSE otherwise + */ +gboolean +gst_object_sync_values (GstObject * object, GstClockTime timestamp) { + GList *node; + gboolean ret = TRUE; + + g_return_val_if_fail (GST_IS_OBJECT (object), FALSE); + g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE); + + GST_LOG_OBJECT (object, "sync_values"); + if (!object->control_bindings) + return TRUE; + + /* FIXME: this deadlocks */ + /* GST_OBJECT_LOCK (object); */ + g_object_freeze_notify ((GObject *) object); + for (node = object->control_bindings; node; node = g_list_next (node)) { + ret &= gst_control_binding_sync_values ((GstControlBinding *) node->data, + object, timestamp, object->last_sync); + } + object->last_sync = timestamp; + g_object_thaw_notify ((GObject *) object); + /* GST_OBJECT_UNLOCK (object); */ + + return ret; } + /** - * gst_class_signal_connect - * @klass: a #GstObjectClass to attach the signal to - * @name: the name of the signal to attach to - * @func: the signal function - * @func_data: a pointer to user data + * gst_object_has_active_control_bindings: + * @object: the object that has controlled properties * - * Connect to a class signal. + * Check if the @object has active controlled properties. * - * Returns: the signal id. + * Returns: %TRUE if the object has active controlled properties */ -guint -gst_class_signal_connect (GstObjectClass * klass, - const gchar * name, gpointer func, gpointer func_data) +gboolean +gst_object_has_active_control_bindings (GstObject * object) { - /* [0.11] func parameter needs to be changed to a GCallback * - * doing so now would be an API break. */ - return g_signal_connect (klass->signal_object, name, G_CALLBACK (func), - func_data); + gboolean res = FALSE; + GList *node; + + g_return_val_if_fail (GST_IS_OBJECT (object), FALSE); + + GST_OBJECT_LOCK (object); + for (node = object->control_bindings; node; node = g_list_next (node)) { + res |= !gst_control_binding_is_disabled ((GstControlBinding *) node->data); + } + GST_OBJECT_UNLOCK (object); + return res; } -#if !defined(GST_DISABLE_LOADSAVE) && !defined(GST_REMOVE_DEPRECATED) /** - * gst_class_signal_emit_by_name: - * @object: a #GstObject that emits the signal - * @name: the name of the signal to emit - * @self: data for the signal + * gst_object_set_control_bindings_disabled: + * @object: the object that has controlled properties + * @disabled: boolean that specifies whether to disable the controller + * or not. * - * emits the named class signal. + * This function is used to disable all controlled properties of the @object for + * some time, i.e. gst_object_sync_values() will do nothing. */ void -gst_class_signal_emit_by_name (GstObject * object, - const gchar * name, GstXmlNodePtr self) +gst_object_set_control_bindings_disabled (GstObject * object, gboolean disabled) { - GstObjectClass *oclass; + GList *node; - oclass = GST_OBJECT_GET_CLASS (object); + g_return_if_fail (GST_IS_OBJECT (object)); - g_signal_emit_by_name (oclass->signal_object, name, object, self); + GST_OBJECT_LOCK (object); + for (node = object->control_bindings; node; node = g_list_next (node)) { + gst_control_binding_set_disabled ((GstControlBinding *) node->data, + disabled); + } + GST_OBJECT_UNLOCK (object); } -#endif /* GST_DISABLE_LOADSAVE */ +/** + * gst_object_set_control_binding_disabled: + * @object: the object that has controlled properties + * @property_name: property to disable + * @disabled: boolean that specifies whether to disable the controller + * or not. + * + * This function is used to disable the control bindings on a property for + * some time, i.e. gst_object_sync_values() will do nothing for the + * property. + */ +void +gst_object_set_control_binding_disabled (GstObject * object, + const gchar * property_name, gboolean disabled) +{ + GstControlBinding *binding; + + g_return_if_fail (GST_IS_OBJECT (object)); + g_return_if_fail (property_name); + + GST_OBJECT_LOCK (object); + if ((binding = gst_object_find_control_binding (object, property_name))) { + gst_control_binding_set_disabled (binding, disabled); + } + GST_OBJECT_UNLOCK (object); +} + + +/** + * gst_object_add_control_binding: + * @object: the controller object + * @binding: (transfer floating): the #GstControlBinding that should be used + * + * Attach the #GstControlBinding to the object. If there already was a + * #GstControlBinding for this property it will be replaced. + * + * The object's reference count will be incremented, and any floating + * reference will be removed (see gst_object_ref_sink()) + * + * Returns: %FALSE if the given @binding has not been setup for this object or + * has been setup for a non suitable property, %TRUE otherwise. + */ +gboolean +gst_object_add_control_binding (GstObject * object, GstControlBinding * binding) +{ + GstControlBinding *old; + + g_return_val_if_fail (GST_IS_OBJECT (object), FALSE); + g_return_val_if_fail (GST_IS_CONTROL_BINDING (binding), FALSE); + g_return_val_if_fail (binding->pspec, FALSE); + + GST_OBJECT_LOCK (object); + if ((old = gst_object_find_control_binding (object, binding->name))) { + GST_DEBUG_OBJECT (object, "controlled property %s removed", old->name); + object->control_bindings = g_list_remove (object->control_bindings, old); + gst_object_unparent (GST_OBJECT_CAST (old)); + } + object->control_bindings = g_list_prepend (object->control_bindings, binding); + gst_object_set_parent (GST_OBJECT_CAST (binding), object); + GST_DEBUG_OBJECT (object, "controlled property %s added", binding->name); + GST_OBJECT_UNLOCK (object); + + return TRUE; +} + +/** + * gst_object_get_control_binding: + * @object: the object + * @property_name: name of the property + * + * Gets the corresponding #GstControlBinding for the property. This should be + * unreferenced again after use. + * + * Returns: (transfer full) (nullable): the #GstControlBinding for + * @property_name or %NULL if the property is not controlled. + */ +GstControlBinding * +gst_object_get_control_binding (GstObject * object, const gchar * property_name) +{ + GstControlBinding *binding; + + g_return_val_if_fail (GST_IS_OBJECT (object), NULL); + g_return_val_if_fail (property_name, NULL); + + GST_OBJECT_LOCK (object); + if ((binding = gst_object_find_control_binding (object, property_name))) { + gst_object_ref (binding); + } + GST_OBJECT_UNLOCK (object); + + return binding; +} + +/** + * gst_object_remove_control_binding: + * @object: the object + * @binding: the binding + * + * Removes the corresponding #GstControlBinding. If it was the + * last ref of the binding, it will be disposed. + * + * Returns: %TRUE if the binding could be removed. + */ +gboolean +gst_object_remove_control_binding (GstObject * object, + GstControlBinding * binding) +{ + GList *node; + gboolean ret = FALSE; + + g_return_val_if_fail (GST_IS_OBJECT (object), FALSE); + g_return_val_if_fail (GST_IS_CONTROL_BINDING (binding), FALSE); + + GST_OBJECT_LOCK (object); + if ((node = g_list_find (object->control_bindings, binding))) { + GST_DEBUG_OBJECT (object, "controlled property %s removed", binding->name); + object->control_bindings = + g_list_delete_link (object->control_bindings, node); + gst_object_unparent (GST_OBJECT_CAST (binding)); + ret = TRUE; + } + GST_OBJECT_UNLOCK (object); + + return ret; +} + +/** + * gst_object_get_value: + * @object: the object that has controlled properties + * @property_name: the name of the property to get + * @timestamp: the time the control-change should be read from + * + * Gets the value for the given controlled property at the requested time. + * + * Returns: (nullable): the GValue of the property at the given time, + * or %NULL if the property isn't controlled. + */ +GValue * +gst_object_get_value (GstObject * object, const gchar * property_name, + GstClockTime timestamp) +{ + GstControlBinding *binding; + GValue *val = NULL; + + g_return_val_if_fail (GST_IS_OBJECT (object), NULL); + g_return_val_if_fail (property_name, NULL); + g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), NULL); + + GST_OBJECT_LOCK (object); + if ((binding = gst_object_find_control_binding (object, property_name))) { + val = gst_control_binding_get_value (binding, timestamp); + } + GST_OBJECT_UNLOCK (object); + + return val; +} + +/** + * gst_object_get_value_array: (skip) + * @object: the object that has controlled properties + * @property_name: the name of the property to get + * @timestamp: the time that should be processed + * @interval: the time spacing between subsequent values + * @n_values: the number of values + * @values: array to put control-values in + * + * Gets a number of values for the given controlled property starting at the + * requested time. The array @values need to hold enough space for @n_values of + * the same type as the objects property's type. + * + * This function is useful if one wants to e.g. draw a graph of the control + * curve or apply a control curve sample by sample. + * + * The values are unboxed and ready to be used. The similar function + * gst_object_get_g_value_array() returns the array as #GValues and is + * better suites for bindings. + * + * Returns: %TRUE if the given array could be filled, %FALSE otherwise + */ +gboolean +gst_object_get_value_array (GstObject * object, const gchar * property_name, + GstClockTime timestamp, GstClockTime interval, guint n_values, + gpointer values) +{ + gboolean res = FALSE; + GstControlBinding *binding; + + g_return_val_if_fail (GST_IS_OBJECT (object), FALSE); + g_return_val_if_fail (property_name, FALSE); + g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE); + g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (interval), FALSE); + g_return_val_if_fail (values, FALSE); + + GST_OBJECT_LOCK (object); + if ((binding = gst_object_find_control_binding (object, property_name))) { + res = gst_control_binding_get_value_array (binding, timestamp, interval, + n_values, values); + } + GST_OBJECT_UNLOCK (object); + return res; +} + +/** + * gst_object_get_g_value_array: + * @object: the object that has controlled properties + * @property_name: the name of the property to get + * @timestamp: the time that should be processed + * @interval: the time spacing between subsequent values + * @n_values: the number of values + * @values: (array length=n_values): array to put control-values in + * + * Gets a number of #GValues for the given controlled property starting at the + * requested time. The array @values need to hold enough space for @n_values of + * #GValue. + * + * This function is useful if one wants to e.g. draw a graph of the control + * curve or apply a control curve sample by sample. + * + * Returns: %TRUE if the given array could be filled, %FALSE otherwise + */ +gboolean +gst_object_get_g_value_array (GstObject * object, const gchar * property_name, + GstClockTime timestamp, GstClockTime interval, guint n_values, + GValue * values) +{ + gboolean res = FALSE; + GstControlBinding *binding; + + g_return_val_if_fail (GST_IS_OBJECT (object), FALSE); + g_return_val_if_fail (property_name, FALSE); + g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE); + g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (interval), FALSE); + g_return_val_if_fail (values, FALSE); + + GST_OBJECT_LOCK (object); + if ((binding = gst_object_find_control_binding (object, property_name))) { + res = gst_control_binding_get_g_value_array (binding, timestamp, interval, + n_values, values); + } + GST_OBJECT_UNLOCK (object); + return res; +} + + +/** + * gst_object_get_control_rate: + * @object: the object that has controlled properties + * + * Obtain the control-rate for this @object. Audio processing #GstElement + * objects will use this rate to sub-divide their processing loop and call + * gst_object_sync_values() in between. The length of the processing segment + * should be up to @control-rate nanoseconds. + * + * If the @object is not under property control, this will return + * %GST_CLOCK_TIME_NONE. This allows the element to avoid the sub-dividing. + * + * The control-rate is not expected to change if the element is in + * %GST_STATE_PAUSED or %GST_STATE_PLAYING. + * + * Returns: the control rate in nanoseconds + */ +GstClockTime +gst_object_get_control_rate (GstObject * object) +{ + g_return_val_if_fail (GST_IS_OBJECT (object), FALSE); + + return object->control_rate; +} + +/** + * gst_object_set_control_rate: + * @object: the object that has controlled properties + * @control_rate: the new control-rate in nanoseconds. + * + * Change the control-rate for this @object. Audio processing #GstElement + * objects will use this rate to sub-divide their processing loop and call + * gst_object_sync_values() in between. The length of the processing segment + * should be up to @control-rate nanoseconds. + * + * The control-rate should not change if the element is in %GST_STATE_PAUSED or + * %GST_STATE_PLAYING. + */ +void +gst_object_set_control_rate (GstObject * object, GstClockTime control_rate) +{ + g_return_if_fail (GST_IS_OBJECT (object)); + + object->control_rate = control_rate; +}