controller: move GstControlledProperty into a separate class

Add a GstControlBinding class. This is a preparation for making the
controlsources generate double valued control curves and do the gparamspec
mapping in the control binding. Now the API in GstObject is again mostly
for convenience.
This commit is contained in:
Stefan Sauer 2011-12-20 22:36:18 +01:00
parent 81ce8b76d0
commit a80401b22c
12 changed files with 540 additions and 208 deletions

View file

@ -67,6 +67,7 @@ Windows. It is released under the GNU Library General Public License
<xi:include href="xml/gstchildproxy.xml" />
<xi:include href="xml/gstclock.xml" />
<xi:include href="xml/gstconfig.xml" />
<xi:include href="xml/gstcontrolbinding.xml" />
<xi:include href="xml/gstcontrolsource.xml" />
<xi:include href="xml/gstdatetime.xml" />
<xi:include href="xml/gstelement.xml" />

View file

@ -557,6 +557,27 @@ GST_USING_PRINTF_EXTENSION
</SECTION>
<SECTION>
<FILE>gstcontrolbinding</FILE>
<TITLE>GstControlBinding</TITLE>
GstControlBinding
GstControlBindingClass
gst_control_binding_new
gst_control_binding_sync_values
gst_control_binding_get_control_source
gst_control_binding_set_disabled
gst_control_binding_is_disabled
<SUBSECTION Standard>
GST_CONTROL_BINDING
GST_IS_CONTROL_BINDING
GST_CONTROL_BINDING_CLASS
GST_IS_CONTROL_BINDING_CLASS
GST_CONTROL_BINDING_GET_CLASS
GST_TYPE_CONTROL_BINDING
<SUBSECTION Private>
gst_control_binding_get_type
</SECTION>
<SECTION>
<FILE>gstcontrolsource</FILE>
<TITLE>GstControlSource</TITLE>
@ -566,7 +587,6 @@ GstControlSourceBind
GstControlSourceGetValue
GstControlSourceGetValueArray
GstTimedValue
GstValueArray
gst_control_source_bind
gst_control_source_get_value
gst_control_source_get_value_array
@ -1525,9 +1545,11 @@ gst_object_get_path_string
gst_object_suggest_next_sync
gst_object_sync_values
gst_object_has_active_controlled_properties
gst_object_set_controlled_properties_disabled
gst_object_set_controlled_property_disabled
gst_object_has_active_control_bindings
gst_object_set_control_bindings_disabled
gst_object_set_control_binding_disabled
gst_object_set_control_binding
gst_object_get_control_binding
gst_object_get_control_source
gst_object_set_control_source
gst_object_get_value

View file

@ -12,6 +12,7 @@ gst_bin_get_type
gst_bus_get_type
gst_child_proxy_get_type
gst_clock_get_type
gst_control_binding_get_type
gst_control_source_get_type
gst_element_factory_get_type
gst_element_get_type

View file

@ -56,6 +56,7 @@ libgstreamer_@GST_MAJORMINOR@_la_SOURCES = \
gstcaps.c \
gstchildproxy.c \
gstclock.c \
gstcontrolbinding.c \
gstcontrolsource.c \
gstdatetime.c \
gstdebugutils.c \
@ -150,6 +151,7 @@ gst_headers = \
gstchildproxy.h \
gstclock.h \
gstcompat.h \
gstcontrolbinding.h \
gstcontrolsource.h \
gstdatetime.h \
gstdebugutils.h \

View file

