X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gst%2Fgstobject.c;h=9ce57fdf5deaf35e9582fc5141273358f63e0f2b;hb=321ee10bd6a3fec0951e93e3e112603693d86c33;hp=9b3c29e95d4850433afa1d2cec7ea14a182a094b;hpb=483c0fe4909c4e2f55681496652fb1f291fe3bd7;p=platform%2Fupstream%2Fgstreamer.git diff --git a/gst/gstobject.c b/gst/gstobject.c index 9b3c29e..9ce57fd 100644 --- a/gst/gstobject.c +++ b/gst/gstobject.c @@ -80,14 +80,70 @@ * gst_object_set_name() and gst_object_get_name() are used to set/get the name * of the object. * + * + * 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 continously pull + * values 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 made the controller to update all gobject properties that are under + * control with the current values based on timestamp. + * + * + * + * What needs to be done in applications? + * Again its not a lot to change. + * + * + * first put some properties under control, by calling + * gst_object_control_properties (object, "prop1", "prop2",...); + * + * + * 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_control_binding_direct_new (objetct, "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 + * + * + * + * + * * Last reviewed on 2005-11-09 (0.9.4) */ #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 @@ -148,7 +204,8 @@ gst_object_class_init (GstObjectClass * klass) GObjectClass *gobject_class = G_OBJECT_CLASS (klass); #ifndef GST_DISABLE_TRACE - _gst_object_trace = gst_alloc_trace_register (g_type_name (GST_TYPE_OBJECT)); + _gst_object_trace = + _gst_alloc_trace_register (g_type_name (GST_TYPE_OBJECT), 0); #endif gobject_class->set_property = gst_object_set_property; @@ -157,14 +214,12 @@ gst_object_class_init (GstObjectClass * klass) 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); - g_object_class_install_property (gobject_class, PROP_NAME, - properties[PROP_NAME]); properties[PROP_PARENT] = g_param_spec_object ("parent", "Parent", "The parent of the object", GST_TYPE_OBJECT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (gobject_class, PROP_PARENT, - properties[PROP_PARENT]); + + g_object_class_install_properties (gobject_class, PROP_LAST, properties); /** * GstObject::deep-notify: @@ -196,16 +251,19 @@ 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); + _gst_alloc_trace_new (_gst_object_trace, object); #endif object->flags = 0; + + object->control_rate = 100 * GST_MSECOND; + object->last_sync = GST_CLOCK_TIME_NONE; } /** @@ -261,7 +319,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 @@ -292,21 +350,20 @@ gst_object_ref_sink (gpointer object) * replace * @newobj: (transfer 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. + * + * Either @newobj and the value pointed to by @oldobj may be NULL. * - * Make sure not to LOCK @oldobj because it might be unreffed - * which could cause a deadlock when it is disposed. + * Returns: TRUE if @newobj was different from @oldobj */ -void +gboolean gst_object_replace (GstObject ** oldobj, GstObject * newobj) { - 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)); + GstObject *oldptr; + + 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)", @@ -316,14 +373,25 @@ gst_object_replace (GstObject ** oldobj, GstObject * newobj) newobj ? G_OBJECT (newobj)->ref_count : 0); #endif - if (G_LIKELY (*oldobj != newobj)) { - if (newobj) - gst_object_ref (newobj); - if (*oldobj) - gst_object_unref (*oldobj); + oldptr = g_atomic_pointer_get ((gpointer *) oldobj); + + if (G_UNLIKELY (oldptr == newobj)) + return FALSE; + + if (newobj) + g_object_ref (newobj); - *oldobj = 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); + + return oldptr != newobj; } /* dispose is called when the object has to release all links @@ -331,6 +399,7 @@ 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"); @@ -341,6 +410,16 @@ gst_object_dispose (GObject * object) GST_OBJECT_PARENT (object) = NULL; GST_OBJECT_UNLOCK (object); + if (self->control_bindings) { + GList *node; + + for (node = self->control_bindings; node; node = g_list_next (node)) { + g_object_unref (node->data); + } + g_list_free (self->control_bindings); + self->control_bindings = NULL; + } + ((GObjectClass *) gst_object_parent_class)->dispose (object); return; @@ -370,10 +449,10 @@ gst_object_finalize (GObject * 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); + _gst_alloc_trace_free (_gst_object_trace, object); #endif ((GObjectClass *) gst_object_parent_class)->finalize (object); @@ -405,7 +484,7 @@ gst_object_dispatch_properties_changed (GObject * object, 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 @@ -654,7 +733,7 @@ gst_object_set_parent (GstObject * object, GstObject * parent) goto had_parent; object->parent = parent; - g_object_ref_sink (object); + gst_object_ref_sink (object); GST_OBJECT_UNLOCK (object); /* FIXME, this does not work, the deep notify takes the lock from the parent @@ -939,3 +1018,402 @@ gst_object_get_path_string (GstObject * object) return path; } + +/* controller helper functions */ + +/* + * 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: a #GstControlBinding or %NULL if the property is not being + * controlled. + */ +static GstControlBinding * +gst_object_find_control_binding (GstObject * self, const gchar * name) +{ + 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; +} + +/* 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) +{ + GstClockTime ret; + + 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); + + GST_OBJECT_LOCK (object); + + /* 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; +} + +/** + * 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_object_has_active_control_bindings: + * @object: the object that has controlled properties + * + * Check if the @object has an active controlled properties. + * + * Returns: %TRUE if the object has active controlled properties + */ +gboolean +gst_object_has_active_control_bindings (GstObject * object) +{ + 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; +} + +/** + * gst_object_set_control_bindings_disabled: + * @object: the object that has controlled properties + * @disabled: boolean that specifies whether to disable the controller + * or not. + * + * 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_object_set_control_bindings_disabled (GstObject * object, gboolean disabled) +{ + GList *node; + + g_return_if_fail (GST_IS_OBJECT (object)); + + 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); +} + +/** + * 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 #GstController on a property for + * some time, i.e. gst_controller_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 full): the #GstControlBinding that should be used + * + * Sets the #GstControlBinding. If there already was a #GstControlBinding + * for this property it will be replaced. + * The @object will take ownership of the @binding. + * + * Returns: %FALSE if the given @binding has not been setup for this object or + * %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 (g_type_is_a (binding->pspec->owner_type, + // G_OBJECT_TYPE (object)), 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): 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))) { + g_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: 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: + * @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 controllered 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. + * + * 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, + 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_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() inbetween. 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() inbetween. 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; +}