diff options
author | Daniel Klamt <graphics@pengutronix.de> | 2019-04-05 15:58:38 +0200 |
---|---|---|
committer | Philipp Zabel <philipp.zabel@gmail.com> | 2019-12-09 09:23:55 +0100 |
commit | fecfe451a7566740960d87da8a177f8a776f6137 (patch) | |
tree | e34a6eb39e3c931853d6d32fdbd81b27fdf4a986 | |
parent | 91d05de9b93c38621bd70240b154dfdccf3bb0e1 (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-x | examples/plugins/python/exampleTransform.py | 49 | ||||
-rw-r--r-- | gi/overrides/Gst.py | 57 | ||||
-rw-r--r-- | gi/overrides/gstmodule.c | 253 |
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} }; |