python: Add overrides to be able to write into the GstStructure inside GstCaps
Add Python bindings for allowing to modify GstCaps structures with proper writability checks. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/9027>
This commit is contained in:
parent
9754f31fdb
commit
c3d2209c1f
@ -110,6 +110,20 @@ Bin = override(Bin)
|
|||||||
__all__.append('Bin')
|
__all__.append('Bin')
|
||||||
|
|
||||||
|
|
||||||
|
class NotWritableCaps(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
__all__.append('NotWritableCaps')
|
||||||
|
|
||||||
|
|
||||||
|
class NotWritableStructure(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
__all__.append('NotWritableStructure')
|
||||||
|
|
||||||
|
|
||||||
class Caps(Gst.Caps):
|
class Caps(Gst.Caps):
|
||||||
|
|
||||||
def __nonzero__(self):
|
def __nonzero__(self):
|
||||||
@ -118,9 +132,11 @@ class Caps(Gst.Caps):
|
|||||||
def __new__(cls, *args):
|
def __new__(cls, *args):
|
||||||
if not args:
|
if not args:
|
||||||
return Caps.new_empty()
|
return Caps.new_empty()
|
||||||
elif len(args) > 1:
|
if len(args) > 1:
|
||||||
raise TypeError("wrong arguments when creating GstCaps object")
|
raise TypeError("wrong arguments when creating GstCaps object")
|
||||||
elif isinstance(args[0], str):
|
|
||||||
|
assert len(args) == 1
|
||||||
|
if isinstance(args[0], str):
|
||||||
return Caps.from_string(args[0])
|
return Caps.from_string(args[0])
|
||||||
elif isinstance(args[0], Caps):
|
elif isinstance(args[0], Caps):
|
||||||
return args[0].copy()
|
return args[0].copy()
|
||||||
@ -150,6 +166,15 @@ class Caps(Gst.Caps):
|
|||||||
def __len__(self):
|
def __len__(self):
|
||||||
return self.get_size()
|
return self.get_size()
|
||||||
|
|
||||||
|
def get_structure_writable(self, index):
|
||||||
|
return StructureWrapper(_gi_gst.caps_get_writable_structure(self, index), self, True)
|
||||||
|
|
||||||
|
def make_writable(self):
|
||||||
|
return _gi_gst.caps_make_writable(self)
|
||||||
|
|
||||||
|
def is_writable(self):
|
||||||
|
return _gi_gst.caps_is_writable(self)
|
||||||
|
|
||||||
|
|
||||||
Caps = override(Caps)
|
Caps = override(Caps)
|
||||||
__all__.append('Caps')
|
__all__.append('Caps')
|
||||||
@ -318,23 +343,32 @@ class Structure(Gst.Structure):
|
|||||||
if kwargs:
|
if kwargs:
|
||||||
raise TypeError("wrong arguments when creating GstStructure, first argument"
|
raise TypeError("wrong arguments when creating GstStructure, first argument"
|
||||||
" must be the structure name.")
|
" must be the structure name.")
|
||||||
return Structure.new_empty()
|
struct = Structure.new_empty()
|
||||||
|
struct._writable = True
|
||||||
|
return struct
|
||||||
elif len(args) > 1:
|
elif len(args) > 1:
|
||||||
raise TypeError("wrong arguments when creating GstStructure object")
|
raise TypeError("wrong arguments when creating GstStructure object")
|
||||||
elif isinstance(args[0], str):
|
elif isinstance(args[0], str):
|
||||||
if not kwargs:
|
if not kwargs:
|
||||||
return Structure.from_string(args[0])[0]
|
struct = Structure.from_string(args[0])[0]
|
||||||
|
struct._writable = True
|
||||||
|
return struct
|
||||||
struct = Structure.new_empty(args[0])
|
struct = Structure.new_empty(args[0])
|
||||||
|
struct._writable = True
|
||||||
for k, v in kwargs.items():
|
for k, v in kwargs.items():
|
||||||
struct[k] = v
|
struct[k] = v
|
||||||
|
|
||||||
|
struct._writable = True
|
||||||
return struct
|
return struct
|
||||||
elif isinstance(args[0], Structure):
|
elif isinstance(args[0], Structure):
|
||||||
return args[0].copy()
|
struct = args[0].copy()
|
||||||
|
struct._writable = True
|
||||||
|
return struct
|
||||||
|
|
||||||
raise TypeError("wrong arguments when creating GstStructure object")
|
raise TypeError("wrong arguments when creating GstStructure object")
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
self._writable = False
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
@ -353,6 +387,14 @@ class Structure(Gst.Structure):
|
|||||||
def __setitem__(self, key, value):
|
def __setitem__(self, key, value):
|
||||||
return self.set_value(key, value)
|
return self.set_value(key, value)
|
||||||
|
|
||||||
|
def set_value(self, key, value):
|
||||||
|
if not self._writable:
|
||||||
|
raise NotWritableStructure("Trying to write to a not writable."
|
||||||
|
" Make sure to use the right APIs to have access to structure"
|
||||||
|
" in a writable way.")
|
||||||
|
|
||||||
|
return Gst.Structure.set_value(self, key, value)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.to_string()
|
return self.to_string()
|
||||||
|
|
||||||
@ -729,6 +771,21 @@ def pairwise(iterable):
|
|||||||
return zip(a, b)
|
return zip(a, b)
|
||||||
|
|
||||||
|
|
||||||
|
class StructureWrapper:
|
||||||
|
def __init__(self, structure, parent, writable):
|
||||||
|
structure._writable = writable
|
||||||
|
self.__structure = structure
|
||||||
|
self.__parent__ = parent
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self.__structure
|
||||||
|
|
||||||
|
def __exit__(self, _type, _value, _tb):
|
||||||
|
self.__structure._writable = False
|
||||||
|
self.__parent__ = False
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
class MapInfo:
|
class MapInfo:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.memory = None
|
self.memory = None
|
||||||
|
@ -21,6 +21,8 @@
|
|||||||
* Author: David I. Lehn <dlehn@users.sourceforge.net>
|
* Author: David I. Lehn <dlehn@users.sourceforge.net>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "gst/gstcaps.h"
|
||||||
|
#include "gst/gstminiobject.h"
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#endif
|
#endif
|
||||||
@ -930,6 +932,98 @@ err:
|
|||||||
return Py_False;
|
return Py_False;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_gst_caps_is_writable (PyObject * self, PyObject * args)
|
||||||
|
{
|
||||||
|
PyTypeObject *gst_caps_type;
|
||||||
|
PyObject *py_caps;
|
||||||
|
GstCaps *caps;
|
||||||
|
|
||||||
|
/* Look up Gst.Caps and Gst.Structure parameters */
|
||||||
|
gst_caps_type = pygobject_lookup_class (_gst_caps_type);
|
||||||
|
if (!PyArg_ParseTuple (args, "O!", gst_caps_type, &py_caps))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Extract GstCaps from Gst.Caps parameter */
|
||||||
|
caps = GST_CAPS (pygobject_get (py_caps));
|
||||||
|
if (gst_caps_is_writable (caps)) {
|
||||||
|
Py_INCREF (Py_True);
|
||||||
|
return Py_True;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_INCREF (Py_False);
|
||||||
|
return Py_False;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_gst_caps_make_writable (PyObject * self, PyObject * args)
|
||||||
|
{
|
||||||
|
PyTypeObject *gst_caps_type;
|
||||||
|
PyObject *py_caps, *res;
|
||||||
|
GstCaps *caps;
|
||||||
|
|
||||||
|
/* Look up Gst.Caps and Gst.Structure parameters */
|
||||||
|
gst_caps_type = pygobject_lookup_class (_gst_caps_type);
|
||||||
|
if (!PyArg_ParseTuple (args, "O!", gst_caps_type, &py_caps))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Extract GstCaps from Gst.Caps parameter */
|
||||||
|
caps = GST_CAPS (pygobject_get (py_caps));
|
||||||
|
|
||||||
|
if (!gst_caps_is_writable (caps)) {
|
||||||
|
GstMiniObject *writable_caps =
|
||||||
|
gst_mini_object_copy (GST_MINI_OBJECT (caps));
|
||||||
|
|
||||||
|
GST_DEBUG ("Copied caps %p to writable caps %p", caps, writable_caps);
|
||||||
|
|
||||||
|
// Drop our reference to the original caps
|
||||||
|
gst_caps_unref (caps);
|
||||||
|
pyg_boxed_set_ptr (py_caps, writable_caps);
|
||||||
|
Py_INCREF (Py_True);
|
||||||
|
res = Py_True;
|
||||||
|
} else {
|
||||||
|
Py_INCREF (Py_False);
|
||||||
|
res = Py_False;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_gst_caps_get_writable_structure (PyObject * self, PyObject * args)
|
||||||
|
{
|
||||||
|
PyTypeObject *gst_caps_type;
|
||||||
|
PyObject *py_caps, *py_structure;
|
||||||
|
GstCaps *caps;
|
||||||
|
gint idx;
|
||||||
|
|
||||||
|
/* Look up Gst.Caps and Gst.Structure parameters */
|
||||||
|
gst_caps_type = pygobject_lookup_class (_gst_caps_type);
|
||||||
|
if (!PyArg_ParseTuple (args, "O!i", gst_caps_type, &py_caps, &idx))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Extract GstCaps from Gst.Caps parameter */
|
||||||
|
caps = GST_CAPS (pygobject_get (py_caps));
|
||||||
|
if (!gst_caps_is_writable (caps)) {
|
||||||
|
PyObject *gstmodule = PyImport_ImportModule ("gi.repository.Gst");
|
||||||
|
PyObject *exc_class = PyObject_GetAttrString (gstmodule, "NotWritableCaps");
|
||||||
|
PyObject *args_tuple = Py_BuildValue ("(s)",
|
||||||
|
"Trying to get writable structure from immutable caps");
|
||||||
|
PyObject *exc_instance = PyObject_Call (exc_class, args_tuple, NULL);
|
||||||
|
|
||||||
|
PyErr_SetObject (exc_class, exc_instance);
|
||||||
|
Py_DECREF (exc_instance);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the structure at the given index */
|
||||||
|
py_structure = pyg_boxed_new (_gst_structure_type,
|
||||||
|
gst_caps_get_structure (caps, idx), FALSE, FALSE);
|
||||||
|
|
||||||
|
return py_structure;
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
_gst_memory_override_unmap (PyObject * self, PyObject * args)
|
_gst_memory_override_unmap (PyObject * self, PyObject * args)
|
||||||
{
|
{
|
||||||
@ -1073,38 +1167,27 @@ _gst_buffer_override_unmap (PyObject * self, PyObject * args)
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* *INDENT-OFF* */
|
||||||
static PyMethodDef _gi_gst_functions[] = {
|
static PyMethodDef _gi_gst_functions[] = {
|
||||||
{"trace", (PyCFunction) _wrap_gst_trace, METH_VARARGS,
|
{"trace", (PyCFunction) _wrap_gst_trace, METH_VARARGS, NULL},
|
||||||
NULL},
|
{"log", (PyCFunction) _wrap_gst_log, METH_VARARGS, NULL},
|
||||||
{"log", (PyCFunction) _wrap_gst_log, METH_VARARGS,
|
{"debug", (PyCFunction) _wrap_gst_debug, METH_VARARGS, NULL},
|
||||||
NULL},
|
{"info", (PyCFunction) _wrap_gst_info, METH_VARARGS, NULL},
|
||||||
{"debug", (PyCFunction) _wrap_gst_debug, METH_VARARGS,
|
{"warning", (PyCFunction) _wrap_gst_warning, METH_VARARGS, NULL},
|
||||||
NULL},
|
{"error", (PyCFunction) _wrap_gst_error, METH_VARARGS, NULL},
|
||||||
{"info", (PyCFunction) _wrap_gst_info, METH_VARARGS,
|
{"fixme", (PyCFunction) _wrap_gst_fixme, METH_VARARGS, NULL},
|
||||||
NULL},
|
{"memdump", (PyCFunction) _wrap_gst_memdump, METH_VARARGS, NULL},
|
||||||
{"warning", (PyCFunction) _wrap_gst_warning, METH_VARARGS,
|
{"buffer_override_map_range", (PyCFunction) _gst_buffer_override_map_range, METH_VARARGS, NULL},
|
||||||
NULL},
|
{"buffer_override_map", (PyCFunction) _gst_buffer_override_map, METH_VARARGS, NULL},
|
||||||
{"error", (PyCFunction) _wrap_gst_error, METH_VARARGS,
|
{"buffer_override_unmap", (PyCFunction) _gst_buffer_override_unmap, METH_VARARGS, NULL},
|
||||||
NULL},
|
{"memory_override_map", (PyCFunction) _gst_memory_override_map, METH_VARARGS, NULL},
|
||||||
{"fixme", (PyCFunction) _wrap_gst_fixme, METH_VARARGS,
|
{"memory_override_unmap", (PyCFunction) _gst_memory_override_unmap, METH_VARARGS, NULL},
|
||||||
NULL},
|
{"caps_get_writable_structure", (PyCFunction) _gst_caps_get_writable_structure, METH_VARARGS, NULL},
|
||||||
{"memdump", (PyCFunction) _wrap_gst_memdump, METH_VARARGS,
|
{"caps_make_writable", (PyCFunction) _gst_caps_make_writable, METH_VARARGS, NULL},
|
||||||
NULL},
|
{"caps_is_writable", (PyCFunction) _gst_caps_is_writable, 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}
|
{NULL, NULL, 0, NULL}
|
||||||
};
|
};
|
||||||
|
/* *INDENT-ON* */
|
||||||
|
|
||||||
static const gchar *const *
|
static const gchar *const *
|
||||||
py_uri_handler_get_protocols (GType type)
|
py_uri_handler_get_protocols (GType type)
|
||||||
|
@ -17,11 +17,11 @@
|
|||||||
# License along with this library; if not, write to the Free Software
|
# License along with this library; if not, write to the Free Software
|
||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
from gi.repository import Gst
|
||||||
|
from common import TestCase, unittest
|
||||||
import sys
|
import sys
|
||||||
import overrides_hack
|
import overrides_hack
|
||||||
overrides_hack
|
overrides_hack
|
||||||
from common import TestCase, unittest
|
|
||||||
from gi.repository import Gst
|
|
||||||
|
|
||||||
|
|
||||||
class TimeArgsTest(TestCase):
|
class TimeArgsTest(TestCase):
|
||||||
@ -57,9 +57,9 @@ class TestNotInitialized(TestCase):
|
|||||||
def testNotDeinitialized(self):
|
def testNotDeinitialized(self):
|
||||||
Gst.init(None)
|
Gst.init(None)
|
||||||
|
|
||||||
assert(Gst.Caps.from_string("audio/x-raw"))
|
self.assertIsNotNone(Gst.Caps.from_string("audio/x-raw"))
|
||||||
assert(Gst.Structure.from_string("audio/x-raw"))
|
self.assertIsNotNone(Gst.Structure.from_string("audio/x-raw"))
|
||||||
assert(Gst.ElementFactory.make("identity", None))
|
self.assertIsNotNone(Gst.ElementFactory.make("identity", None))
|
||||||
|
|
||||||
Gst.deinit()
|
Gst.deinit()
|
||||||
if sys.version_info >= (3, 0):
|
if sys.version_info >= (3, 0):
|
||||||
@ -77,6 +77,73 @@ class TestNotInitialized(TestCase):
|
|||||||
Gst.ElementFactory.make("identity", None)
|
Gst.ElementFactory.make("identity", None)
|
||||||
|
|
||||||
|
|
||||||
|
class TestCaps(TestCase):
|
||||||
|
|
||||||
|
def test_writable_make_writable_no_copy(self):
|
||||||
|
Gst.init(None)
|
||||||
|
caps = Gst.Caps("audio/x-raw")
|
||||||
|
repr = caps.__repr__()
|
||||||
|
caps.make_writable()
|
||||||
|
self.assertEqual(repr, caps.__repr__())
|
||||||
|
del caps
|
||||||
|
|
||||||
|
def test_make_writable_with_copy(self):
|
||||||
|
Gst.init(None)
|
||||||
|
|
||||||
|
caps = Gst.Caps("audio/x-raw")
|
||||||
|
repr = caps.__repr__()
|
||||||
|
miniobj = caps.mini_object
|
||||||
|
self.assertFalse(caps.is_writable())
|
||||||
|
repr = caps.__repr__()
|
||||||
|
self.assertTrue(caps.make_writable())
|
||||||
|
self.assertNotEqual(repr, caps.__repr__())
|
||||||
|
self.assertTrue(caps.is_writable())
|
||||||
|
del caps
|
||||||
|
del miniobj
|
||||||
|
|
||||||
|
def test_no_writable(self):
|
||||||
|
Gst.init(None)
|
||||||
|
caps = Gst.Caps("audio/x-raw")
|
||||||
|
caps.mini_object.refcount += 1
|
||||||
|
|
||||||
|
with self.assertRaises(Gst.NotWritableCaps):
|
||||||
|
with caps.get_structure_writable(0) as s:
|
||||||
|
s.set_value("rate", 44100)
|
||||||
|
|
||||||
|
caps.mini_object.refcount -= 1
|
||||||
|
|
||||||
|
def test_make_writable(self):
|
||||||
|
Gst.init(None)
|
||||||
|
caps = Gst.Caps("audio/x-raw")
|
||||||
|
|
||||||
|
capsfilter = Gst.ElementFactory.make("capsfilter", None)
|
||||||
|
|
||||||
|
if not capsfilter:
|
||||||
|
self.skipTest("capsfilter not available")
|
||||||
|
|
||||||
|
capsfilter.set_property("caps", caps)
|
||||||
|
caps = capsfilter.get_property("caps")
|
||||||
|
|
||||||
|
with self.assertRaises(Gst.NotWritableCaps):
|
||||||
|
with caps.get_structure_writable(0) as s:
|
||||||
|
s.set_value("rate", 44100)
|
||||||
|
caps.make_writable()
|
||||||
|
with caps.get_structure_writable(0) as s:
|
||||||
|
s.set_value("rate", 44100)
|
||||||
|
capsfilter.set_property("caps", caps)
|
||||||
|
caps = capsfilter.get_property("caps")
|
||||||
|
self.assertEqual(caps[0]["rate"], 44100)
|
||||||
|
|
||||||
|
def test_writable(self):
|
||||||
|
Gst.init(None)
|
||||||
|
caps = Gst.Caps("audio/x-raw")
|
||||||
|
|
||||||
|
with caps.get_structure_writable(0) as s:
|
||||||
|
s.set_value("rate", 44100)
|
||||||
|
s.set_value("channels", 2)
|
||||||
|
self.assertEqual(caps[0]["rate"], 44100)
|
||||||
|
self.assertEqual(caps[0]["channels"], 2)
|
||||||
|
|
||||||
class TestStructure(TestCase):
|
class TestStructure(TestCase):
|
||||||
|
|
||||||
def test_new(self):
|
def test_new(self):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user