python: Add support for the GstURIHandlerInterface
authorThibault Saunier <tsaunier@igalia.com>
Mon, 6 Dec 2021 22:27:24 +0000 (19:27 -0300)
committerGStreamer Marge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Mon, 28 Mar 2022 11:25:24 +0000 (11:25 +0000)
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1423>

subprojects/gst-python/examples/plugins/python/sinkelement.py
subprojects/gst-python/gi/overrides/Gst.py
subprojects/gst-python/gi/overrides/gstmodule.c

index 2ec3608..94281e4 100644 (file)
 #
 #  $ export GST_PLUGIN_PATH=$GST_PLUGIN_PATH:$PWD/plugin:$PWD/examples/plugins
 #  $ GST_DEBUG=python:4 gst-launch-1.0 fakesrc num-buffers=10 ! mysink
+#
+# Since this implements the `mypysink://` protocol you can also do:
+#
+#  $ gst-launch-1.0 videotestsrc ! pymysink://
 
 from gi.repository import Gst, GObject, GstBase
 Gst.init(None)
@@ -22,7 +26,7 @@ Gst.init(None)
 #
 # Simple Sink element created entirely in python
 #
-class MySink(GstBase.BaseSink):
+class MySink(GstBase.BaseSink, Gst.URIHandler):
     __gstmetadata__ = ('CustomSink','Sink', \
                       'Custom test sink element', 'Edward Hervey')
 
@@ -31,9 +35,23 @@ class MySink(GstBase.BaseSink):
                                            Gst.PadPresence.ALWAYS,
                                            Gst.Caps.new_any())
 
+    __protocols__ = ("pymysink",)
+    __uritype__ = Gst.URIType.SINK
+
+    def __init__(self):
+        super().__init__()
+        # Working around https://gitlab.gnome.org/GNOME/pygobject/-/merge_requests/129
+        self.__dontkillme = self
+
     def do_render(self, buffer):
         Gst.info("timestamp(buffer):%s" % (Gst.TIME_ARGS(buffer.pts)))
         return Gst.FlowReturn.OK
 
+    def do_get_uri(self, uri):
+        return "pymysink://"
+
+    def do_set_uri(self, uri):
+        return True
+
 GObject.type_register(MySink)
 __gstelementfactory__ = ("mysink", Gst.Rank.NONE, MySink)
index 1b75e35..1d5a44e 100644 (file)
@@ -48,6 +48,14 @@ python module to use with Gst 0.10"
     warnings.warn(warn_msg, RuntimeWarning)
 
 
+# Ensuring that PyGObject loads the URIHandler interface
+# so we can force our own implementation soon enough (in gstmodule.c)
+class URIHandler(Gst.URIHandler):
+    pass
+
+URIHandler = override(URIHandler)
+__all__.append('URIHandler')
+
 class Element(Gst.Element):
     @staticmethod
     def link_many(*args):
index dc8364a..da52cd5 100644 (file)
@@ -32,6 +32,9 @@
 
 #include <locale.h>
 
+#define URI_HANDLER_PROTOCOLS_QUARK g_quark_from_static_string("__gst__uri_handler_protocols")
+#define URI_HANDLER_URITYPE_QUARK g_quark_from_static_string("__gst__uri_handler_uritype")
+
 #define PYGLIB_MODULE_START(symbol, modname)           \
     static struct PyModuleDef _##symbol##module = {     \
     PyModuleDef_HEAD_INIT,                              \
@@ -1103,6 +1106,113 @@ static PyMethodDef _gi_gst_functions[] = {
   {NULL, NULL, 0, NULL}
 };
 
