/* -*- Mode: C; c-basic-offset: 4 -*- */ /* gst-python * Copyright (C) 2005 Johan Dahlin * 2005 Benjamin Otte * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Author: Johan Dahlin * Benjamin Otte */ %% headers /* This is a (hopefully) smart hack to allow access to a caps' * structures without falling into traps when the caps get destroyed * before the structures get. * This Hash Table uses the structure PyObjects as keys and the caps * PyObjects as values. No clue if this is a fast data structure but it * probably doesn't matter anyway. */ static GHashTable *structure_caps_map = NULL; static void pygst_caps_map_add (PyObject *structure, PyObject *caps) { /* we can't have free_on_dealloc stuff in here */ g_assert (((PyGBoxed *)structure)->free_on_dealloc == FALSE); g_hash_table_insert (structure_caps_map, structure, caps); } static void pygst_caps_map_remove_structure (PyObject *structure) { g_hash_table_remove (structure_caps_map, structure); } static gboolean pygst_caps_map_foreach (gpointer structure, gpointer caps, gpointer match) { PyGBoxed *boxed = structure; if (match != caps) return FALSE; /* we can't have free_on_dealloc stuff in here */ g_assert (boxed->free_on_dealloc == FALSE); boxed->boxed = gst_structure_copy (boxed->boxed); boxed->free_on_dealloc = TRUE; return TRUE; } static void pygst_caps_map_modified (PyObject *caps) { g_hash_table_foreach_remove (structure_caps_map, pygst_caps_map_foreach, caps); } %% init structure_caps_map = g_hash_table_new (g_direct_hash, g_direct_equal); %% ignore gst_caps_new_simple gst_caps_new_full gst_caps_set_simple %% override gst_caps_get_structure kwargs static PyObject *pygst_caps_sq_item(PyObject *self, int i); static PyObject * _wrap_gst_caps_get_structure(PyObject *self, PyObject *args, PyObject *kwargs) { static char *kwlist[] = { "index", NULL }; int index; if (PyErr_Warn(PyExc_DeprecationWarning, "caps.get_structure(i) is deprecated, use caps[i]") < 0) return NULL; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i:GstCaps.get_structure", kwlist, &index)) return NULL; return pygst_caps_sq_item (self, index); } %% override gst_caps_new_empty kwargs static int _wrap_gst_caps_new_empty(PyGBoxed *self, PyObject *args, PyObject *kwargs) { PyObject* item; int len, i; /* we wrap caps_new, caps_from_string and caps_new_full */ len = PyTuple_Size(args); self->gtype = GST_TYPE_CAPS; self->free_on_dealloc = FALSE; if (len == 0) { /* 0 length creates a new empty caps */ self->boxed = gst_caps_new_empty(); } else if (len == 1) { item = PyTuple_GetItem(args, 0); /* 1 length is either a string or a structure */ self->boxed = pygst_caps_from_pyobject (item, NULL); } else { /* it's multiple arguments that can all be made to caps */ GstCaps *append; self->boxed = gst_caps_new_empty(); for (i = 0; i < len; i++) { item = PyTuple_GetItem(args, i); append = pygst_caps_from_pyobject (item, NULL); if (!append) { gst_caps_unref (self->boxed); self->boxed = NULL; break; } gst_caps_append (self->boxed, append); } } if (!self->boxed) { PyErr_SetString(PyExc_TypeError, "wrong arguemntes when creating GstCaps object"); return -1; } return 0; } %% override-slot GstCaps.tp_richcompare static gboolean pygst_caps_is_true_subset (GstCaps *caps1, GstCaps *caps2) { GstCaps *tmp; gboolean ret; /* order is important here */ if (gst_caps_is_any (caps1)) return FALSE; if (gst_caps_is_any (caps2)) return TRUE; if (gst_caps_is_empty (caps2)) return FALSE; if (gst_caps_is_empty (caps1)) return TRUE; tmp = gst_caps_subtract (caps1, caps2); ret = gst_caps_is_empty (tmp); gst_caps_unref (tmp); if (!ret) return FALSE; tmp = gst_caps_subtract (caps2, caps1); ret = gst_caps_is_empty (tmp); gst_caps_unref (tmp); return !ret; } static PyObject * _wrap_gst_caps_tp_richcompare (PyObject *py_caps1, PyObject *py_caps2, int comparison) { GstCaps *caps1, *caps2; gboolean caps2_is_copy; PyObject *ret; caps1 = pyg_boxed_get (py_caps1, GstCaps); caps2 = pygst_caps_from_pyobject (py_caps2, &caps2_is_copy); if (PyErr_Occurred()) { /* the second arg is not a caps */ switch (comparison) { case Py_EQ: PyErr_Clear(); ret = Py_False; Py_INCREF (ret); return ret; case Py_NE: PyErr_Clear(); ret = Py_True; Py_INCREF (ret); return ret; default: return NULL; } } switch (comparison) { case Py_LT: ret = pygst_caps_is_true_subset (caps1, caps2) ? Py_True : Py_False; break; case Py_LE: ret = gst_caps_is_subset (caps1, caps2) ? Py_True : Py_False; break; case Py_NE: ret = gst_caps_is_equal (caps1, caps2) ? Py_False : Py_True; break; case Py_EQ: ret = gst_caps_is_equal (caps1, caps2) ? Py_True : Py_False; break; case Py_GE: ret = gst_caps_is_subset (caps2, caps1) ? Py_True : Py_False; break; case Py_GT: ret = pygst_caps_is_true_subset (caps2, caps1) ? Py_True : Py_False; break; default: PyErr_SetString (PyExc_RuntimeError, "invalid comparison operation"); if (caps2 && caps2_is_copy) gst_caps_unref (caps2); return NULL; } if (caps2 && caps2_is_copy) gst_caps_unref (caps2); Py_INCREF (ret); return ret; } %% override-slot GstCaps.tp_as_number /* In this number code, we mimic the Python set.Set datatype. * The same operations are supported. If you want to have an operation * supported for caps, add it to Python's Set type first. */ #define BINARY_FUNC(name,func) \ static PyObject * \ name (PyObject *py_caps1, PyObject *py_caps2) \ { \ GstCaps *caps1, *caps2, *ret; \ gboolean caps2_is_copy; \ \ caps1 = pyg_boxed_get (py_caps1, GstCaps); \ caps2 = pygst_caps_from_pyobject (py_caps2, &caps2_is_copy); \ if (PyErr_Occurred()) \ return NULL; \ ret = func (caps1, caps2); \ if (caps2 && caps2_is_copy) \ gst_caps_unref (caps2); \ return pyg_boxed_new (GST_TYPE_CAPS, ret, FALSE, TRUE); \ } BINARY_FUNC (pygst_caps_nb_subtract, gst_caps_subtract) BINARY_FUNC (pygst_caps_nb_and, gst_caps_intersect) BINARY_FUNC (pygst_caps_nb_or, gst_caps_union) static GstCaps * pygst_caps_xor (const GstCaps *caps1, const GstCaps *caps2) { GstCaps *intersect, *_union, *ret; intersect = gst_caps_intersect (caps1, caps2); _union = gst_caps_union (caps1, caps2); ret = gst_caps_subtract (_union, intersect); gst_caps_unref (_union); gst_caps_unref (intersect); gst_caps_do_simplify (ret); return ret; } BINARY_FUNC (pygst_caps_nb_xor, pygst_caps_xor) static int pygst_caps_nb_nonzero (PyObject *py_caps) { GstCaps *caps = pyg_boxed_get (py_caps, GstCaps); if (gst_caps_is_empty (caps)) return 0; else return 1; } static int pygst_caps_nb_coerce (PyObject **py_caps1, PyObject **py_caps2) { GstCaps *caps1, *caps2 = NULL; gboolean caps1_is_copy, caps2_is_copy; caps1 = pygst_caps_from_pyobject (*py_caps1, &caps1_is_copy); if (!caps1) goto error; caps2 = pygst_caps_from_pyobject (*py_caps2, &caps2_is_copy); if (!caps2) goto error; /* if they're not copies, they're caps already */ if (caps1_is_copy) *py_caps1 = pyg_boxed_new (GST_TYPE_CAPS, caps1, FALSE, TRUE); else Py_INCREF (*py_caps1); if (caps2_is_copy) *py_caps2 = pyg_boxed_new (GST_TYPE_CAPS, caps2, FALSE, TRUE); else Py_INCREF (*py_caps2); return 0; error: g_assert (PyErr_Occurred ()); PyErr_Clear (); if (caps1 && !caps1_is_copy) gst_caps_unref (caps1); if (caps2 && !caps2_is_copy) gst_caps_unref (caps2); return 1; } static PyNumberMethods _wrap_gst_caps_tp_as_number = { 0, /* nb_add */ pygst_caps_nb_subtract, /* nb_subtract */ 0, /* nb_multiply */ 0, /* nb_divide */ 0, /* nb_remainder */ 0, /* nb_divmod */ 0, /* nb_power */ 0, /* nb_negative */ 0, /* nb_positive */ 0, /* nb_absolute */ pygst_caps_nb_nonzero, /* nb_nonzero */ 0, /* nb_invert */ 0, /* nb_lshift */ 0, /* nb_rshift */ pygst_caps_nb_and, /* nb_and */ pygst_caps_nb_xor, /* nb_xor */ pygst_caps_nb_or, /* nb_or */ pygst_caps_nb_coerce, /* nb_coerce */ 0, /* nb_int */ 0, /* nb_long */ 0, /* nb_float */ 0, /* nb_oct */ 0, /* nb_hex */ 0, /* nb_inplace_add */ 0, /* nb_inplace_subtract */ 0, /* nb_inplace_multiply */ 0, /* nb_inplace_divide */ 0, /* nb_inplace_remainder */ 0, /* nb_inplace_power */ 0, /* nb_inplace_lshift */ 0, /* nb_inplace_rshift */ 0, /* nb_inplace_and */ 0, /* nb_inplace_xor */ 0, /* nb_inplace_or */ 0, /* nb_floor_divide */ 0, /* nb_true_divide */ 0, /* nb_inplace_floor_divide */ 0, /* nb_inplace_true_divide */ }; %% override-slot GstCaps.tp_as_sequence static int pygst_caps_sq_length(PyObject *self) { GstCaps *caps = pyg_boxed_get (self, GstCaps); return gst_caps_get_size(caps); } static PyObject * pygst_caps_sq_item(PyObject *self, int i) { GstCaps *caps = pyg_boxed_get (self, GstCaps); GstStructure *structure; PyObject *ret; if (i < 0 || i >= gst_caps_get_size(caps)) { PyErr_SetString(PyExc_IndexError, "list index out of range"); return NULL; } structure = gst_caps_get_structure(caps, i); /* pyg_boxed_new handles NULL checking */ ret = pyg_boxed_new(GST_TYPE_STRUCTURE, gst_caps_get_structure(pyg_boxed_get(self, GstCaps), i), FALSE, FALSE); if (ret) pygst_caps_map_add (ret, self); return ret; } /* FIXME: This syntax sucks */ static PyObject * pygst_caps_sq_slice(PyObject *self, int start, int end) { GstCaps *caps = pyg_boxed_get (self, GstCaps); GstCaps *ret = gst_caps_new_empty (); int i; if (start < 0) start = 0; if (end > gst_caps_get_size (caps)) end = gst_caps_get_size (caps); for (i = start; i < end; i++) gst_caps_append_structure (ret, gst_structure_copy (gst_caps_get_structure (caps, i))); return pyg_boxed_new(GST_TYPE_CAPS, ret, FALSE, TRUE); } static PySequenceMethods _wrap_gst_caps_tp_as_sequence = { pygst_caps_sq_length, NULL, /* not allowed for sets, use | instead of + */ NULL, /* doesn't make sense, because it'd still be the same */ pygst_caps_sq_item, pygst_caps_sq_slice, NULL, /* doesn't make sense, you can only append */ NULL, /* doesn't make sense, you can only append */ NULL, /* doesn't make sense really, unless you use is_subset */ NULL, /* not allowed for sets, use | instead of + */ NULL /* doesn't make sense, because it'd still be the same */ }; %% override-slot GstCaps.tp_dealloc static void _wrap_gst_caps_tp_dealloc (PyObject *self) { PyGBoxed *boxed = (PyGBoxed *) self; if (boxed->free_on_dealloc && boxed->boxed) { pygst_caps_map_modified (self); GST_DEBUG ("unreffing caps %" GST_PTR_FORMAT "with refcount %d", boxed->boxed, GST_CAPS_REFCOUNT (boxed->boxed)); gst_caps_unref (boxed->boxed); } self->ob_type->tp_free((PyObject *)self); } %% override-slot GstCaps.tp_str static PyObject * _wrap_gst_caps_tp_str(PyGObject *self) { gchar *tmp; PyObject *retval; tmp = gst_caps_to_string((GstCaps*)self->obj); retval = PyString_FromString(tmp); g_free(tmp); return retval; } %% override-attr GstCaps.__refcount__ static PyObject * _wrap_gst_caps__get___refcount__(PyGObject *self, void *closure) { return PyInt_FromLong(GST_CAPS_REFCOUNT(self->obj)); }