Implement pad query proxying so that python elements can answer pad queries. Fixes...
authorJan Schmidt <thaytan@mad.scientist.com>
Tue, 10 Apr 2007 18:01:25 +0000 (18:01 +0000)
committerJan Schmidt <thaytan@mad.scientist.com>
Tue, 10 Apr 2007 18:01:25 +0000 (18:01 +0000)
Original commit message from CVS:
* examples/pyidentity.py:
* gst/common.h:
* gst/gstpad.override:
Implement pad query proxying so that python elements can
answer pad queries. Fixes: #428299

ChangeLog
examples/pyidentity.py
gst/common.h
gst/gstpad.override

index 5543d72..31fb2e1 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,14 @@
 2007-04-10  Jan Schmidt  <thaytan@mad.scientist.com>
 
        * examples/pyidentity.py:
+       * gst/common.h:
+       * gst/gstpad.override:
+       Implement pad query proxying so that python elements can
+       answer pad queries. Fixes: #428299
+
+2007-04-10  Jan Schmidt  <thaytan@mad.scientist.com>
+
+       * examples/pyidentity.py:
        Add a simple example that implements an identity-like element in
        python and passes buffers through. It lacks buffer-alloc & query
        handling at the moment, because the required gstreamer funcs aren't
index e864892..184c8c5 100644 (file)
@@ -32,6 +32,7 @@ class PyIdentity(gst.Element):
         self.srcpad = gst.Pad(self._srcpadtemplate, "src")
 
         self.srcpad.set_event_function(self.srceventfunc)
+        self.srcpad.set_query_function(self.srcqueryfunc)
         self.srcpad.set_getcaps_function(gst.Pad.proxy_getcaps)
         self.srcpad.set_setcaps_function(gst.Pad.proxy_setcaps)
         self.add_pad (self.srcpad)
@@ -43,6 +44,8 @@ class PyIdentity(gst.Element):
     def eventfunc(self, pad, event):
         return self.srcpad.push_event (event)
         
+    def srcqueryfunc (self, pad, query):
+        return self.sinkpad.query (query)
     def srceventfunc (self, pad, event):
         return self.sinkpad.push_event (event)
 
index bf4676c..68ce75f 100644 (file)
@@ -54,6 +54,9 @@ typedef struct {
        GClosure *activate_function;
        GClosure *activatepull_function;
        GClosure *activatepush_function;
+        /* Query is not implemented as a closure to avoid refcounting
+         * making the query immutable and therefore useless */
+       PyObject *query_function;
 } PyGstPadPrivate;
 
 typedef struct {
index cc9e1ff..07c24e6 100644 (file)
@@ -85,6 +85,11 @@ free_pad_private (gpointer data)
   INVALIDATE_CLOSURE (private->activatepull_function)
   INVALIDATE_CLOSURE (private->activatepush_function)
 #undef INVALIDATE_CLOSURE
+
+  if (private->query_function) {
+    Py_DECREF (private->query_function);
+    private->query_function = NULL;
+  }
 }
 
 static PyGstPadPrivate*
@@ -352,6 +357,148 @@ _wrap_gst_pad_set_event_function (PyGObject *self,
 }
 
 %%
+override gst_pad_set_query_function kwargs
+
+static gboolean
+pypad_copy_struct_members (GQuark field_id, const GValue * value, 
+    GstStructure* to_structure)
+{
+    gst_structure_id_set_value (to_structure, field_id, value);
+
+    return TRUE;
+}
+
+static gboolean
+call_query_function (GstPad *pad, GstQuery *query)
+{
+    PyGILState_STATE __py_state;
+    PyGObject *py_pad;
+    PyGstPadPrivate *priv;
+
+    PyObject *py_ret;
+    PyObject *py_args;
+    gboolean ret = FALSE;
+    GstQuery *query_copy;
+    PyObject *py_query;
+
+    /* Push our GIL state */
+    __py_state = pyg_gil_state_ensure();
+
+    /* Get the python version of the pad */
+    py_pad = (PyGObject *) pygobject_new((GObject*) (pad));
+    if (!py_pad) {
+        if (PyErr_Occurred())
+            PyErr_Print();
+        goto beach;
+    }
+    /* Private data, where our callback should be stored */
+    priv = py_pad_private(py_pad);
+    if (priv->query_function == NULL) {
+        /* FIXME: Generate an error message somewhere? */
+        Py_DECREF(py_pad);
+        goto beach;
+    }
+    
+    /* Create our arguments tuple and populate */
+    py_args = PyTuple_New(2);
+
+    /* We copy the query into a new one so that it can have a refcount
+     * of exactly 1 and be owned by python */
+    pyg_begin_allow_threads;
+    query_copy = gst_query_copy (query);
+    pyg_end_allow_threads;
+    py_query = pygstminiobject_new((GstMiniObject *)query_copy);
+    gst_query_unref (query_copy);
+
+    PyTuple_SetItem(py_args, 0, (PyObject *) (py_pad));
+    PyTuple_SetItem(py_args, 1, py_query);
+
+    /* Perform the callback into python, then parse the result */
+    py_ret = PyObject_CallObject(priv->query_function, py_args);
+    if (!py_ret) {
+        if (PyErr_Occurred())
+            PyErr_Print();
+
+        Py_DECREF(py_args);
+        goto beach;
+    }
+
+    ret = (py_ret == Py_True ? TRUE : FALSE);
+
+    /* If the query succeeded, copy the result back into the original query.
+     * We still have a refcount to it, because we didn't unref the py_query
+     * wrapper yet. */
+    if (ret) {
+        /* I feel ill violating the poor query like this, but it's the only
+         * way to transfer data from our copy back to the original query */
+        GstStructure *from, *to;
+        
+        pyg_begin_allow_threads;
+        from = GST_QUERY (query_copy)->structure; 
+        to = query->structure;
+        gst_structure_foreach (from, 
+            (GstStructureForeachFunc) pypad_copy_struct_members, to);
+        pyg_end_allow_threads;
+    }
+
+    Py_DECREF(py_args);
+    Py_DECREF(py_ret);
+beach:
+    pyg_gil_state_release(__py_state);
+    
+    return ret;
+}
+
+static PyObject*
+_wrap_gst_pad_set_query_function (PyGObject *self,
+                  PyObject  *args,
+                  PyObject  *kwargs)
+{
+    static char *kwlist[] = { "query_function", NULL };
+    PyObject *function;
+    GstPad *pad;
+    PyGstPadPrivate *priv;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+                     "O:GstPad.set_query_function",
+                     kwlist,
+                     &function)) {
+        return NULL;
+    }
+
+    pad = (GstPad*)pygobject_get(self);
+    priv = py_pad_private(self);
+
+    /* Allow setting query_function to None to clear it to NULL */
+    if (function == Py_None) {
+        if (priv->query_function) {
+            Py_DECREF (priv->query_function);
+            priv->query_function = NULL;
+        }
+        gst_pad_set_query_function (pad, NULL);
+        goto out;
+    }
+
+    if (!PyCallable_Check(function)) {
+        PyErr_SetString(PyExc_TypeError, "Passed query_function not callable");
+        return NULL;
+    }
+
+    if (priv->query_function) {
+        Py_DECREF (priv->query_function);
+    }
+
+    Py_INCREF(function);
+    priv->query_function = function;
+
+    gst_pad_set_query_function (pad, call_query_function);
+
+out:
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+%%
 override gst_pad_set_setcaps_function kwargs
 
 static void EXCEPTION_HANDLER