diff --git a/subprojects/gst-python/gi/overrides/Gst.py b/subprojects/gst-python/gi/overrides/Gst.py index ebe96db59b..0939798b55 100644 --- a/subprojects/gst-python/gi/overrides/Gst.py +++ b/subprojects/gst-python/gi/overrides/Gst.py @@ -110,6 +110,20 @@ Bin = override(Bin) __all__.append('Bin') +class NotWritableCaps(Exception): + pass + + +__all__.append('NotWritableCaps') + + +class NotWritableStructure(Exception): + pass + + +__all__.append('NotWritableStructure') + + class Caps(Gst.Caps): def __nonzero__(self): @@ -118,9 +132,11 @@ class Caps(Gst.Caps): def __new__(cls, *args): if not args: return Caps.new_empty() - elif len(args) > 1: + if len(args) > 1: 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]) elif isinstance(args[0], Caps): return args[0].copy() @@ -150,6 +166,15 @@ class Caps(Gst.Caps): def __len__(self): 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) __all__.append('Caps') @@ -318,23 +343,32 @@ class Structure(Gst.Structure): if kwargs: raise TypeError("wrong arguments when creating GstStructure, first argument" " must be the structure name.") - return Structure.new_empty() + struct = Structure.new_empty() + struct._writable = True + return struct elif len(args) > 1: raise TypeError("wrong arguments when creating GstStructure object") elif isinstance(args[0], str): 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._writable = True for k, v in kwargs.items(): struct[k] = v + struct._writable = True return struct 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") def __init__(self, *args, **kwargs): + self._writable = False pass def __getitem__(self, key): @@ -353,6 +387,14 @@ class Structure(Gst.Structure): def __setitem__(self, 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): return self.to_string() @@ -729,6 +771,21 @@ def pairwise(iterable): 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: def __init__(self): self.memory = None diff --git a/subprojects/gst-python/gi/overrides/gstmodule.c b/subprojects/gst-python/gi/overrides/gstmodule.c index a2c268273d..9909c17ad0 100644 --- a/subprojects/gst-python/gi/overrides/gstmodule.c +++ b/subprojects/gst-python/gi/overrides/gstmodule.c @@ -21,6 +21,8 @@ * Author: David I. Lehn */ +#include "gst/gstcaps.h" +#include "gst/gstminiobject.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -930,6 +932,98 @@ err: 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 * _gst_memory_override_unmap (PyObject * self, PyObject * args) { @@ -1073,38 +1167,27 @@ _gst_buffer_override_unmap (PyObject * self, PyObject * args) return success; } +/* *INDENT-OFF* */ static PyMethodDef _gi_gst_functions[] = { - {"trace", (PyCFunction) _wrap_gst_trace, METH_VARARGS, - NULL}, - {"log", (PyCFunction) _wrap_gst_log, METH_VARARGS, - NULL}, - {"debug", (PyCFunction) _wrap_gst_debug, METH_VARARGS, - NULL}, - {"info", (PyCFunction) _wrap_gst_info, METH_VARARGS, - NULL}, - {"warning", (PyCFunction) _wrap_gst_warning, METH_VARARGS, - NULL}, - {"error", (PyCFunction) _wrap_gst_error, METH_VARARGS, - NULL}, - {"fixme", (PyCFunction) _wrap_gst_fixme, METH_VARARGS, - 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}, + {"trace", (PyCFunction) _wrap_gst_trace, METH_VARARGS, NULL}, + {"log", (PyCFunction) _wrap_gst_log, METH_VARARGS, NULL}, + {"debug", (PyCFunction) _wrap_gst_debug, METH_VARARGS, NULL}, + {"info", (PyCFunction) _wrap_gst_info, METH_VARARGS, NULL}, + {"warning", (PyCFunction) _wrap_gst_warning, METH_VARARGS, NULL}, + {"error", (PyCFunction) _wrap_gst_error, METH_VARARGS, NULL}, + {"fixme", (PyCFunction) _wrap_gst_fixme, METH_VARARGS, 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}, + {"caps_get_writable_structure", (PyCFunction) _gst_caps_get_writable_structure, METH_VARARGS, NULL}, + {"caps_make_writable", (PyCFunction) _gst_caps_make_writable, METH_VARARGS, NULL}, + {"caps_is_writable", (PyCFunction) _gst_caps_is_writable, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} }; +/* *INDENT-ON* */ static const gchar *const * py_uri_handler_get_protocols (GType type) diff --git a/subprojects/gst-python/testsuite/test_gst.py b/subprojects/gst-python/testsuite/test_gst.py index 2111b9688c..474c81786a 100644 --- a/subprojects/gst-python/testsuite/test_gst.py +++ b/subprojects/gst-python/testsuite/test_gst.py @@ -17,11 +17,11 @@ # License along with this library; if not, write to the Free Software # 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 overrides_hack overrides_hack -from common import TestCase, unittest -from gi.repository import Gst class TimeArgsTest(TestCase): @@ -57,9 +57,9 @@ class TestNotInitialized(TestCase): def testNotDeinitialized(self): Gst.init(None) - assert(Gst.Caps.from_string("audio/x-raw")) - assert(Gst.Structure.from_string("audio/x-raw")) - assert(Gst.ElementFactory.make("identity", None)) + self.assertIsNotNone(Gst.Caps.from_string("audio/x-raw")) + self.assertIsNotNone(Gst.Structure.from_string("audio/x-raw")) + self.assertIsNotNone(Gst.ElementFactory.make("identity", None)) Gst.deinit() if sys.version_info >= (3, 0): @@ -77,6 +77,73 @@ class TestNotInitialized(TestCase): 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): def test_new(self):