@ -771,6 +771,9 @@ init_post (GOptionContext * context, GOptionGroup * group, gpointer data,
g_type_class_ref (gst_segment_flags_get_type ());
g_type_class_ref (gst_scheduling_flags_get_type ());
g_type_class_ref (gst_control_binding_get_type ());
g_type_class_ref (gst_control_source_get_type ());
_priv_gst_event_initialize ();
_priv_gst_buffer_initialize ();
_priv_gst_message_initialize ();

233
gst/gstcontrolbinding.c Normal file
View file

@ -0,0 +1,233 @@
/* GStreamer
*
* Copyright (C) 2011 Stefan Sauer <ensonic@users.sf.net>
*
* gstcontrolbinding.c: Attachment for control sources
*
* 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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/**
* SECTION:gstcontrolbinding
* @short_description: attachment for control source sources
*
* A value mapping object that attaches control sources to gobject properties.
*/
#include "gst_private.h"
#include <glib-object.h>
#include <gst/gst.h>
#include "gstcontrolbinding.h"
#define GST_CAT_DEFAULT control_binding_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
static void gst_control_binding_dispose (GObject * object);
static void gst_control_binding_finalize (GObject * object);
#define _do_init \
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "gstcontrolbinding", 0, \
"dynamic parameter control source attachment");
G_DEFINE_TYPE_WITH_CODE (GstControlBinding, gst_control_binding, G_TYPE_OBJECT,
_do_init);
static void
gst_control_binding_class_init (GstControlBindingClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->dispose = gst_control_binding_dispose;
gobject_class->finalize = gst_control_binding_finalize;
}
static void
gst_control_binding_init (GstControlBinding * self)
{
}
static void
gst_control_binding_dispose (GObject * object)
{
GstControlBinding *self = GST_CONTROL_BINDING (object);
if (self->csource) {
// FIXME: hack
self->csource->bound = FALSE;
g_object_unref (self->csource);
self->csource = NULL;
}
}
static void
gst_control_binding_finalize (GObject * object)
{
GstControlBinding *self = GST_CONTROL_BINDING (object);
g_value_unset (&self->cur_value);
g_value_unset (&self->last_value);
}
/**
* gst_control_binding_new:
* @object: the object of the property
* @property_name: the property-name to attach the control source
* @csource: the control source
*
* Create a new control-binding that attaches the #GstControlSource to the
* #GObject property.
*
* Returns: the new #GstControlBinding
*/
GstControlBinding *
gst_control_binding_new (GstObject * object, const gchar * property_name,
GstControlSource * csource)
{
GstControlBinding *self = NULL;
GParamSpec *pspec;
GST_INFO_OBJECT (object, "trying to put property '%s' under control",
property_name);
/* check if the object has a property of that name */
if ((pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object),
property_name))) {
GST_DEBUG_OBJECT (object, " psec->flags : 0x%08x", pspec->flags);
/* check if this param is witable && controlable && !construct-only */
g_return_val_if_fail ((pspec->flags & (G_PARAM_WRITABLE |
GST_PARAM_CONTROLLABLE | G_PARAM_CONSTRUCT_ONLY)) ==
(G_PARAM_WRITABLE | GST_PARAM_CONTROLLABLE), NULL);
if (gst_control_source_bind (csource, pspec)) {
if ((self = (GstControlBinding *) g_object_newv (GST_TYPE_CONTROL_BINDING,
0, NULL))) {
self->pspec = pspec;
self->name = pspec->name;
self->csource = g_object_ref (csource);
self->disabled = FALSE;
g_value_init (&self->cur_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
g_value_init (&self->last_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
}
}
} else {
GST_WARNING_OBJECT (object, "class '%s' has no property '%s'",
G_OBJECT_TYPE_NAME (object), property_name);
}
return self;
}
/* functions */
/**
* gst_control_binding_sync_values:
* @self: the control binding
* @object: the object that has controlled properties
* @timestamp: the time that should be processed
* @last_sync: the last time this was called
*
* Sets the property of the @object, according to the #GstControlSources that
* 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 value could be applied to the object
* property, %FALSE otherwise
*/
gboolean
gst_control_binding_sync_values (GstControlBinding * self, GstObject * object,
GstClockTime timestamp, GstClockTime last_sync)
{
GValue *value;
gboolean ret;
g_return_val_if_fail (GST_IS_CONTROL_BINDING (self), FALSE);
if (self->disabled)
return TRUE;
GST_LOG_OBJECT (object, "property '%s' at ts=%" G_GUINT64_FORMAT,
self->name, timestamp);
value = &self->cur_value;
ret = gst_control_source_get_value (self->csource, timestamp, value);
if (G_LIKELY (ret)) {
/* always set the value for first time, but then only if it changed
* this should limit g_object_notify invocations.
* FIXME: can we detect negative playback rates?
*/
if ((timestamp < last_sync) ||
gst_value_compare (value, &self->last_value) != GST_VALUE_EQUAL) {
/* we can make this faster
* http://bugzilla.gnome.org/show_bug.cgi?id=536939
*/
g_object_set_property ((GObject *) object, self->name, value);
g_value_copy (value, &self->last_value);
}
} else {
GST_DEBUG_OBJECT (object, "no control value for param %s", self->name);
}
return (ret);
}
/**
* gst_control_binding_get_control_source:
* @self: the control binding
*
* Get the control source.
*
* Returns: the control source. Unref when done with it.
*/
GstControlSource *
gst_control_binding_get_control_source (GstControlBinding * self)
{
g_return_val_if_fail (GST_IS_CONTROL_BINDING (self), NULL);
return g_object_ref (self->csource);
}
/**
* gst_control_binding_set_disabled:
* @self: the control binding
* @disabled: boolean that specifies whether to disable the controller
* or not.
*
* This function is used to disable a control binding for some time, i.e.
* gst_object_sync_values() will do nothing.
*/
void
gst_control_binding_set_disabled (GstControlBinding * self, gboolean disabled)
{
g_return_if_fail (GST_IS_CONTROL_BINDING (self));
self->disabled = disabled;
}
/**
* gst_control_binding_is_disabled:
* @self: the control binding
*
* Check if the control binding is disabled.
*
* Returns: %TRUE if the binding is inactive
*/
gboolean
gst_control_binding_is_disabled (GstControlBinding * self)
{
g_return_val_if_fail (GST_IS_CONTROL_BINDING (self), TRUE);
return (self->disabled == TRUE);
}

98
gst/gstcontrolbinding.h Normal file
View file

@ -0,0 +1,98 @@
/* GStreamer
*
* Copyright (C) 2011 Stefan Sauer <ensonic@users.sf.net>
*
* gstcontrolbinding.h: Attachment for control sources
*
* 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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GST_CONTROL_BINDING_H__
#define __GST_CONTROL_BINDING_H__
#include <gst/gstconfig.h>
#include <glib-object.h>
#include <gst/gstcontrolsource.h>
G_BEGIN_DECLS
#define GST_TYPE_CONTROL_BINDING \
(gst_control_binding_get_type())
#define GST_CONTROL_BINDING(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CONTROL_BINDING,GstControlBinding))
#define GST_CONTROL_BINDING_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CONTROL_BINDING,GstControlBindingClass))
#define GST_IS_CONTROL_BINDING(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CONTROL_BINDING))
#define GST_IS_CONTROL_BINDING_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CONTROL_BINDING))
#define GST_CONTROL_BINDING_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CONTOL_SOURCE, GstControlBindingClass))
typedef struct _GstControlBinding GstControlBinding;
typedef struct _GstControlBindingClass GstControlBindingClass;
/**
* GstControlBinding:
*
* The instance structure of #GstControlBinding.
*/
struct _GstControlBinding {
GObject parent;
/*< public >*/
GParamSpec *pspec; /* GParamSpec for this property */
const gchar *name; /* name of the property */
GstControlSource *csource; /* GstControlSource for this property */
gboolean disabled;
GValue cur_value, last_value;
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
};
/**
* GstControlBindingClass:
* @parent_class: Parent class
*
* The class structure of #GstControlBinding.
*/
struct _GstControlBindingClass
{
GObjectClass parent_class;
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
};
GType gst_control_binding_get_type (void);
/* Functions */
GstControlBinding * gst_control_binding_new (GstObject * object, const gchar * property_name,
GstControlSource * csource);
gboolean gst_control_binding_sync_values (GstControlBinding * self, GstObject *object,
GstClockTime timestamp, GstClockTime last_sync);
GstControlSource * gst_control_binding_get_control_source (GstControlBinding * self);
void gst_control_binding_set_disabled (GstControlBinding * self, gboolean disabled);
gboolean gst_control_binding_is_disabled (GstControlBinding * self);
G_END_DECLS
#endif /* __GST_CONTROL_BINDING_H__ */

View file

@ -51,7 +51,12 @@
#define GST_CAT_DEFAULT control_source_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
G_DEFINE_ABSTRACT_TYPE (GstControlSource, gst_control_source, G_TYPE_OBJECT);
#define _do_init \
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "gstcontrolsource", 0, \
"dynamic parameter control sources");
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstControlSource, gst_control_source,
G_TYPE_OBJECT, _do_init);
static void
gst_control_source_class_init (GstControlSourceClass * klass)
@ -60,15 +65,11 @@ gst_control_source_class_init (GstControlSourceClass * klass)
/* Has to be implemented by children */
klass->bind = NULL;
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "gstcontrolsource", 0,
"dynamic parameter control sources");
}
static void
gst_control_source_init (GstControlSource * self)
{
/* Set default handlers that print a warning */
self->get_value = NULL;
self->get_value_array = NULL;
self->bound = FALSE;
@ -150,8 +151,12 @@ gst_control_source_bind (GstControlSource * self, GParamSpec * pspec)
ret = GST_CONTROL_SOURCE_GET_CLASS (self)->bind (self, pspec);
if (ret)
if (ret) {
GST_DEBUG ("bound control source for param %s", pspec->name);
self->bound = TRUE;
} else {
GST_DEBUG ("failed to bind control source for param %s", pspec->name);
}
return ret;
}

