[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