*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
*/
/**
* SECTION:gstdirectcontrolbinding
- * @short_description: direct attachment for control source sources
+ * @title: GstDirectControlBinding
+ * @short_description: direct attachment for control sources
*
- * A value mapping object that attaches control sources to gobject properties.
+ * A value mapping object that attaches control sources to gobject properties. It
+ * will map the control values directly to the target property range. If a
+ * non-absolute direct control binding is used, the value range [0.0 ... 1.0]
+ * is mapped to full target property range, and all values outside the range
+ * will be clipped. An absolute control binding will not do any value
+ * transformations.
*/
-
#include <glib-object.h>
#include <gst/gst.h>
#include "gstdirectcontrolbinding.h"
-#include <math.h>
+#include <gst/math-compat.h>
#define GST_CAT_DEFAULT control_binding_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
+
static GObject *gst_direct_control_binding_constructor (GType type,
guint n_construct_params, GObjectConstructParam * construct_params);
static void gst_direct_control_binding_set_property (GObject * object,
GstClockTime timestamp);
static gboolean gst_direct_control_binding_get_value_array (GstControlBinding *
_self, GstClockTime timestamp, GstClockTime interval, guint n_values,
+ gpointer values);
+static gboolean gst_direct_control_binding_get_g_value_array (GstControlBinding
+ * _self, GstClockTime timestamp, GstClockTime interval, guint n_values,
GValue * values);
#define _do_init \
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "gstdirectcontrolbinding", 0, \
"dynamic parameter control source attachment");
+#define gst_direct_control_binding_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstDirectControlBinding, gst_direct_control_binding,
GST_TYPE_CONTROL_BINDING, _do_init);
{
PROP_0,
PROP_CS,
+ PROP_ABSOLUTE,
PROP_LAST
};
/* mapping functions */
-#define DEFINE_CONVERT(type,Type,TYPE) \
+#define DEFINE_CONVERT(type,Type,TYPE,ROUNDING_OP) \
static void \
-convert_to_##type (GstDirectControlBinding *self, gdouble s, GValue *d) \
+convert_g_value_to_##type (GstDirectControlBinding *self, gdouble s, GValue *d) \
{ \
GParamSpec##Type *pspec = G_PARAM_SPEC_##TYPE (((GstControlBinding *)self)->pspec); \
g##type v; \
\
s = CLAMP (s, 0.0, 1.0); \
- v = pspec->minimum + (g##type) ((pspec->maximum - pspec->minimum) * s); \
+ v = (g##type) ROUNDING_OP (pspec->minimum * (1-s)) + (g##type) ROUNDING_OP (pspec->maximum * s); \
+ g_value_set_##type (d, v); \
+} \
+\
+static void \
+convert_value_to_##type (GstDirectControlBinding *self, gdouble s, gpointer d_) \
+{ \
+ GParamSpec##Type *pspec = G_PARAM_SPEC_##TYPE (((GstControlBinding *)self)->pspec); \
+ g##type *d = (g##type *)d_; \
+ \
+ s = CLAMP (s, 0.0, 1.0); \
+ *d = (g##type) ROUNDING_OP (pspec->minimum * (1-s)) + (g##type) ROUNDING_OP (pspec->maximum * s); \
+} \
+\
+static void \
+abs_convert_g_value_to_##type (GstDirectControlBinding *self, gdouble s, GValue *d) \
+{ \
+ g##type v; \
+ v = (g##type) ROUNDING_OP (s); \
g_value_set_##type (d, v); \
+} \
+\
+static void \
+abs_convert_value_to_##type (GstDirectControlBinding *self, gdouble s, gpointer d_) \
+{ \
+ g##type *d = (g##type *)d_; \
+ *d = (g##type) ROUNDING_OP (s); \
}
-DEFINE_CONVERT (int, Int, INT);
-DEFINE_CONVERT (uint, UInt, UINT);
-DEFINE_CONVERT (long, Long, LONG);
-DEFINE_CONVERT (ulong, ULong, ULONG);
-DEFINE_CONVERT (int64, Int64, INT64);
-DEFINE_CONVERT (uint64, UInt64, UINT64);
-DEFINE_CONVERT (float, Float, FLOAT);
-DEFINE_CONVERT (double, Double, DOUBLE);
+DEFINE_CONVERT (int, Int, INT, rint);
+DEFINE_CONVERT (uint, UInt, UINT, rint);
+DEFINE_CONVERT (long, Long, LONG, rint);
+DEFINE_CONVERT (ulong, ULong, ULONG, rint);
+DEFINE_CONVERT (int64, Int64, INT64, rint);
+DEFINE_CONVERT (uint64, UInt64, UINT64, rint);
+DEFINE_CONVERT (float, Float, FLOAT, /*NOOP*/);
+DEFINE_CONVERT (double, Double, DOUBLE, /*NOOP*/);
static void
-convert_to_boolean (GstDirectControlBinding * self, gdouble s, GValue * d)
+convert_g_value_to_boolean (GstDirectControlBinding * self, gdouble s,
+ GValue * d)
{
s = CLAMP (s, 0.0, 1.0);
g_value_set_boolean (d, (gboolean) (s + 0.5));
}
static void
-convert_to_enum (GstDirectControlBinding * self, gdouble s, GValue * d)
+convert_value_to_boolean (GstDirectControlBinding * self, gdouble s,
+ gpointer d_)
+{
+ gboolean *d = (gboolean *) d_;
+
+ s = CLAMP (s, 0.0, 1.0);
+ *d = (gboolean) (s + 0.5);
+}
+
+static void
+convert_g_value_to_enum (GstDirectControlBinding * self, gdouble s, GValue * d)
{
GParamSpecEnum *pspec =
G_PARAM_SPEC_ENUM (((GstControlBinding *) self)->pspec);
g_value_set_enum (d, e->values[v].value);
}
+static void
+convert_value_to_enum (GstDirectControlBinding * self, gdouble s, gpointer d_)
+{
+ GParamSpecEnum *pspec =
+ G_PARAM_SPEC_ENUM (((GstControlBinding *) self)->pspec);
+ GEnumClass *e = pspec->enum_class;
+ gint *d = (gint *) d_;
+
+ s = CLAMP (s, 0.0, 1.0);
+ *d = e->values[(gint) (s * (e->n_values - 1))].value;
+}
+
/* vmethods */
static void
control_binding_class->get_value = gst_direct_control_binding_get_value;
control_binding_class->get_value_array =
gst_direct_control_binding_get_value_array;
+ control_binding_class->get_g_value_array =
+ gst_direct_control_binding_get_g_value_array;
properties[PROP_CS] =
g_param_spec_object ("control-source", "ControlSource",
"The control source",
GST_TYPE_CONTROL_SOURCE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_ABSOLUTE] =
+ g_param_spec_boolean ("absolute", "Absolute",
+ "Whether the control values are absolute",
+ FALSE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, PROP_LAST, properties);
GstDirectControlBinding *self;
self =
- GST_DIRECT_CONTROL_BINDING (G_OBJECT_CLASS
- (gst_direct_control_binding_parent_class)
- ->constructor (type, n_construct_params, construct_params));
+ GST_DIRECT_CONTROL_BINDING (G_OBJECT_CLASS (parent_class)->constructor
+ (type, n_construct_params, construct_params));
if (GST_CONTROL_BINDING_PSPEC (self)) {
GType type, base;
GST_DEBUG (" using type %s", g_type_name (base));
- // select mapping function
+ /* select mapping function */
+
+#define SET_CONVERT_FUNCTION(type) \
+ if (self->ABI.abi.want_absolute) { \
+ self->convert_g_value = abs_convert_g_value_to_##type; \
+ self->convert_value = abs_convert_value_to_##type; \
+ } \
+ else { \
+ self->convert_g_value = convert_g_value_to_##type; \
+ self->convert_value = convert_value_to_##type; \
+ } \
+ self->byte_size = sizeof (g##type);
+
+
switch (base) {
case G_TYPE_INT:
- self->convert = convert_to_int;
+ SET_CONVERT_FUNCTION (int);
break;
case G_TYPE_UINT:
- self->convert = convert_to_uint;
+ SET_CONVERT_FUNCTION (uint);
break;
case G_TYPE_LONG:
- self->convert = convert_to_long;
+ SET_CONVERT_FUNCTION (long);
break;
case G_TYPE_ULONG:
- self->convert = convert_to_ulong;
+ SET_CONVERT_FUNCTION (ulong);
break;
case G_TYPE_INT64:
- self->convert = convert_to_int64;
+ SET_CONVERT_FUNCTION (int64);
break;
case G_TYPE_UINT64:
- self->convert = convert_to_uint64;
+ SET_CONVERT_FUNCTION (uint64);
break;
case G_TYPE_FLOAT:
- self->convert = convert_to_float;
+ SET_CONVERT_FUNCTION (float);
break;
case G_TYPE_DOUBLE:
- self->convert = convert_to_double;
+ SET_CONVERT_FUNCTION (double);
break;
case G_TYPE_BOOLEAN:
- self->convert = convert_to_boolean;
+ self->convert_g_value = convert_g_value_to_boolean;
+ self->convert_value = convert_value_to_boolean;
+ self->byte_size = sizeof (gboolean);
break;
case G_TYPE_ENUM:
- self->convert = convert_to_enum;
+ self->convert_g_value = convert_g_value_to_enum;
+ self->convert_value = convert_value_to_enum;
+ self->byte_size = sizeof (gint);
break;
default:
GST_WARNING ("incomplete implementation for paramspec type '%s'",
case PROP_CS:
self->cs = g_value_dup_object (value);
break;
+ case PROP_ABSOLUTE:
+ self->ABI.abi.want_absolute = g_value_get_boolean (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
case PROP_CS:
g_value_set_object (value, self->cs);
break;
+ case PROP_ABSOLUTE:
+ g_value_set_boolean (value, self->ABI.abi.want_absolute);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
if (self->cs)
gst_object_replace ((GstObject **) & self->cs, NULL);
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
{
GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (object);
- g_value_unset (&self->cur_value);
+ if (G_IS_VALUE (&self->cur_value))
+ g_value_unset (&self->cur_value);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
}
static gboolean
GST_LOG_OBJECT (object, " mapping %s to value of type %s", _self->name,
G_VALUE_TYPE_NAME (dst_val));
/* run mapping function to convert gdouble to GValue */
- self->convert (self, src_val, dst_val);
+ self->convert_g_value (self, src_val, dst_val);
/* we can make this faster
* http://bugzilla.gnome.org/show_bug.cgi?id=536939
*/
if (gst_control_source_get_value (self->cs, timestamp, &src_val)) {
dst_val = g_new0 (GValue, 1);
g_value_init (dst_val, G_PARAM_SPEC_VALUE_TYPE (_self->pspec));
- self->convert (self, src_val, dst_val);
+ self->convert_g_value (self, src_val, dst_val);
} else {
GST_LOG ("no control value for property %s at ts %" GST_TIME_FORMAT,
_self->name, GST_TIME_ARGS (timestamp));
static gboolean
gst_direct_control_binding_get_value_array (GstControlBinding * _self,
GstClockTime timestamp, GstClockTime interval, guint n_values,
+ gpointer values_)
+{
+ GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (_self);
+ gint i;
+ gdouble *src_val;
+ gboolean res = FALSE;
+ GstDirectControlBindingConvertValue convert;
+ gint byte_size;
+ guint8 *values = (guint8 *) values_;
+
+ g_return_val_if_fail (GST_IS_DIRECT_CONTROL_BINDING (self), 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);
+ g_return_val_if_fail (GST_CONTROL_BINDING_PSPEC (self), FALSE);
+
+ convert = self->convert_value;
+ byte_size = self->byte_size;
+
+ src_val = g_new0 (gdouble, n_values);
+ if ((res = gst_control_source_get_value_array (self->cs, timestamp,
+ interval, n_values, src_val))) {
+ for (i = 0; i < n_values; i++) {
+ /* we will only get NAN for sparse control sources, such as triggers */
+ if (!isnan (src_val[i])) {
+ convert (self, src_val[i], (gpointer) values);
+ } else {
+ GST_LOG ("no control value for property %s at index %d", _self->name,
+ i);
+ }
+ values += byte_size;
+ }
+ } else {
+ GST_LOG ("failed to get control value for property %s at ts %"
+ GST_TIME_FORMAT, _self->name, GST_TIME_ARGS (timestamp));
+ }
+ g_free (src_val);
+ return res;
+}
+
+static gboolean
+gst_direct_control_binding_get_g_value_array (GstControlBinding * _self,
+ GstClockTime timestamp, GstClockTime interval, guint n_values,
GValue * values)
{
GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (_self);
gdouble *src_val;
gboolean res = FALSE;
GType type;
- GstDirectControlBindingConvert convert;
+ GstDirectControlBindingConvertGValue convert;
g_return_val_if_fail (GST_IS_DIRECT_CONTROL_BINDING (self), FALSE);
g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
g_return_val_if_fail (values, FALSE);
g_return_val_if_fail (GST_CONTROL_BINDING_PSPEC (self), FALSE);
- convert = self->convert;
+ convert = self->convert_g_value;
type = G_PARAM_SPEC_VALUE_TYPE (_self->pspec);
src_val = g_new0 (gdouble, n_values);
if ((res = gst_control_source_get_value_array (self->cs, timestamp,
interval, n_values, src_val))) {
for (i = 0; i < n_values; i++) {
+ /* we will only get NAN for sparse control sources, such as triggers */
if (!isnan (src_val[i])) {
g_value_init (&values[i], type);
convert (self, src_val[i], &values[i]);
* gst_direct_control_binding_new:
* @object: the object of the property
* @property_name: the property-name to attach the control source
- * @csource: the control source
+ * @cs: the control source
*
* Create a new control-binding that attaches the #GstControlSource to the
- * #GObject property.
+ * #GObject property. It will map the control source range [0.0 ... 1.0] to
+ * the full target property range, and clip all values outside this range.
*
* Returns: (transfer floating): the new #GstDirectControlBinding
*/
return (GstControlBinding *) g_object_new (GST_TYPE_DIRECT_CONTROL_BINDING,
"object", object, "name", property_name, "control-source", cs, NULL);
}
+
+/**
+ * gst_direct_control_binding_new_absolute:
+ * @object: the object of the property
+ * @property_name: the property-name to attach the control source
+ * @cs: the control source
+ *
+ * Create a new control-binding that attaches the #GstControlSource to the
+ * #GObject property. It will directly map the control source values to the
+ * target property range without any transformations.
+ *
+ * Returns: (transfer floating): the new #GstDirectControlBinding
+ *
+ * Since: 1.6
+ */
+GstControlBinding *
+gst_direct_control_binding_new_absolute (GstObject * object,
+ const gchar * property_name, GstControlSource * cs)
+{
+ return (GstControlBinding *) g_object_new (GST_TYPE_DIRECT_CONTROL_BINDING,
+ "object", object, "name", property_name, "control-source", cs, "absolute",
+ TRUE, NULL);
+}