View file

@ -140,6 +140,7 @@
#include "gstobject.h"
#include "gstmarshal.h"
#include "gstclock.h"
#include "gstcontrolbinding.h"
#include "gstcontrolsource.h"
#include "gstinfo.h"
#include "gstparamspecs.h"
@ -195,21 +196,6 @@ static guint gst_object_signals[LAST_SIGNAL] = { 0 };
static GParamSpec *properties[PROP_LAST];
/*
* GstControlledProperty:
*/
typedef struct _GstControlledProperty
{
GParamSpec *pspec; /* GParamSpec for this property */
const gchar *name; /* name of the property */
GstControlSource *csource; /* GstControlSource for this property */
gboolean disabled;
GValue last_value;
} GstControlledProperty;
static void gst_controlled_property_free (GstControlledProperty * prop);
G_DEFINE_ABSTRACT_TYPE (GstObject, gst_object, G_TYPE_INITIALLY_UNOWNED);
static void
@ -425,14 +411,14 @@ gst_object_dispose (GObject * object)
GST_OBJECT_PARENT (object) = NULL;
GST_OBJECT_UNLOCK (object);
if (self->properties) {
if (self->control_bindings) {
GList *node;
for (node = self->properties; node; node = g_list_next (node)) {
gst_controlled_property_free ((GstControlledProperty *) node->data);
for (node = self->control_bindings; node; node = g_list_next (node)) {
g_object_unref (node->data);
}
g_list_free (self->properties);
self->properties = NULL;
g_list_free (self->control_bindings);
self->control_bindings = NULL;
}
((GObjectClass *) gst_object_parent_class)->dispose (object);
@ -1037,85 +1023,27 @@ gst_object_get_path_string (GstObject * object)
/* controller helper functions */
/*
* gst_controlled_property_new:
* @object: for which object the controlled property should be set up
* @name: the name of the property to be controlled
*
* Private method which initializes the fields of a new controlled property
* structure.
*
* Returns: a freshly allocated structure or %NULL
*/
static GstControlledProperty *
gst_controlled_property_new (GstObject * object, const gchar * name)
{
GstControlledProperty *prop = NULL;
GParamSpec *pspec;
GST_INFO ("trying to put property '%s' under control", name);
/* check if the object has a property of that name */
if ((pspec =
g_object_class_find_property (G_OBJECT_GET_CLASS (object), name))) {
GST_DEBUG (" psec->flags : 0x%08x", pspec->flags);
/* check if this param is witable && controlable && !construct-only */
g_return_val_if_fail ((pspec->flags & (G_PARAM_WRITABLE |
GST_PARAM_CONTROLLABLE | G_PARAM_CONSTRUCT_ONLY)) ==
(G_PARAM_WRITABLE | GST_PARAM_CONTROLLABLE), NULL);
if ((prop = g_slice_new (GstControlledProperty))) {
prop->pspec = pspec;
prop->name = pspec->name;
prop->csource = NULL;
prop->disabled = FALSE;
memset (&prop->last_value, 0, sizeof (GValue));
g_value_init (&prop->last_value, G_PARAM_SPEC_VALUE_TYPE (prop->pspec));
}
} else {
GST_WARNING ("class '%s' has no property '%s'", G_OBJECT_TYPE_NAME (object),
name);
}
return prop;
}
/*
* gst_controlled_property_free:
* @prop: the object to free
*
* Private method which frees all data allocated by a #GstControlledProperty
* instance.
*/
static void
gst_controlled_property_free (GstControlledProperty * prop)
{
if (prop->csource)
g_object_unref (prop->csource);
g_value_unset (&prop->last_value);
g_slice_free (GstControlledProperty, prop);
}
/*
* gst_object_find_controlled_property:
* 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 #GstControlledProperty or %NULL if the property is not being
* Returns: a #GstControlBinding or %NULL if the property is not being
* controlled.
*/
static GstControlledProperty *
gst_object_find_controlled_property (GstObject * self, const gchar * name)
static GstControlBinding *
gst_object_find_control_binding (GstObject * self, const gchar * name)
{
GstControlledProperty *prop;
GstControlBinding *binding;
GList *node;
for (node = self->properties; node; node = g_list_next (node)) {
prop = node->data;
for (node = self->control_bindings; node; node = g_list_next (node)) {
binding = node->data;
/* FIXME: eventually use GQuark to speed it up */
if (!strcmp (prop->name, name)) {
return prop;
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);
@ -1174,10 +1102,8 @@ gst_object_suggest_next_sync (GstObject * object)
gboolean
gst_object_sync_values (GstObject * object, GstClockTime timestamp)
{
GstControlledProperty *prop;
GList *node;
gboolean ret = TRUE, val_ret;
GValue value = { 0, };
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);
@ -1187,36 +1113,9 @@ gst_object_sync_values (GstObject * object, GstClockTime timestamp)
/* FIXME: this deadlocks */
/* GST_OBJECT_LOCK (object); */
g_object_freeze_notify ((GObject *) object);
/* go over the controlled properties of the controller */
for (node = object->properties; node; node = g_list_next (node)) {
prop = node->data;
if (prop->disabled)
continue;
GST_LOG_OBJECT (object, "property '%s' at ts=%" G_GUINT64_FORMAT,
prop->name, timestamp);
/* we can make this faster
* http://bugzilla.gnome.org/show_bug.cgi?id=536939
*/
g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (prop->pspec));
val_ret = gst_control_source_get_value (prop->csource, timestamp, &value);
if (G_LIKELY (val_ret)) {
/* always set the value for first time, but then only if it changed
* this should limit g_object_notify invocations.
* FIXME: can we detect negative playback rates?
*/
if ((timestamp < object->last_sync) ||
gst_value_compare (&value, &prop->last_value) != GST_VALUE_EQUAL) {
g_object_set_property ((GObject *) object, prop->name, &value);
g_value_copy (&value, &prop->last_value);
}
} else {
GST_DEBUG_OBJECT (object, "no control value for param %s", prop->name);
}
g_value_unset (&value);
ret &= val_ret;
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);
@ -1225,8 +1124,9 @@ gst_object_sync_values (GstObject * object, GstClockTime timestamp)
return ret;
}
/**
* gst_object_has_active_controlled_properties:
* gst_object_has_active_control_bindings:
* @object: the object that has controlled properties
*
* Check if the @object has an active controlled properties.
@ -1234,51 +1134,47 @@ gst_object_sync_values (GstObject * object, GstClockTime timestamp)
* Returns: %TRUE if the object has active controlled properties
*/
gboolean
gst_object_has_active_controlled_properties (GstObject * object)
gst_object_has_active_control_bindings (GstObject * object)
{
gboolean res = FALSE;
GList *node;
GstControlledProperty *prop;
g_return_val_if_fail (GST_IS_OBJECT (object), FALSE);
GST_OBJECT_LOCK (object);
for (node = object->properties; node; node = node->next) {
prop = node->data;
res |= !prop->disabled;
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_controlled_properties_disabled:
* 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..
* some time, i.e. gst_object_sync_values() will do nothing.
*/
void
gst_object_set_controlled_properties_disabled (GstObject * object,
gboolean disabled)
gst_object_set_control_bindings_disabled (GstObject * object, gboolean disabled)
{
GList *node;
GstControlledProperty *prop;
g_return_if_fail (GST_IS_OBJECT (object));
GST_OBJECT_LOCK (object);
for (node = object->properties; node; node = node->next) {
prop = node->data;
prop->disabled = disabled;
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_controlled_property_disabled:
* 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
@ -1289,21 +1185,88 @@ gst_object_set_controlled_properties_disabled (GstObject * object,
* property.
*/
void
gst_object_set_controlled_property_disabled (GstObject * object,
gst_object_set_control_binding_disabled (GstObject * object,
const gchar * property_name, gboolean disabled)
{
GstControlledProperty *prop;
GstControlBinding *binding;
g_return_if_fail (GST_IS_OBJECT (object));
g_return_if_fail (property_name);
GST_OBJECT_LOCK (object);
if ((prop = gst_object_find_controlled_property (object, property_name))) {
prop->disabled = disabled;
if ((binding = gst_object_find_control_binding (object, property_name))) {
gst_control_binding_set_disabled (binding, disabled);
}
GST_OBJECT_UNLOCK (object);
}
/**
* gst_object_set_control_binding:
* @object: the controller object
* @binding: the #GstControlBinding that should be used for the property
*
* Sets the #GstControlBinding. If there already was a #GstControlBinding
* for this property it will be replaced. Use %NULL for @binding to unset it.
*
* Returns: %FALSE if the given property isn't handled by the controller or
* %TRUE if everything worked as expected.
*/
gboolean
gst_object_set_control_binding (GstObject * object, GstControlBinding * binding)
{
GstControlBinding *old;
gboolean ret = FALSE;
g_return_val_if_fail (GST_IS_OBJECT (object), FALSE);
g_return_val_if_fail ((!binding || GST_IS_CONTROL_BINDING (binding)), FALSE);
GST_OBJECT_LOCK (object);
if ((old = gst_object_find_control_binding (object, binding->name))) {
object->control_bindings = g_list_remove (object->control_bindings, old);
g_object_unref (old);
GST_DEBUG_OBJECT (object, "controlled property %s removed", binding->name);
ret = TRUE;
}
if (binding) {
object->control_bindings =
g_list_prepend (object->control_bindings, g_object_ref (binding));
GST_DEBUG_OBJECT (object, "controlled property %s added", binding->name);
ret = TRUE;
}
GST_OBJECT_UNLOCK (object);
return ret;
}
/**
* 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_set_control_source:
* @object: the controller object
@ -1313,6 +1276,8 @@ gst_object_set_controlled_property_disabled (GstObject * object,
* Sets the #GstControlSource for @property_name. If there already was a #GstControlSource
* for this property it will be unreferenced.
*
* This is a convenience function for gst_object_set_control_binding().
*
* Returns: %FALSE if the given property isn't handled by the controller or the new #GstControlSource
* couldn't be bound to the property, %TRUE if everything worked as expected.
*/
@ -1320,7 +1285,7 @@ gboolean
gst_object_set_control_source (GstObject * object, const gchar * property_name,
GstControlSource * csource)
{
GstControlledProperty *prop;
GstControlBinding *binding;
gboolean ret = FALSE;
g_return_val_if_fail (GST_IS_OBJECT (object), FALSE);
@ -1328,30 +1293,21 @@ gst_object_set_control_source (GstObject * object, const gchar * property_name,
g_return_val_if_fail ((!csource || GST_IS_CONTROL_SOURCE (csource)), FALSE);
GST_OBJECT_LOCK (object);
prop = gst_object_find_controlled_property (object, property_name);
if (!prop) {
if ((prop = gst_controlled_property_new (object, property_name))) {
object->properties = g_list_prepend (object->properties, prop);
GST_DEBUG_OBJECT (object, "controlled property %s added", property_name);
}
if ((binding = gst_object_find_control_binding (object, property_name))) {
object->control_bindings =
g_list_remove (object->control_bindings, binding);
g_object_unref (binding);
GST_DEBUG_OBJECT (object, "controlled property %s removed", property_name);
ret = TRUE;
}
if (prop) {
GstControlSource *old = prop->csource;
if (csource != old) {
if (csource && (ret = gst_control_source_bind (csource, prop->pspec))) {
prop->csource = g_object_ref (csource);
} else if (!csource) {
ret = TRUE;
prop->csource = NULL;
object->properties = g_list_remove (object->properties, prop);
//g_signal_handler_disconnect (self->object, prop->notify_handler_id);
gst_controlled_property_free (prop);
GST_DEBUG_OBJECT (object, "controlled property %s removed",
property_name);
}
if (ret && old)
g_object_unref (old);
if (csource) {
if ((binding = gst_control_binding_new (object, property_name, csource))) {
object->control_bindings =
g_list_prepend (object->control_bindings, binding);
GST_DEBUG_OBJECT (object, "controlled property %s added", property_name);
ret = TRUE;
} else {
ret = FALSE;
}
}
GST_OBJECT_UNLOCK (object);
@ -1362,33 +1318,35 @@ gst_object_set_control_source (GstObject * object, const gchar * property_name,
/**
* gst_object_get_control_source:
* @object: the object
* @property_name: name of the property for which the #GstControlSource should be get
* @property_name: name of the property
*
* Gets the corresponding #GstControlSource for the property. This should be unreferenced
* again after use.
* Gets the corresponding #GstControlSource for the property. This should be
* unreferenced again after use.
*
* Returns: (transfer full): the #GstControlSource for @property_name or NULL if
* the property is not controlled by this controller or no #GstControlSource was
* assigned yet.
* This is a convenience function for gst_object_get_control_binding().
*
* Returns: (transfer full): the #GstControlSource for @property_name or %NULL if
* the property is not controlled.
*/
GstControlSource *
gst_object_get_control_source (GstObject * object, const gchar * property_name)
{
GstControlledProperty *prop;
GstControlBinding *binding;
GstControlSource *ret = NULL;
g_return_val_if_fail (GST_IS_OBJECT (object), NULL);
g_return_val_if_fail (property_name, NULL);
GST_OBJECT_LOCK (object);
if ((prop = gst_object_find_controlled_property (object, property_name))) {
ret = g_object_ref (prop->csource);
if ((binding = gst_object_find_control_binding (object, property_name))) {
ret = gst_control_binding_get_control_source (binding);
}
GST_OBJECT_UNLOCK (object);
return ret;
}
/**
* gst_object_get_value:
* @object: the object that has controlled properties
@ -1404,7 +1362,7 @@ GValue *
gst_object_get_value (GstObject * object, const gchar * property_name,
GstClockTime timestamp)
{
GstControlledProperty *prop;
GstControlBinding *binding;
GValue *val = NULL;
g_return_val_if_fail (GST_IS_OBJECT (object), NULL);
@ -1412,12 +1370,12 @@ gst_object_get_value (GstObject * object, const gchar * property_name,
g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), NULL);
GST_OBJECT_LOCK (object);
if ((prop = gst_object_find_controlled_property (object, property_name))) {
if ((binding = gst_object_find_control_binding (object, property_name))) {
val = g_new0 (GValue, 1);
g_value_init (val, G_PARAM_SPEC_VALUE_TYPE (prop->pspec));
g_value_init (val, G_PARAM_SPEC_VALUE_TYPE (binding->pspec));
/* get current value via control source */
if (!gst_control_source_get_value (prop->csource, timestamp, val)) {
if (!gst_control_source_get_value (binding->csource, timestamp, val)) {
g_free (val);
val = NULL;
}
@ -1451,7 +1409,7 @@ gst_object_get_value_array (GstObject * object, const gchar * property_name,
gpointer values)
{
gboolean res = FALSE;
GstControlledProperty *prop;
GstControlBinding *binding;
g_return_val_if_fail (GST_IS_OBJECT (object), FALSE);
g_return_val_if_fail (property_name, FALSE);
@ -1460,14 +1418,15 @@ gst_object_get_value_array (GstObject * object, const gchar * property_name,
g_return_val_if_fail (values, FALSE);
GST_OBJECT_LOCK (object);
if ((prop = gst_object_find_controlled_property (object, property_name))) {
res = gst_control_source_get_value_array (prop->csource, timestamp,
if ((binding = gst_object_find_control_binding (object, property_name))) {
res = gst_control_source_get_value_array (binding->csource, timestamp,
interval, n_values, values);
}
GST_OBJECT_UNLOCK (object);
return res;
}
/**
* gst_object_get_control_rate:
* @object: the object that has controlled properties

View file

@ -171,7 +171,7 @@ struct _GstObject {
guint32 flags;
/*< private >*/
GList *properties; /* List of GstControlledProperty */
GList *control_bindings; /* List of GstControlBinding */
guint64 control_rate;
guint64 last_sync;
@ -232,30 +232,35 @@ gchar * gst_object_get_path_string (GstObject *object);
gboolean gst_object_check_uniqueness (GList *list, const gchar *name);
/* controller functions */
#include <gst/gstcontrolbinding.h>
#include <gst/gstcontrolsource.h>
GstClockTime gst_object_suggest_next_sync (GstObject * object);
gboolean gst_object_sync_values (GstObject * object, GstClockTime timestamp);
gboolean gst_object_has_active_controlled_properties (GstObject *object);
void gst_object_set_controlled_properties_disabled (GstObject *object, gboolean disabled);
void gst_object_set_controlled_property_disabled (GstObject *object,
gboolean gst_object_has_active_control_bindings (GstObject *object);
void gst_object_set_control_bindings_disabled (GstObject *object, gboolean disabled);
void gst_object_set_control_binding_disabled (GstObject *object,
const gchar * property_name,
gboolean disabled);
gboolean gst_object_set_control_binding (GstObject * object, GstControlBinding * binding);
GstControlBinding *
gst_object_get_control_binding (GstObject *object, const gchar * property_name);
gboolean gst_object_set_control_source (GstObject *object, const gchar * property_name,
GstControlSource *csource);
GstControlSource *
gst_object_get_control_source (GstObject *object, const gchar * property_name);
GValue * gst_object_get_value (GstObject * object, const gchar * property_name,
GValue * gst_object_get_value (GstObject * object, const gchar * property_name,
GstClockTime timestamp);
gboolean gst_object_get_value_array (GstObject * object, const gchar * property_name,
gboolean gst_object_get_value_array (GstObject * object, const gchar * property_name,
GstClockTime timestamp, GstClockTime interval,
guint n_values, gpointer values);
GstClockTime gst_object_get_control_rate (GstObject * object);
void gst_object_set_control_rate (GstObject * object, GstClockTime control_rate);
GstClockTime gst_object_get_control_rate (GstObject * object);
void gst_object_set_control_rate (GstObject * object, GstClockTime control_rate);
G_END_DECLS

View file

@ -102,6 +102,10 @@ gst_timed_value_control_source_bind (GstControlSource * source,
GstTimedValueControlSource *self = (GstTimedValueControlSource *) source;
gboolean ret = TRUE;
if (self->type != G_TYPE_INVALID) {
gst_timed_value_control_source_reset (self);
}
/* get the fundamental base type */
self->type = base = type = G_PARAM_SPEC_VALUE_TYPE (pspec);
while ((type = g_type_parent (type)))

View file

@ -378,10 +378,12 @@ GST_START_TEST (controller_param_twice)
GST_CONTROL_SOURCE (cs));
fail_unless (res, NULL);
/* setting it again should not work */
/* setting it again will just unset the old and set it again
* this might cause some trouble with binding the control source again
*/
res = gst_object_set_control_source (GST_OBJECT (elem), "ulong",
GST_CONTROL_SOURCE (cs));
fail_unless (!res, NULL);
fail_unless (res, NULL);
/* it should have been added at least once, let remove it */
res = gst_object_set_control_source (GST_OBJECT (elem), "ulong", NULL);
@ -1091,8 +1093,7 @@ GST_START_TEST (controller_interpolate_linear_disabled)
/* now pull in values for some timestamps, prop double disabled */
GST_TEST_MONO_SOURCE (elem)->val_ulong = 0;
GST_TEST_MONO_SOURCE (elem)->val_double = 0.0;
gst_object_set_controlled_property_disabled (GST_OBJECT (elem), "double",
TRUE);
gst_object_set_control_binding_disabled (GST_OBJECT (elem), "double", TRUE);
gst_object_sync_values (GST_OBJECT (elem), 0 * GST_SECOND);
fail_unless_equals_int (GST_TEST_MONO_SOURCE (elem)->val_ulong, 0);
fail_unless (GST_TEST_MONO_SOURCE (elem)->val_double == 0.0, NULL);
@ -1106,8 +1107,7 @@ GST_START_TEST (controller_interpolate_linear_disabled)
/* now pull in values for some timestamps, after enabling double again */
GST_TEST_MONO_SOURCE (elem)->val_ulong = 0;
GST_TEST_MONO_SOURCE (elem)->val_double = 0.0;
gst_object_set_controlled_property_disabled (GST_OBJECT (elem), "double",
FALSE);
gst_object_set_control_binding_disabled (GST_OBJECT (elem), "double", FALSE);
gst_object_sync_values (GST_OBJECT (elem), 0 * GST_SECOND);
fail_unless_equals_int (GST_TEST_MONO_SOURCE (elem)->val_ulong, 0);
fail_unless (GST_TEST_MONO_SOURCE (elem)->val_double == 2.0, NULL);
@ -1121,7 +1121,7 @@ GST_START_TEST (controller_interpolate_linear_disabled)
/* now pull in values for some timestamps, after disabling all props */
GST_TEST_MONO_SOURCE (elem)->val_ulong = 0;
GST_TEST_MONO_SOURCE (elem)->val_double = 0.0;
gst_object_set_controlled_properties_disabled (GST_OBJECT (elem), TRUE);
gst_object_set_control_bindings_disabled (GST_OBJECT (elem), TRUE);
gst_object_sync_values (GST_OBJECT (elem), 0 * GST_SECOND);
fail_unless_equals_int (GST_TEST_MONO_SOURCE (elem)->val_ulong, 0);
fail_unless (GST_TEST_MONO_SOURCE (elem)->val_double == 0.0, NULL);
@ -1135,8 +1135,7 @@ GST_START_TEST (controller_interpolate_linear_disabled)
/* now pull in values for some timestamps, enabling double again */
GST_TEST_MONO_SOURCE (elem)->val_ulong = 0;
GST_TEST_MONO_SOURCE (elem)->val_double = 0.0;
gst_object_set_controlled_property_disabled (GST_OBJECT (elem), "double",
FALSE);
gst_object_set_control_binding_disabled (GST_OBJECT (elem), "double", FALSE);
gst_object_sync_values (GST_OBJECT (elem), 0 * GST_SECOND);
fail_unless_equals_int (GST_TEST_MONO_SOURCE (elem)->val_ulong, 0);
fail_unless (GST_TEST_MONO_SOURCE (elem)->val_double == 2.0, NULL);
@ -1150,7 +1149,7 @@ GST_START_TEST (controller_interpolate_linear_disabled)
/* now pull in values for some timestamps, enabling all */
GST_TEST_MONO_SOURCE (elem)->val_ulong = 0;
GST_TEST_MONO_SOURCE (elem)->val_double = 0.0;
gst_object_set_controlled_properties_disabled (GST_OBJECT (elem), FALSE);
gst_object_set_control_bindings_disabled (GST_OBJECT (elem), FALSE);
gst_object_sync_values (GST_OBJECT (elem), 0 * GST_SECOND);
fail_unless_equals_int (GST_TEST_MONO_SOURCE (elem)->val_ulong, 0);
fail_unless (GST_TEST_MONO_SOURCE (elem)->val_double == 2.0, NULL);