+static const gchar *const *
+py_uri_handler_get_protocols (GType type)
+{
+  /* FIXME: Ideally we should be able to free the list of protocols on
+   * deinitialization */
+  return g_type_get_qdata (type, URI_HANDLER_PROTOCOLS_QUARK);
+}
+
+static GstURIType
+py_uri_handler_get_type (GType type)
+{
+  return GPOINTER_TO_INT (g_type_get_qdata (type, URI_HANDLER_URITYPE_QUARK));
+}
+
+static const GStrv
+py_uri_handler_get_protocols_from_pyobject (PyObject * protocols)
+{
+  GStrv res = NULL;
+
+  if (PyTuple_Check (protocols)) {
+    gint i, len;
+
+    len = PyTuple_Size (protocols);
+    if (len == 0) {
+      PyErr_Format (PyExc_TypeError,
+          "Empty tuple for GstUriHandler.__protocols");
+      goto err;
+    }
+
+    res = g_malloc (len * sizeof (gchar *));
+    for (i = 0; i < len; i++) {
+      PyObject *protocol = (PyObject *) PyTuple_GetItem (protocols, i);
+
+      if (!PyUnicode_Check (protocol)) {
+        Py_DECREF (protocol);
+        goto err;
+      }
+
+      res[i] = g_strdup (PyUnicode_AsUTF8 (protocol));
+    }
+  } else {
+    PyErr_Format (PyExc_TypeError, "invalid type for GstUriHandler.__protocols."
+        " Should be a tuple");
+    goto err;
+  }
+
+  return res;
+
+err:
+  Py_DECREF (protocols);
+  g_strfreev (res);
+  return FALSE;
+}
+
+static void
+uri_handler_iface_init (GstURIHandlerInterface * iface, PyTypeObject * pytype)
+{
+  gint uritype;
+  GStrv protocols;
+  PyObject *pyprotocols = pytype ? PyObject_GetAttrString ((PyObject *) pytype,
+      "__protocols__") : NULL;
+  PyObject *pyuritype = pytype ? PyObject_GetAttrString ((PyObject *) pytype,
+      "__uritype__") : NULL;
+  GType gtype = pyg_type_from_object ((PyObject *) pytype);
+
+  if (pyprotocols == NULL) {
+    PyErr_Format (PyExc_KeyError, "__protocols__ missing in %s",
+        pytype->tp_name);
+    goto done;
+  }
+
+  if (pyuritype == NULL) {
+    PyErr_Format (PyExc_KeyError, "__pyuritype__ missing in %s",
+        pytype->tp_name);
+    goto done;
+  }
+
+  protocols = py_uri_handler_get_protocols_from_pyobject (pyprotocols);
+  if (!protocols)
+    goto done;
+
+  if (pyg_enum_get_value (GST_TYPE_URI_TYPE, pyuritype, &uritype) < 0) {
+    PyErr_SetString (PyExc_TypeError,
+        "entry for __uritype__ must be of type GstURIType");
+    goto done;
+  }
+
+  iface->get_protocols = py_uri_handler_get_protocols;
+  g_type_set_qdata (gtype, URI_HANDLER_PROTOCOLS_QUARK, protocols);
+
+  iface->get_type = py_uri_handler_get_type;
+  g_type_set_qdata (gtype, URI_HANDLER_URITYPE_QUARK,
+      GINT_TO_POINTER (uritype));
+
+done:
+  if (pyprotocols)
+    Py_DECREF (pyprotocols);
+  if (pyuritype)
+    Py_DECREF (pyuritype);
+}
+
+static const GInterfaceInfo GstURIHandlerInterfaceInfo = {
+  (GInterfaceInitFunc) uri_handler_iface_init,
+  NULL,
+  NULL
+};
+
 PYGLIB_MODULE_START (_gi_gst, "_gi_gst")
 {
   PyObject *d;
@@ -1120,6 +1230,8 @@ PYGLIB_MODULE_START (_gi_gst, "_gi_gst")
   d = PyModule_GetDict (module);
   gi_gst_register_types (d);
   pyg_register_class_init (GST_TYPE_ELEMENT, _pygst_element_init);
+  pyg_register_interface_info (GST_TYPE_URI_HANDLER,
+      &GstURIHandlerInterfaceInfo);
 }
 
 PYGLIB_MODULE_END;