summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Klamt <graphics@pengutronix.de>2019-04-05 15:58:38 +0200
committerPhilipp Zabel <philipp.zabel@gmail.com>2019-12-09 09:23:55 +0100
commitfecfe451a7566740960d87da8a177f8a776f6137 (patch)
treee34a6eb39e3c931853d6d32fdbd81b27fdf4a986
parent91d05de9b93c38621bd70240b154dfdccf3bb0e1 (diff)
Changes the mapinfo so that the mapped data is writable
The Problem is, that in the current state it is not easily possible to edit the buffer data in a gstreamer python element since you get a copy of the real buffer. This patch overrides the mapinfo and the function generating it in a way so that mapinfo.data is now a memoryview pointing to the real buffer. Depending on the flags given for this buffer the memoryview is r/w.
-rwxr-xr-xexamples/plugins/python/exampleTransform.py49
-rw-r--r--gi/overrides/Gst.py57
-rw-r--r--gi/overrides/gstmodule.c253
3 files changed, 359 insertions, 0 deletions
diff --git a/examples/plugins/python/exampleTransform.py b/examples/plugins/python/exampleTransform.py
new file mode 100755
index 0000000..3d9bdb3
--- /dev/null
+++ b/examples/plugins/python/exampleTransform.py
@@ -0,0 +1,49 @@
+#!/usr/bin/python3
+# exampleTransform.py
+# 2019 Daniel Klamt <graphics@pengutronix.de>
+
+# Inverts a grayscale image in place, requires numpy.
+#
+# gst-launch-1.0 videotestsrc ! ExampleTransform ! videoconvert ! xvimagesink
+
+import gi
+gi.require_version('Gst', '1.0')
+gi.require_version('GstBase', '1.0')
+gi.require_version('GstVideo', '1.0')
+
+from gi.repository import Gst, GObject, GstBase, GstVideo
+
+import numpy as np
+
+Gst.init(None)
+FIXED_CAPS = Gst.Caps.from_string('video/x-raw,format=GRAY8,width=[1,2147483647],height=[1,2147483647]')
+
+class ExampleTransform(GstBase.BaseTransform):
+ __gstmetadata__ = ('ExampleTransform Python','Transform',
+ 'example gst-python element that can modify the buffer gst-launch-1.0 videotestsrc ! ExampleTransform ! videoconvert ! xvimagesink', 'dkl')
+
+ __gsttemplates__ = (Gst.PadTemplate.new("src",
+ Gst.PadDirection.SRC,
+ Gst.PadPresence.ALWAYS,
+ FIXED_CAPS),
+ Gst.PadTemplate.new("sink",
+ Gst.PadDirection.SINK,
+ Gst.PadPresence.ALWAYS,
+ FIXED_CAPS))
+
+ def do_set_caps(self, incaps, outcaps):
+ struct = incaps.get_structure(0)
+ self.width = struct.get_int("width").value
+ self.height = struct.get_int("height").value
+ return True
+
+ def do_transform_ip(self, buf):
+ with buf.map(Gst.MapFlags.READ | Gst.MapFlags.WRITE) as info:
+ # Create a NumPy ndarray from the memoryview and modify it in place:
+ A = np.ndarray(shape = (self.height, self.width), dtype = np.uint8, buffer = info.data)
+ A[:] = np.invert(A)
+
+ return Gst.FlowReturn.OK
+
+GObject.type_register(ExampleTransform)
+__gstelementfactory__ = ("ExampleTransform", Gst.Rank.NONE, ExampleTransform)
diff --git a/gi/overrides/Gst.py b/gi/overrides/Gst.py
index 3afc9c2..54bdd5d 100644
--- a/gi/overrides/Gst.py
+++ b/gi/overrides/Gst.py
@@ -591,6 +591,63 @@ def pairwise(iterable):
next(b, None)
return zip(a, b)
+class MapInfo:
+ def __init__(self):
+ self.memory = None
+ self.flags = Gst.MapFlags(0)
+ self.size = 0
+ self.maxsize = 0
+ self.data = None
+ self.user_data = None
+ self.__parent__ = None
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, type, value, tb):
+ self.__parent__.unmap(self)
+
+__all__.append("MapInfo")
+
+class Buffer(Gst.Buffer):
+
+ def map_range(self, idx, length, flags):
+ mapinfo = MapInfo()
+ if (_gi_gst.buffer_override_map_range(self, mapinfo, idx, length, int(flags))):
+ mapinfo.__parent__ = self
+ return (mapinfo)
+ raise Exception('MappingError','Buffer mapping was not successfull')
+ return None
+
+ def map(self, flags):
+ mapinfo = MapInfo()
+ if (_gi_gst.buffer_override_map(self, mapinfo, int(flags))):
+ mapinfo.__parent__ = self
+ return (mapinfo)
+ raise Exception('MappingError','Buffer mapping was not successfull')
+ return None
+
+ def unmap(self, mapinfo):
+ _gi_gst.buffer_override_unmap(self, mapinfo)
+
+Buffer = override(Buffer)
+__all__.append('Buffer')
+
+class Memory(Gst.Memory):
+
+ def map(self, flags):
+ mapinfo = MapInfo()
+ if (_gi_gst.memory_override_map(self, mapinfo, int(flags))):
+ mapinfo.__parent__ = self
+ return (mapinfo)
+ raise Exception('MappingError','Memory mapping was not successfull')
+ return None
+
+ def unmap(self, mapinfo):
+ _gi_gst.memory_override_unmap(self, mapinfo)
+
+Memory = override(Memory)
+__all__.append('Memory')
def TIME_ARGS(time):
if time == Gst.CLOCK_TIME_NONE:
diff --git a/gi/overrides/gstmodule.c b/gi/overrides/gstmodule.c
index 71f67aa..6e65753 100644
--- a/gi/overrides/gstmodule.c
+++ b/gi/overrides/gstmodule.c
@@ -723,6 +723,246 @@ _wrap_gst_memdump (PyObject * whatever, PyObject * string)
return pygst_debug_log (whatever, string, GST_LEVEL_MEMDUMP, FALSE);
}
+static PyObject *
+_remap (GstMapInfo * mapinfo, PyObject * py_mapinfo)
+{
+
+ PyObject *mview, *py_memory;
+
+ /* Create memoryview with compatible flags */
+ int flags;
+ flags = (mapinfo->flags & GST_MAP_WRITE) ? PyBUF_WRITE : PyBUF_READ;
+ mview =
+ PyMemoryView_FromMemory ((char *) mapinfo->data, mapinfo->size, flags);
+
+ /* Box GstMemory into a Gst.Memory */
+ py_memory = pyg_boxed_new (_gst_memory_type, mapinfo->memory, TRUE, TRUE);
+ /* Fill out Gst.MapInfo with values corresponding to GstMapInfo */
+ if (PyObject_SetAttrString (py_mapinfo, "memory", py_memory) == -1)
+ return NULL;
+ if (PyObject_SetAttrString (py_mapinfo, "flags", Py_BuildValue ("i",
+ mapinfo->flags)) == -1)
+ return NULL;
+ if (PyObject_SetAttrString (py_mapinfo, "data", mview) == -1)
+ return NULL;
+ if (PyObject_SetAttrString (py_mapinfo, "size", Py_BuildValue ("i",
+ mapinfo->size)) == -1)
+ return NULL;
+ if (PyObject_SetAttrString (py_mapinfo, "maxsize", Py_BuildValue ("i",
+ mapinfo->maxsize)) == -1)
+ return NULL;
+ if (PyObject_SetAttrString (py_mapinfo, "__cmapinfo", PyCapsule_New (mapinfo,
+ "__cmapinfo", NULL)) == -1)
+ return NULL;
+ return Py_True;
+}
+
+static PyObject *
+_gst_memory_override_map (PyObject * self, PyObject * args)
+{
+ PyTypeObject *gst_memory_type;
+ PyObject *py_memory, *py_mapinfo;
+ int flags;
+ GstMemory *memory;
+ GstMapInfo *mapinfo;
+ _Bool ok;
+
+ /* Look up Gst.memory, Gst.MapInfo, and Gst.MapFlags parameters */
+ gst_memory_type = pygobject_lookup_class (_gst_memory_type);
+ if (!PyArg_ParseTuple (args, "O!Oi", gst_memory_type, &py_memory,
+ &py_mapinfo, &flags))
+ return NULL;
+
+ /* Since Python does only support r/o or r/w it has to be changed to either */
+ flags = (flags & GST_MAP_WRITE) ? GST_MAP_READWRITE : GST_MAP_READ;
+
+ /* Extract GstMemory from Gst.Memory parameter */
+ memory = GST_MEMORY_CAST (pygobject_get (py_memory));
+
+ /* Map the memory, fill out GstMapInfo */
+ mapinfo = g_new0 (GstMapInfo, 1);
+ ok = gst_memory_map (memory, mapinfo, flags);
+ if (!ok) {
+ g_free (mapinfo);
+ return Py_False;
+ }
+
+ PyObject *success = _remap (mapinfo, py_mapinfo);
+ if (!success) {
+ gst_memory_unmap (memory, mapinfo);
+ g_free (mapinfo);
+ }
+ return success;
+}
+
+static PyObject *
+_gst_memory_override_unmap (PyObject * self, PyObject * args)
+{
+ PyTypeObject *gst_memory_type;
+ PyObject *py_memory, *py_cmapinfo, *py_mapinfo, *mview;
+ GstMemory *memory;
+ GstMapInfo *mapinfo;
+
+ /* Look up Gst.Buffer and Gst.Mapinfo parameters */
+ gst_memory_type = pygobject_lookup_class (_gst_memory_type);
+ if (!PyArg_ParseTuple (args, "O!O", gst_memory_type, &py_memory, &py_mapinfo))
+ return NULL;
+
+ /* Extract attributes from Gst.MapInfo */
+ if (!(mview = PyObject_GetAttrString (py_mapinfo, "data")))
+ goto err;
+ if (!PyObject_HasAttrString (py_mapinfo, "__cmapinfo"))
+ goto end;
+ if (!(py_cmapinfo = PyObject_GetAttrString (py_mapinfo, "__cmapinfo")))
+ goto err;
+
+ /* Extract GstBuffer from Gst.Buffer parameter */
+ memory = GST_MEMORY_CAST (pygobject_get (py_memory));
+ /* Reconstruct GstMapInfo from Gst.MapInfo contents */
+ mapinfo = PyCapsule_GetPointer (py_cmapinfo, "__cmapinfo");
+
+ /* Call the memoryview.release() Python method, there is no C API */
+ PyObject *ret = PyObject_CallMethod (mview, "release", NULL);
+ if (!ret)
+ goto err;
+ Py_DECREF (ret);
+ Py_DECREF (py_cmapinfo);
+ PyObject_SetAttrString (py_mapinfo, "__cmapinfo", NULL);
+
+ /* Unmap the buffer, using reconstructed GstMapInfo */
+ gst_memory_unmap (memory, mapinfo);
+
+ g_free (mapinfo);
+end:
+ Py_DECREF (mview);
+ Py_RETURN_NONE;
+
+err:
+ return NULL;
+}
+
+static PyObject *
+_gst_buffer_override_map_range (PyObject * self, PyObject * args)
+{
+ PyTypeObject *gst_buffer_type;
+ PyObject *py_buffer, *py_mapinfo;
+ int flags, range;
+ unsigned int idx;
+ GstBuffer *buffer;
+ GstMapInfo *mapinfo;
+ _Bool ok;
+
+ /* Look up Gst.Buffer, Gst.MapInfo, idx, range, and Gst.MapFlags parameters */
+ gst_buffer_type = pygobject_lookup_class (_gst_buffer_type);
+ if (!PyArg_ParseTuple (args, "O!OIii", gst_buffer_type, &py_buffer,
+ &py_mapinfo, &idx, &range, &flags))
+ return NULL;
+
+ /* Since Python does only support r/o or r/w it has to be changed to either */
+ flags = (flags & GST_MAP_WRITE) ? GST_MAP_READWRITE : GST_MAP_READ;
+
+ /* Extract GstBuffer from Gst.Buffer parameter */
+ buffer = GST_BUFFER (pygobject_get (py_buffer));
+
+ /* Map the buffer, fill out GstMapInfo */
+ mapinfo = g_new0 (GstMapInfo, 1);
+ ok = gst_buffer_map_range (buffer, idx, range, mapinfo, flags);
+ if (!ok) {
+ g_free (mapinfo);
+ return Py_False;
+ }
+
+ PyObject *success = _remap (mapinfo, py_mapinfo);
+ if (!success) {
+ gst_buffer_unmap (buffer, mapinfo);
+ g_free (mapinfo);
+ }
+ return success;
+}
+
+static PyObject *
+_gst_buffer_override_map (PyObject * self, PyObject * args)
+{
+ PyTypeObject *gst_buffer_type;
+ PyObject *py_buffer, *py_mapinfo;
+ int flags;
+ GstBuffer *buffer;
+ GstMapInfo *mapinfo;
+ _Bool ok;
+
+ /* Look up Gst.Buffer, Gst.MapInfo, and Gst.MapFlags parameters */
+ gst_buffer_type = pygobject_lookup_class (_gst_buffer_type);
+ if (!PyArg_ParseTuple (args, "O!Oi", gst_buffer_type, &py_buffer, &py_mapinfo,
+ &flags))
+ return NULL;
+
+ /* Since Python does only support r/o or r/w it has to be changed to either */
+ flags = (flags & GST_MAP_WRITE) ? GST_MAP_READWRITE : GST_MAP_READ;
+
+ /* Extract GstBuffer from Gst.Buffer parameter */
+ buffer = GST_BUFFER (pygobject_get (py_buffer));
+
+ /* Map the buffer, fill out GstMapInfo */
+ mapinfo = g_new0 (GstMapInfo, 1);
+ ok = gst_buffer_map (buffer, mapinfo, flags);
+ if (!ok) {
+ g_free (mapinfo);
+ return Py_False;
+ }
+
+ PyObject *success = _remap (mapinfo, py_mapinfo);
+ if (!success) {
+ gst_buffer_unmap (buffer, mapinfo);
+ g_free (mapinfo);
+ }
+ return success;
+}
+
+static PyObject *
+_gst_buffer_override_unmap (PyObject * self, PyObject * args)
+{
+ PyTypeObject *gst_buf_type;
+ PyObject *py_buffer, *py_cmapinfo, *py_mapinfo, *mview;
+ GstBuffer *buffer;
+ GstMapInfo *mapinfo;
+
+ /* Look up Gst.Buffer and Gst.Mapinfo parameters */
+ gst_buf_type = pygobject_lookup_class (_gst_buffer_type);
+ if (!PyArg_ParseTuple (args, "O!O", gst_buf_type, &py_buffer, &py_mapinfo))
+ return NULL;
+
+ /* Extract attributes from Gst.MapInfo */
+ if (!(mview = PyObject_GetAttrString (py_mapinfo, "data")))
+ goto err;
+ if (!PyObject_HasAttrString (py_mapinfo, "__cmapinfo"))
+ goto end;
+ if (!(py_cmapinfo = PyObject_GetAttrString (py_mapinfo, "__cmapinfo")))
+ goto err;
+
+ /* Extract GstBuffer from Gst.Buffer parameter */
+ buffer = GST_BUFFER (pygobject_get (py_buffer));
+ /* Reconstruct GstMapInfo from Gst.MapInfo contents */
+ mapinfo = PyCapsule_GetPointer (py_cmapinfo, "__cmapinfo");
+
+ /* Call the memoryview.release() Python method, there is no C API */
+ PyObject *ret = PyObject_CallMethod (mview, "release", NULL);
+ if (!ret)
+ goto err;
+ Py_DECREF (ret);
+ Py_DECREF (py_cmapinfo);
+ PyObject_SetAttrString (py_mapinfo, "__cmapinfo", NULL);
+
+ /* Unmap the buffer, using reconstructed GstMapInfo */
+ gst_buffer_unmap (buffer, mapinfo);
+ g_free (mapinfo);
+end:
+ Py_DECREF (mview);
+ Py_RETURN_NONE;
+
+err:
+ return NULL;
+}
+
static PyMethodDef _gi_gst_functions[] = {
{"trace", (PyCFunction) _wrap_gst_trace, METH_VARARGS,
NULL},
@@ -740,6 +980,19 @@ static PyMethodDef _gi_gst_functions[] = {
NULL},
{"memdump", (PyCFunction) _wrap_gst_memdump, METH_VARARGS,
NULL},
+ {"buffer_override_map_range", (PyCFunction) _gst_buffer_override_map_range,
+ METH_VARARGS,
+ NULL},
+ {"buffer_override_map", (PyCFunction) _gst_buffer_override_map, METH_VARARGS,
+ NULL},
+ {"buffer_override_unmap", (PyCFunction) _gst_buffer_override_unmap,
+ METH_VARARGS,
+ NULL},
+ {"memory_override_map", (PyCFunction) _gst_memory_override_map, METH_VARARGS,
+ NULL},
+ {"memory_override_unmap", (PyCFunction) _gst_memory_override_unmap,
+ METH_VARARGS,
+ NULL},
{NULL, NULL, 0, NULL}
};