[gst-devel] property change notification / info event
Benjamin Otte
in7y118 at public.uni-hamburg.de
Thu Jan 31 14:00:28 CET 2002
i all,
attached is a patch, that implements a new system that should replace
the INFO event. It is basically the same as GObject property
notification with a new signal to allow recursive receiving.
The attached patch implements this and patches filesrc, md5sink and
gst-launch.
Ok, now a little FAQ:
- How does notification work?
In GLib2, you call g_object_notify (object, propertyname) whenever a
property changes. GObject then emits a "notify" signal.
- How does it work in Gstreamer?
GstObject was changed to emit a second signal "recursenotify" (for lack
of a better name) after that. This signal is emitted by each parent of
the current GstObject. This allows you to just listen to signals from
the main pipeline and receive each and every change from elements inside
the pipeline. Even elements inside bins inside the pipeline.
- How do I make my app receive these signals?
You just need to connect a signal handler to the GstObject from that you
want to be notified. This signal handler should be called
"recursenotify" if you want to receive every change or
"recursenotify::propertyname" if you just want to handle one specific
property. Look for an example in tools/gst-launch.c
- How do I change my plugins to send notifications?
As mentioned above: Everytime a property changes, just call
g_object_notify (object, propertyname). GStreamer takes care of the
rest. If you want to change many properties at once, you should call
g_object_freeze_notify (object);
g_object_notify (object1, property1);
g_object_notify (object2, property2);
...
g_object_thaw_notify (object);
This allows for a faster execution of the signal sending. An example is
gst/elements/gstfilesrc.c
- What about compatibility?
Everything is source compatible. Because of a new signal in GstObject,
the binary compatibility is broken.
Additionally, the GST_EVENT_INFO event output is no longer included in
gst-launch. I plan to update all elements using GST_EVENT_INFO to use
the new system anyway.
- What about Gtk1.2?
I haven't looked at Gtk1.2 much, I just made sure it compiles and works.
Notification is currently just ignored.
Let me now if I may commit.
Benjamin
-------------- next part --------------
? acinclude.m4
? compile
? depcomp
? diff
? install-sh
? mkinstalldirs
? stamp-h1
? gst/autoplug/spidertest
? gst/autoplug/spidertest2.c
? gst/cothreads/acinclude.m4
? gst/cothreads/aclocal.m4
? gst/cothreads/config.log
? gst/cothreads/config.status
? gst/cothreads/configure
? gst/cothreads/libtool
? gst/cothreads/pth.h
? gst/cothreads/pth_acdef.h
? gst/cothreads/pth_acdef.h.in
? gst/cothreads/pth_acmac.h
? gst/cothreads/stamp-h.in
? gst/cothreads/stamp-h1
? tests/lat
? tests/bufspeed/test1
? tests/bufspeed/test2
? tests/memchunk/gmemchunktest
? tests/memchunk/gstmemchunktest
? tests/sched/dynamic-pipeline
? testsuite/test_gst_init
? testsuite/caps/compatibility
? testsuite/caps/intersection
? testsuite/caps/normalisation
? testsuite/cleanup/cleanup1
? testsuite/cleanup/cleanup2
? testsuite/cleanup/cleanup3
? testsuite/cleanup/cleanup4
? testsuite/cleanup/cleanup5
? tools/gst-complete
? tools/gst-compprep
? tools/gst-inspect
? tools/gst-launch
? tools/gst-register
? tools/gst-xmllaunch
Index: gst/gobject2gtk.h
===================================================================
RCS file: /cvsroot/gstreamer/gstreamer/gst/gobject2gtk.h,v
retrieving revision 1.27
diff -u -r1.27 gobject2gtk.h
--- gst/gobject2gtk.h 2001/12/15 18:15:13 1.27
+++ gst/gobject2gtk.h 2002/01/31 21:33:09
@@ -40,6 +40,11 @@
#define g_object_ref(obj) gtk_object_ref((GtkObject *)(obj))
#define g_object_unref(obj) gtk_object_unref((GtkObject *)(obj))
+/* notification - just disable it */
+#define g_object_freeze_notify(obj)
+#define g_object_notify(obj,name)
+#define g_object_thaw_notify(obj)
+
/* the helper macros for type checking */
#define G_TYPE_CHECK_INSTANCE_CAST GTK_CHECK_CAST
#define G_TYPE_CHECK_INSTANCE_TYPE GTK_CHECK_TYPE
@@ -68,7 +73,7 @@
#define G_TYPE_STRING GTK_TYPE_STRING
#define G_TYPE_POINTER GTK_TYPE_POINTER
#define G_TYPE_BOXED GTK_TYPE_BOXED
-#define G_TYPE_PARAM GTK_TYPE_PARAM
+#define G_TYPE_PARAM GTK_TYPE_POINTER
/* marshallers */
#define g_cclosure_marshal_VOID__VOID gtk_marshal_NONE__NONE
@@ -98,6 +103,7 @@
#define gst_marshal_VOID__STRING gtk_marshal_NONE__STRING
#define gst_marshal_VOID__POINTER gtk_marshal_NONE__POINTER
#define gst_marshal_VOID__OBJECT gtk_marshal_NONE__POINTER
+#define gst_marshal_VOID__OBJECT_PARAM gtk_marshal_NONE__POINTER_POINTER
#define gst_marshal_VOID__OBJECT_POINTER gtk_marshal_NONE__POINTER_POINTER
#define gst_marshal_VOID__INT_INT gtk_marshal_NONE__INT_INT
@@ -194,6 +200,7 @@
#define G_SIGNAL_RUN_CLEANUP 0
#define G_SIGNAL_NO_RECURSE GTK_RUN_NO_RECURSE
#define G_SIGNAL_NO_HOOKS GTK_RUN_NO_HOOKS
+#define G_SIGNAL_DETAILED 0
#define GCallback gpointer /* FIXME?*/
#define G_CALLBACK(f) ((gpointer)(f))
@@ -428,6 +435,10 @@
const GValue *value, GParamSpec *pspec);
void (*get_property) (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec);
+
+ void (*dispatch_properties_changed) (GObject *object, guint n_pspecs,
+ GParamSpec **pspecs);
+
};
GType g_object_get_type (void);
Index: gst/gstmarshal.list
===================================================================
RCS file: /cvsroot/gstreamer/gstreamer/gst/gstmarshal.list,v
retrieving revision 1.2
diff -u -r1.2 gstmarshal.list
--- gst/gstmarshal.list 2001/09/10 19:00:42 1.2
+++ gst/gstmarshal.list 2002/01/31 21:33:09
@@ -4,5 +4,6 @@
VOID:STRING
VOID:POINTER
VOID:OBJECT
+VOID:OBJECT,PARAM
VOID:OBJECT,POINTER
VOID:INT,INT
Index: gst/gstobject.c
===================================================================
RCS file: /cvsroot/gstreamer/gstreamer/gst/gstobject.c,v
retrieving revision 1.35
diff -u -r1.35 gstobject.c
--- gst/gstobject.c 2002/01/11 15:49:46 1.35
+++ gst/gstobject.c 2002/01/31 21:33:10
@@ -27,6 +27,7 @@
/* Object signals and args */
enum {
PARENT_SET,
+ RECURSE_NOTIFY,
#ifndef GST_DISABLE_LOADSAVE_REGISTRY
OBJECT_SAVED,
#endif
@@ -62,6 +63,7 @@
GParamSpec * pspec);
static void gst_object_get_property (GObject * object, guint prop_id, GValue * value,
GParamSpec * pspec);
+static void gst_object_dispatch_properties_changed (GObject * object, guint n_pspecs, GParamSpec **pspecs);
static void gst_object_dispose (GObject *object);
static void gst_object_finalize (GObject *object);
@@ -105,6 +107,7 @@
gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_object_set_property);
gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_object_get_property);
+ gobject_class->dispatch_properties_changed = GST_DEBUG_FUNCPTR (gst_object_dispatch_properties_changed);
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_NAME,
g_param_spec_string ("name", "Name", "The name of the object",
@@ -115,6 +118,12 @@
G_STRUCT_OFFSET (GstObjectClass, parent_set), NULL, NULL,
g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
G_TYPE_OBJECT);
+ gst_object_signals[RECURSE_NOTIFY] =
+ g_signal_new ("recursenotify", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_DETAILED | G_SIGNAL_NO_HOOKS,
+ G_STRUCT_OFFSET (GstObjectClass, recurse_notify), NULL, NULL,
+ gst_marshal_VOID__OBJECT_PARAM, G_TYPE_NONE,
+ 2, G_TYPE_OBJECT, G_TYPE_PARAM);
#ifndef GST_DISABLE_LOADSAVE_REGISTRY
gst_object_signals[OBJECT_SAVED] =
g_signal_new ("object_saved", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
@@ -574,6 +583,32 @@
break;
}
}
+static void
+gst_object_dispatch_properties_changed (GObject *object,
+ guint n_pspecs,
+ GParamSpec **pspecs)
+{
+#ifdef USE_GLIB2
+ GstObject *gst_object;
+ guint i;
+
+ /* do the standard dispatching */
+ parent_class->dispatch_properties_changed (object, n_pspecs, pspecs);
+
+ /* now let the parent dispatch those, too */
+ gst_object = GST_OBJECT (object);
+ while (gst_object)
+ {
+ /* need own category? */
+ GST_DEBUG (GST_CAT_EVENT, "recurse notification to %s", GST_OBJECT_NAME (gst_object));
+ for (i = 0; i < n_pspecs; i++)
+ g_signal_emit (gst_object, gst_object_signals[RECURSE_NOTIFY], g_quark_from_string (pspecs[i]->name), (GstObject *) object, pspecs[i]);
+
+ gst_object = GST_OBJECT_PARENT (gst_object);
+ }
+#endif /* USE_GLIB2 */
+}
+
/**
* gst_object_get_path_string:
* @object: GstObject to get the path from
Index: gst/gstobject.h
===================================================================
RCS file: /cvsroot/gstreamer/gstreamer/gst/gstobject.h,v
retrieving revision 1.20
diff -u -r1.20 gstobject.h
--- gst/gstobject.h 2001/12/22 21:18:17 1.20
+++ gst/gstobject.h 2002/01/31 21:33:11
@@ -102,6 +102,7 @@
/* signals */
void (*parent_set) (GstObject *object, GstObject *parent);
+ void (*recurse_notify) (GstObject *object, GstObject *orig, GParamSpec *pspec);
#ifndef GST_DISABLE_LOADSAVE_REGISTRY
void (*object_saved) (GstObject *object, xmlNodePtr parent);
#endif
Index: gst/elements/gstfilesrc.c
===================================================================
RCS file: /cvsroot/gstreamer/gstreamer/gst/elements/gstfilesrc.c,v
retrieving revision 1.20
diff -u -r1.20 gstfilesrc.c
--- gst/elements/gstfilesrc.c 2002/01/26 18:28:55 1.20
+++ gst/elements/gstfilesrc.c 2002/01/31 21:33:12
@@ -247,21 +247,28 @@
} else {
src->filename = g_strdup (g_value_get_string (value));
}
+ g_object_notify (G_OBJECT (src), "location");
break;
case ARG_BLOCKSIZE:
src->block_size = g_value_get_ulong (value);
+ g_object_notify (G_OBJECT (src), "blocksize");
break;
case ARG_OFFSET:
src->curoffset = g_value_get_int64 (value);
+ g_object_notify (G_OBJECT (src), "offset");
break;
case ARG_MAPSIZE:
if ((src->mapsize % src->pagesize) == 0)
+ {
src->mapsize = g_value_get_ulong (value);
+ g_object_notify (G_OBJECT (src), "mmapsize");
+ }
else
GST_INFO(0, "invalid mapsize, must a multiple of pagesize, which is %d\n",src->pagesize);
break;
case ARG_TOUCH:
src->touch = g_value_get_boolean (value);
+ g_object_notify (G_OBJECT (src), "touch");
break;
default:
break;
@@ -537,6 +544,7 @@
/* we're done, return the buffer */
src->curoffset += GST_BUFFER_SIZE(buf);
+ g_object_notify (G_OBJECT (src), "offset");
return buf;
}
@@ -574,6 +582,12 @@
src->curoffset = 0;
+ /* now notify of the changes */
+ g_object_freeze_notify (G_OBJECT (src));
+ g_object_notify (G_OBJECT (src), "filesize");
+ g_object_notify (G_OBJECT (src), "offset");
+ g_object_thaw_notify (G_OBJECT (src));
+
GST_FLAG_SET (src, GST_FILESRC_OPEN);
}
return TRUE;
@@ -592,6 +606,12 @@
src->fd = 0;
src->filelen = 0;
src->curoffset = 0;
+ /* and notify that things changed */
+ g_object_freeze_notify (G_OBJECT (src));
+ g_object_notify (G_OBJECT (src), "filesize");
+ g_object_notify (G_OBJECT (src), "offset");
+ g_object_thaw_notify (G_OBJECT (src));
+
if (src->mapbuf)
gst_buffer_unref (src->mapbuf);
@@ -649,6 +669,7 @@
return FALSE;
break;
}
+ g_object_notify (G_OBJECT (src), "offset");
src->seek_happened = TRUE;
gst_event_free (event);
/* push a discontinuous event? */
Index: gst/elements/gstmd5sink.c
===================================================================
RCS file: /cvsroot/gstreamer/gstreamer/gst/elements/gstmd5sink.c,v
retrieving revision 1.2
diff -u -r1.2 gstmd5sink.c
--- gst/elements/gstmd5sink.c 2002/01/29 20:42:18 1.2
+++ gst/elements/gstmd5sink.c 2002/01/31 21:33:13
@@ -422,15 +422,17 @@
/* element check */
sink = GST_MD5SINK (element);
- g_return_val_if_fail (sink != NULL, GST_PAD_CONNECT_REFUSED);
- g_return_val_if_fail (GST_IS_MD5SINK (sink), GST_PAD_CONNECT_REFUSED);
+
+ g_return_val_if_fail (GST_IS_MD5SINK (sink), GST_STATE_FAILURE);
switch (GST_STATE_TRANSITION (element)) {
case GST_STATE_READY_TO_PAUSED:
md5_init_ctx (sink);
+ g_object_notify (G_OBJECT (element), "md5");
break;
case GST_STATE_PAUSED_TO_READY:
md5_finish_ctx (sink, sink->md5);
+ g_object_notify (G_OBJECT (element), "md5");
break;
default:
break;
@@ -454,7 +456,8 @@
switch (prop_id) {
case ARG_MD5:
- md5_read_ctx (sink, sink->md5);
+ /* you could actually get a value for the current md5. This is currently disabled.
+ * md5_read_ctx (sink, sink->md5); */
g_value_set_pointer (value, sink->md5);
break;
default:
Index: tools/gst-launch.c
===================================================================
RCS file: /cvsroot/gstreamer/gstreamer/tools/gst-launch.c,v
retrieving revision 1.6
diff -u -r1.6 gst-launch.c
--- tools/gst-launch.c 2002/01/15 05:58:45 1.6
+++ tools/gst-launch.c 2002/01/31 21:33:14
@@ -42,42 +42,45 @@
return busy;
}
-static void
-print_props (gpointer data, gpointer user_data)
+/* write more */
+static void
+property_change_callback (GObject *object, GstObject *orig, GParamSpec *pspec)
{
- GstPropsEntry *entry = (GstPropsEntry *)data;
- GstElement *element = GST_ELEMENT (user_data);
-
- g_print ("%s: %s: ", gst_element_get_name (element),
- g_quark_to_string (entry->propid));
- switch (entry->propstype) {
- case GST_PROPS_INT_ID:
- g_print ("%d\n", entry->data.int_data);
- break;
- case GST_PROPS_STRING_ID:
- g_print ("%s\n", entry->data.string_data.string);
- break;
- case GST_PROPS_FLOAT_ID:
- g_print ("%f\n", entry->data.float_data);
- break;
- default:
- g_print ("unknown\n");
- }
-}
-
-static void
-event_func (GstElement *element, GstEvent *event)
-{
- GstProps *props;
-
- if (event == NULL)
- return;
-
- if (GST_EVENT_TYPE (event) == GST_EVENT_INFO) {
- props = GST_EVENT_INFO_PROPS (event);
-
- g_list_foreach (props->properties, print_props, GST_EVENT_SRC (event));
+#ifdef USE_GLIB2
+ if (G_IS_PARAM_SPEC_STRING (pspec))
+ {
+ gchar *str = NULL;
+ g_object_get (orig, pspec->name, &str, NULL);
+ g_print ("%s: %s = \"%s\"\n", GST_OBJECT_NAME (orig), pspec->name, str);
+ g_free (str);
+ } else if (G_IS_PARAM_SPEC_CHAR (pspec)) {
+ gchar str;
+ g_object_get (orig, pspec->name, &str, NULL);
+ g_print ("%s: %s = \"%c\"\n", GST_OBJECT_NAME (orig), pspec->name, str);
+ } else if (G_IS_PARAM_SPEC_INT (pspec) || G_IS_PARAM_SPEC_INT64 (pspec)) {
+ gint64 i;
+ g_object_get (orig, pspec->name, &i, NULL);
+ g_print ("%s: %s = %lld\n", GST_OBJECT_NAME (orig), pspec->name, i);
+ } else if (G_IS_PARAM_SPEC_UINT (pspec) || G_IS_PARAM_SPEC_UINT64 (pspec)) {
+ guint64 i;
+ g_object_get (orig, pspec->name, &i, NULL);
+ g_print ("%s: %s = %llu\n", GST_OBJECT_NAME (orig), pspec->name, i);
+ } else if (G_IS_PARAM_SPEC_ENUM (pspec)) {
+ guint64 i;
+ g_object_get (orig, pspec->name, &i, NULL);
+ g_print ("%s: %s = \"%llu\"\n", GST_OBJECT_NAME (orig), pspec->name, i);
+ } else if (G_IS_PARAM_SPEC_FLOAT (pspec)) {
+ gfloat i;
+ g_object_get (orig, pspec->name, &i, NULL);
+ g_print ("%s: %s = %f\n", GST_OBJECT_NAME (orig), pspec->name, i);
+ } else if (G_IS_PARAM_SPEC_DOUBLE (pspec)) {
+ gdouble i;
+ g_object_get (orig, pspec->name, &i, NULL);
+ g_print ("%s: %s = %f\n", GST_OBJECT_NAME (orig), pspec->name, i);
+ } else {
+ g_print ("%s: changed \"%s\"\n", GST_OBJECT_NAME (orig), pspec->name);
}
+#endif /* USE_GLIB2 */
}
static GstElement*
@@ -185,7 +188,7 @@
exit(1);
}
- g_signal_connect (G_OBJECT (pipeline), "event", G_CALLBACK (event_func), NULL);
+ g_signal_connect (pipeline, "recursenotify", G_CALLBACK (property_change_callback), NULL);
#ifndef GST_DISABLE_LOADSAVE
if (save_pipeline) {
More information about the gstreamer-devel
mailing list