mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-23 16:50:47 +00:00
added controller code removed dparam pc files
Original commit message from CVS: added controller code removed dparam pc files
This commit is contained in:
parent
75c955843e
commit
c32233b7fc
39 changed files with 3700 additions and 47 deletions
50
ChangeLog
50
ChangeLog
|
@ -1,3 +1,53 @@
|
|||
2005-08-03 Stefan Kost <ensonic@users.sf.net>
|
||||
|
||||
* configure.ac:
|
||||
* docs/libs/Makefile.am:
|
||||
* docs/libs/gstreamer-libs-docs.sgml:
|
||||
* docs/libs/gstreamer-libs-sections.txt:
|
||||
* docs/libs/gstreamer-libs.types:
|
||||
* examples/Makefile.am:
|
||||
* examples/controller/.cvsignore:
|
||||
* examples/controller/Makefile.am:
|
||||
* examples/controller/audio-example.c: (main):
|
||||
* libs/gst/Makefile.am:
|
||||
* libs/gst/controller/.cvsignore:
|
||||
* libs/gst/controller/Makefile.am:
|
||||
* libs/gst/controller/gst-controller.c:
|
||||
(on_object_controlled_property_changed), (gst_timed_value_compare),
|
||||
(gst_timed_value_find),
|
||||
(gst_controlled_property_set_interpolation_mode),
|
||||
(gst_controlled_property_new), (gst_controlled_property_free),
|
||||
(gst_controller_find_controlled_property),
|
||||
(gst_controller_new_valist), (gst_controller_new),
|
||||
(gst_controller_remove_properties_valist),
|
||||
(gst_controller_remove_properties), (gst_controller_set),
|
||||
(gst_controller_set_from_list), (gst_controller_unset),
|
||||
(gst_controller_get), (gst_controller_get_all),
|
||||
(gst_controller_sink_values), (gst_controller_get_value_arrays),
|
||||
(gst_controller_get_value_array),
|
||||
(gst_controller_set_interpolation_mode),
|
||||
(_gst_controller_finalize), (_gst_controller_init),
|
||||
(_gst_controller_class_init), (gst_controller_get_type):
|
||||
* libs/gst/controller/gst-controller.h:
|
||||
* libs/gst/controller/gst-helper.c: (g_object_control_properties),
|
||||
(g_object_uncontrol_properties), (g_object_get_controller),
|
||||
(g_object_set_controller), (g_object_sink_values),
|
||||
(g_object_get_value_arrays), (g_object_get_value_array):
|
||||
* libs/gst/controller/gst-interpolation.c:
|
||||
(gst_controlled_property_find_timed_value_node),
|
||||
(interpolate_none_get), (interpolate_trigger_get),
|
||||
(interpolate_trigger_get_value_array):
|
||||
* libs/gst/controller/lib.c: (gst_controller_init):
|
||||
* pkgconfig/Makefile.am:
|
||||
* pkgconfig/gstreamer-control-uninstalled.pc.in:
|
||||
* pkgconfig/gstreamer-control.pc.in:
|
||||
* testsuite/Makefile.am:
|
||||
* testsuite/controller/.cvsignore:
|
||||
* testsuite/controller/Makefile.am:
|
||||
* testsuite/controller/interpolator.c: (main):
|
||||
added controller code
|
||||
removed dparam pc files
|
||||
|
||||
2005-08-01 Jan Schmidt <thaytan@mad.scientist.com>
|
||||
* gst/base/gstcollectpads.c: (gst_collectpads_finalize),
|
||||
(gst_collectpads_stop):
|
||||
|
|
|
@ -630,6 +630,7 @@ gst/parse/Makefile
|
|||
gst/registries/Makefile
|
||||
libs/Makefile
|
||||
libs/gst/Makefile
|
||||
libs/gst/controller/Makefile
|
||||
libs/gst/dataprotocol/Makefile
|
||||
libs/gst/getbits/Makefile
|
||||
po/Makefile.in
|
||||
|
@ -645,6 +646,7 @@ testsuite/Makefile
|
|||
testsuite/bytestream/Makefile
|
||||
testsuite/caps/Makefile
|
||||
testsuite/cleanup/Makefile
|
||||
testsuite/controller/Makefile
|
||||
testsuite/debug/Makefile
|
||||
testsuite/dlopen/Makefile
|
||||
testsuite/elements/Makefile
|
||||
|
@ -659,6 +661,7 @@ testsuite/states/Makefile
|
|||
testsuite/threads/Makefile
|
||||
testsuite/trigger/Makefile
|
||||
examples/Makefile
|
||||
examples/controller/Makefile
|
||||
examples/cutter/Makefile
|
||||
examples/helloworld/Makefile
|
||||
examples/launch/Makefile
|
||||
|
@ -691,8 +694,8 @@ pkgconfig/gstreamer.pc
|
|||
pkgconfig/gstreamer-uninstalled.pc
|
||||
pkgconfig/gstreamer-base.pc
|
||||
pkgconfig/gstreamer-base-uninstalled.pc
|
||||
pkgconfig/gstreamer-control.pc
|
||||
pkgconfig/gstreamer-control-uninstalled.pc
|
||||
pkgconfig/gstreamer-controller.pc
|
||||
pkgconfig/gstreamer-controller-uninstalled.pc
|
||||
pkgconfig/gstreamer-dataprotocol.pc
|
||||
pkgconfig/gstreamer-dataprotocol-uninstalled.pc
|
||||
gstreamer.spec,
|
||||
|
|
|
@ -223,8 +223,6 @@ Copy the event using the event specific copy function
|
|||
|
||||
@taglist:
|
||||
@Returns:
|
||||
<!-- # Unused Parameters # -->
|
||||
@list:
|
||||
|
||||
|
||||
<!-- ##### FUNCTION gst_event_parse_tag ##### -->
|
||||
|
|
|
@ -32,6 +32,9 @@ GstFakeSrc
|
|||
@arg1:
|
||||
@:
|
||||
@:
|
||||
@:
|
||||
@:
|
||||
@:
|
||||
@:
|
||||
|
||||
<!-- ##### ARG GstFakeSrc:data ##### -->
|
||||
|
|
|
@ -47,7 +47,7 @@ SCAN_OPTIONS=--deprecated-guards="GST_DISABLE_DEPRECATED"
|
|||
#EXTRA_DIST = gstreamer.types.in gstreamer.hierarchy $(DOC_MODULE)-sections.txt gstreamer-sections.txt $(DOC_MAIN_SGML_FILE)
|
||||
|
||||
# Extra options to supply to gtkdoc-mkdb.
|
||||
MKDB_OPTIONS=--sgml-mode --ignore-files=trio
|
||||
MKDB_OPTIONS= --output-format=xml --sgml-mode --ignore-files=trio
|
||||
|
||||
# Extra options to supply to gtkdoc-fixref.
|
||||
FIXXREF_OPTIONS=--extra-dir=../gst/html
|
||||
|
@ -56,13 +56,14 @@ FIXXREF_OPTIONS=--extra-dir=../gst/html
|
|||
HFILE_GLOB=$(DOC_SOURCE_DIR)/*/*.h
|
||||
CFILE_GLOB=$(DOC_SOURCE_DIR)/*/*.c
|
||||
|
||||
# this is a wingo addition
|
||||
# thomasvs: another nice wingo addition would be an explanation on why
|
||||
# this is useful ;)
|
||||
|
||||
# Dependencies for the intermediate scanobj tool
|
||||
#SCANOBJ_DEPS = $(top_builddir)/gst/elements/libgstelements.la \
|
||||
# $(top_builddir)/gst/schedulers/libgstbasicomegascheduler.la \
|
||||
# $(top_builddir)/libs/gst/control/libgstcontrol-@GST_MAJORMINOR@.la
|
||||
SCANOBJ_DEPS = $(top_builddir)/libs/gst/controller/libgstcontroller-@GST_MAJORMINOR@.la
|
||||
|
||||
# Extra options to pass to gtkdoc-scanobj or gtkdoc-scangobj.
|
||||
SCANOBJ_OPTIONS=--type-init-func="g_type_init();gst_init(&argc,&argv)"
|
||||
|
||||
# Header files to ignore when scanning.
|
||||
IGNORE_HFILES = \
|
||||
|
@ -80,7 +81,7 @@ extra_files =
|
|||
|
||||
# CFLAGS and LDFLAGS for compiling scan program. Only needed if your app/lib
|
||||
# contains GtkObjects/GObjects and you want to document signals and properties.
|
||||
GTKDOC_CFLAGS = $(GST_OBJ_CFLAGS) $(POPT_CFLAGS) -I$(top_builddir)
|
||||
GTKDOC_CFLAGS = $(GST_OBJ_CFLAGS) $(POPT_CFLAGS) -I$(top_builddir) -I$(top_builddir)/libs
|
||||
GTKDOC_LIBS = $(GST_OBJ_LIBS) $(POPT_LIBS) $(SCANOBJ_DEPS)
|
||||
|
||||
GTKDOC_CC=$(LIBTOOL) --mode=compile $(CC)
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
<!ENTITY % version-entities SYSTEM "version.entities">
|
||||
%version-entities;
|
||||
<!ENTITY GstBytestream SYSTEM "xml/gstbytestream.xml">
|
||||
<!ENTITY GstController SYSTEM "xml/gstcontroller.xml">
|
||||
<!ENTITY GstGetbits SYSTEM "xml/gstgetbits.xml">
|
||||
<!-- has not yet been written
|
||||
<!ENTITY GstPutbits SYSTEM "xml/gstputbits.xml">
|
||||
|
@ -35,8 +36,13 @@
|
|||
<!-- has not yet been written
|
||||
&GstPutbits;
|
||||
-->
|
||||
<!--link linkend="GObject">GObject</link-->
|
||||
|
||||
|
||||
<chapter id="gstreamer-control">
|
||||
<title>gstcontrol</title>
|
||||
&GstController;
|
||||
<!--&GstControllerGObject; -->
|
||||
</chapter>
|
||||
|
||||
</part>
|
||||
|
||||
<part id="gstreamer-libs-hierarchy">
|
||||
|
|
|
@ -111,3 +111,34 @@ gst_dp_validate_packet
|
|||
<SUBSECTION Standard>
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gstcontroller</FILE>
|
||||
<TITLE>GstController</TITLE>
|
||||
<INCLUDE>libs/controller/gstcontroller.h</INCLUDE>
|
||||
GstController
|
||||
gst_controller_init
|
||||
gst_controller_new
|
||||
gst_controller_new_valist
|
||||
gst_controller_remove_properties
|
||||
gst_controller_remove_properties_valist
|
||||
gst_controller_set
|
||||
gst_controller_set_from_list
|
||||
gst_controller_unset
|
||||
gst_controller_get
|
||||
gst_controller_get_all
|
||||
gst_controller_sink_values
|
||||
gst_controller_get_value_arrays
|
||||
gst_controller_get_value_array
|
||||
gst_controller_set_interpolation_mode
|
||||
GST_PARAM_CONTROLLABLE
|
||||
<SUBSECTION Standard>
|
||||
GstControllerClass
|
||||
GST_CONTROLLER
|
||||
GST_IS_CONTROLLER
|
||||
GST_CONTROLLER_CLASS
|
||||
GST_IS_CONTROLLER_CLASS
|
||||
GST_CONTROLLER_GET_CLASS
|
||||
GST_TYPE_CONTROLLER
|
||||
<SUBSECTION Private>
|
||||
gst_controller_get_type
|
||||
</SECTION>
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include <gst/gst.h>
|
||||
#include <gst/controller/gst-controller.h>
|
||||
|
||||
|
||||
gst_controller_get_type
|
||||
|
|
|
@ -11,6 +11,7 @@ dirs = \
|
|||
thread \
|
||||
plugins \
|
||||
mixer \
|
||||
controller \
|
||||
cutter \
|
||||
pingpong \
|
||||
manual \
|
||||
|
|
4
examples/controller/.gitignore
vendored
Normal file
4
examples/controller/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
audio-example
|
||||
*.bb
|
||||
*.bbg
|
||||
*.da
|
5
examples/controller/Makefile.am
Normal file
5
examples/controller/Makefile.am
Normal file
|
@ -0,0 +1,5 @@
|
|||
noinst_PROGRAMS = audio-example
|
||||
|
||||
audio_example_CFLAGS = $(GST_OBJ_CFLAGS) -I$(top_builddir)/libs
|
||||
audio_example_LDADD = $(GST_OBJ_LIBS)
|
||||
audio_example_LDFLAGS = $(top_builddir)/libs/gst/controller/libgstcontroller-@GST_MAJORMINOR@.la
|
64
examples/controller/audio-example.c
Normal file
64
examples/controller/audio-example.c
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* audio-example.c
|
||||
*
|
||||
* Build a pipeline with testaudiosource->alsasink
|
||||
* and sweep frequency and volume
|
||||
*
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/controller/gst-controller.h>
|
||||
|
||||
gint
|
||||
main (gint argc, gchar ** argv)
|
||||
{
|
||||
gint res = 1;
|
||||
GstElement *src, *sink;
|
||||
GstBin *bin;
|
||||
GstController *ctrl;
|
||||
GValue vol = { 0, };
|
||||
|
||||
gst_init (&argc, &argv);
|
||||
gst_controller_init (&argc, &argv);
|
||||
|
||||
// build pipeline
|
||||
bin = GST_BIN (gst_pipeline_new ("pipeline"));
|
||||
/* TODO make this "testaudiosrc", when its ready */
|
||||
src = gst_element_factory_make ("sinesrc", "gen_audio");
|
||||
sink = gst_element_factory_make ("alsasink", "play_audio");
|
||||
gst_bin_add_many (bin, src, sink, NULL);
|
||||
|
||||
// add a controller to the source
|
||||
if (!(ctrl =
|
||||
gst_controller_new (G_OBJECT (src), "frequency", "volume", NULL))) {
|
||||
goto Error;
|
||||
}
|
||||
// set interpolation
|
||||
gst_controller_set_interpolation_mode (ctrl, "volume",
|
||||
GST_INTERPOLATE_LINEAR);
|
||||
|
||||
// set control values
|
||||
g_value_init (&vol, G_TYPE_DOUBLE);
|
||||
g_value_set_double (&vol, 0.0);
|
||||
gst_controller_set (ctrl, "volume", 0 * GST_SECOND, &vol);
|
||||
g_value_set_double (&vol, 1.0);
|
||||
gst_controller_set (ctrl, "volume", 1 * GST_SECOND, &vol);
|
||||
|
||||
// iterate two seconds
|
||||
/*
|
||||
if(gst_element_set_state (bin, GST_STATE_PLAYING))
|
||||
{
|
||||
while (gst_bin_iterate (bin))
|
||||
{
|
||||
}
|
||||
}
|
||||
gst_element_set_state (bin, GST_STATE_NULL);
|
||||
*/
|
||||
|
||||
// cleanup
|
||||
g_object_unref (G_OBJECT (ctrl));
|
||||
g_object_unref (G_OBJECT (bin));
|
||||
res = 0;
|
||||
Error:
|
||||
return (res);
|
||||
}
|
|
@ -1 +1 @@
|
|||
SUBDIRS = dataprotocol getbits
|
||||
SUBDIRS = controller dataprotocol getbits
|
||||
|
|
5
libs/gst/controller/.gitignore
vendored
Normal file
5
libs/gst/controller/.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
*.bb
|
||||
*.bbg
|
||||
*.da
|
||||
*.def
|
||||
*.gcno
|
15
libs/gst/controller/Makefile.am
Normal file
15
libs/gst/controller/Makefile.am
Normal file
|
@ -0,0 +1,15 @@
|
|||
lib_LTLIBRARIES = libgstcontroller-@GST_MAJORMINOR@.la
|
||||
|
||||
libgstcontroller_@GST_MAJORMINOR@_includedir = $(includedir)/gstreamer-@GST_MAJORMINOR@/gst/controller
|
||||
libgstcontroller_@GST_MAJORMINOR@_include_HEADERS = \
|
||||
gst-controller.h
|
||||
|
||||
libgstcontroller_@GST_MAJORMINOR@_la_SOURCES = \
|
||||
lib.c \
|
||||
gst-controller.c \
|
||||
gst-interpolation.c \
|
||||
gst-helper.c
|
||||
|
||||
libgstcontroller_@GST_MAJORMINOR@_la_CFLAGS = $(GST_OBJ_CFLAGS) -I$(top_srcdir)/libs
|
||||
libgstcontroller_@GST_MAJORMINOR@_la_LDFLAGS = @GST_LIB_LDFLAGS@
|
||||
libgstcontroller_@GST_MAJORMINOR@_la_LIBADD = $(GST_OBJ_LIBS)
|
929
libs/gst/controller/gst-controller.c
Normal file
929
libs/gst/controller/gst-controller.c
Normal file
|
@ -0,0 +1,929 @@
|
|||
/*
|
||||
* gst-controller.c
|
||||
*
|
||||
* New dynamic properties
|
||||
*
|
||||
*/
|
||||
|
||||
/* What needs to be done in plugins?
|
||||
Very little - it is just two steps to make a plugin controllable!
|
||||
|
||||
1) Just mark gobject-properties that make sense to be controlled,
|
||||
by GST_PARAM_CONTROLLABLE for a start.
|
||||
|
||||
2) When processing data (get, chain, loop function) at the beginning call
|
||||
gst_element_sink_values(element,timestamp).
|
||||
This will made the controller to update all gobject properties that are under
|
||||
control with the current values based on timestamp.
|
||||
*/
|
||||
|
||||
/* What needs to be done in applications?
|
||||
|
||||
1) First put some properties under control, by calling
|
||||
controller=g_object_control_properties(object, "prop1", "prop2",...);
|
||||
|
||||
2) Set how the controller will smooth inbetween values.
|
||||
gst_controller_set_interpolation_mode(controller,"prop1",mode);
|
||||
|
||||
3) Set key values
|
||||
gst_controller_set(controller,"prop1",0*GST_SECOND,value1);
|
||||
gst_controller_set(controller,"prop1",1*GST_SECOND,value2);
|
||||
|
||||
4) Start your pipeline ;-)
|
||||
|
||||
5) Live control params from the GUI
|
||||
g_object_set_live_value(object, "prop1", timestamp, value);
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "gst-controller.h"
|
||||
|
||||
#define GST_CAT_DEFAULT gst_controller_debug
|
||||
GST_DEBUG_CATEGORY_EXTERN (GST_CAT_DEFAULT);
|
||||
|
||||
static GObjectClass *parent_class = NULL;
|
||||
GQuark controller_key;
|
||||
|
||||
|
||||
/* imports from gst-interpolation.c */
|
||||
|
||||
extern GList
|
||||
*gst_controlled_property_find_timed_value_node (GstControlledProperty *
|
||||
prop, GstClockTime timestamp);
|
||||
extern GstInterpolateMethod *interpolation_methods[];
|
||||
|
||||
/* callbacks */
|
||||
|
||||
void
|
||||
on_object_controlled_property_changed (const GObject * object, GParamSpec * arg,
|
||||
gpointer user_data)
|
||||
{
|
||||
GstControlledProperty *prop = GST_CONTROLLED_PROPERTY (user_data);
|
||||
GstController *ctrl;
|
||||
|
||||
GST_INFO ("notify for '%s'", prop->name);
|
||||
|
||||
ctrl = g_object_get_qdata (G_OBJECT (object), controller_key);
|
||||
g_return_if_fail (ctrl);
|
||||
|
||||
if (g_mutex_trylock (ctrl->lock)) {
|
||||
if (!G_IS_VALUE (&prop->live_value.value)) {
|
||||
//g_value_unset (&prop->live_value.value);
|
||||
g_value_init (&prop->live_value.value, prop->type);
|
||||
}
|
||||
g_object_get_property (G_OBJECT (object), prop->name,
|
||||
&prop->live_value.value);
|
||||
prop->live_value.timestamp = prop->last_value.timestamp;
|
||||
g_mutex_unlock (ctrl->lock);
|
||||
GST_DEBUG ("-> is live update : ts=%" G_GUINT64_FORMAT,
|
||||
prop->live_value.timestamp);
|
||||
}
|
||||
//else {
|
||||
//GST_DEBUG ("-> is control change");
|
||||
//}
|
||||
}
|
||||
|
||||
/* helper */
|
||||
|
||||
/*
|
||||
* gst_timed_value_compare:
|
||||
* @p1: a pointer to a #GstTimedValue
|
||||
* @p2: a pointer to a #GstTimedValue
|
||||
*
|
||||
* Compare function for g_list operations that operates on two #GstTimedValue
|
||||
* parameters.
|
||||
*/
|
||||
static gint
|
||||
gst_timed_value_compare (gconstpointer p1, gconstpointer p2)
|
||||
{
|
||||
GstClockTime ct1 = ((GstTimedValue *) p1)->timestamp;
|
||||
GstClockTime ct2 = ((GstTimedValue *) p2)->timestamp;
|
||||
|
||||
return ((ct1 < ct2) ? -1 : ((ct1 == ct2) ? 0 : 1));
|
||||
/* this does not produce an gint :(
|
||||
return ((ct1 - ct2));
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* gst_timed_value_find:
|
||||
* @p1: a pointer to a #GstTimedValue
|
||||
* @p2: a pointer to a #GstClockTime
|
||||
*
|
||||
* Compare function for g_list operations that operates on a #GstTimedValue and
|
||||
* a #GstClockTime.
|
||||
*/
|
||||
static gint
|
||||
gst_timed_value_find (gconstpointer p1, gconstpointer p2)
|
||||
{
|
||||
GstClockTime ct1 = ((GstTimedValue *) p1)->timestamp;
|
||||
GstClockTime ct2 = *(GstClockTime *) p2;
|
||||
|
||||
return ((ct1 < ct2) ? -1 : ((ct1 == ct2) ? 0 : 1));
|
||||
/* this does not produce an gint :(
|
||||
return ((ct1 - ct2));
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* gst_controlled_property_set_interpolation_mode:
|
||||
* @self: the controlled property object to change
|
||||
* @mode: the new interpolation mode
|
||||
*
|
||||
* Sets the given Interpolation mode for the controlled property and activates
|
||||
* the respective interpolation hooks.
|
||||
*/
|
||||
static gboolean
|
||||
gst_controlled_property_set_interpolation_mode (GstControlledProperty * self,
|
||||
GstInterpolateMode mode)
|
||||
{
|
||||
self->interpolation = mode;
|
||||
if (mode != GST_INTERPOLATE_USER) {
|
||||
switch (self->type) {
|
||||
case G_TYPE_INT:
|
||||
case G_TYPE_UINT:
|
||||
self->get = interpolation_methods[mode]->get_int;
|
||||
self->get_value_array =
|
||||
interpolation_methods[mode]->get_int_value_array;
|
||||
break;
|
||||
case G_TYPE_LONG:
|
||||
case G_TYPE_ULONG:
|
||||
self->get = interpolation_methods[mode]->get_long;
|
||||
self->get_value_array =
|
||||
interpolation_methods[mode]->get_long_value_array;
|
||||
break;
|
||||
case G_TYPE_FLOAT:
|
||||
self->get = interpolation_methods[mode]->get_float;
|
||||
self->get_value_array =
|
||||
interpolation_methods[mode]->get_float_value_array;
|
||||
break;
|
||||
case G_TYPE_DOUBLE:
|
||||
self->get = interpolation_methods[mode]->get_double;
|
||||
self->get_value_array =
|
||||
interpolation_methods[mode]->get_double_value_array;
|
||||
break;
|
||||
default:
|
||||
self->get = NULL;
|
||||
self->get_value_array = NULL;
|
||||
GST_WARNING ("incomplete implementation for type '%d'", self->type);
|
||||
}
|
||||
} else {
|
||||
/* TODO shouldn't this also get a GstInterpolateMethod *user_method
|
||||
for the case mode==GST_INTERPOLATE_USER
|
||||
*/
|
||||
}
|
||||
return (TRUE);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 (GObject * 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 controlable
|
||||
g_return_val_if_fail (!(pspec->
|
||||
flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)), NULL);
|
||||
//g_return_val_if_fail((pspec->flags&GST_PARAM_CONTROLLABLE),NULL);
|
||||
/* TODO do sanity checks
|
||||
we don't control some pspec->value_type:
|
||||
G_TYPE_PARAM_BOXED
|
||||
G_TYPE_PARAM_ENUM
|
||||
G_TYPE_PARAM_FLAGS
|
||||
G_TYPE_PARAM_OBJECT
|
||||
G_TYPE_PARAM_PARAM
|
||||
G_TYPE_PARAM_POINTER
|
||||
G_TYPE_PARAM_STRING
|
||||
*/
|
||||
|
||||
if ((prop = g_new0 (GstControlledProperty, 1))) {
|
||||
gchar *signal_name;
|
||||
|
||||
prop->name = pspec->name; // so we don't use the same mem twice
|
||||
prop->object = object;
|
||||
prop->type = G_PARAM_SPEC_VALUE_TYPE (pspec);
|
||||
gst_controlled_property_set_interpolation_mode (prop,
|
||||
GST_INTERPOLATE_NONE);
|
||||
/* prepare our gvalues */
|
||||
g_value_init (&prop->default_value, prop->type);
|
||||
g_value_init (&prop->result_value, prop->type);
|
||||
g_value_init (&prop->last_value.value, prop->type);
|
||||
switch (prop->type) {
|
||||
case G_TYPE_INT:{
|
||||
GParamSpecInt *tpspec = G_PARAM_SPEC_INT (pspec);
|
||||
|
||||
g_value_set_int (&prop->default_value, tpspec->default_value);
|
||||
}
|
||||
break;
|
||||
case G_TYPE_UINT:{
|
||||
GParamSpecUInt *tpspec = G_PARAM_SPEC_UINT (pspec);
|
||||
|
||||
g_value_set_uint (&prop->default_value, tpspec->default_value);
|
||||
}
|
||||
break;
|
||||
case G_TYPE_LONG:{
|
||||
GParamSpecLong *tpspec = G_PARAM_SPEC_LONG (pspec);
|
||||
|
||||
g_value_set_long (&prop->default_value, tpspec->default_value);
|
||||
}
|
||||
break;
|
||||
case G_TYPE_ULONG:{
|
||||
GParamSpecULong *tpspec = G_PARAM_SPEC_ULONG (pspec);
|
||||
|
||||
g_value_set_ulong (&prop->default_value, tpspec->default_value);
|
||||
}
|
||||
break;
|
||||
case G_TYPE_FLOAT:{
|
||||
GParamSpecFloat *tpspec = G_PARAM_SPEC_FLOAT (pspec);
|
||||
|
||||
g_value_set_float (&prop->default_value, tpspec->default_value);
|
||||
}
|
||||
case G_TYPE_DOUBLE:{
|
||||
GParamSpecDouble *tpspec = G_PARAM_SPEC_DOUBLE (pspec);
|
||||
|
||||
g_value_set_double (&prop->default_value, tpspec->default_value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
GST_WARNING ("incomplete implementation for paramspec type '%s'",
|
||||
G_PARAM_SPEC_TYPE_NAME (pspec));
|
||||
}
|
||||
/* TODO what about adding a timedval with timestamp=0 and value=default
|
||||
+ a bit easier for interpolators, example:
|
||||
* first timestamp is at 5
|
||||
* requested value if for timestamp=3
|
||||
* LINEAR and Co. would need to interpolate from default value
|
||||
to value at timestamp 5
|
||||
*/
|
||||
signal_name = g_alloca (8 + 1 + strlen (name));
|
||||
g_sprintf (signal_name, "notify::%s", name);
|
||||
prop->notify_handler_id =
|
||||
g_signal_connect (object, signal_name,
|
||||
G_CALLBACK (on_object_controlled_property_changed), (gpointer) prop);
|
||||
}
|
||||
} 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)
|
||||
{
|
||||
GList *node;
|
||||
|
||||
g_signal_handler_disconnect (prop->object, prop->notify_handler_id);
|
||||
for (node = prop->values; node; node = g_list_next (node)) {
|
||||
g_free (node->data);
|
||||
}
|
||||
g_list_free (prop->values);
|
||||
g_free (prop);
|
||||
}
|
||||
|
||||
/*
|
||||
* gst_controller_find_controlled_property:
|
||||
* @self: the controller object to search for a property in
|
||||
* @name: the gobject property name to look for
|
||||
*
|
||||
* Searches the list of properties under control.
|
||||
*
|
||||
* Returns: a #GstControlledProperty object of %NULL if the property is not
|
||||
* being controlled.
|
||||
*/
|
||||
static GstControlledProperty *
|
||||
gst_controller_find_controlled_property (GstController * self,
|
||||
const gchar * name)
|
||||
{
|
||||
GstControlledProperty *prop;
|
||||
GList *node;
|
||||
|
||||
for (node = self->properties; node; node = g_list_next (node)) {
|
||||
prop = node->data;
|
||||
if (!strcmp (prop->name, name)) {
|
||||
return (prop);
|
||||
}
|
||||
}
|
||||
GST_WARNING ("controller does not manage property '%s'", name);
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* methods */
|
||||
|
||||
/**
|
||||
* gst_controller_new_valist:
|
||||
* @object: the object of which some properties should be controlled
|
||||
* @var_args: %NULL terminated list of property names that should be controlled
|
||||
*
|
||||
* Creates a new GstController for the given object's properties
|
||||
*
|
||||
* Returns: the new controller.
|
||||
*/
|
||||
GstController *
|
||||
gst_controller_new_valist (GObject * object, va_list var_args)
|
||||
{
|
||||
GstController *self;
|
||||
GstControlledProperty *prop;
|
||||
gchar *name;
|
||||
|
||||
g_return_val_if_fail (G_IS_OBJECT (object), NULL);
|
||||
|
||||
GST_INFO ("setting up a new controller");
|
||||
|
||||
/* TODO should this method check if the given object implements GstParent and
|
||||
if so instantiate a GstParentController ?
|
||||
|
||||
BilboEd: This is too specific to be put here, don't we want
|
||||
GstController to be as generic as possible ?
|
||||
|
||||
Ensonic: So we will have gst_parent_controller_new as well and maybe a
|
||||
convinience function that automatically chooses the right one (how to name it)?
|
||||
GstParent will be in core after all.
|
||||
*/
|
||||
|
||||
self = g_object_get_qdata (object, controller_key);
|
||||
if (!self) {
|
||||
self = g_object_new (GST_TYPE_CONTROLLER, NULL);
|
||||
self->lock = g_mutex_new ();
|
||||
// store the controller
|
||||
g_object_set_qdata (object, controller_key, self);
|
||||
}
|
||||
// create GstControlledProperty for each property
|
||||
while ((name = va_arg (var_args, gchar *))) {
|
||||
// create GstControlledProperty and add to self->propeties List
|
||||
if ((prop = gst_controlled_property_new (object, name)))
|
||||
self->properties = g_list_prepend (self->properties, prop);
|
||||
}
|
||||
va_end (var_args);
|
||||
|
||||
return (self);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_controller_new:
|
||||
* @object: the object of which some properties should be controlled
|
||||
* @...: %NULL terminated list of property names that should be controlled
|
||||
*
|
||||
* Creates a new GstController for the given object's properties
|
||||
*
|
||||
* Returns: the new controller.
|
||||
*/
|
||||
GstController *
|
||||
gst_controller_new (GObject * object, ...)
|
||||
{
|
||||
GstController *self;
|
||||
va_list var_args;
|
||||
|
||||
g_return_val_if_fail (G_IS_OBJECT (object), NULL);
|
||||
|
||||
va_start (var_args, object);
|
||||
self = gst_controller_new_valist (object, var_args);
|
||||
va_end (var_args);
|
||||
|
||||
return (self);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_controller_remove_properties:
|
||||
* @self: the controller object from which some properties should be removed
|
||||
* @var_args: %NULL terminated list of property names that should be removed
|
||||
*
|
||||
* Removes the given object properties from the controller
|
||||
*
|
||||
* Returns: %FALSE if one of the given property isn't handled by the controller, %TRUE otherwise
|
||||
*/
|
||||
gboolean
|
||||
gst_controller_remove_properties_valist (GstController * self, va_list var_args)
|
||||
{
|
||||
gboolean res = TRUE;
|
||||
GstControlledProperty *prop;
|
||||
gchar *name;
|
||||
|
||||
g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
|
||||
|
||||
while ((name = va_arg (var_args, gchar *))) {
|
||||
// find the property in the properties list of the controller, remove and free it
|
||||
g_mutex_lock (self->lock);
|
||||
if ((prop = gst_controller_find_controlled_property (self, name))) {
|
||||
self->properties = g_list_remove (self->properties, prop);
|
||||
gst_controlled_property_free (prop);
|
||||
} else {
|
||||
res = FALSE;
|
||||
}
|
||||
g_mutex_unlock (self->lock);
|
||||
}
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_controller_remove_properties:
|
||||
* @self: the controller object from which some properties should be removed
|
||||
* @...: %NULL terminated list of property names that should be removed
|
||||
*
|
||||
* Removes the given object properties from the controller
|
||||
*
|
||||
* Returns: %FALSE if one of the given property isn't handled by the controller, %TRUE otherwise
|
||||
*/
|
||||
gboolean
|
||||
gst_controller_remove_properties (GstController * self, ...)
|
||||
{
|
||||
gboolean res;
|
||||
va_list var_args;
|
||||
|
||||
g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
|
||||
|
||||
va_start (var_args, self);
|
||||
res = gst_controller_remove_properties_valist (self, var_args);
|
||||
va_end (var_args);
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_controller_set:
|
||||
* @self: the controller object which handles the properties
|
||||
* @property_name: the name of the property to set
|
||||
* @timestamp: the time the control-change is schedules for
|
||||
* @value: the control-value
|
||||
*
|
||||
* Set the value of given controller-handled property at a certain time.
|
||||
*
|
||||
* Returns: FALSE if the values couldn't be set (ex : properties not handled by controller), TRUE otherwise
|
||||
*/
|
||||
gboolean
|
||||
gst_controller_set (GstController * self, gchar * property_name,
|
||||
GstClockTime timestamp, GValue * value)
|
||||
{
|
||||
gboolean res = FALSE;
|
||||
GstControlledProperty *prop;
|
||||
|
||||
g_return_val_if_fail (GST_IS_CONTROLLER (self), 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 (G_IS_VALUE (value), FALSE);
|
||||
|
||||
g_mutex_lock (self->lock);
|
||||
if ((prop = gst_controller_find_controlled_property (self, property_name))) {
|
||||
if (G_VALUE_TYPE (value) == prop->type) {
|
||||
GstTimedValue *tv;
|
||||
GList *node;
|
||||
|
||||
// check if a timed_value for the timestamp already exists
|
||||
if ((node = g_list_find_custom (prop->values, ×tamp,
|
||||
gst_timed_value_find))) {
|
||||
tv = node->data;
|
||||
memcpy (&tv->value, value, sizeof (GValue));
|
||||
} else {
|
||||
// create a new GstTimedValue
|
||||
tv = g_new (GstTimedValue, 1);
|
||||
tv->timestamp = timestamp;
|
||||
memcpy (&tv->value, value, sizeof (GValue));
|
||||
// and sort it into the prop->values list
|
||||
prop->values =
|
||||
g_list_insert_sorted (prop->values, tv, gst_timed_value_compare);
|
||||
}
|
||||
res = TRUE;
|
||||
} else {
|
||||
GST_WARNING ("incompatible value type for property '%s'", prop->name);
|
||||
}
|
||||
}
|
||||
g_mutex_unlock (self->lock);
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_controller_set_from_list:
|
||||
* @self: the controller object which handles the properties
|
||||
* @property_name: the name of the property to set
|
||||
* @timedvalues: a list with #GstTimedValue items
|
||||
*
|
||||
* Sets multiple timed values at once.
|
||||
*
|
||||
* Returns: %FALSE if the values couldn't be set (ex : properties not handled by controller), %TRUE otherwise
|
||||
*/
|
||||
|
||||
gboolean
|
||||
gst_controller_set_from_list (GstController * self, gchar * property_name,
|
||||
GSList * timedvalues)
|
||||
{
|
||||
gboolean res = FALSE;
|
||||
GstControlledProperty *prop;
|
||||
GSList *node;
|
||||
GstTimedValue *tv;
|
||||
|
||||
g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
|
||||
g_return_val_if_fail (property_name, FALSE);
|
||||
|
||||
g_mutex_lock (self->lock);
|
||||
if ((prop = gst_controller_find_controlled_property (self, property_name))) {
|
||||
for (node = timedvalues; node; node = g_slist_next (node)) {
|
||||
tv = node->data;
|
||||
if (G_VALUE_TYPE (&tv->value) == prop->type) {
|
||||
g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (tv->timestamp), FALSE);
|
||||
/* TODO copy the timed value or just link in? */
|
||||
prop->values =
|
||||
g_list_insert_sorted (prop->values, tv, gst_timed_value_compare);
|
||||
res = TRUE;
|
||||
} else {
|
||||
GST_WARNING ("incompatible value type for property '%s'", prop->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
g_mutex_unlock (self->lock);
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_controller_unset:
|
||||
* @self: the controller object which handles the properties
|
||||
* @property_name: the name of the property to unset
|
||||
* @timestamp: the time the control-change should be removed from
|
||||
*
|
||||
* Used to remove the value of given controller-handled property at a certain
|
||||
* time.
|
||||
*
|
||||
* Returns: %FALSE if the values couldn't be unset (ex : properties not handled by controller), %TRUE otherwise
|
||||
*/
|
||||
gboolean
|
||||
gst_controller_unset (GstController * self, gchar * property_name,
|
||||
GstClockTime timestamp)
|
||||
{
|
||||
gboolean res = FALSE;
|
||||
GstControlledProperty *prop;
|
||||
|
||||
g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
|
||||
g_return_val_if_fail (property_name, FALSE);
|
||||
g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
|
||||
|
||||
g_mutex_lock (self->lock);
|
||||
if ((prop = gst_controller_find_controlled_property (self, property_name))) {
|
||||
prop->values = g_list_remove (prop->values, prop);
|
||||
res = TRUE;
|
||||
}
|
||||
g_mutex_unlock (self->lock);
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_controller_get:
|
||||
* @self: the controller object which handles the 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 controller-handled property at the requested
|
||||
* time.
|
||||
*
|
||||
* Returns: the GValue of the property at the given time, or %NULL if the property isn't handled by the controller
|
||||
*/
|
||||
|
||||
GValue *
|
||||
gst_controller_get (GstController * self, gchar * property_name,
|
||||
GstClockTime timestamp)
|
||||
{
|
||||
GstControlledProperty *prop;
|
||||
GValue *val = NULL;
|
||||
|
||||
g_return_val_if_fail (GST_IS_CONTROLLER (self), NULL);
|
||||
g_return_val_if_fail (property_name, NULL);
|
||||
g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), NULL);
|
||||
|
||||
g_mutex_lock (self->lock);
|
||||
if ((prop = gst_controller_find_controlled_property (self, property_name))) {
|
||||
//get current value via interpolator
|
||||
val = prop->get (prop, timestamp);
|
||||
}
|
||||
g_mutex_unlock (self->lock);
|
||||
|
||||
return (val);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_controller_get_all:
|
||||
* @self: the controller to get the list from
|
||||
* @property_name: the name of the property to get the list for
|
||||
*
|
||||
* Returns a read-only copy of the list of GstTimedValue for the given property.
|
||||
* Free the list after done with it.
|
||||
*
|
||||
* Returns: a copy of the list, or %NULL if the property isn't handled by the controller
|
||||
*/
|
||||
const GList *
|
||||
gst_controller_get_all (GstController * self, gchar * property_name)
|
||||
{
|
||||
GList *res = NULL;
|
||||
GstControlledProperty *prop;
|
||||
|
||||
g_return_val_if_fail (GST_IS_CONTROLLER (self), NULL);
|
||||
g_return_val_if_fail (property_name, NULL);
|
||||
|
||||
g_mutex_lock (self->lock);
|
||||
if ((prop = gst_controller_find_controlled_property (self, property_name))) {
|
||||
res = g_list_copy (prop->values);
|
||||
}
|
||||
g_mutex_unlock (self->lock);
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_controller_sink_values:
|
||||
* @self: the controller that handles the values
|
||||
* @timestamp: the time that should be processed
|
||||
*
|
||||
* Sets the properties of the element, according to the controller that (maybe)
|
||||
* handles them and for the given timestamp.
|
||||
*
|
||||
* Returns: %TRUE if the controller values could be applied to the object
|
||||
* properties, %FALSE otherwise
|
||||
*/
|
||||
gboolean
|
||||
gst_controller_sink_values (GstController * self, GstClockTime timestamp)
|
||||
{
|
||||
GstControlledProperty *prop;
|
||||
GList *node;
|
||||
GValue *value;
|
||||
gboolean live;
|
||||
|
||||
g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
|
||||
g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
|
||||
|
||||
GST_INFO ("sink_values");
|
||||
|
||||
g_mutex_lock (self->lock);
|
||||
// go over the controlled properties of the controller
|
||||
for (node = self->properties; node; node = g_list_next (node)) {
|
||||
prop = node->data;
|
||||
GST_DEBUG (" property '%s' at ts=%" G_GUINT64_FORMAT, prop->name,
|
||||
timestamp);
|
||||
|
||||
live = FALSE;
|
||||
if (G_IS_VALUE (&prop->live_value.value)) {
|
||||
GList *lnode =
|
||||
gst_controlled_property_find_timed_value_node (prop, timestamp);
|
||||
if (!lnode) {
|
||||
GST_DEBUG (" no control changes in the queue");
|
||||
live = TRUE;
|
||||
} else {
|
||||
GstTimedValue *tv = lnode->data;
|
||||
|
||||
//GST_DEBUG ("live.ts %"G_UINT64_FORMAT" <-> now %"G_UINT64_FORMAT, prop->live_value.timestamp, tv->timestamp);
|
||||
if (prop->live_value.timestamp < tv->timestamp) {
|
||||
g_value_unset (&prop->live_value.value);
|
||||
GST_DEBUG (" live value resetted");
|
||||
} else if (prop->live_value.timestamp < timestamp) {
|
||||
live = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!live) {
|
||||
//get current value via interpolator
|
||||
value = prop->get (prop, timestamp);
|
||||
prop->last_value.timestamp = timestamp;
|
||||
g_value_copy (value, &prop->last_value.value);
|
||||
g_object_set_property (prop->object, prop->name, value);
|
||||
}
|
||||
}
|
||||
g_mutex_unlock (self->lock);
|
||||
/* TODO what can here go wrong, to return FALSE ?
|
||||
BilboEd : Nothing I guess, as long as all the checks are made when creating the controller,
|
||||
adding/removing controlled properties, etc...
|
||||
*/
|
||||
|
||||
return (TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_controller_get_value_arrays:
|
||||
* @self: the controller that handles the values
|
||||
* @timestamp: the time that should be processed
|
||||
* @value_arrays: list to return the control-values in
|
||||
*
|
||||
* Function to be able to get an array of values for one or more given element
|
||||
* properties.
|
||||
*
|
||||
* If the GstValueArray->values array in list nodes is NULL, it will be created
|
||||
* by the function.
|
||||
* The type of the values in the array are the same as the property's type.
|
||||
*
|
||||
* Returns: %TRUE if the given array(s) could be filled, %FALSE otherwise
|
||||
*/
|
||||
gboolean
|
||||
gst_controller_get_value_arrays (GstController * self,
|
||||
GstClockTime timestamp, GSList * value_arrays)
|
||||
{
|
||||
gboolean res = TRUE;
|
||||
GSList *node;
|
||||
|
||||
g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
|
||||
g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
|
||||
g_return_val_if_fail (value_arrays, FALSE);
|
||||
|
||||
for (node = value_arrays; (res && node); node = g_slist_next (node)) {
|
||||
res = gst_controller_get_value_array (self, timestamp, node->data);
|
||||
}
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_controller_get_value_array:
|
||||
* @self: the controller that handles the values
|
||||
* @timestamp: the time that should be processed
|
||||
* @value_array: array to put control-values in
|
||||
*
|
||||
* Function to be able to get an array of values for one element properties
|
||||
*
|
||||
* If the GstValueArray->values array is NULL, it will be created by the function.
|
||||
* The type of the values in the array are the same as the property's type.
|
||||
*
|
||||
* Returns: %TRUE if the given array(s) could be filled, %FALSE otherwise
|
||||
*/
|
||||
gboolean
|
||||
gst_controller_get_value_array (GstController * self, GstClockTime timestamp,
|
||||
GstValueArray * value_array)
|
||||
{
|
||||
gboolean res = FALSE;
|
||||
GstControlledProperty *prop;
|
||||
|
||||
g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
|
||||
g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
|
||||
g_return_val_if_fail (value_array, FALSE);
|
||||
g_return_val_if_fail (value_array->property_name, FALSE);
|
||||
|
||||
/* TODO and if GstValueArray->values is not NULL, the caller is resposible that
|
||||
is is big enough for nbsamples values, right?
|
||||
*/
|
||||
|
||||
g_mutex_lock (self->lock);
|
||||
if ((prop =
|
||||
gst_controller_find_controlled_property (self,
|
||||
value_array->property_name))) {
|
||||
if (!value_array->values) {
|
||||
/* TODO from where to get the base size
|
||||
value_array->values=g_new(sizeof(???),nbsamples);
|
||||
*/
|
||||
}
|
||||
//get current value_array via interpolator
|
||||
res = prop->get_value_array (prop, timestamp, value_array);
|
||||
}
|
||||
g_mutex_unlock (self->lock);
|
||||
return (res);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_controller_set_interpolation_mode:
|
||||
* @controller:
|
||||
* @property_name:
|
||||
* @mode: interpolation mode
|
||||
*
|
||||
* Sets the given interpolation mode on the given property.
|
||||
*
|
||||
* Returns: %TRUE if the property is handled by the controller, %FALSE otherwise
|
||||
*/
|
||||
gboolean
|
||||
gst_controller_set_interpolation_mode (GstController * self,
|
||||
gchar * property_name, GstInterpolateMode mode)
|
||||
{
|
||||
gboolean res = FALSE;
|
||||
GstControlledProperty *prop;
|
||||
|
||||
g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
|
||||
g_return_val_if_fail (property_name, FALSE);
|
||||
|
||||
g_mutex_lock (self->lock);
|
||||
if ((prop = gst_controller_find_controlled_property (self, property_name))) {
|
||||
/* TODO shouldn't this also get a GstInterpolateMethod *user_method
|
||||
for the case mode==GST_INTERPOLATE_USER
|
||||
*/
|
||||
gst_controlled_property_set_interpolation_mode (prop, mode);
|
||||
res = TRUE;
|
||||
}
|
||||
g_mutex_unlock (self->lock);
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
/*
|
||||
void
|
||||
gst_controller_set_live_value(GstController * self, gchar *property_name,
|
||||
GstClockTime timestamp, GValue *value)
|
||||
{
|
||||
GstControlledProperty *prop;
|
||||
|
||||
g_return_if_fail (GST_IS_CONTROLLER (self));
|
||||
g_return_if_fail (property_name);
|
||||
|
||||
g_mutex_lock (self->lock);
|
||||
if ((prop = gst_controller_find_controlled_property (self, property_name))) {
|
||||
g_value_unset (&prop->live_value.value);
|
||||
g_value_init (&prop->live_value.value, prop->type);
|
||||
g_value_copy (value, &prop->live_value.value);
|
||||
prop->live_value.timestamp = timestamp;
|
||||
}
|
||||
g_mutex_unlock (self->lock);
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
/* gobject handling */
|
||||
|
||||
static void
|
||||
_gst_controller_finalize (GObject * object)
|
||||
{
|
||||
GstController *self = GST_CONTROLLER (object);
|
||||
GList *node;
|
||||
|
||||
// free list of properties
|
||||
if (self->properties) {
|
||||
for (node = self->properties; node; node = g_list_next (node)) {
|
||||
gst_controlled_property_free (node->data);
|
||||
}
|
||||
g_list_free (self->properties);
|
||||
self->properties = NULL;
|
||||
}
|
||||
g_mutex_free (self->lock);
|
||||
|
||||
if (G_OBJECT_CLASS (parent_class)->finalize)
|
||||
(G_OBJECT_CLASS (parent_class)->finalize) (object);
|
||||
}
|
||||
|
||||
static void
|
||||
_gst_controller_init (GTypeInstance * instance, gpointer g_class)
|
||||
{
|
||||
GstController *self = GST_CONTROLLER (instance);
|
||||
|
||||
self->lock = g_mutex_new ();
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
_gst_controller_class_init (GstControllerClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
parent_class = g_type_class_ref (G_TYPE_OBJECT);
|
||||
|
||||
gobject_class->finalize = _gst_controller_finalize;
|
||||
|
||||
controller_key = g_quark_from_string ("gst::controller");
|
||||
|
||||
// register properties
|
||||
// register signals
|
||||
// set defaults for overridable methods
|
||||
/* TODO which of theses do we need ?
|
||||
BilboEd : none :)
|
||||
*/
|
||||
}
|
||||
|
||||
GType
|
||||
gst_controller_get_type (void)
|
||||
{
|
||||
static GType type = 0;
|
||||
|
||||
if (type == 0) {
|
||||
static const GTypeInfo info = {
|
||||
sizeof (GstControllerClass),
|
||||
NULL, // base_init
|
||||
NULL, // base_finalize
|
||||
(GClassInitFunc) _gst_controller_class_init, // class_init
|
||||
NULL, // class_finalize
|
||||
NULL, // class_data
|
||||
sizeof (GstController),
|
||||
0, // n_preallocs
|
||||
(GInstanceInitFunc) _gst_controller_init, // instance_init
|
||||
NULL // value_table
|
||||
};
|
||||
type = g_type_register_static (G_TYPE_OBJECT, "GstController", &info, 0);
|
||||
}
|
||||
return type;
|
||||
}
|
228
libs/gst/controller/gst-controller.h
Normal file
228
libs/gst/controller/gst-controller.h
Normal file
|
@ -0,0 +1,228 @@
|
|||
/*
|
||||
* gst-controller.h
|
||||
*
|
||||
* New dynamic properties
|
||||
*/
|
||||
|
||||
#ifndef __GST_CONTROLLER_H__
|
||||
#define __GST_CONTROLLER_H__
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
#include <glib/gprintf.h>
|
||||
#include <gst/gst.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* GST_PARAM_CONTROLLABLE:
|
||||
*
|
||||
* Use this flag on GstElement properties you wish to be (eventually) handled
|
||||
* by a GstController.
|
||||
* TODO: needs to go to gstelemnt.h (to avoid clashes on G_PARAM_USER_SHIFT)
|
||||
*/
|
||||
#define GST_PARAM_CONTROLLABLE (1 << (G_PARAM_USER_SHIFT + 1))
|
||||
|
||||
|
||||
/**
|
||||
* GstTimedValue:
|
||||
*
|
||||
* a structure for value+time
|
||||
*/
|
||||
typedef struct _GstTimedValue
|
||||
{
|
||||
GstClockTime timestamp; // timestamp of the value change
|
||||
GValue value; // the new value
|
||||
/* TODO what about storing the difference to next timestamp and value here
|
||||
+ make calculations slightly easier and faster
|
||||
- determining the GType for the value_dif is not simple
|
||||
e.g. if value is G_TYPE_UCHAR value_diff needs to be G_TYPE_INT
|
||||
*/
|
||||
} GstTimedValue;
|
||||
|
||||
|
||||
/**
|
||||
* GstValueArray:
|
||||
*
|
||||
* Structure to receive multiple values at once
|
||||
*/
|
||||
typedef struct _GstValueArray
|
||||
{
|
||||
gchar *property_name;
|
||||
gint nbsamples; // Number of samples requested
|
||||
GstClockTime sample_interval; // Interval between each sample
|
||||
gpointer *values; // pointer to the array (so it can be filled if NULL)
|
||||
} GstValueArray;
|
||||
|
||||
|
||||
/**
|
||||
* GstInterpolateMode:
|
||||
* @GST_INTERPOLATE_NONE: steps-like interpolation, default
|
||||
* @GST_INTERPOLATE_TRIGGER: returns the default value of the property,
|
||||
* except for times with specific values
|
||||
* @GST_INTERPOLATE_LINEAR: linear interpolation
|
||||
* @GST_INTERPOLATE_QUADRATIC: square interpolation
|
||||
* @GST_INTERPOLATE_CUBIC: cubic interpolation
|
||||
* @GST_INTERPOLATE_USER: user-provided interpolation
|
||||
*
|
||||
* The various interpolation modes available.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
GST_INTERPOLATE_NONE,
|
||||
GST_INTERPOLATE_TRIGGER,
|
||||
GST_INTERPOLATE_LINEAR,
|
||||
GST_INTERPOLATE_QUADRATIC,
|
||||
GST_INTERPOLATE_CUBIC,
|
||||
GST_INTERPOLATE_USER
|
||||
} GstInterpolateMode;
|
||||
|
||||
|
||||
struct _GstControlledProperty;
|
||||
|
||||
typedef GValue *(*InterpolateGet) (struct _GstControlledProperty * prop,
|
||||
GstClockTime timestamp);
|
||||
typedef gboolean (*InterpolateGetValueArray) (struct _GstControlledProperty * prop,
|
||||
GstClockTime timestamp, GstValueArray * value_array);
|
||||
|
||||
/**
|
||||
* GstInterpolateMethod:
|
||||
*
|
||||
* Function pointer structure to do user-defined interpolation methods
|
||||
*/
|
||||
typedef struct _GstInterpolateMethod
|
||||
{
|
||||
InterpolateGet get_int;
|
||||
InterpolateGetValueArray get_int_value_array;
|
||||
InterpolateGet get_long;
|
||||
InterpolateGetValueArray get_long_value_array;
|
||||
InterpolateGet get_float;
|
||||
InterpolateGetValueArray get_float_value_array;
|
||||
InterpolateGet get_double;
|
||||
InterpolateGetValueArray get_double_value_array;
|
||||
} GstInterpolateMethod;
|
||||
|
||||
/**
|
||||
* GstControlledProperty:
|
||||
*/
|
||||
typedef struct _GstControlledProperty
|
||||
{
|
||||
gchar *name; // name of the property
|
||||
GObject *object; // the object we control
|
||||
GType type; // type of the handled property
|
||||
GValue default_value; // default value for the handled property
|
||||
GValue result_value; // result value location for the interpolation method
|
||||
GstTimedValue last_value; // the last value a _sink call wrote
|
||||
GstTimedValue live_value; // temporary value override for live input
|
||||
gulong notify_handler_id; // id of the notify::<name> signal handler
|
||||
GstInterpolateMode interpolation; // Interpolation mode
|
||||
/* TODO instead of *method, have pointers to get() and get_value_array() here
|
||||
gst_controller_set_interpolation_mode() will pick the right ones for the
|
||||
properties value type
|
||||
GstInterpolateMethod *method; // User-implemented handler (if interpolation == GST_INTERPOLATE_USER)
|
||||
*/
|
||||
InterpolateGet get;
|
||||
InterpolateGetValueArray get_value_array;
|
||||
|
||||
GList *values; // List of GstTimedValue
|
||||
/* TODO keep the last search result to be able to continue
|
||||
GList *last_value; // last search result, can be used for incremental searches
|
||||
*/
|
||||
} GstControlledProperty;
|
||||
|
||||
#define GST_CONTROLLED_PROPERTY(obj) ((GstControlledProperty *)(obj))
|
||||
|
||||
/* type macros */
|
||||
|
||||
#define GST_TYPE_CONTROLLER (gst_controller_get_type ())
|
||||
#define GST_CONTROLLER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_CONTROLLER, GstController))
|
||||
#define GST_CONTROLLER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_CONTROLLER, GstControllerClass))
|
||||
#define GST_IS_CONTROLLER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_CONTROLLER))
|
||||
#define GST_IS_CONTROLLER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_CONTROLLERE))
|
||||
#define GST_CONTROLLER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CONTROLLER, GstControllerClass))
|
||||
|
||||
typedef struct _GstController GstController;
|
||||
typedef struct _GstControllerClass GstControllerClass;
|
||||
|
||||
//typedef struct _GstControllerPrivate GstControllerPrivate;
|
||||
|
||||
/**
|
||||
* GstController:
|
||||
*
|
||||
* The instance structure of GstController
|
||||
*/
|
||||
|
||||
struct _GstController
|
||||
{
|
||||
GObject parent;
|
||||
|
||||
GList *properties; // List of GstControlledProperty
|
||||
GMutex *lock; // Secure property access, elements will access from threads
|
||||
};
|
||||
|
||||
struct _GstControllerClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
GType gst_controller_get_type (void);
|
||||
|
||||
/* GstController functions */
|
||||
|
||||
GstController *gst_controller_new_valist (GObject * object, va_list var_args);
|
||||
GstController *gst_controller_new (GObject * object, ...);
|
||||
|
||||
gboolean gst_controller_remove_properties_valist (GstController * self,
|
||||
va_list var_args);
|
||||
gboolean gst_controller_remove_properties (GstController * self, ...);
|
||||
|
||||
gboolean gst_controller_set (GstController * self, gchar * property_name,
|
||||
GstClockTime timestamp, GValue * value);
|
||||
gboolean gst_controller_set_from_list (GstController * self,
|
||||
gchar * property_name, GSList * timedvalues);
|
||||
|
||||
gboolean gst_controller_unset (GstController * self, gchar * property_name,
|
||||
GstClockTime timestamp);
|
||||
|
||||
|
||||
GValue *gst_controller_get (GstController * self, gchar * property_name,
|
||||
GstClockTime timestamp);
|
||||
const GList *gst_controller_get_all (GstController * self,
|
||||
gchar * property_name);
|
||||
|
||||
|
||||
gboolean gst_controller_sink_values (GstController * self,
|
||||
GstClockTime timestamp);
|
||||
|
||||
gboolean gst_controller_get_value_arrays (GstController * self,
|
||||
GstClockTime timestamp, GSList * value_arrays);
|
||||
gboolean gst_controller_get_value_array (GstController * self,
|
||||
GstClockTime timestamp, GstValueArray * value_array);
|
||||
|
||||
gboolean gst_controller_set_interpolation_mode (GstController * self,
|
||||
gchar * property_name, GstInterpolateMode mode);
|
||||
|
||||
|
||||
/* GObject convenience functions */
|
||||
|
||||
GstController *g_object_control_properties (GObject * object, ...);
|
||||
gboolean g_object_uncontrol_properties (GObject * object, ...);
|
||||
|
||||
GstController *g_object_get_controller (GObject * object);
|
||||
gboolean g_object_set_controller (GObject * object, GstController * controller);
|
||||
|
||||
gboolean g_object_sink_values (GObject * object, GstClockTime timestamp);
|
||||
|
||||
gboolean g_object_get_value_arrays (GObject * object,
|
||||
GstClockTime timestamp, GSList * value_arrays);
|
||||
gboolean g_object_get_value_array (GObject * object,
|
||||
GstClockTime timestamp, GstValueArray * value_array);
|
||||
|
||||
/* lib init/done */
|
||||
|
||||
gboolean gst_controller_init (int * argc, char ***argv);
|
||||
|
||||
G_END_DECLS
|
||||
#endif /* __GST_CONTROLLER_H__ */
|
194
libs/gst/controller/gst-helper.c
Normal file
194
libs/gst/controller/gst-helper.c
Normal file
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* gst-helper.c
|
||||
*
|
||||
* GObject convinience methods for using dynamic properties
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "gst-controller.h"
|
||||
|
||||
#define GST_CAT_DEFAULT gst_controller_debug
|
||||
GST_DEBUG_CATEGORY_EXTERN (GST_CAT_DEFAULT);
|
||||
|
||||
extern GQuark controller_key;
|
||||
|
||||
/**
|
||||
* g_object_control_properties:
|
||||
* @object: the object of which some properties should be controlled
|
||||
* @var_args: %NULL terminated list of property names that should be controlled
|
||||
*
|
||||
* Convenience function for GObject
|
||||
*
|
||||
* Creates a GstController that allows you to dynamically control one, or more, GObject properties.
|
||||
* If the given GObject already has a GstController, it adds the given properties to the existing
|
||||
* controller and returns that controller.
|
||||
*
|
||||
* Returns: The GstController with which the user can control the given properties dynamically or NULL if
|
||||
* one or more of the given properties aren't available, or cannot be controlled, for the given element.
|
||||
*/
|
||||
GstController *
|
||||
g_object_control_properties (GObject * object, ...)
|
||||
{
|
||||
GstController *ctrl;
|
||||
|
||||
g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
|
||||
|
||||
va_list var_args;
|
||||
|
||||
va_start (var_args, object);
|
||||
ctrl = gst_controller_new_valist (object, var_args);
|
||||
va_end (var_args);
|
||||
return (ctrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_object_uncontrol_properties:
|
||||
* @object: the object of which some properties should not be controlled anymore
|
||||
* @var_args: %NULL terminated list of property names that should be controlled
|
||||
*
|
||||
* Convenience function for GObject
|
||||
*
|
||||
* Removes the given element's properties from it's controller
|
||||
*
|
||||
* Returns: %FALSE if one of the given property names isn't handled by the
|
||||
* controller, %TRUE otherwise
|
||||
*/
|
||||
gboolean
|
||||
g_object_uncontrol_properties (GObject * object, ...)
|
||||
{
|
||||
gboolean res = FALSE;
|
||||
GstController *ctrl;
|
||||
|
||||
g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
|
||||
|
||||
if ((ctrl = g_object_get_qdata (object, controller_key))) {
|
||||
va_list var_args;
|
||||
|
||||
va_start (var_args, object);
|
||||
res = gst_controller_remove_properties_valist (ctrl, var_args);
|
||||
va_end (var_args);
|
||||
}
|
||||
return (res);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_object_get_controller:
|
||||
* @object: the object that has controlled properties
|
||||
*
|
||||
* Returns: the controller handling some of the given element's properties,
|
||||
* %NULL if no controller
|
||||
*/
|
||||
GstController *
|
||||
g_object_get_controller (GObject * object)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
|
||||
|
||||
return (g_object_get_qdata (object, controller_key));
|
||||
}
|
||||
|
||||
/**
|
||||
* g_object_set_controller:
|
||||
* @object: the object that should get the controller
|
||||
* @controller: the controller object to plug in
|
||||
*
|
||||
* Sets the controller on the given GObject
|
||||
*
|
||||
* Returns: %FALSE if the GObject already has an controller, %TRUE otherwise
|
||||
*/
|
||||
gboolean
|
||||
g_object_set_controller (GObject * object, GstController * controller)
|
||||
{
|
||||
GstController *ctrl;
|
||||
|
||||
g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
|
||||
g_return_val_if_fail (controller, FALSE);
|
||||
|
||||
ctrl = g_object_get_qdata (object, controller_key);
|
||||
g_return_val_if_fail (!ctrl, FALSE);
|
||||
g_object_set_qdata (object, controller_key, controller);
|
||||
return (TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_object_sink_values:
|
||||
* @object: the object that has controlled properties
|
||||
* @timestamp: the time that should be processed
|
||||
*
|
||||
* Convenience function for GObject
|
||||
*
|
||||
* Returns: same thing as gst_controller_sink_values()
|
||||
*/
|
||||
gboolean
|
||||
g_object_sink_values (GObject * object, GstClockTime timestamp)
|
||||
{
|
||||
GstController *ctrl = NULL;
|
||||
|
||||
g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
|
||||
g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
|
||||
|
||||
ctrl = g_object_get_qdata (object, controller_key);
|
||||
g_return_val_if_fail (ctrl, FALSE);
|
||||
return gst_controller_sink_values (ctrl, timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_object_get_value_arrays:
|
||||
* @object: the object that has controlled properties
|
||||
* @timestamp: the time that should be processed
|
||||
* @value_arrays: list to return the control-values in
|
||||
*
|
||||
* Function to be able to get an array of values for one or more given element
|
||||
* properties.
|
||||
*
|
||||
* If the GstValueArray->values array in list nodes is NULL, it will be created
|
||||
* by the function.
|
||||
* The type of the values in the array are the same as the property's type.
|
||||
*
|
||||
* The g_object_* functions are just convenience functions for GObject
|
||||
*
|
||||
* Returns: %TRUE if the given array(s) could be filled, %FALSE otherwise
|
||||
*/
|
||||
gboolean
|
||||
g_object_get_value_arrays (GObject * object, GstClockTime timestamp,
|
||||
GSList * value_arrays)
|
||||
{
|
||||
GstController *ctrl;
|
||||
|
||||
g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
|
||||
g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
|
||||
|
||||
ctrl = g_object_get_qdata (object, controller_key);
|
||||
g_return_val_if_fail (ctrl, FALSE);
|
||||
return gst_controller_get_value_arrays (ctrl, timestamp, value_arrays);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_object_get_value_array:
|
||||
* @self: the object that has controlled properties
|
||||
* @timestamp: the time that should be processed
|
||||
* @value_array: array to put control-values in
|
||||
*
|
||||
* Function to be able to get an array of values for one element properties
|
||||
*
|
||||
* If the GstValueArray->values array is NULL, it will be created by the function.
|
||||
* The type of the values in the array are the same as the property's type.
|
||||
*
|
||||
* The g_object_* functions are just convenience functions for GObject
|
||||
*
|
||||
* Returns: %TRUE if the given array(s) could be filled, %FALSE otherwise
|
||||
*/
|
||||
gboolean
|
||||
g_object_get_value_array (GObject * object, GstClockTime timestamp,
|
||||
GstValueArray * value_array)
|
||||
{
|
||||
GstController *ctrl;
|
||||
|
||||
g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
|
||||
g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
|
||||
|
||||
ctrl = g_object_get_qdata (object, controller_key);
|
||||
g_return_val_if_fail (ctrl, FALSE);
|
||||
|
||||
return gst_controller_get_value_array (ctrl, timestamp, value_array);
|
||||
}
|
231
libs/gst/controller/gst-interpolation.c
Normal file
231
libs/gst/controller/gst-interpolation.c
Normal file
|
@ -0,0 +1,231 @@
|
|||
/*
|
||||
* gst-interpolation.h
|
||||
*
|
||||
* Interpolation methodws for dynamic properties
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "gst-controller.h"
|
||||
|
||||
#define GST_CAT_DEFAULT gst_controller_debug
|
||||
GST_DEBUG_CATEGORY_EXTERN (GST_CAT_DEFAULT);
|
||||
|
||||
// common helper
|
||||
|
||||
/*
|
||||
* gst_controlled_property_find_timed_value_node:
|
||||
* @prop: the controlled property to search in
|
||||
* @timestamp: the search key
|
||||
*
|
||||
* Find last value before given timestamp in timed value list.
|
||||
*
|
||||
* Returns: the found #GList node or %NULL
|
||||
*/
|
||||
GList *
|
||||
gst_controlled_property_find_timed_value_node (GstControlledProperty * prop,
|
||||
GstClockTime timestamp)
|
||||
{
|
||||
//GList *prev_node = NULL;
|
||||
GList *prev_node = g_list_last (prop->values);
|
||||
GList *node;
|
||||
GstTimedValue *tv;
|
||||
|
||||
/*
|
||||
if((prop->last_value) &&
|
||||
(timestamp>((GstTimedValue *)(prop->last_value->data))->timestamp)) {
|
||||
node=prop->last_value;
|
||||
}
|
||||
else {
|
||||
node=prop->values;
|
||||
}
|
||||
*/
|
||||
|
||||
/* iterate over timed value list */
|
||||
for (node = prop->values; node; node = g_list_next (node)) {
|
||||
tv = node->data;
|
||||
/* this timestamp is newer that the one we look for */
|
||||
if (timestamp < tv->timestamp) {
|
||||
/* get previous one again */
|
||||
prev_node = g_list_previous (node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/*
|
||||
if(node) {
|
||||
prop->last_value=prev_node;
|
||||
}
|
||||
*/
|
||||
return (prev_node);
|
||||
}
|
||||
|
||||
// steps-like (no-)interpolation, default
|
||||
// just returns the value for the most recent key-frame
|
||||
|
||||
static GValue *
|
||||
interpolate_none_get (GstControlledProperty * prop, GstClockTime timestamp)
|
||||
{
|
||||
GList *node;
|
||||
|
||||
if ((node = gst_controlled_property_find_timed_value_node (prop, timestamp))) {
|
||||
GstTimedValue *tv = node->data;
|
||||
|
||||
return (&tv->value);
|
||||
}
|
||||
return (&prop->default_value);
|
||||
}
|
||||
|
||||
#define DEFINE_NONE_GET(type) \
|
||||
static gboolean \
|
||||
interpolate_none_get_##type##_value_array (GstControlledProperty * prop, \
|
||||
GstClockTime timestamp, GstValueArray * value_array) \
|
||||
{ \
|
||||
gint i; \
|
||||
GstClockTime ts=timestamp; \
|
||||
g##type *values=(g##type *)value_array->values; \
|
||||
\
|
||||
for(i=0;i<value_array->nbsamples;i++) { \
|
||||
*values=g_value_get_##type (interpolate_none_get (prop,ts)); \
|
||||
ts+=value_array->sample_interval; \
|
||||
values++; \
|
||||
} \
|
||||
return (TRUE); \
|
||||
}
|
||||
|
||||
DEFINE_NONE_GET (int)
|
||||
DEFINE_NONE_GET (long)
|
||||
DEFINE_NONE_GET (float)
|
||||
DEFINE_NONE_GET (double)
|
||||
|
||||
static GstInterpolateMethod interpolate_none = {
|
||||
interpolate_none_get,
|
||||
interpolate_none_get_int_value_array,
|
||||
interpolate_none_get,
|
||||
interpolate_none_get_long_value_array,
|
||||
interpolate_none_get,
|
||||
interpolate_none_get_float_value_array,
|
||||
interpolate_none_get,
|
||||
interpolate_none_get_double_value_array
|
||||
};
|
||||
|
||||
// returns the default value of the property, except for times with specific values
|
||||
// needed for one-shot events, such as notes and triggers
|
||||
|
||||
static GValue *
|
||||
interpolate_trigger_get (GstControlledProperty * prop, GstClockTime timestamp)
|
||||
{
|
||||
GList *node;
|
||||
GstTimedValue *tv;
|
||||
|
||||
/* check if there is a value at the registered timestamp */
|
||||
for (node = prop->values; node; node = g_list_next (node)) {
|
||||
tv = node->data;
|
||||
if (timestamp == tv->timestamp) {
|
||||
return (&tv->value);
|
||||
}
|
||||
}
|
||||
|
||||
return (&prop->default_value);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
interpolate_trigger_get_value_array (GstControlledProperty * prop,
|
||||
GstClockTime timestamp, GstValueArray * value_array)
|
||||
{
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
static GstInterpolateMethod interpolate_trigger = {
|
||||
interpolate_trigger_get,
|
||||
interpolate_trigger_get_value_array,
|
||||
interpolate_trigger_get,
|
||||
NULL,
|
||||
interpolate_trigger_get,
|
||||
NULL,
|
||||
interpolate_trigger_get,
|
||||
NULL
|
||||
};
|
||||
|
||||
// linear interpolation
|
||||
// smoothes inbetween values
|
||||
|
||||
#define DEFINE_LINEAR_GET(type) \
|
||||
static g##type \
|
||||
_interpolate_linear_get_##type (GstControlledProperty * prop, GstClockTime timestamp) \
|
||||
{ \
|
||||
GList *node; \
|
||||
\
|
||||
if ((node = gst_controlled_property_find_timed_value_node (prop, timestamp))) { \
|
||||
GstTimedValue *tv1, *tv2; \
|
||||
\
|
||||
tv1 = node->data; \
|
||||
if ((node = g_list_next (node))) { \
|
||||
gdouble timediff,valuediff; \
|
||||
g##type value1,value2; \
|
||||
\
|
||||
tv2 = node->data; \
|
||||
\
|
||||
timediff = (gdouble)(tv2->timestamp - tv1->timestamp); \
|
||||
value1 = g_value_get_##type (&tv1->value); \
|
||||
value2 = g_value_get_##type (&tv2->value); \
|
||||
valuediff = (gdouble)(value2-value1); \
|
||||
\
|
||||
return((g##type)(value1+valuediff*((timestamp-tv1->timestamp)/timediff))); \
|
||||
} \
|
||||
else { \
|
||||
return (g_value_get_##type (&tv1->value)); \
|
||||
} \
|
||||
} \
|
||||
return (g_value_get_##type (&prop->default_value)); \
|
||||
} \
|
||||
\
|
||||
static GValue * \
|
||||
interpolate_linear_get_##type (GstControlledProperty * prop, GstClockTime timestamp) \
|
||||
{ \
|
||||
g_value_set_##type (&prop->result_value,_interpolate_linear_get_##type (prop,timestamp)); \
|
||||
return (&prop->result_value); \
|
||||
} \
|
||||
\
|
||||
static gboolean \
|
||||
interpolate_linear_get_##type##_value_array (GstControlledProperty * prop, \
|
||||
GstClockTime timestamp, GstValueArray * value_array) \
|
||||
{ \
|
||||
gint i; \
|
||||
GstClockTime ts=timestamp; \
|
||||
gint *values=(gint *)value_array->values; \
|
||||
\
|
||||
for(i=0;i<value_array->nbsamples;i++) { \
|
||||
*values=_interpolate_linear_get_##type (prop,ts); \
|
||||
ts+=value_array->sample_interval; \
|
||||
values++; \
|
||||
} \
|
||||
return (TRUE); \
|
||||
}
|
||||
|
||||
DEFINE_LINEAR_GET (int)
|
||||
DEFINE_LINEAR_GET (long)
|
||||
DEFINE_LINEAR_GET (float)
|
||||
DEFINE_LINEAR_GET (double)
|
||||
|
||||
static GstInterpolateMethod interpolate_linear = {
|
||||
interpolate_linear_get_int,
|
||||
interpolate_linear_get_int_value_array,
|
||||
interpolate_linear_get_long,
|
||||
interpolate_linear_get_long_value_array,
|
||||
interpolate_linear_get_float,
|
||||
interpolate_linear_get_float_value_array,
|
||||
interpolate_linear_get_double,
|
||||
interpolate_linear_get_double_value_array,
|
||||
};
|
||||
|
||||
// square interpolation
|
||||
|
||||
// cubic interpolation
|
||||
|
||||
// register all interpolation methods
|
||||
GstInterpolateMethod *interpolation_methods[] = {
|
||||
&interpolate_none,
|
||||
&interpolate_trigger,
|
||||
&interpolate_linear,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
929
libs/gst/controller/gstcontroller.c
Normal file
929
libs/gst/controller/gstcontroller.c
Normal file
|
@ -0,0 +1,929 @@
|
|||
/*
|
||||
* gst-controller.c
|
||||
*
|
||||
* New dynamic properties
|
||||
*
|
||||
*/
|
||||
|
||||
/* What needs to be done in plugins?
|
||||
Very little - it is just two steps to make a plugin controllable!
|
||||
|
||||
1) Just mark gobject-properties that make sense to be controlled,
|
||||
by GST_PARAM_CONTROLLABLE for a start.
|
||||
|
||||
2) When processing data (get, chain, loop function) at the beginning call
|
||||
gst_element_sink_values(element,timestamp).
|
||||
This will made the controller to update all gobject properties that are under
|
||||
control with the current values based on timestamp.
|
||||
*/
|
||||
|
||||
/* What needs to be done in applications?
|
||||
|
||||
1) First put some properties under control, by calling
|
||||
controller=g_object_control_properties(object, "prop1", "prop2",...);
|
||||
|
||||
2) Set how the controller will smooth inbetween values.
|
||||
gst_controller_set_interpolation_mode(controller,"prop1",mode);
|
||||
|
||||
3) Set key values
|
||||
gst_controller_set(controller,"prop1",0*GST_SECOND,value1);
|
||||
gst_controller_set(controller,"prop1",1*GST_SECOND,value2);
|
||||
|
||||
4) Start your pipeline ;-)
|
||||
|
||||
5) Live control params from the GUI
|
||||
g_object_set_live_value(object, "prop1", timestamp, value);
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "gst-controller.h"
|
||||
|
||||
#define GST_CAT_DEFAULT gst_controller_debug
|
||||
GST_DEBUG_CATEGORY_EXTERN (GST_CAT_DEFAULT);
|
||||
|
||||
static GObjectClass *parent_class = NULL;
|
||||
GQuark controller_key;
|
||||
|
||||
|
||||
/* imports from gst-interpolation.c */
|
||||
|
||||
extern GList
|
||||
*gst_controlled_property_find_timed_value_node (GstControlledProperty *
|
||||
prop, GstClockTime timestamp);
|
||||
extern GstInterpolateMethod *interpolation_methods[];
|
||||
|
||||
/* callbacks */
|
||||
|
||||
void
|
||||
on_object_controlled_property_changed (const GObject * object, GParamSpec * arg,
|
||||
gpointer user_data)
|
||||
{
|
||||
GstControlledProperty *prop = GST_CONTROLLED_PROPERTY (user_data);
|
||||
GstController *ctrl;
|
||||
|
||||
GST_INFO ("notify for '%s'", prop->name);
|
||||
|
||||
ctrl = g_object_get_qdata (G_OBJECT (object), controller_key);
|
||||
g_return_if_fail (ctrl);
|
||||
|
||||
if (g_mutex_trylock (ctrl->lock)) {
|
||||
if (!G_IS_VALUE (&prop->live_value.value)) {
|
||||
//g_value_unset (&prop->live_value.value);
|
||||
g_value_init (&prop->live_value.value, prop->type);
|
||||
}
|
||||
g_object_get_property (G_OBJECT (object), prop->name,
|
||||
&prop->live_value.value);
|
||||
prop->live_value.timestamp = prop->last_value.timestamp;
|
||||
g_mutex_unlock (ctrl->lock);
|
||||
GST_DEBUG ("-> is live update : ts=%" G_GUINT64_FORMAT,
|
||||
prop->live_value.timestamp);
|
||||
}
|
||||
//else {
|
||||
//GST_DEBUG ("-> is control change");
|
||||
//}
|
||||
}
|
||||
|
||||
/* helper */
|
||||
|
||||
/*
|
||||
* gst_timed_value_compare:
|
||||
* @p1: a pointer to a #GstTimedValue
|
||||
* @p2: a pointer to a #GstTimedValue
|
||||
*
|
||||
* Compare function for g_list operations that operates on two #GstTimedValue
|
||||
* parameters.
|
||||
*/
|
||||
static gint
|
||||
gst_timed_value_compare (gconstpointer p1, gconstpointer p2)
|
||||
{
|
||||
GstClockTime ct1 = ((GstTimedValue *) p1)->timestamp;
|
||||
GstClockTime ct2 = ((GstTimedValue *) p2)->timestamp;
|
||||
|
||||
return ((ct1 < ct2) ? -1 : ((ct1 == ct2) ? 0 : 1));
|
||||
/* this does not produce an gint :(
|
||||
return ((ct1 - ct2));
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* gst_timed_value_find:
|
||||
* @p1: a pointer to a #GstTimedValue
|
||||
* @p2: a pointer to a #GstClockTime
|
||||
*
|
||||
* Compare function for g_list operations that operates on a #GstTimedValue and
|
||||
* a #GstClockTime.
|
||||
*/
|
||||
static gint
|
||||
gst_timed_value_find (gconstpointer p1, gconstpointer p2)
|
||||
{
|
||||
GstClockTime ct1 = ((GstTimedValue *) p1)->timestamp;
|
||||
GstClockTime ct2 = *(GstClockTime *) p2;
|
||||
|
||||
return ((ct1 < ct2) ? -1 : ((ct1 == ct2) ? 0 : 1));
|
||||
/* this does not produce an gint :(
|
||||
return ((ct1 - ct2));
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* gst_controlled_property_set_interpolation_mode:
|
||||
* @self: the controlled property object to change
|
||||
* @mode: the new interpolation mode
|
||||
*
|
||||
* Sets the given Interpolation mode for the controlled property and activates
|
||||
* the respective interpolation hooks.
|
||||
*/
|
||||
static gboolean
|
||||
gst_controlled_property_set_interpolation_mode (GstControlledProperty * self,
|
||||
GstInterpolateMode mode)
|
||||
{
|
||||
self->interpolation = mode;
|
||||
if (mode != GST_INTERPOLATE_USER) {
|
||||
switch (self->type) {
|
||||
case G_TYPE_INT:
|
||||
case G_TYPE_UINT:
|
||||
self->get = interpolation_methods[mode]->get_int;
|
||||
self->get_value_array =
|
||||
interpolation_methods[mode]->get_int_value_array;
|
||||
break;
|
||||
case G_TYPE_LONG:
|
||||
case G_TYPE_ULONG:
|
||||
self->get = interpolation_methods[mode]->get_long;
|
||||
self->get_value_array =
|
||||
interpolation_methods[mode]->get_long_value_array;
|
||||
break;
|
||||
case G_TYPE_FLOAT:
|
||||
self->get = interpolation_methods[mode]->get_float;
|
||||
self->get_value_array =
|
||||
interpolation_methods[mode]->get_float_value_array;
|
||||
break;
|
||||
case G_TYPE_DOUBLE:
|
||||
self->get = interpolation_methods[mode]->get_double;
|
||||
self->get_value_array =
|
||||
interpolation_methods[mode]->get_double_value_array;
|
||||
break;
|
||||
default:
|
||||
self->get = NULL;
|
||||
self->get_value_array = NULL;
|
||||
GST_WARNING ("incomplete implementation for type '%d'", self->type);
|
||||
}
|
||||
} else {
|
||||
/* TODO shouldn't this also get a GstInterpolateMethod *user_method
|
||||
for the case mode==GST_INTERPOLATE_USER
|
||||
*/
|
||||
}
|
||||
return (TRUE);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 (GObject * 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 controlable
|
||||
g_return_val_if_fail (!(pspec->
|
||||
flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)), NULL);
|
||||
//g_return_val_if_fail((pspec->flags&GST_PARAM_CONTROLLABLE),NULL);
|
||||
/* TODO do sanity checks
|
||||
we don't control some pspec->value_type:
|
||||
G_TYPE_PARAM_BOXED
|
||||
G_TYPE_PARAM_ENUM
|
||||
G_TYPE_PARAM_FLAGS
|
||||
G_TYPE_PARAM_OBJECT
|
||||
G_TYPE_PARAM_PARAM
|
||||
G_TYPE_PARAM_POINTER
|
||||
G_TYPE_PARAM_STRING
|
||||
*/
|
||||
|
||||
if ((prop = g_new0 (GstControlledProperty, 1))) {
|
||||
gchar *signal_name;
|
||||
|
||||
prop->name = pspec->name; // so we don't use the same mem twice
|
||||
prop->object = object;
|
||||
prop->type = G_PARAM_SPEC_VALUE_TYPE (pspec);
|
||||
gst_controlled_property_set_interpolation_mode (prop,
|
||||
GST_INTERPOLATE_NONE);
|
||||
/* prepare our gvalues */
|
||||
g_value_init (&prop->default_value, prop->type);
|
||||
g_value_init (&prop->result_value, prop->type);
|
||||
g_value_init (&prop->last_value.value, prop->type);
|
||||
switch (prop->type) {
|
||||
case G_TYPE_INT:{
|
||||
GParamSpecInt *tpspec = G_PARAM_SPEC_INT (pspec);
|
||||
|
||||
g_value_set_int (&prop->default_value, tpspec->default_value);
|
||||
}
|
||||
break;
|
||||
case G_TYPE_UINT:{
|
||||
GParamSpecUInt *tpspec = G_PARAM_SPEC_UINT (pspec);
|
||||
|
||||
g_value_set_uint (&prop->default_value, tpspec->default_value);
|
||||
}
|
||||
break;
|
||||
case G_TYPE_LONG:{
|
||||
GParamSpecLong *tpspec = G_PARAM_SPEC_LONG (pspec);
|
||||
|
||||
g_value_set_long (&prop->default_value, tpspec->default_value);
|
||||
}
|
||||
break;
|
||||
case G_TYPE_ULONG:{
|
||||
GParamSpecULong *tpspec = G_PARAM_SPEC_ULONG (pspec);
|
||||
|
||||
g_value_set_ulong (&prop->default_value, tpspec->default_value);
|
||||
}
|
||||
break;
|
||||
case G_TYPE_FLOAT:{
|
||||
GParamSpecFloat *tpspec = G_PARAM_SPEC_FLOAT (pspec);
|
||||
|
||||
g_value_set_float (&prop->default_value, tpspec->default_value);
|
||||
}
|
||||
case G_TYPE_DOUBLE:{
|
||||
GParamSpecDouble *tpspec = G_PARAM_SPEC_DOUBLE (pspec);
|
||||
|
||||
g_value_set_double (&prop->default_value, tpspec->default_value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
GST_WARNING ("incomplete implementation for paramspec type '%s'",
|
||||
G_PARAM_SPEC_TYPE_NAME (pspec));
|
||||
}
|
||||
/* TODO what about adding a timedval with timestamp=0 and value=default
|
||||
+ a bit easier for interpolators, example:
|
||||
* first timestamp is at 5
|
||||
* requested value if for timestamp=3
|
||||
* LINEAR and Co. would need to interpolate from default value
|
||||
to value at timestamp 5
|
||||
*/
|
||||
signal_name = g_alloca (8 + 1 + strlen (name));
|
||||
g_sprintf (signal_name, "notify::%s", name);
|
||||
prop->notify_handler_id =
|
||||
g_signal_connect (object, signal_name,
|
||||
G_CALLBACK (on_object_controlled_property_changed), (gpointer) prop);
|
||||
}
|
||||
} 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)
|
||||
{
|
||||
GList *node;
|
||||
|
||||
g_signal_handler_disconnect (prop->object, prop->notify_handler_id);
|
||||
for (node = prop->values; node; node = g_list_next (node)) {
|
||||
g_free (node->data);
|
||||
}
|
||||
g_list_free (prop->values);
|
||||
g_free (prop);
|
||||
}
|
||||
|
||||
/*
|
||||
* gst_controller_find_controlled_property:
|
||||
* @self: the controller object to search for a property in
|
||||
* @name: the gobject property name to look for
|
||||
*
|
||||
* Searches the list of properties under control.
|
||||
*
|
||||
* Returns: a #GstControlledProperty object of %NULL if the property is not
|
||||
* being controlled.
|
||||
*/
|
||||
static GstControlledProperty *
|
||||
gst_controller_find_controlled_property (GstController * self,
|
||||
const gchar * name)
|
||||
{
|
||||
GstControlledProperty *prop;
|
||||
GList *node;
|
||||
|
||||
for (node = self->properties; node; node = g_list_next (node)) {
|
||||
prop = node->data;
|
||||
if (!strcmp (prop->name, name)) {
|
||||
return (prop);
|
||||
}
|
||||
}
|
||||
GST_WARNING ("controller does not manage property '%s'", name);
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* methods */
|
||||
|
||||
/**
|
||||
* gst_controller_new_valist:
|
||||
* @object: the object of which some properties should be controlled
|
||||
* @var_args: %NULL terminated list of property names that should be controlled
|
||||
*
|
||||
* Creates a new GstController for the given object's properties
|
||||
*
|
||||
* Returns: the new controller.
|
||||
*/
|
||||
GstController *
|
||||
gst_controller_new_valist (GObject * object, va_list var_args)
|
||||
{
|
||||
GstController *self;
|
||||
GstControlledProperty *prop;
|
||||
gchar *name;
|
||||
|
||||
g_return_val_if_fail (G_IS_OBJECT (object), NULL);
|
||||
|
||||
GST_INFO ("setting up a new controller");
|
||||
|
||||
/* TODO should this method check if the given object implements GstParent and
|
||||
if so instantiate a GstParentController ?
|
||||
|
||||
BilboEd: This is too specific to be put here, don't we want
|
||||
GstController to be as generic as possible ?
|
||||
|
||||
Ensonic: So we will have gst_parent_controller_new as well and maybe a
|
||||
convinience function that automatically chooses the right one (how to name it)?
|
||||
GstParent will be in core after all.
|
||||
*/
|
||||
|
||||
self = g_object_get_qdata (object, controller_key);
|
||||
if (!self) {
|
||||
self = g_object_new (GST_TYPE_CONTROLLER, NULL);
|
||||
self->lock = g_mutex_new ();
|
||||
// store the controller
|
||||
g_object_set_qdata (object, controller_key, self);
|
||||
}
|
||||
// create GstControlledProperty for each property
|
||||
while ((name = va_arg (var_args, gchar *))) {
|
||||
// create GstControlledProperty and add to self->propeties List
|
||||
if ((prop = gst_controlled_property_new (object, name)))
|
||||
self->properties = g_list_prepend (self->properties, prop);
|
||||
}
|
||||
va_end (var_args);
|
||||
|
||||
return (self);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_controller_new:
|
||||
* @object: the object of which some properties should be controlled
|
||||
* @...: %NULL terminated list of property names that should be controlled
|
||||
*
|
||||
* Creates a new GstController for the given object's properties
|
||||
*
|
||||
* Returns: the new controller.
|
||||
*/
|
||||
GstController *
|
||||
gst_controller_new (GObject * object, ...)
|
||||
{
|
||||
GstController *self;
|
||||
va_list var_args;
|
||||
|
||||
g_return_val_if_fail (G_IS_OBJECT (object), NULL);
|
||||
|
||||
va_start (var_args, object);
|
||||
self = gst_controller_new_valist (object, var_args);
|
||||
va_end (var_args);
|
||||
|
||||
return (self);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_controller_remove_properties:
|
||||
* @self: the controller object from which some properties should be removed
|
||||
* @var_args: %NULL terminated list of property names that should be removed
|
||||
*
|
||||
* Removes the given object properties from the controller
|
||||
*
|
||||
* Returns: %FALSE if one of the given property isn't handled by the controller, %TRUE otherwise
|
||||
*/
|
||||
gboolean
|
||||
gst_controller_remove_properties_valist (GstController * self, va_list var_args)
|
||||
{
|
||||
gboolean res = TRUE;
|
||||
GstControlledProperty *prop;
|
||||
gchar *name;
|
||||
|
||||
g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
|
||||
|
||||
while ((name = va_arg (var_args, gchar *))) {
|
||||
// find the property in the properties list of the controller, remove and free it
|
||||
g_mutex_lock (self->lock);
|
||||
if ((prop = gst_controller_find_controlled_property (self, name))) {
|
||||
self->properties = g_list_remove (self->properties, prop);
|
||||
gst_controlled_property_free (prop);
|
||||
} else {
|
||||
res = FALSE;
|
||||
}
|
||||
g_mutex_unlock (self->lock);
|
||||
}
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_controller_remove_properties:
|
||||
* @self: the controller object from which some properties should be removed
|
||||
* @...: %NULL terminated list of property names that should be removed
|
||||
*
|
||||
* Removes the given object properties from the controller
|
||||
*
|
||||
* Returns: %FALSE if one of the given property isn't handled by the controller, %TRUE otherwise
|
||||
*/
|
||||
gboolean
|
||||
gst_controller_remove_properties (GstController * self, ...)
|
||||
{
|
||||
gboolean res;
|
||||
va_list var_args;
|
||||
|
||||
g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
|
||||
|
||||
va_start (var_args, self);
|
||||
res = gst_controller_remove_properties_valist (self, var_args);
|
||||
va_end (var_args);
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_controller_set:
|
||||
* @self: the controller object which handles the properties
|
||||
* @property_name: the name of the property to set
|
||||
* @timestamp: the time the control-change is schedules for
|
||||
* @value: the control-value
|
||||
*
|
||||
* Set the value of given controller-handled property at a certain time.
|
||||
*
|
||||
* Returns: FALSE if the values couldn't be set (ex : properties not handled by controller), TRUE otherwise
|
||||
*/
|
||||
gboolean
|
||||
gst_controller_set (GstController * self, gchar * property_name,
|
||||
GstClockTime timestamp, GValue * value)
|
||||
{
|
||||
gboolean res = FALSE;
|
||||
GstControlledProperty *prop;
|
||||
|
||||
g_return_val_if_fail (GST_IS_CONTROLLER (self), 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 (G_IS_VALUE (value), FALSE);
|
||||
|
||||
g_mutex_lock (self->lock);
|
||||
if ((prop = gst_controller_find_controlled_property (self, property_name))) {
|
||||
if (G_VALUE_TYPE (value) == prop->type) {
|
||||
GstTimedValue *tv;
|
||||
GList *node;
|
||||
|
||||
// check if a timed_value for the timestamp already exists
|
||||
if ((node = g_list_find_custom (prop->values, ×tamp,
|
||||
gst_timed_value_find))) {
|
||||
tv = node->data;
|
||||
memcpy (&tv->value, value, sizeof (GValue));
|
||||
} else {
|
||||
// create a new GstTimedValue
|
||||
tv = g_new (GstTimedValue, 1);
|
||||
tv->timestamp = timestamp;
|
||||
memcpy (&tv->value, value, sizeof (GValue));
|
||||
// and sort it into the prop->values list
|
||||
prop->values =
|
||||
g_list_insert_sorted (prop->values, tv, gst_timed_value_compare);
|
||||
}
|
||||
res = TRUE;
|
||||
} else {
|
||||
GST_WARNING ("incompatible value type for property '%s'", prop->name);
|
||||
}
|
||||
}
|
||||
g_mutex_unlock (self->lock);
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_controller_set_from_list:
|
||||
* @self: the controller object which handles the properties
|
||||
* @property_name: the name of the property to set
|
||||
* @timedvalues: a list with #GstTimedValue items
|
||||
*
|
||||
* Sets multiple timed values at once.
|
||||
*
|
||||
* Returns: %FALSE if the values couldn't be set (ex : properties not handled by controller), %TRUE otherwise
|
||||
*/
|
||||
|
||||
gboolean
|
||||
gst_controller_set_from_list (GstController * self, gchar * property_name,
|
||||
GSList * timedvalues)
|
||||
{
|
||||
gboolean res = FALSE;
|
||||
GstControlledProperty *prop;
|
||||
GSList *node;
|
||||
GstTimedValue *tv;
|
||||
|
||||
g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
|
||||
g_return_val_if_fail (property_name, FALSE);
|
||||
|
||||
g_mutex_lock (self->lock);
|
||||
if ((prop = gst_controller_find_controlled_property (self, property_name))) {
|
||||
for (node = timedvalues; node; node = g_slist_next (node)) {
|
||||
tv = node->data;
|
||||
if (G_VALUE_TYPE (&tv->value) == prop->type) {
|
||||
g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (tv->timestamp), FALSE);
|
||||
/* TODO copy the timed value or just link in? */
|
||||
prop->values =
|
||||
g_list_insert_sorted (prop->values, tv, gst_timed_value_compare);
|
||||
res = TRUE;
|
||||
} else {
|
||||
GST_WARNING ("incompatible value type for property '%s'", prop->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
g_mutex_unlock (self->lock);
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_controller_unset:
|
||||
* @self: the controller object which handles the properties
|
||||
* @property_name: the name of the property to unset
|
||||
* @timestamp: the time the control-change should be removed from
|
||||
*
|
||||
* Used to remove the value of given controller-handled property at a certain
|
||||
* time.
|
||||
*
|
||||
* Returns: %FALSE if the values couldn't be unset (ex : properties not handled by controller), %TRUE otherwise
|
||||
*/
|
||||
gboolean
|
||||
gst_controller_unset (GstController * self, gchar * property_name,
|
||||
GstClockTime timestamp)
|
||||
{
|
||||
gboolean res = FALSE;
|
||||
GstControlledProperty *prop;
|
||||
|
||||
g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
|
||||
g_return_val_if_fail (property_name, FALSE);
|
||||
g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
|
||||
|
||||
g_mutex_lock (self->lock);
|
||||
if ((prop = gst_controller_find_controlled_property (self, property_name))) {
|
||||
prop->values = g_list_remove (prop->values, prop);
|
||||
res = TRUE;
|
||||
}
|
||||
g_mutex_unlock (self->lock);
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_controller_get:
|
||||
* @self: the controller object which handles the 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 controller-handled property at the requested
|
||||
* time.
|
||||
*
|
||||
* Returns: the GValue of the property at the given time, or %NULL if the property isn't handled by the controller
|
||||
*/
|
||||
|
||||
GValue *
|
||||
gst_controller_get (GstController * self, gchar * property_name,
|
||||
GstClockTime timestamp)
|
||||
{
|
||||
GstControlledProperty *prop;
|
||||
GValue *val = NULL;
|
||||
|
||||
g_return_val_if_fail (GST_IS_CONTROLLER (self), NULL);
|
||||
g_return_val_if_fail (property_name, NULL);
|
||||
g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), NULL);
|
||||
|
||||
g_mutex_lock (self->lock);
|
||||
if ((prop = gst_controller_find_controlled_property (self, property_name))) {
|
||||
//get current value via interpolator
|
||||
val = prop->get (prop, timestamp);
|
||||
}
|
||||
g_mutex_unlock (self->lock);
|
||||
|
||||
return (val);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_controller_get_all:
|
||||
* @self: the controller to get the list from
|
||||
* @property_name: the name of the property to get the list for
|
||||
*
|
||||
* Returns a read-only copy of the list of GstTimedValue for the given property.
|
||||
* Free the list after done with it.
|
||||
*
|
||||
* Returns: a copy of the list, or %NULL if the property isn't handled by the controller
|
||||
*/
|
||||
const GList *
|
||||
gst_controller_get_all (GstController * self, gchar * property_name)
|
||||
{
|
||||
GList *res = NULL;
|
||||
GstControlledProperty *prop;
|
||||
|
||||
g_return_val_if_fail (GST_IS_CONTROLLER (self), NULL);
|
||||
g_return_val_if_fail (property_name, NULL);
|
||||
|
||||
g_mutex_lock (self->lock);
|
||||
if ((prop = gst_controller_find_controlled_property (self, property_name))) {
|
||||
res = g_list_copy (prop->values);
|
||||
}
|
||||
g_mutex_unlock (self->lock);
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_controller_sink_values:
|
||||
* @self: the controller that handles the values
|
||||
* @timestamp: the time that should be processed
|
||||
*
|
||||
* Sets the properties of the element, according to the controller that (maybe)
|
||||
* handles them and for the given timestamp.
|
||||
*
|
||||
* Returns: %TRUE if the controller values could be applied to the object
|
||||
* properties, %FALSE otherwise
|
||||
*/
|
||||
gboolean
|
||||
gst_controller_sink_values (GstController * self, GstClockTime timestamp)
|
||||
{
|
||||
GstControlledProperty *prop;
|
||||
GList *node;
|
||||
GValue *value;
|
||||
gboolean live;
|
||||
|
||||
g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
|
||||
g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
|
||||
|
||||
GST_INFO ("sink_values");
|
||||
|
||||
g_mutex_lock (self->lock);
|
||||
// go over the controlled properties of the controller
|
||||
for (node = self->properties; node; node = g_list_next (node)) {
|
||||
prop = node->data;
|
||||
GST_DEBUG (" property '%s' at ts=%" G_GUINT64_FORMAT, prop->name,
|
||||
timestamp);
|
||||
|
||||
live = FALSE;
|
||||
if (G_IS_VALUE (&prop->live_value.value)) {
|
||||
GList *lnode =
|
||||
gst_controlled_property_find_timed_value_node (prop, timestamp);
|
||||
if (!lnode) {
|
||||
GST_DEBUG (" no control changes in the queue");
|
||||
live = TRUE;
|
||||
} else {
|
||||
GstTimedValue *tv = lnode->data;
|
||||
|
||||
//GST_DEBUG ("live.ts %"G_UINT64_FORMAT" <-> now %"G_UINT64_FORMAT, prop->live_value.timestamp, tv->timestamp);
|
||||
if (prop->live_value.timestamp < tv->timestamp) {
|
||||
g_value_unset (&prop->live_value.value);
|
||||
GST_DEBUG (" live value resetted");
|
||||
} else if (prop->live_value.timestamp < timestamp) {
|
||||
live = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!live) {
|
||||
//get current value via interpolator
|
||||
value = prop->get (prop, timestamp);
|
||||
prop->last_value.timestamp = timestamp;
|
||||
g_value_copy (value, &prop->last_value.value);
|
||||
g_object_set_property (prop->object, prop->name, value);
|
||||
}
|
||||
}
|
||||
g_mutex_unlock (self->lock);
|
||||
/* TODO what can here go wrong, to return FALSE ?
|
||||
BilboEd : Nothing I guess, as long as all the checks are made when creating the controller,
|
||||
adding/removing controlled properties, etc...
|
||||
*/
|
||||
|
||||
return (TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_controller_get_value_arrays:
|
||||
* @self: the controller that handles the values
|
||||
* @timestamp: the time that should be processed
|
||||
* @value_arrays: list to return the control-values in
|
||||
*
|
||||
* Function to be able to get an array of values for one or more given element
|
||||
* properties.
|
||||
*
|
||||
* If the GstValueArray->values array in list nodes is NULL, it will be created
|
||||
* by the function.
|
||||
* The type of the values in the array are the same as the property's type.
|
||||
*
|
||||
* Returns: %TRUE if the given array(s) could be filled, %FALSE otherwise
|
||||
*/
|
||||
gboolean
|
||||
gst_controller_get_value_arrays (GstController * self,
|
||||
GstClockTime timestamp, GSList * value_arrays)
|
||||
{
|
||||
gboolean res = TRUE;
|
||||
GSList *node;
|
||||
|
||||
g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
|
||||
g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
|
||||
g_return_val_if_fail (value_arrays, FALSE);
|
||||
|
||||
for (node = value_arrays; (res && node); node = g_slist_next (node)) {
|
||||
res = gst_controller_get_value_array (self, timestamp, node->data);
|
||||
}
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_controller_get_value_array:
|
||||
* @self: the controller that handles the values
|
||||
* @timestamp: the time that should be processed
|
||||
* @value_array: array to put control-values in
|
||||
*
|
||||
* Function to be able to get an array of values for one element properties
|
||||
*
|
||||
* If the GstValueArray->values array is NULL, it will be created by the function.
|
||||
* The type of the values in the array are the same as the property's type.
|
||||
*
|
||||
* Returns: %TRUE if the given array(s) could be filled, %FALSE otherwise
|
||||
*/
|
||||
gboolean
|
||||
gst_controller_get_value_array (GstController * self, GstClockTime timestamp,
|
||||
GstValueArray * value_array)
|
||||
{
|
||||
gboolean res = FALSE;
|
||||
GstControlledProperty *prop;
|
||||
|
||||
g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
|
||||
g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
|
||||
g_return_val_if_fail (value_array, FALSE);
|
||||
g_return_val_if_fail (value_array->property_name, FALSE);
|
||||
|
||||
/* TODO and if GstValueArray->values is not NULL, the caller is resposible that
|
||||
is is big enough for nbsamples values, right?
|
||||
*/
|
||||
|
||||
g_mutex_lock (self->lock);
|
||||
if ((prop =
|
||||
gst_controller_find_controlled_property (self,
|
||||
value_array->property_name))) {
|
||||
if (!value_array->values) {
|
||||
/* TODO from where to get the base size
|
||||
value_array->values=g_new(sizeof(???),nbsamples);
|
||||
*/
|
||||
}
|
||||
//get current value_array via interpolator
|
||||
res = prop->get_value_array (prop, timestamp, value_array);
|
||||
}
|
||||
g_mutex_unlock (self->lock);
|
||||
return (res);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_controller_set_interpolation_mode:
|
||||
* @controller:
|
||||
* @property_name:
|
||||
* @mode: interpolation mode
|
||||
*
|
||||
* Sets the given interpolation mode on the given property.
|
||||
*
|
||||
* Returns: %TRUE if the property is handled by the controller, %FALSE otherwise
|
||||
*/
|
||||
gboolean
|
||||
gst_controller_set_interpolation_mode (GstController * self,
|
||||
gchar * property_name, GstInterpolateMode mode)
|
||||
{
|
||||
gboolean res = FALSE;
|
||||
GstControlledProperty *prop;
|
||||
|
||||
g_return_val_if_fail (GST_IS_CONTROLLER (self), FALSE);
|
||||
g_return_val_if_fail (property_name, FALSE);
|
||||
|
||||
g_mutex_lock (self->lock);
|
||||
if ((prop = gst_controller_find_controlled_property (self, property_name))) {
|
||||
/* TODO shouldn't this also get a GstInterpolateMethod *user_method
|
||||
for the case mode==GST_INTERPOLATE_USER
|
||||
*/
|
||||
gst_controlled_property_set_interpolation_mode (prop, mode);
|
||||
res = TRUE;
|
||||
}
|
||||
g_mutex_unlock (self->lock);
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
/*
|
||||
void
|
||||
gst_controller_set_live_value(GstController * self, gchar *property_name,
|
||||
GstClockTime timestamp, GValue *value)
|
||||
{
|
||||
GstControlledProperty *prop;
|
||||
|
||||
g_return_if_fail (GST_IS_CONTROLLER (self));
|
||||
g_return_if_fail (property_name);
|
||||
|
||||
g_mutex_lock (self->lock);
|
||||
if ((prop = gst_controller_find_controlled_property (self, property_name))) {
|
||||
g_value_unset (&prop->live_value.value);
|
||||
g_value_init (&prop->live_value.value, prop->type);
|
||||
g_value_copy (value, &prop->live_value.value);
|
||||
prop->live_value.timestamp = timestamp;
|
||||
}
|
||||
g_mutex_unlock (self->lock);
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
/* gobject handling */
|
||||
|
||||
static void
|
||||
_gst_controller_finalize (GObject * object)
|
||||
{
|
||||
GstController *self = GST_CONTROLLER (object);
|
||||
GList *node;
|
||||
|
||||
// free list of properties
|
||||
if (self->properties) {
|
||||
for (node = self->properties; node; node = g_list_next (node)) {
|
||||
gst_controlled_property_free (node->data);
|
||||
}
|
||||
g_list_free (self->properties);
|
||||
self->properties = NULL;
|
||||
}
|
||||
g_mutex_free (self->lock);
|
||||
|
||||
if (G_OBJECT_CLASS (parent_class)->finalize)
|
||||
(G_OBJECT_CLASS (parent_class)->finalize) (object);
|
||||
}
|
||||
|
||||
static void
|
||||
_gst_controller_init (GTypeInstance * instance, gpointer g_class)
|
||||
{
|
||||
GstController *self = GST_CONTROLLER (instance);
|
||||
|
||||
self->lock = g_mutex_new ();
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
_gst_controller_class_init (GstControllerClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
parent_class = g_type_class_ref (G_TYPE_OBJECT);
|
||||
|
||||
gobject_class->finalize = _gst_controller_finalize;
|
||||
|
||||
controller_key = g_quark_from_string ("gst::controller");
|
||||
|
||||
// register properties
|
||||
// register signals
|
||||
// set defaults for overridable methods
|
||||
/* TODO which of theses do we need ?
|
||||
BilboEd : none :)
|
||||
*/
|
||||
}
|
||||
|
||||
GType
|
||||
gst_controller_get_type (void)
|
||||
{
|
||||
static GType type = 0;
|
||||
|
||||
if (type == 0) {
|
||||
static const GTypeInfo info = {
|
||||
sizeof (GstControllerClass),
|
||||
NULL, // base_init
|
||||
NULL, // base_finalize
|
||||
(GClassInitFunc) _gst_controller_class_init, // class_init
|
||||
NULL, // class_finalize
|
||||
NULL, // class_data
|
||||
sizeof (GstController),
|
||||
0, // n_preallocs
|
||||
(GInstanceInitFunc) _gst_controller_init, // instance_init
|
||||
NULL // value_table
|
||||
};
|
||||
type = g_type_register_static (G_TYPE_OBJECT, "GstController", &info, 0);
|
||||
}
|
||||
return type;
|
||||
}
|
228
libs/gst/controller/gstcontroller.h
Normal file
228
libs/gst/controller/gstcontroller.h
Normal file
|
@ -0,0 +1,228 @@
|
|||
/*
|
||||
* gst-controller.h
|
||||
*
|
||||
* New dynamic properties
|
||||
*/
|
||||
|
||||
#ifndef __GST_CONTROLLER_H__
|
||||
#define __GST_CONTROLLER_H__
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
#include <glib/gprintf.h>
|
||||
#include <gst/gst.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* GST_PARAM_CONTROLLABLE:
|
||||
*
|
||||
* Use this flag on GstElement properties you wish to be (eventually) handled
|
||||
* by a GstController.
|
||||
* TODO: needs to go to gstelemnt.h (to avoid clashes on G_PARAM_USER_SHIFT)
|
||||
*/
|
||||
#define GST_PARAM_CONTROLLABLE (1 << (G_PARAM_USER_SHIFT + 1))
|
||||
|
||||
|
||||
/**
|
||||
* GstTimedValue:
|
||||
*
|
||||
* a structure for value+time
|
||||
*/
|
||||
typedef struct _GstTimedValue
|
||||
{
|
||||
GstClockTime timestamp; // timestamp of the value change
|
||||
GValue value; // the new value
|
||||
/* TODO what about storing the difference to next timestamp and value here
|
||||
+ make calculations slightly easier and faster
|
||||
- determining the GType for the value_dif is not simple
|
||||
e.g. if value is G_TYPE_UCHAR value_diff needs to be G_TYPE_INT
|
||||
*/
|
||||
} GstTimedValue;
|
||||
|
||||
|
||||
/**
|
||||
* GstValueArray:
|
||||
*
|
||||
* Structure to receive multiple values at once
|
||||
*/
|
||||
typedef struct _GstValueArray
|
||||
{
|
||||
gchar *property_name;
|
||||
gint nbsamples; // Number of samples requested
|
||||
GstClockTime sample_interval; // Interval between each sample
|
||||
gpointer *values; // pointer to the array (so it can be filled if NULL)
|
||||
} GstValueArray;
|
||||
|
||||
|
||||
/**
|
||||
* GstInterpolateMode:
|
||||
* @GST_INTERPOLATE_NONE: steps-like interpolation, default
|
||||
* @GST_INTERPOLATE_TRIGGER: returns the default value of the property,
|
||||
* except for times with specific values
|
||||
* @GST_INTERPOLATE_LINEAR: linear interpolation
|
||||
* @GST_INTERPOLATE_QUADRATIC: square interpolation
|
||||
* @GST_INTERPOLATE_CUBIC: cubic interpolation
|
||||
* @GST_INTERPOLATE_USER: user-provided interpolation
|
||||
*
|
||||
* The various interpolation modes available.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
GST_INTERPOLATE_NONE,
|
||||
GST_INTERPOLATE_TRIGGER,
|
||||
GST_INTERPOLATE_LINEAR,
|
||||
GST_INTERPOLATE_QUADRATIC,
|
||||
GST_INTERPOLATE_CUBIC,
|
||||
GST_INTERPOLATE_USER
|
||||
} GstInterpolateMode;
|
||||
|
||||
|
||||
struct _GstControlledProperty;
|
||||
|
||||
typedef GValue *(*InterpolateGet) (struct _GstControlledProperty * prop,
|
||||
GstClockTime timestamp);
|
||||
typedef gboolean (*InterpolateGetValueArray) (struct _GstControlledProperty * prop,
|
||||
GstClockTime timestamp, GstValueArray * value_array);
|
||||
|
||||
/**
|
||||
* GstInterpolateMethod:
|
||||
*
|
||||
* Function pointer structure to do user-defined interpolation methods
|
||||
*/
|
||||
typedef struct _GstInterpolateMethod
|
||||
{
|
||||
InterpolateGet get_int;
|
||||
InterpolateGetValueArray get_int_value_array;
|
||||
InterpolateGet get_long;
|
||||
InterpolateGetValueArray get_long_value_array;
|
||||
InterpolateGet get_float;
|
||||
InterpolateGetValueArray get_float_value_array;
|
||||
InterpolateGet get_double;
|
||||
InterpolateGetValueArray get_double_value_array;
|
||||
} GstInterpolateMethod;
|
||||
|
||||
/**
|
||||
* GstControlledProperty:
|
||||
*/
|
||||
typedef struct _GstControlledProperty
|
||||
{
|
||||
gchar *name; // name of the property
|
||||
GObject *object; // the object we control
|
||||
GType type; // type of the handled property
|
||||
GValue default_value; // default value for the handled property
|
||||
GValue result_value; // result value location for the interpolation method
|
||||
GstTimedValue last_value; // the last value a _sink call wrote
|
||||
GstTimedValue live_value; // temporary value override for live input
|
||||
gulong notify_handler_id; // id of the notify::<name> signal handler
|
||||
GstInterpolateMode interpolation; // Interpolation mode
|
||||
/* TODO instead of *method, have pointers to get() and get_value_array() here
|
||||
gst_controller_set_interpolation_mode() will pick the right ones for the
|
||||
properties value type
|
||||
GstInterpolateMethod *method; // User-implemented handler (if interpolation == GST_INTERPOLATE_USER)
|
||||
*/
|
||||
InterpolateGet get;
|
||||
InterpolateGetValueArray get_value_array;
|
||||
|
||||
GList *values; // List of GstTimedValue
|
||||
/* TODO keep the last search result to be able to continue
|
||||
GList *last_value; // last search result, can be used for incremental searches
|
||||
*/
|
||||
} GstControlledProperty;
|
||||
|
||||
#define GST_CONTROLLED_PROPERTY(obj) ((GstControlledProperty *)(obj))
|
||||
|
||||
/* type macros */
|
||||
|
||||
#define GST_TYPE_CONTROLLER (gst_controller_get_type ())
|
||||
#define GST_CONTROLLER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_CONTROLLER, GstController))
|
||||
#define GST_CONTROLLER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_CONTROLLER, GstControllerClass))
|
||||
#define GST_IS_CONTROLLER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_CONTROLLER))
|
||||
#define GST_IS_CONTROLLER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_CONTROLLERE))
|
||||
#define GST_CONTROLLER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CONTROLLER, GstControllerClass))
|
||||
|
||||
typedef struct _GstController GstController;
|
||||
typedef struct _GstControllerClass GstControllerClass;
|
||||
|
||||
//typedef struct _GstControllerPrivate GstControllerPrivate;
|
||||
|
||||
/**
|
||||
* GstController:
|
||||
*
|
||||
* The instance structure of GstController
|
||||
*/
|
||||
|
||||
struct _GstController
|
||||
{
|
||||
GObject parent;
|
||||
|
||||
GList *properties; // List of GstControlledProperty
|
||||
GMutex *lock; // Secure property access, elements will access from threads
|
||||
};
|
||||
|
||||
struct _GstControllerClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
GType gst_controller_get_type (void);
|
||||
|
||||
/* GstController functions */
|
||||
|
||||
GstController *gst_controller_new_valist (GObject * object, va_list var_args);
|
||||
GstController *gst_controller_new (GObject * object, ...);
|
||||
|
||||
gboolean gst_controller_remove_properties_valist (GstController * self,
|
||||
va_list var_args);
|
||||
gboolean gst_controller_remove_properties (GstController * self, ...);
|
||||
|
||||
gboolean gst_controller_set (GstController * self, gchar * property_name,
|
||||
GstClockTime timestamp, GValue * value);
|
||||
gboolean gst_controller_set_from_list (GstController * self,
|
||||
gchar * property_name, GSList * timedvalues);
|
||||
|
||||
gboolean gst_controller_unset (GstController * self, gchar * property_name,
|
||||
GstClockTime timestamp);
|
||||
|
||||
|
||||
GValue *gst_controller_get (GstController * self, gchar * property_name,
|
||||
GstClockTime timestamp);
|
||||
const GList *gst_controller_get_all (GstController * self,
|
||||
gchar * property_name);
|
||||
|
||||
|
||||
gboolean gst_controller_sink_values (GstController * self,
|
||||
GstClockTime timestamp);
|
||||
|
||||
gboolean gst_controller_get_value_arrays (GstController * self,
|
||||
GstClockTime timestamp, GSList * value_arrays);
|
||||
gboolean gst_controller_get_value_array (GstController * self,
|
||||
GstClockTime timestamp, GstValueArray * value_array);
|
||||
|
||||
gboolean gst_controller_set_interpolation_mode (GstController * self,
|
||||
gchar * property_name, GstInterpolateMode mode);
|
||||
|
||||
|
||||
/* GObject convenience functions */
|
||||
|
||||
GstController *g_object_control_properties (GObject * object, ...);
|
||||
gboolean g_object_uncontrol_properties (GObject * object, ...);
|
||||
|
||||
GstController *g_object_get_controller (GObject * object);
|
||||
gboolean g_object_set_controller (GObject * object, GstController * controller);
|
||||
|
||||
gboolean g_object_sink_values (GObject * object, GstClockTime timestamp);
|
||||
|
||||
gboolean g_object_get_value_arrays (GObject * object,
|
||||
GstClockTime timestamp, GSList * value_arrays);
|
||||
gboolean g_object_get_value_array (GObject * object,
|
||||
GstClockTime timestamp, GstValueArray * value_array);
|
||||
|
||||
/* lib init/done */
|
||||
|
||||
gboolean gst_controller_init (int * argc, char ***argv);
|
||||
|
||||
G_END_DECLS
|
||||
#endif /* __GST_CONTROLLER_H__ */
|
194
libs/gst/controller/gsthelper.c
Normal file
194
libs/gst/controller/gsthelper.c
Normal file
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* gst-helper.c
|
||||
*
|
||||
* GObject convinience methods for using dynamic properties
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "gst-controller.h"
|
||||
|
||||
#define GST_CAT_DEFAULT gst_controller_debug
|
||||
GST_DEBUG_CATEGORY_EXTERN (GST_CAT_DEFAULT);
|
||||
|
||||
extern GQuark controller_key;
|
||||
|
||||
/**
|
||||
* g_object_control_properties:
|
||||
* @object: the object of which some properties should be controlled
|
||||
* @var_args: %NULL terminated list of property names that should be controlled
|
||||
*
|
||||
* Convenience function for GObject
|
||||
*
|
||||
* Creates a GstController that allows you to dynamically control one, or more, GObject properties.
|
||||
* If the given GObject already has a GstController, it adds the given properties to the existing
|
||||
* controller and returns that controller.
|
||||
*
|
||||
* Returns: The GstController with which the user can control the given properties dynamically or NULL if
|
||||
* one or more of the given properties aren't available, or cannot be controlled, for the given element.
|
||||
*/
|
||||
GstController *
|
||||
g_object_control_properties (GObject * object, ...)
|
||||
{
|
||||
GstController *ctrl;
|
||||
|
||||
g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
|
||||
|
||||
va_list var_args;
|
||||
|
||||
va_start (var_args, object);
|
||||
ctrl = gst_controller_new_valist (object, var_args);
|
||||
va_end (var_args);
|
||||
return (ctrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_object_uncontrol_properties:
|
||||
* @object: the object of which some properties should not be controlled anymore
|
||||
* @var_args: %NULL terminated list of property names that should be controlled
|
||||
*
|
||||
* Convenience function for GObject
|
||||
*
|
||||
* Removes the given element's properties from it's controller
|
||||
*
|
||||
* Returns: %FALSE if one of the given property names isn't handled by the
|
||||
* controller, %TRUE otherwise
|
||||
*/
|
||||
gboolean
|
||||
g_object_uncontrol_properties (GObject * object, ...)
|
||||
{
|
||||
gboolean res = FALSE;
|
||||
GstController *ctrl;
|
||||
|
||||
g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
|
||||
|
||||
if ((ctrl = g_object_get_qdata (object, controller_key))) {
|
||||
va_list var_args;
|
||||
|
||||
va_start (var_args, object);
|
||||
res = gst_controller_remove_properties_valist (ctrl, var_args);
|
||||
va_end (var_args);
|
||||
}
|
||||
return (res);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_object_get_controller:
|
||||
* @object: the object that has controlled properties
|
||||
*
|
||||
* Returns: the controller handling some of the given element's properties,
|
||||
* %NULL if no controller
|
||||
*/
|
||||
GstController *
|
||||
g_object_get_controller (GObject * object)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
|
||||
|
||||
return (g_object_get_qdata (object, controller_key));
|
||||
}
|
||||
|
||||
/**
|
||||
* g_object_set_controller:
|
||||
* @object: the object that should get the controller
|
||||
* @controller: the controller object to plug in
|
||||
*
|
||||
* Sets the controller on the given GObject
|
||||
*
|
||||
* Returns: %FALSE if the GObject already has an controller, %TRUE otherwise
|
||||
*/
|
||||
gboolean
|
||||
g_object_set_controller (GObject * object, GstController * controller)
|
||||
{
|
||||
GstController *ctrl;
|
||||
|
||||
g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
|
||||
g_return_val_if_fail (controller, FALSE);
|
||||
|
||||
ctrl = g_object_get_qdata (object, controller_key);
|
||||
g_return_val_if_fail (!ctrl, FALSE);
|
||||
g_object_set_qdata (object, controller_key, controller);
|
||||
return (TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_object_sink_values:
|
||||
* @object: the object that has controlled properties
|
||||
* @timestamp: the time that should be processed
|
||||
*
|
||||
* Convenience function for GObject
|
||||
*
|
||||
* Returns: same thing as gst_controller_sink_values()
|
||||
*/
|
||||
gboolean
|
||||
g_object_sink_values (GObject * object, GstClockTime timestamp)
|
||||
{
|
||||
GstController *ctrl = NULL;
|
||||
|
||||
g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
|
||||
g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
|
||||
|
||||
ctrl = g_object_get_qdata (object, controller_key);
|
||||
g_return_val_if_fail (ctrl, FALSE);
|
||||
return gst_controller_sink_values (ctrl, timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_object_get_value_arrays:
|
||||
* @object: the object that has controlled properties
|
||||
* @timestamp: the time that should be processed
|
||||
* @value_arrays: list to return the control-values in
|
||||
*
|
||||
* Function to be able to get an array of values for one or more given element
|
||||
* properties.
|
||||
*
|
||||
* If the GstValueArray->values array in list nodes is NULL, it will be created
|
||||
* by the function.
|
||||
* The type of the values in the array are the same as the property's type.
|
||||
*
|
||||
* The g_object_* functions are just convenience functions for GObject
|
||||
*
|
||||
* Returns: %TRUE if the given array(s) could be filled, %FALSE otherwise
|
||||
*/
|
||||
gboolean
|
||||
g_object_get_value_arrays (GObject * object, GstClockTime timestamp,
|
||||
GSList * value_arrays)
|
||||
{
|
||||
GstController *ctrl;
|
||||
|
||||
g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
|
||||
g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
|
||||
|
||||
ctrl = g_object_get_qdata (object, controller_key);
|
||||
g_return_val_if_fail (ctrl, FALSE);
|
||||
return gst_controller_get_value_arrays (ctrl, timestamp, value_arrays);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_object_get_value_array:
|
||||
* @self: the object that has controlled properties
|
||||
* @timestamp: the time that should be processed
|
||||
* @value_array: array to put control-values in
|
||||
*
|
||||
* Function to be able to get an array of values for one element properties
|
||||
*
|
||||
* If the GstValueArray->values array is NULL, it will be created by the function.
|
||||
* The type of the values in the array are the same as the property's type.
|
||||
*
|
||||
* The g_object_* functions are just convenience functions for GObject
|
||||
*
|
||||
* Returns: %TRUE if the given array(s) could be filled, %FALSE otherwise
|
||||
*/
|
||||
gboolean
|
||||
g_object_get_value_array (GObject * object, GstClockTime timestamp,
|
||||
GstValueArray * value_array)
|
||||
{
|
||||
GstController *ctrl;
|
||||
|
||||
g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
|
||||
g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
|
||||
|
||||
ctrl = g_object_get_qdata (object, controller_key);
|
||||
g_return_val_if_fail (ctrl, FALSE);
|
||||
|
||||
return gst_controller_get_value_array (ctrl, timestamp, value_array);
|
||||
}
|
231
libs/gst/controller/gstinterpolation.c
Normal file
231
libs/gst/controller/gstinterpolation.c
Normal file
|
@ -0,0 +1,231 @@
|
|||
/*
|
||||
* gst-interpolation.h
|
||||
*
|
||||
* Interpolation methodws for dynamic properties
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "gst-controller.h"
|
||||
|
||||
#define GST_CAT_DEFAULT gst_controller_debug
|
||||
GST_DEBUG_CATEGORY_EXTERN (GST_CAT_DEFAULT);
|
||||
|
||||
// common helper
|
||||
|
||||
/*
|
||||
* gst_controlled_property_find_timed_value_node:
|
||||
* @prop: the controlled property to search in
|
||||
* @timestamp: the search key
|
||||
*
|
||||
* Find last value before given timestamp in timed value list.
|
||||
*
|
||||
* Returns: the found #GList node or %NULL
|
||||
*/
|
||||
GList *
|
||||
gst_controlled_property_find_timed_value_node (GstControlledProperty * prop,
|
||||
GstClockTime timestamp)
|
||||
{
|
||||
//GList *prev_node = NULL;
|
||||
GList *prev_node = g_list_last (prop->values);
|
||||
GList *node;
|
||||
GstTimedValue *tv;
|
||||
|
||||
/*
|
||||
if((prop->last_value) &&
|
||||
(timestamp>((GstTimedValue *)(prop->last_value->data))->timestamp)) {
|
||||
node=prop->last_value;
|
||||
}
|
||||
else {
|
||||
node=prop->values;
|
||||
}
|
||||
*/
|
||||
|
||||
/* iterate over timed value list */
|
||||
for (node = prop->values; node; node = g_list_next (node)) {
|
||||
tv = node->data;
|
||||
/* this timestamp is newer that the one we look for */
|
||||
if (timestamp < tv->timestamp) {
|
||||
/* get previous one again */
|
||||
prev_node = g_list_previous (node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/*
|
||||
if(node) {
|
||||
prop->last_value=prev_node;
|
||||
}
|
||||
*/
|
||||
return (prev_node);
|
||||
}
|
||||
|
||||
// steps-like (no-)interpolation, default
|
||||
// just returns the value for the most recent key-frame
|
||||
|
||||
static GValue *
|
||||
interpolate_none_get (GstControlledProperty * prop, GstClockTime timestamp)
|
||||
{
|
||||
GList *node;
|
||||
|
||||
if ((node = gst_controlled_property_find_timed_value_node (prop, timestamp))) {
|
||||
GstTimedValue *tv = node->data;
|
||||
|
||||
return (&tv->value);
|
||||
}
|
||||
return (&prop->default_value);
|
||||
}
|
||||
|
||||
#define DEFINE_NONE_GET(type) \
|
||||
static gboolean \
|
||||
interpolate_none_get_##type##_value_array (GstControlledProperty * prop, \
|
||||
GstClockTime timestamp, GstValueArray * value_array) \
|
||||
{ \
|
||||
gint i; \
|
||||
GstClockTime ts=timestamp; \
|
||||
g##type *values=(g##type *)value_array->values; \
|
||||
\
|
||||
for(i=0;i<value_array->nbsamples;i++) { \
|
||||
*values=g_value_get_##type (interpolate_none_get (prop,ts)); \
|
||||
ts+=value_array->sample_interval; \
|
||||
values++; \
|
||||
} \
|
||||
return (TRUE); \
|
||||
}
|
||||
|
||||
DEFINE_NONE_GET (int)
|
||||
DEFINE_NONE_GET (long)
|
||||
DEFINE_NONE_GET (float)
|
||||
DEFINE_NONE_GET (double)
|
||||
|
||||
static GstInterpolateMethod interpolate_none = {
|
||||
interpolate_none_get,
|
||||
interpolate_none_get_int_value_array,
|
||||
interpolate_none_get,
|
||||
interpolate_none_get_long_value_array,
|
||||
interpolate_none_get,
|
||||
interpolate_none_get_float_value_array,
|
||||
interpolate_none_get,
|
||||
interpolate_none_get_double_value_array
|
||||
};
|
||||
|
||||
// returns the default value of the property, except for times with specific values
|
||||
// needed for one-shot events, such as notes and triggers
|
||||
|
||||
static GValue *
|
||||
interpolate_trigger_get (GstControlledProperty * prop, GstClockTime timestamp)
|
||||
{
|
||||
GList *node;
|
||||
GstTimedValue *tv;
|
||||
|
||||
/* check if there is a value at the registered timestamp */
|
||||
for (node = prop->values; node; node = g_list_next (node)) {
|
||||
tv = node->data;
|
||||
if (timestamp == tv->timestamp) {
|
||||
return (&tv->value);
|
||||
}
|
||||
}
|
||||
|
||||
return (&prop->default_value);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
interpolate_trigger_get_value_array (GstControlledProperty * prop,
|
||||
GstClockTime timestamp, GstValueArray * value_array)
|
||||
{
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
static GstInterpolateMethod interpolate_trigger = {
|
||||
interpolate_trigger_get,
|
||||
interpolate_trigger_get_value_array,
|
||||
interpolate_trigger_get,
|
||||
NULL,
|
||||
interpolate_trigger_get,
|
||||
NULL,
|
||||
interpolate_trigger_get,
|
||||
NULL
|
||||
};
|
||||
|
||||
// linear interpolation
|
||||
// smoothes inbetween values
|
||||
|
||||
#define DEFINE_LINEAR_GET(type) \
|
||||
static g##type \
|
||||
_interpolate_linear_get_##type (GstControlledProperty * prop, GstClockTime timestamp) \
|
||||
{ \
|
||||
GList *node; \
|
||||
\
|
||||
if ((node = gst_controlled_property_find_timed_value_node (prop, timestamp))) { \
|
||||
GstTimedValue *tv1, *tv2; \
|
||||
\
|
||||
tv1 = node->data; \
|
||||
if ((node = g_list_next (node))) { \
|
||||
gdouble timediff,valuediff; \
|
||||
g##type value1,value2; \
|
||||
\
|
||||
tv2 = node->data; \
|
||||
\
|
||||
timediff = (gdouble)(tv2->timestamp - tv1->timestamp); \
|
||||
value1 = g_value_get_##type (&tv1->value); \
|
||||
value2 = g_value_get_##type (&tv2->value); \
|
||||
valuediff = (gdouble)(value2-value1); \
|
||||
\
|
||||
return((g##type)(value1+valuediff*((timestamp-tv1->timestamp)/timediff))); \
|
||||
} \
|
||||
else { \
|
||||
return (g_value_get_##type (&tv1->value)); \
|
||||
} \
|
||||
} \
|
||||
return (g_value_get_##type (&prop->default_value)); \
|
||||
} \
|
||||
\
|
||||
static GValue * \
|
||||
interpolate_linear_get_##type (GstControlledProperty * prop, GstClockTime timestamp) \
|
||||
{ \
|
||||
g_value_set_##type (&prop->result_value,_interpolate_linear_get_##type (prop,timestamp)); \
|
||||
return (&prop->result_value); \
|
||||
} \
|
||||
\
|
||||
static gboolean \
|
||||
interpolate_linear_get_##type##_value_array (GstControlledProperty * prop, \
|
||||
GstClockTime timestamp, GstValueArray * value_array) \
|
||||
{ \
|
||||
gint i; \
|
||||
GstClockTime ts=timestamp; \
|
||||
gint *values=(gint *)value_array->values; \
|
||||
\
|
||||
for(i=0;i<value_array->nbsamples;i++) { \
|
||||
*values=_interpolate_linear_get_##type (prop,ts); \
|
||||
ts+=value_array->sample_interval; \
|
||||
values++; \
|
||||
} \
|
||||
return (TRUE); \
|
||||
}
|
||||
|
||||
DEFINE_LINEAR_GET (int)
|
||||
DEFINE_LINEAR_GET (long)
|
||||
DEFINE_LINEAR_GET (float)
|
||||
DEFINE_LINEAR_GET (double)
|
||||
|
||||
static GstInterpolateMethod interpolate_linear = {
|
||||
interpolate_linear_get_int,
|
||||
interpolate_linear_get_int_value_array,
|
||||
interpolate_linear_get_long,
|
||||
interpolate_linear_get_long_value_array,
|
||||
interpolate_linear_get_float,
|
||||
interpolate_linear_get_float_value_array,
|
||||
interpolate_linear_get_double,
|
||||
interpolate_linear_get_double_value_array,
|
||||
};
|
||||
|
||||
// square interpolation
|
||||
|
||||
// cubic interpolation
|
||||
|
||||
// register all interpolation methods
|
||||
GstInterpolateMethod *interpolation_methods[] = {
|
||||
&interpolate_none,
|
||||
&interpolate_trigger,
|
||||
&interpolate_linear,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
33
libs/gst/controller/lib.c
Normal file
33
libs/gst/controller/lib.c
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* lib.c
|
||||
*
|
||||
* New dynamic properties
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <gst/gst.h>
|
||||
|
||||
/* library initialisation */
|
||||
|
||||
#define GST_CAT_DEFAULT gst_controller_debug
|
||||
GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
|
||||
|
||||
/**
|
||||
* gst_controller_init:
|
||||
* @argc: pointer to the commandline argument count
|
||||
* @argv: pointer to the commandline argument values
|
||||
*
|
||||
* Initializes the use of the controller library. Suggested to be called right
|
||||
* after gst_init().
|
||||
*
|
||||
* Returns: the %TRUE for success.
|
||||
*/
|
||||
gboolean
|
||||
gst_controller_init (int *argc, char ***argv)
|
||||
{
|
||||
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "gstcontroller", 0,
|
||||
"dynamic parameter control for gstreamer elements");
|
||||
|
||||
return TRUE;
|
||||
}
|
|
@ -2,13 +2,13 @@
|
|||
pcfiles = \
|
||||
gstreamer-@GST_MAJORMINOR@.pc \
|
||||
gstreamer-base-@GST_MAJORMINOR@.pc \
|
||||
gstreamer-control-@GST_MAJORMINOR@.pc \
|
||||
gstreamer-controller-@GST_MAJORMINOR@.pc \
|
||||
gstreamer-dataprotocol-@GST_MAJORMINOR@.pc
|
||||
|
||||
pcfiles_uninstalled = \
|
||||
gstreamer-@GST_MAJORMINOR@-uninstalled.pc \
|
||||
gstreamer-base-@GST_MAJORMINOR@-uninstalled.pc \
|
||||
gstreamer-control-@GST_MAJORMINOR@-uninstalled.pc \
|
||||
gstreamer-controller-@GST_MAJORMINOR@-uninstalled.pc \
|
||||
gstreamer-dataprotocol-@GST_MAJORMINOR@-uninstalled.pc
|
||||
|
||||
all-local: $(pcfiles) $(pcfiles_uninstalled)
|
||||
|
@ -27,8 +27,8 @@ EXTRA_DIST = \
|
|||
gstreamer-uninstalled.pc.in \
|
||||
gstreamer-base.pc.in \
|
||||
gstreamer-base-uninstalled.pc.in \
|
||||
gstreamer-control.pc.in \
|
||||
gstreamer-control-uninstalled.pc.in \
|
||||
gstreamer-controller.pc.in \
|
||||
gstreamer-controller-uninstalled.pc.in \
|
||||
gstreamer-dataprotocol.pc.in \
|
||||
gstreamer-dataprotocol-uninstalled.pc.in
|
||||
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
# the standard variables don't make sense for an uninstalled copy
|
||||
prefix=
|
||||
exec_prefix=
|
||||
libdir=${pcfiledir}/../libs
|
||||
includedir=${pcfiledir}/..
|
||||
gstcontrol_libs=-lgstcontrol-@GST_MAJORMINOR@
|
||||
|
||||
Name: GStreamer control library, uninstalled
|
||||
Description: Dynamic parameters for plug-ins
|
||||
Requires: gstreamer-@GST_MAJORMINOR@ = @VERSION@
|
||||
Version: @VERSION@
|
||||
|
||||
Libs: ${libdir}/gst/control/libgstcontrol-@GST_MAJORMINOR@.la
|
||||
Cflags: -I${includedir} -I${includedir}/libs @GST_PKG_CFLAGS@
|
|
@ -1,11 +0,0 @@
|
|||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@/gstreamer-@GST_MAJORMINOR@
|
||||
|
||||
Name: GStreamer control library
|
||||
Description: Dynamic parameters for plug-ins
|
||||
Requires: gstreamer-@GST_MAJORMINOR@
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -lgstcontrol-@GST_MAJORMINOR@
|
||||
Cflags: -I${includedir} @GST_PKG_CFLAGS@
|
|
@ -11,6 +11,7 @@ dirs = \
|
|||
thread \
|
||||
plugins \
|
||||
mixer \
|
||||
controller \
|
||||
cutter \
|
||||
pingpong \
|
||||
manual \
|
||||
|
|
4
tests/old/examples/controller/.gitignore
vendored
Normal file
4
tests/old/examples/controller/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
audio-example
|
||||
*.bb
|
||||
*.bbg
|
||||
*.da
|
5
tests/old/examples/controller/Makefile.am
Normal file
5
tests/old/examples/controller/Makefile.am
Normal file
|
@ -0,0 +1,5 @@
|
|||
noinst_PROGRAMS = audio-example
|
||||
|
||||
audio_example_CFLAGS = $(GST_OBJ_CFLAGS) -I$(top_builddir)/libs
|
||||
audio_example_LDADD = $(GST_OBJ_LIBS)
|
||||
audio_example_LDFLAGS = $(top_builddir)/libs/gst/controller/libgstcontroller-@GST_MAJORMINOR@.la
|
64
tests/old/examples/controller/audio-example.c
Normal file
64
tests/old/examples/controller/audio-example.c
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* audio-example.c
|
||||
*
|
||||
* Build a pipeline with testaudiosource->alsasink
|
||||
* and sweep frequency and volume
|
||||
*
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/controller/gst-controller.h>
|
||||
|
||||
gint
|
||||
main (gint argc, gchar ** argv)
|
||||
{
|
||||
gint res = 1;
|
||||
GstElement *src, *sink;
|
||||
GstBin *bin;
|
||||
GstController *ctrl;
|
||||
GValue vol = { 0, };
|
||||
|
||||
gst_init (&argc, &argv);
|
||||
gst_controller_init (&argc, &argv);
|
||||
|
||||
// build pipeline
|
||||
bin = GST_BIN (gst_pipeline_new ("pipeline"));
|
||||
/* TODO make this "testaudiosrc", when its ready */
|
||||
src = gst_element_factory_make ("sinesrc", "gen_audio");
|
||||
sink = gst_element_factory_make ("alsasink", "play_audio");
|
||||
gst_bin_add_many (bin, src, sink, NULL);
|
||||
|
||||
// add a controller to the source
|
||||
if (!(ctrl =
|
||||
gst_controller_new (G_OBJECT (src), "frequency", "volume", NULL))) {
|
||||
goto Error;
|
||||
}
|
||||
// set interpolation
|
||||
gst_controller_set_interpolation_mode (ctrl, "volume",
|
||||
GST_INTERPOLATE_LINEAR);
|
||||
|
||||
// set control values
|
||||
g_value_init (&vol, G_TYPE_DOUBLE);
|
||||
g_value_set_double (&vol, 0.0);
|
||||
gst_controller_set (ctrl, "volume", 0 * GST_SECOND, &vol);
|
||||
g_value_set_double (&vol, 1.0);
|
||||
gst_controller_set (ctrl, "volume", 1 * GST_SECOND, &vol);
|
||||
|
||||
// iterate two seconds
|
||||
/*
|
||||
if(gst_element_set_state (bin, GST_STATE_PLAYING))
|
||||
{
|
||||
while (gst_bin_iterate (bin))
|
||||
{
|
||||
}
|
||||
}
|
||||
gst_element_set_state (bin, GST_STATE_NULL);
|
||||
*/
|
||||
|
||||
// cleanup
|
||||
g_object_unref (G_OBJECT (ctrl));
|
||||
g_object_unref (G_OBJECT (bin));
|
||||
res = 0;
|
||||
Error:
|
||||
return (res);
|
||||
}
|
|
@ -14,7 +14,7 @@ GST_DEBUG_DIRS = debug
|
|||
endif
|
||||
|
||||
SUBDIRS = \
|
||||
bytestream caps cleanup \
|
||||
bytestream caps cleanup controller \
|
||||
$(GST_DEBUG_DIRS) \
|
||||
dlopen \
|
||||
elements indexers negotiation pad \
|
||||
|
@ -22,7 +22,7 @@ SUBDIRS = \
|
|||
plugin refcounting schedulers states threads trigger
|
||||
|
||||
DIST_SUBDIRS = \
|
||||
bytestream caps cleanup \
|
||||
bytestream caps cleanup controller \
|
||||
debug \
|
||||
dlopen \
|
||||
elements indexers negotiation pad \
|
||||
|
|
11
tests/old/testsuite/controller/.gitignore
vendored
Normal file
11
tests/old/testsuite/controller/.gitignore
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
Makefile
|
||||
Makefile.in
|
||||
*.o
|
||||
*.lo
|
||||
*.la
|
||||
*.bb
|
||||
*.bbg
|
||||
*.da
|
||||
.deps
|
||||
.libs
|
||||
interpolator
|
9
tests/old/testsuite/controller/Makefile.am
Normal file
9
tests/old/testsuite/controller/Makefile.am
Normal file
|
@ -0,0 +1,9 @@
|
|||
include ../Rules
|
||||
|
||||
tests_pass = interpolator
|
||||
tests_fail =
|
||||
tests_ignore =
|
||||
|
||||
interpolator_SOURCES = interpolator.c
|
||||
interpolator_CFLAGS = $(GST_OBJ_CFLAGS) -I$(top_builddir)/libs
|
||||
interpolator_LDFLAGS = $(top_builddir)/libs/gst/controller/libgstcontroller-@GST_MAJORMINOR@.la
|
90
tests/old/testsuite/controller/interpolator.c
Normal file
90
tests/old/testsuite/controller/interpolator.c
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* interpolator.c
|
||||
*
|
||||
* test interpolator methods
|
||||
*
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/controller/gst-controller.h>
|
||||
|
||||
extern GstInterpolateMethod *interpolation_methods[];
|
||||
|
||||
gint
|
||||
main (gint argc, gchar ** argv)
|
||||
{
|
||||
gint res = 1;
|
||||
GstControlledProperty *prop = NULL;
|
||||
GType type = G_TYPE_INT;
|
||||
GstTimedValue tv1 = { 0, }, tv2 = {
|
||||
0,}, tv3 = {
|
||||
0,};
|
||||
GValue *val;
|
||||
gint i;
|
||||
|
||||
gst_init (&argc, &argv);
|
||||
gst_controller_init (&argc, &argv);
|
||||
|
||||
// build fake controlled property
|
||||
|
||||
if ((prop = g_new0 (GstControlledProperty, 1))) {
|
||||
prop->name = "test";
|
||||
//prop->parent_type = G_OBJECT_TYPE (object);
|
||||
prop->type = type;
|
||||
|
||||
g_value_init (&prop->default_value, type);
|
||||
g_value_set_int (&prop->default_value, 0);
|
||||
g_value_init (&prop->result_value, type);
|
||||
|
||||
// set timed values
|
||||
tv1.timestamp = 0;
|
||||
g_value_init (&tv1.value, type);
|
||||
g_value_set_int (&tv1.value, 0);
|
||||
prop->values = g_list_append (prop->values, &tv1);
|
||||
|
||||
tv2.timestamp = 10 * GST_SECOND;
|
||||
g_value_init (&tv2.value, type);
|
||||
g_value_set_int (&tv2.value, 100);
|
||||
prop->values = g_list_append (prop->values, &tv2);
|
||||
|
||||
tv3.timestamp = 20 * GST_SECOND;
|
||||
g_value_init (&tv3.value, type);
|
||||
g_value_set_int (&tv3.value, 50);
|
||||
prop->values = g_list_append (prop->values, &tv3);
|
||||
|
||||
g_print ("# time trig none line\n");
|
||||
|
||||
// test interpolator
|
||||
for (i = 0; i < 25; i++) {
|
||||
g_print (" %4d", i);
|
||||
|
||||
prop->interpolation = GST_INTERPOLATE_TRIGGER;
|
||||
prop->get = interpolation_methods[prop->interpolation]->get_int;
|
||||
prop->get_value_array =
|
||||
interpolation_methods[prop->interpolation]->get_int_value_array;
|
||||
val = prop->get (prop, i * GST_SECOND);
|
||||
g_print (" %4d", (val ? g_value_get_int (val) : 0));
|
||||
|
||||
prop->interpolation = GST_INTERPOLATE_NONE;
|
||||
prop->get = interpolation_methods[prop->interpolation]->get_int;
|
||||
prop->get_value_array =
|
||||
interpolation_methods[prop->interpolation]->get_int_value_array;
|
||||
val = prop->get (prop, i * GST_SECOND);
|
||||
g_print (" %4d", (val ? g_value_get_int (val) : 0));
|
||||
|
||||
prop->interpolation = GST_INTERPOLATE_LINEAR;
|
||||
prop->get = interpolation_methods[prop->interpolation]->get_int;
|
||||
prop->get_value_array =
|
||||
interpolation_methods[prop->interpolation]->get_int_value_array;
|
||||
val = prop->get (prop, i * GST_SECOND);
|
||||
g_print (" %4d", (val ? g_value_get_int (val) : 0));
|
||||
|
||||
g_print ("\n");
|
||||
}
|
||||
|
||||
g_list_free (prop->values);
|
||||
g_free (prop);
|
||||
res = 0;
|
||||
}
|
||||
return (res);
|
||||
}
|
|
@ -14,7 +14,7 @@ GST_DEBUG_DIRS = debug
|
|||
endif
|
||||
|
||||
SUBDIRS = \
|
||||
bytestream caps cleanup \
|
||||
bytestream caps cleanup controller \
|
||||
$(GST_DEBUG_DIRS) \
|
||||
dlopen \
|
||||
elements indexers negotiation pad \
|
||||
|
@ -22,7 +22,7 @@ SUBDIRS = \
|
|||
plugin refcounting schedulers states threads trigger
|
||||
|
||||
DIST_SUBDIRS = \
|
||||
bytestream caps cleanup \
|
||||
bytestream caps cleanup controller \
|
||||
debug \
|
||||
dlopen \
|
||||
elements indexers negotiation pad \
|
||||
|
|
11
testsuite/controller/.gitignore
vendored
Normal file
11
testsuite/controller/.gitignore
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
Makefile
|
||||
Makefile.in
|
||||
*.o
|
||||
*.lo
|
||||
*.la
|
||||
*.bb
|
||||
*.bbg
|
||||
*.da
|
||||
.deps
|
||||
.libs
|
||||
interpolator
|
9
testsuite/controller/Makefile.am
Normal file
9
testsuite/controller/Makefile.am
Normal file
|
@ -0,0 +1,9 @@
|
|||
include ../Rules
|
||||
|
||||
tests_pass = interpolator
|
||||
tests_fail =
|
||||
tests_ignore =
|
||||
|
||||
interpolator_SOURCES = interpolator.c
|
||||
interpolator_CFLAGS = $(GST_OBJ_CFLAGS) -I$(top_builddir)/libs
|
||||
interpolator_LDFLAGS = $(top_builddir)/libs/gst/controller/libgstcontroller-@GST_MAJORMINOR@.la
|
90
testsuite/controller/interpolator.c
Normal file
90
testsuite/controller/interpolator.c
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* interpolator.c
|
||||
*
|
||||
* test interpolator methods
|
||||
*
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/controller/gst-controller.h>
|
||||
|
||||
extern GstInterpolateMethod *interpolation_methods[];
|
||||
|
||||
gint
|
||||
main (gint argc, gchar ** argv)
|
||||
{
|
||||
gint res = 1;
|
||||
GstControlledProperty *prop = NULL;
|
||||
GType type = G_TYPE_INT;
|
||||
GstTimedValue tv1 = { 0, }, tv2 = {
|
||||
0,}, tv3 = {
|
||||
0,};
|
||||
GValue *val;
|
||||
gint i;
|
||||
|
||||
gst_init (&argc, &argv);
|
||||
gst_controller_init (&argc, &argv);
|
||||
|
||||
// build fake controlled property
|
||||
|
||||
if ((prop = g_new0 (GstControlledProperty, 1))) {
|
||||
prop->name = "test";
|
||||
//prop->parent_type = G_OBJECT_TYPE (object);
|
||||
prop->type = type;
|
||||
|
||||
g_value_init (&prop->default_value, type);
|
||||
g_value_set_int (&prop->default_value, 0);
|
||||
g_value_init (&prop->result_value, type);
|
||||
|
||||
// set timed values
|
||||
tv1.timestamp = 0;
|
||||
g_value_init (&tv1.value, type);
|
||||
g_value_set_int (&tv1.value, 0);
|
||||
prop->values = g_list_append (prop->values, &tv1);
|
||||
|
||||
tv2.timestamp = 10 * GST_SECOND;
|
||||
g_value_init (&tv2.value, type);
|
||||
g_value_set_int (&tv2.value, 100);
|
||||
prop->values = g_list_append (prop->values, &tv2);
|
||||
|
||||
tv3.timestamp = 20 * GST_SECOND;
|
||||
g_value_init (&tv3.value, type);
|
||||
g_value_set_int (&tv3.value, 50);
|
||||
prop->values = g_list_append (prop->values, &tv3);
|
||||
|
||||
g_print ("# time trig none line\n");
|
||||
|
||||
// test interpolator
|
||||
for (i = 0; i < 25; i++) {
|
||||
g_print (" %4d", i);
|
||||
|
||||
prop->interpolation = GST_INTERPOLATE_TRIGGER;
|
||||
prop->get = interpolation_methods[prop->interpolation]->get_int;
|
||||
prop->get_value_array =
|
||||
interpolation_methods[prop->interpolation]->get_int_value_array;
|
||||
val = prop->get (prop, i * GST_SECOND);
|
||||
g_print (" %4d", (val ? g_value_get_int (val) : 0));
|
||||
|
||||
prop->interpolation = GST_INTERPOLATE_NONE;
|
||||
prop->get = interpolation_methods[prop->interpolation]->get_int;
|
||||
prop->get_value_array =
|
||||
interpolation_methods[prop->interpolation]->get_int_value_array;
|
||||
val = prop->get (prop, i * GST_SECOND);
|
||||
g_print (" %4d", (val ? g_value_get_int (val) : 0));
|
||||
|
||||
prop->interpolation = GST_INTERPOLATE_LINEAR;
|
||||
prop->get = interpolation_methods[prop->interpolation]->get_int;
|
||||
prop->get_value_array =
|
||||
interpolation_methods[prop->interpolation]->get_int_value_array;
|
||||
val = prop->get (prop, i * GST_SECOND);
|
||||
g_print (" %4d", (val ? g_value_get_int (val) : 0));
|
||||
|
||||
g_print ("\n");
|
||||
}
|
||||
|
||||
g_list_free (prop->values);
|
||||
g_free (prop);
|
||||
res = 0;
|
||||
}
|
||||
return (res);
|
||||
}
|
Loading…
Reference in a new issue