mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 04:01:08 +00:00
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.
This commit is contained in:
parent
91d05de9b9
commit
fecfe451a7
3 changed files with 359 additions and 0 deletions
49
examples/plugins/python/exampleTransform.py
Executable file
49
examples/plugins/python/exampleTransform.py
Executable file
|
@ -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)
|
|
@ -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:
|
||||
|
|
|
@ -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}
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue