| /* GStreamer |
| * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> |
| * 2000 Wim Taymans <wtay@chello.be> |
| * 2005 Wim Taymans <wim@fluendo.com> |
| * |
| * gstobject.c: Fundamental class used for all of GStreamer |
| * |
| * 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: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 |
| * #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 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()). |
| * |
| * 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. |
| * |
| * <refsect2> |
| * <title>controlled properties</title> |
| * <para> |
| * 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! |
| * <orderedlist> |
| * <listitem><para> |
| * mark gobject-properties paramspecs that make sense to be controlled, |
| * by GST_PARAM_CONTROLLABLE. |
| * </para></listitem> |
| * <listitem><para> |
| * when processing data (get, chain, loop function) at the beginning call |
| * gst_object_sync_values(element,timestamp). |
| * This will make the controller to update all gobject properties that are under |
| * control with the current values based on timestamp. |
| * </para></listitem> |
| * </orderedlist> |
| * |
| * What needs to be done in applications? |
| * Again it's not a lot to change. |
| * <orderedlist> |
| * <listitem><para> |
| * create a #GstControlSource. |
| * csource = gst_interpolation_control_source_new (); |
| * g_object_set (csource, "mode", GST_INTERPOLATION_MODE_LINEAR, NULL); |
| * </para></listitem> |
| * <listitem><para> |
| * Attach the #GstControlSource on the controller to a property. |
| * gst_object_add_control_binding (object, gst_direct_control_binding_new (object, "prop1", csource)); |
| * </para></listitem> |
| * <listitem><para> |
| * 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); |
| * </para></listitem> |
| * <listitem><para> |
| * start your pipeline |
| * </para></listitem> |
| * </orderedlist> |
| * </para> |
| * </refsect2> |
| * |
| * Last reviewed on 2012-03-29 (0.11.3) |
| */ |
| |
| #include "gst_private.h" |
| #include "glib-compat-private.h" |
| |
| #include "gstobject.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 */ |
| enum |
| { |
| DEEP_NOTIFY, |
| LAST_SIGNAL |
| }; |
| |
| enum |
| { |
| PROP_0, |
| PROP_NAME, |
| PROP_PARENT, |
| PROP_LAST |
| }; |
| |
| enum |
| { |
| SO_OBJECT_LOADED, |
| SO_LAST_SIGNAL |
| }; |
| |
| /* maps type name quark => count */ |
| static GData *object_name_counts = NULL; |
| |
| G_LOCK_DEFINE_STATIC (object_name_mutex); |
| |
| 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); |
| |
| static void gst_object_dispose (GObject * object); |
| static void gst_object_finalize (GObject * object); |
| |
| static gboolean gst_object_set_name_default (GstObject * object); |
| |
| static guint gst_object_signals[LAST_SIGNAL] = { 0 }; |
| |
| static GParamSpec *properties[PROP_LAST]; |
| |
| G_DEFINE_ABSTRACT_TYPE (GstObject, gst_object, G_TYPE_INITIALLY_UNOWNED); |
| |
| static void |
| 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), -2); |
| #endif |
| |
| gobject_class->set_property = gst_object_set_property; |
| gobject_class->get_property = gst_object_get_property; |
| |
| 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: |
| * |
| * 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. |
| */ |
| 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_properties (gobject_class, PROP_LAST, properties); |
| |
| /** |
| * GstObject::deep-notify: |
| * @gstobject: a #GstObject |
| * @prop_object: the object that originated the signal |
| * @prop: the property that changed |
| * |
| * The deep notify signal is used to be notified of property changes. It is |
| * typically attached to the toplevel bin to receive notifications from all |
| * the elements contained in that bin. |
| */ |
| gst_object_signals[DEEP_NOTIFY] = |
| 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, g_cclosure_marshal_generic, G_TYPE_NONE, 2, GST_TYPE_OBJECT, |
| G_TYPE_PARAM); |
| |
| klass->path_string_separator = "/"; |
| |
| /* see the comments at gst_object_dispatch_properties_changed */ |
| gobject_class->dispatch_properties_changed |
| = GST_DEBUG_FUNCPTR (gst_object_dispatch_properties_changed); |
| |
| gobject_class->dispose = gst_object_dispose; |
| gobject_class->finalize = gst_object_finalize; |
| } |
| |
| static void |
| gst_object_init (GstObject * object) |
| { |
| 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; |
| |
| object->control_rate = 100 * GST_MSECOND; |
| object->last_sync = GST_CLOCK_TIME_NONE; |
| } |
| |
| /** |
| * gst_object_ref: |
| * @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 |
| * atomic refcounting. |
| * |
| * This object returns the input parameter to ease writing |
| * constructs like : |
| * result = gst_object_ref (object->parent); |
| * |
| * Returns: (transfer full) (type Gst.Object): A pointer to @object |
| */ |
| gpointer |
| gst_object_ref (gpointer object) |
| { |
| g_return_val_if_fail (object != NULL, NULL); |
| |
| #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); |
| #endif |
| g_object_ref (object); |
| |
| return object; |
| } |
| |
| /** |
| * gst_object_unref: |
| * @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 |
| * on @object as it relies on atomic refcounting. |
| * |
| * The unref method should never be called with the LOCK held since |
| * this might deadlock the dispose function. |
| */ |
| void |
| gst_object_unref (gpointer object) |
| { |
| g_return_if_fail (object != NULL); |
| g_return_if_fail (((GObject *) object)->ref_count > 0); |
| |
| #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); |
| #endif |
| g_object_unref (object); |
| } |
| |
| /** |
| * gst_object_ref_sink: (skip) |
| * @object: a #GstObject to sink |
| * |
| * Increase the reference count of @object, and possibly remove the floating |
| * reference, if @object has a floating reference. |
| * |
| * In other words, if the object is floating, then this call "assumes ownership" |
| * of the floating reference, converting it to a normal reference by clearing |
| * the floating flag while leaving the reference count unchanged. If the object |
| * is not floating, then this call adds a new normal reference increasing the |
| * reference count by one. |
| */ |
| gpointer |
| gst_object_ref_sink (gpointer object) |
| { |
| g_return_val_if_fail (object != NULL, NULL); |
| |
| #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_replace: |
| * @oldobj: (inout) (transfer full): pointer to a place of a #GstObject to |
| * replace |
| * @newobj: (transfer none): a new #GstObject |
| * |
| * 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. |
| * |
| * Returns: TRUE if @newobj was different from @oldobj |
| */ |
| gboolean |
| gst_object_replace (GstObject ** oldobj, GstObject * 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)", |
| *oldobj, *oldobj ? GST_STR_NULL (GST_OBJECT_NAME (*oldobj)) : "(NONE)", |
| *oldobj ? G_OBJECT (*oldobj)->ref_count : 0, |
| newobj, newobj ? GST_STR_NULL (GST_OBJECT_NAME (newobj)) : "(NONE)", |
| newobj ? G_OBJECT (newobj)->ref_count : 0); |
| #endif |
| |
| oldptr = g_atomic_pointer_get ((gpointer *) oldobj); |
| |
| if (G_UNLIKELY (oldptr == newobj)) |
| return FALSE; |
| |
| if (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) |
| gst_object_unref (oldptr); |
| |
| return oldptr != newobj; |
| } |
| |
| /* dispose is called when the object has to release all links |
| * to other objects */ |
| static void |
| gst_object_dispose (GObject * object) |
| { |
| GstObject *self = (GstObject *) object; |
| GstObject *parent; |
| |
| GST_CAT_TRACE_OBJECT (GST_CAT_REFCOUNTING, object, "dispose"); |
| |
| GST_OBJECT_LOCK (object); |
| if ((parent = GST_OBJECT_PARENT (object))) |
| goto have_parent; |
| 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)) { |
| gst_object_unparent (node->data); |
| } |
| g_list_free (self->control_bindings); |
| self->control_bindings = NULL; |
| } |
| |
| ((GObjectClass *) gst_object_parent_class)->dispose (object); |
| |
| return; |
| |
| /* ERRORS */ |
| have_parent: |
| { |
| g_critical ("\nTrying to dispose object \"%s\", but it still has a " |
| "parent \"%s\".\nYou need to let the parent manage the " |
| "object instead of unreffing the object directly.\n", |
| GST_OBJECT_NAME (object), GST_OBJECT_NAME (parent)); |
| GST_OBJECT_UNLOCK (object); |
| /* ref the object again to revive it in this error case */ |
| gst_object_ref (object); |
| return; |
| } |
| } |
| |
| /* finalize is called when the object has to free its resources */ |
| static void |
| gst_object_finalize (GObject * object) |
| { |
| GstObject *gstobject = GST_OBJECT_CAST (object); |
| |
| GST_CAT_TRACE_OBJECT (GST_CAT_REFCOUNTING, object, "finalize"); |
| |
| g_signal_handlers_destroy (object); |
| |
| g_free (gstobject->name); |
| g_mutex_clear (&gstobject->lock); |
| |
| #ifndef GST_DISABLE_TRACE |
| _gst_alloc_trace_free (_gst_object_trace, object); |
| #endif |
| |
| ((GObjectClass *) gst_object_parent_class)->finalize (object); |
| } |
| |
| /* Changing a GObject property of a GstObject will result in "deep-notify" |
| * signals being emitted by the object itself, as well as in each parent |
| * object. This is so that an application can connect a listener to the |
| * top-level bin to catch property-change notifications for all contained |
| * elements. |
| * |
| * MT safe. |
| */ |
| static void |
| gst_object_dispatch_properties_changed (GObject * object, |
| guint n_pspecs, GParamSpec ** pspecs) |
| { |
| GstObject *gst_object, *parent, *old_parent; |
| guint i; |
| #ifndef GST_DISABLE_GST_DEBUG |
| gchar *name = NULL; |
| const gchar *debug_name; |
| #endif |
| |
| /* do the standard dispatching */ |
| ((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)) { |
| name = gst_object_get_name (gst_object); |
| debug_name = GST_STR_NULL (name); |
| } else |
| debug_name = ""; |
| #endif |
| |
| /* now let the parent dispatch those, too */ |
| parent = gst_object_get_parent (gst_object); |
| while (parent) { |
| for (i = 0; i < n_pspecs; i++) { |
| GST_CAT_LOG_OBJECT (GST_CAT_PROPERTIES, parent, |
| "deep notification from %s (%s)", debug_name, pspecs[i]->name); |
| |
| g_signal_emit (parent, gst_object_signals[DEEP_NOTIFY], |
| g_quark_from_string (pspecs[i]->name), gst_object, pspecs[i]); |
| } |
| |
| old_parent = parent; |
| parent = gst_object_get_parent (old_parent); |
| gst_object_unref (old_parent); |
| } |
| #ifndef GST_DISABLE_GST_DEBUG |
| g_free (name); |
| #endif |
| } |
| |
| /** |
| * gst_object_default_deep_notify: |
| * @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. |
| * |
| * 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 |
| * from the notify. The default handler will print the new value of the property |
| * using g_print. |
| * |
| * MT safe. This function grabs and releases @object's LOCK for getting its |
| * path string. |
| */ |
| void |
| gst_object_default_deep_notify (GObject * object, GstObject * orig, |
| GParamSpec * pspec, gchar ** excluded_props) |
| { |
| GValue value = { 0, }; /* the important thing is that value.type = 0 */ |
| gchar *str = NULL; |
| gchar *name = NULL; |
| |
| if (pspec->flags & G_PARAM_READABLE) { |
| /* let's not print these out for excluded properties... */ |
| while (excluded_props != NULL && *excluded_props != NULL) { |
| if (strcmp (pspec->name, *excluded_props) == 0) |
| return; |
| excluded_props++; |
| } |
| g_value_init (&value, pspec->value_type); |
| g_object_get_property (G_OBJECT (orig), pspec->name, &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); |
| g_free (str); |
| g_value_unset (&value); |
| } else { |
| name = gst_object_get_path_string (orig); |
| g_warning ("Parameter %s not readable in %s.", pspec->name, name); |
| g_free (name); |
| } |
| } |
| |
| static gboolean |
| gst_object_set_name_default (GstObject * object) |
| { |
| const gchar *type_name; |
| gint count; |
| gchar *name; |
| GQuark q; |
| guint i, l; |
| |
| /* to ensure guaranteed uniqueness across threads, only one thread |
| * may ever assign a name */ |
| G_LOCK (object_name_mutex); |
| |
| if (!object_name_counts) { |
| g_datalist_init (&object_name_counts); |
| } |
| |
| q = g_type_qname (G_OBJECT_TYPE (object)); |
| count = GPOINTER_TO_INT (g_datalist_id_get_data (&object_name_counts, q)); |
| g_datalist_id_set_data (&object_name_counts, q, GINT_TO_POINTER (count + 1)); |
| |
| G_UNLOCK (object_name_mutex); |
| |
| /* GstFooSink -> foosink<N> */ |
| type_name = g_quark_to_string (q); |
| if (strncmp (type_name, "Gst", 3) == 0) |
| type_name += 3; |
| /* 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]); |
| |
| GST_OBJECT_LOCK (object); |
| if (G_UNLIKELY (object->parent != NULL)) |
| goto had_parent; |
| |
| g_free (object->name); |
| object->name = name; |
| |
| GST_OBJECT_UNLOCK (object); |
| |
| return TRUE; |
| |
| had_parent: |
| { |
| g_free (name); |
| GST_WARNING ("parented objects can't be renamed"); |
| GST_OBJECT_UNLOCK (object); |
| return FALSE; |
| } |
| } |
| |
| /** |
| * gst_object_set_name: |
| * @object: a #GstObject |
| * @name: new name of object |
| * |
| * Sets the name of @object, or gives @object a guaranteed unique |
| * 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 |
| * cases. |
| * |
| * MT safe. This function grabs and releases @object's LOCK. |
| */ |
| gboolean |
| gst_object_set_name (GstObject * object, const gchar * name) |
| { |
| gboolean result; |
| |
| g_return_val_if_fail (GST_IS_OBJECT (object), FALSE); |
| |
| GST_OBJECT_LOCK (object); |
| |
| /* parented objects cannot be renamed */ |
| if (G_UNLIKELY (object->parent != NULL)) |
| goto had_parent; |
| |
| if (name != NULL) { |
| g_free (object->name); |
| object->name = g_strdup (name); |
| GST_OBJECT_UNLOCK (object); |
| result = TRUE; |
| } else { |
| 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). |
| */ |
| return result; |
| |
| /* error */ |
| had_parent: |
| { |
| GST_WARNING ("parented objects can't be renamed"); |
| GST_OBJECT_UNLOCK (object); |
| return FALSE; |
| } |
| } |
| |
| /** |
| * gst_object_get_name: |
| * @object: a #GstObject |
| * |
| * 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() |
| * as well. |
| * |
| * Free-function: g_free |
| * |
| * Returns: (transfer full): the name of @object. g_free() after usage. |
| * |
| * MT safe. This function grabs and releases @object's LOCK. |
| */ |
| gchar * |
| gst_object_get_name (GstObject * object) |
| { |
| gchar *result = NULL; |
| |
| g_return_val_if_fail (GST_IS_OBJECT (object), NULL); |
| |
| GST_OBJECT_LOCK (object); |
| result = g_strdup (object->name); |
| GST_OBJECT_UNLOCK (object); |
| |
| return result; |
| } |
| |
| /** |
| * gst_object_set_parent: |
| * @object: 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_ref_sink()). |
| * |
| * 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. |
| */ |
| gboolean |
| gst_object_set_parent (GstObject * object, GstObject * parent) |
| { |
| g_return_val_if_fail (GST_IS_OBJECT (object), FALSE); |
| g_return_val_if_fail (GST_IS_OBJECT (parent), FALSE); |
| g_return_val_if_fail (object != parent, FALSE); |
| |
| GST_CAT_DEBUG_OBJECT (GST_CAT_REFCOUNTING, object, |
| "set parent (ref and sink)"); |
| |
| GST_OBJECT_LOCK (object); |
| if (G_UNLIKELY (object->parent != NULL)) |
| goto had_parent; |
| |
| object->parent = parent; |
| gst_object_ref_sink (object); |
| GST_OBJECT_UNLOCK (object); |
| |
| /* 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; |
| |
| /* ERROR handling */ |
| had_parent: |
| { |
| GST_CAT_DEBUG_OBJECT (GST_CAT_REFCOUNTING, object, |
| "set parent failed, object already had a parent"); |
| GST_OBJECT_UNLOCK (object); |
| return FALSE; |
| } |
| } |
| |
| /** |
| * gst_object_get_parent: |
| * @object: a #GstObject |
| * |
| * 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. |
| * |
| * MT safe. Grabs and releases @object's LOCK. |
| */ |
| GstObject * |
| gst_object_get_parent (GstObject * object) |
| { |
| GstObject *result = NULL; |
| |
| g_return_val_if_fail (GST_IS_OBJECT (object), NULL); |
| |
| GST_OBJECT_LOCK (object); |
| result = object->parent; |
| if (G_LIKELY (result)) |
| gst_object_ref (result); |
| GST_OBJECT_UNLOCK (object); |
| |
| return result; |
| } |
| |
| /** |
| * gst_object_unparent: |
| * @object: a #GstObject to unparent |
| * |
| * Clear the parent of @object, removing the associated reference. |
| * This function decreases the refcount of @object. |
| * |
| * MT safe. Grabs and releases @object's lock. |
| */ |
| void |
| gst_object_unparent (GstObject * object) |
| { |
| GstObject *parent; |
| |
| g_return_if_fail (GST_IS_OBJECT (object)); |
| |
| GST_OBJECT_LOCK (object); |
| parent = object->parent; |
| |
| if (G_LIKELY (parent != NULL)) { |
| GST_CAT_TRACE_OBJECT (GST_CAT_REFCOUNTING, object, "unparent"); |
| object->parent = NULL; |
| GST_OBJECT_UNLOCK (object); |
| |
| /* g_object_notify_by_pspec ((GObject *)object, properties[PROP_PARENT]); */ |
| |
| gst_object_unref (object); |
| } else { |
| GST_OBJECT_UNLOCK (object); |
| } |
| } |
| |
| /** |
| * 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. |
| * |
| * MT safe. Grabs and releases @object's locks. |
| */ |
| gboolean |
| gst_object_has_ancestor (GstObject * object, GstObject * ancestor) |
| { |
| GstObject *parent, *tmp; |
| |
| if (!ancestor || !object) |
| return FALSE; |
| |
| parent = gst_object_ref (object); |
| do { |
| if (parent == ancestor) { |
| gst_object_unref (parent); |
| return TRUE; |
| } |
| |
| tmp = gst_object_get_parent (parent); |
| gst_object_unref (parent); |
| parent = tmp; |
| } while (parent); |
| |
| return FALSE; |
| } |
| |
| /** |
| * gst_object_check_uniqueness: |
| * @list: (transfer none) (element-type Gst.Object): a list of #GstObject to |
| * check through |
| * @name: the name to search for |
| * |
| * Checks to see if there is any object named @name in @list. This function |
| * 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 |
| * 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. |
| * |
| * MT safe. Grabs and releases the LOCK of each object in the list. |
| */ |
| gboolean |
| gst_object_check_uniqueness (GList * list, const gchar * name) |
| { |
| gboolean result = TRUE; |
| |
| g_return_val_if_fail (name != NULL, FALSE); |
| |
| for (; list; list = g_list_next (list)) { |
| GstObject *child; |
| gboolean eq; |
| |
| child = GST_OBJECT_CAST (list->data); |
| |
| GST_OBJECT_LOCK (child); |
| eq = strcmp (GST_OBJECT_NAME (child), name) == 0; |
| GST_OBJECT_UNLOCK (child); |
| |
| if (G_UNLIKELY (eq)) { |
| result = FALSE; |
| break; |
| } |
| } |
| return result; |
| } |
| |
| |
| static void |
| gst_object_set_property (GObject * object, guint prop_id, |
| const GValue * value, GParamSpec * pspec) |
| { |
| GstObject *gstobject; |
| |
| gstobject = GST_OBJECT_CAST (object); |
| |
| switch (prop_id) { |
| 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; |
| } |
| } |
| |
| static void |
| gst_object_get_property (GObject * object, guint prop_id, |
| GValue * value, GParamSpec * pspec) |
| { |
| GstObject *gstobject; |
| |
| gstobject = GST_OBJECT_CAST (object); |
| |
| switch (prop_id) { |
| 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; |
| } |
| } |
| |
| /** |
| * gst_object_get_path_string: |
| * @object: a #GstObject |
| * |
| * Generates a string describing the path of @object in |
| * the object hierarchy. Only useful (or used) for debugging. |
| * |
| * Free-function: g_free |
| * |
| * Returns: (transfer full): a string describing the path of @object. You must |
| * g_free() the string after usage. |
| * |
| * MT safe. Grabs and releases the #GstObject's LOCK for all objects |
| * in the hierarchy. |
| */ |
| gchar * |
| gst_object_get_path_string (GstObject * object) |
| { |
| GSList *parentage; |
| GSList *parents; |
| void *parent; |
| gchar *prevpath, *path; |
| const gchar *typename; |
| gchar *component; |
| const gchar *separator; |
| |
| /* ref object before adding to list */ |
| gst_object_ref (object); |
| parentage = g_slist_prepend (NULL, object); |
| |
| path = g_strdup (""); |
| |
| /* first walk the object hierarchy to build a list of the parents, |
| * be careful here with refcounting. */ |
| do { |
| if (GST_IS_OBJECT (object)) { |
| parent = gst_object_get_parent (object); |
| /* add parents to list, refcount remains increased while |
| * we handle the object */ |
| if (parent) |
| parentage = g_slist_prepend (parentage, parent); |
| } else { |
| break; |
| } |
| object = parent; |
| } while (object != NULL); |
| |
| /* then walk the parent list and print them out. we need to |
| * decrease the refcounting on each element after we handled |
| * it. */ |
| for (parents = parentage; parents; parents = g_slist_next (parents)) { |
| if (G_IS_OBJECT (parents->data)) { |
| typename = G_OBJECT_TYPE_NAME (parents->data); |
| } else { |
| typename = NULL; |
| } |
| if (GST_IS_OBJECT (parents->data)) { |
| GstObject *item = GST_OBJECT_CAST (parents->data); |
| GstObjectClass *oclass = GST_OBJECT_GET_CLASS (item); |
| gchar *objname = gst_object_get_name (item); |
| |
| component = g_strdup_printf ("%s:%s", typename, objname); |
| separator = oclass->path_string_separator; |
| /* and unref now */ |
| gst_object_unref (item); |
| g_free (objname); |
| } else { |
| if (typename) { |
| component = g_strdup_printf ("%s:%p", typename, parents->data); |
| } else { |
| component = g_strdup_printf ("%p", parents->data); |
| } |
| separator = "/"; |
| } |
| |
| prevpath = path; |
| path = g_strjoin (separator, prevpath, component, NULL); |
| g_free (prevpath); |
| g_free (component); |
| } |
| |
| g_slist_free (parentage); |
| |
| 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 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 full): 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 will take ownership of the @binding. |
| * |
| * 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): 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: 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 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 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() 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; |
| } |