mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 12:11:13 +00:00
Re-implement wrapping of gst_pad_add_*probe in order to avoid leaks of user-data associated with the probes.
Original commit message from CVS: reviewed by: Edward Hervey <edward.hervey@collabora.co.uk> * gst/gstpad.override: * testsuite/test_pad.py: Re-implement wrapping of gst_pad_add_*probe in order to avoid leaks of user-data associated with the probes. Fixes #504786
This commit is contained in:
parent
9077e68aaa
commit
45b80b1e8c
3 changed files with 146 additions and 110 deletions
10
ChangeLog
10
ChangeLog
|
@ -1,3 +1,13 @@
|
||||||
|
2008-01-13 Olivier Crete <tester@tester.ca>
|
||||||
|
|
||||||
|
reviewed by: Edward Hervey <edward.hervey@collabora.co.uk>
|
||||||
|
|
||||||
|
* gst/gstpad.override:
|
||||||
|
* testsuite/test_pad.py:
|
||||||
|
Re-implement wrapping of gst_pad_add_*probe in order to avoid leaks of
|
||||||
|
user-data associated with the probes.
|
||||||
|
Fixes #504786
|
||||||
|
|
||||||
2008-01-13 Edward Hervey <edward.hervey@collabora.co.uk>
|
2008-01-13 Edward Hervey <edward.hervey@collabora.co.uk>
|
||||||
|
|
||||||
* gst/pbutils.override:
|
* gst/pbutils.override:
|
||||||
|
|
|
@ -116,57 +116,6 @@ py_pad_private(PyGObject *pad)
|
||||||
return pad_private ((GstPad *)pygobject_get(pad));
|
return pad_private ((GstPad *)pygobject_get(pad));
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
|
||||||
probe_handler_marshal(GstPad *pad, GstMiniObject *data, gpointer user_data)
|
|
||||||
{
|
|
||||||
PyGILState_STATE state;
|
|
||||||
PyObject *callback, *args;
|
|
||||||
PyObject *ret;
|
|
||||||
PyObject *py_data;
|
|
||||||
PyObject *py_user_data;
|
|
||||||
PyObject *repr;
|
|
||||||
gboolean res;
|
|
||||||
gint len, i;
|
|
||||||
|
|
||||||
g_return_val_if_fail(user_data != NULL, FALSE);
|
|
||||||
|
|
||||||
GST_LOG_OBJECT (pad, "marshalling probe handler for object %"
|
|
||||||
GST_PTR_FORMAT, data);
|
|
||||||
state = pyg_gil_state_ensure();
|
|
||||||
|
|
||||||
py_user_data = (PyObject *) user_data;
|
|
||||||
|
|
||||||
py_data = pygstminiobject_new(data);
|
|
||||||
|
|
||||||
callback = PyTuple_GetItem(py_user_data, 0);
|
|
||||||
args = Py_BuildValue("(NN)",
|
|
||||||
pygobject_new(G_OBJECT(pad)),
|
|
||||||
py_data);
|
|
||||||
|
|
||||||
len = PyTuple_Size(py_user_data);
|
|
||||||
for (i = 1; i < len; ++i) {
|
|
||||||
PyObject *tuple = args;
|
|
||||||
args = PySequence_Concat(tuple, PyTuple_GetItem(py_user_data, i));
|
|
||||||
Py_DECREF(tuple);
|
|
||||||
}
|
|
||||||
repr = PyObject_Repr (callback);
|
|
||||||
GST_LOG_OBJECT (pad, "calling callback %s", PyString_AsString (repr));
|
|
||||||
Py_DECREF (repr);
|
|
||||||
|
|
||||||
ret = PyObject_CallObject(callback, args);
|
|
||||||
|
|
||||||
if (!ret) {
|
|
||||||
PyErr_Print();
|
|
||||||
res = TRUE;
|
|
||||||
} else {
|
|
||||||
res = PyObject_IsTrue(ret);
|
|
||||||
Py_DECREF(ret);
|
|
||||||
}
|
|
||||||
Py_DECREF(args);
|
|
||||||
pyg_gil_state_release(state);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
%%
|
%%
|
||||||
ignore
|
ignore
|
||||||
gst_pad_select
|
gst_pad_select
|
||||||
|
@ -895,90 +844,164 @@ override gst_pad_add_data_probe args
|
||||||
static PyObject *
|
static PyObject *
|
||||||
_wrap_gst_pad_add_data_probe(PyGObject *self, PyObject *args)
|
_wrap_gst_pad_add_data_probe(PyGObject *self, PyObject *args)
|
||||||
{
|
{
|
||||||
PyObject *callback, *cbargs = NULL, *data;
|
GstPad *pad = GST_PAD(self->obj);
|
||||||
gulong sigid;
|
PyObject *method = NULL;
|
||||||
gint len;
|
PyObject *rv = NULL;
|
||||||
|
PyObject *mylist = PyList_New(1);
|
||||||
|
PyObject *mynewlist = NULL;
|
||||||
|
PyObject *myargs = NULL;
|
||||||
|
PyObject *signalname = NULL;
|
||||||
|
|
||||||
len = PyTuple_Size(args);
|
signalname = PyString_FromString("have-data");
|
||||||
|
|
||||||
if (len < 1) {
|
if (PyList_SetItem(mylist, 0, signalname)) {
|
||||||
PyErr_SetString(PyExc_TypeError, "Probe requires at least 1 arg");
|
Py_DECREF(mylist);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
callback = PySequence_GetItem(args, 0);
|
|
||||||
if (!PyCallable_Check(callback)) {
|
mynewlist = PySequence_InPlaceConcat(mylist, args);
|
||||||
PyErr_SetString(PyExc_TypeError, "callback is not callable");
|
|
||||||
|
Py_DECREF(mylist);
|
||||||
|
|
||||||
|
if (!mynewlist)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
myargs = PyList_AsTuple(mynewlist);
|
||||||
|
|
||||||
|
Py_DECREF(mynewlist);
|
||||||
|
|
||||||
|
if (!myargs)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
method = PyObject_GetAttrString((PyObject*)self, "connect");
|
||||||
|
|
||||||
|
if (!method) {
|
||||||
|
Py_DECREF(mylist);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
cbargs = PySequence_GetSlice(args, 1, len);
|
|
||||||
if (cbargs == NULL)
|
|
||||||
return NULL;
|
|
||||||
data = Py_BuildValue("(ON)", callback, cbargs);
|
|
||||||
if (data == NULL)
|
|
||||||
return NULL;
|
|
||||||
sigid = gst_pad_add_data_probe (GST_PAD (self->obj), (GCallback) probe_handler_marshal, data);
|
|
||||||
|
|
||||||
return PyLong_FromUnsignedLong(sigid);
|
GST_OBJECT_LOCK (pad);
|
||||||
|
|
||||||
|
rv = PyObject_CallObject(method, myargs);
|
||||||
|
if (rv) {
|
||||||
|
GST_PAD_DO_BUFFER_SIGNALS (pad)++;
|
||||||
|
GST_PAD_DO_EVENT_SIGNALS (pad)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_OBJECT_UNLOCK (pad);
|
||||||
|
|
||||||
|
Py_DECREF(myargs);
|
||||||
|
Py_DECREF(method);
|
||||||
|
|
||||||
|
return rv;
|
||||||
}
|
}
|
||||||
%%
|
%%
|
||||||
override gst_pad_add_event_probe args
|
override gst_pad_add_event_probe args
|
||||||
static PyObject *
|
static PyObject *
|
||||||
_wrap_gst_pad_add_event_probe(PyGObject *self, PyObject *args)
|
_wrap_gst_pad_add_event_probe(PyGObject *self, PyObject *args)
|
||||||
{
|
{
|
||||||
PyObject *callback, *cbargs = NULL, *data;
|
GstPad *pad = GST_PAD(self->obj);
|
||||||
gulong sigid;
|
PyObject *method = NULL;
|
||||||
gint len;
|
PyObject *rv = NULL;
|
||||||
|
PyObject *mylist = PyList_New(1);
|
||||||
|
PyObject *mynewlist = NULL;
|
||||||
|
PyObject *myargs = NULL;
|
||||||
|
PyObject *signalname = NULL;
|
||||||
|
|
||||||
len = PyTuple_Size(args);
|
signalname = PyString_FromString("have-data::event");
|
||||||
|
|
||||||
if (len < 1) {
|
if (PyList_SetItem(mylist, 0, signalname)) {
|
||||||
PyErr_SetString(PyExc_TypeError, "Probe requires at least 1 arg");
|
Py_DECREF(mylist);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
callback = PySequence_GetItem(args, 0);
|
|
||||||
if (!PyCallable_Check(callback)) {
|
mynewlist = PySequence_InPlaceConcat(mylist, args);
|
||||||
PyErr_SetString(PyExc_TypeError, "callback is not callable");
|
|
||||||
|
Py_DECREF(mylist);
|
||||||
|
|
||||||
|
if (!mynewlist)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
myargs = PyList_AsTuple(mynewlist);
|
||||||
|
|
||||||
|
Py_DECREF(mynewlist);
|
||||||
|
|
||||||
|
if (!myargs)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
method = PyObject_GetAttrString((PyObject*)self, "connect");
|
||||||
|
|
||||||
|
if (!method) {
|
||||||
|
Py_DECREF(mylist);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
cbargs = PySequence_GetSlice(args, 1, len);
|
|
||||||
if (cbargs == NULL)
|
|
||||||
return NULL;
|
|
||||||
data = Py_BuildValue("(ON)", callback, cbargs);
|
|
||||||
if (data == NULL)
|
|
||||||
return NULL;
|
|
||||||
sigid = gst_pad_add_event_probe (GST_PAD (self->obj), (GCallback) probe_handler_marshal, data);
|
|
||||||
|
|
||||||
return PyLong_FromUnsignedLong(sigid);
|
GST_OBJECT_LOCK (pad);
|
||||||
|
|
||||||
|
rv = PyObject_CallObject(method, myargs);
|
||||||
|
if (rv)
|
||||||
|
GST_PAD_DO_EVENT_SIGNALS (pad)++;
|
||||||
|
|
||||||
|
GST_OBJECT_UNLOCK (pad);
|
||||||
|
|
||||||
|
Py_DECREF(myargs);
|
||||||
|
Py_DECREF(method);
|
||||||
|
|
||||||
|
return rv;
|
||||||
}
|
}
|
||||||
%%
|
%%
|
||||||
override gst_pad_add_buffer_probe args
|
override gst_pad_add_buffer_probe args
|
||||||
static PyObject *
|
static PyObject *
|
||||||
_wrap_gst_pad_add_buffer_probe(PyGObject *self, PyObject *args)
|
_wrap_gst_pad_add_buffer_probe(PyGObject *self, PyObject *args)
|
||||||
{
|
{
|
||||||
PyObject *callback, *cbargs = NULL, *data;
|
GstPad *pad = GST_PAD(self->obj);
|
||||||
gulong sigid;
|
PyObject *method = NULL;
|
||||||
gint len;
|
PyObject *rv = NULL;
|
||||||
|
PyObject *mylist = PyList_New(1);
|
||||||
|
PyObject *mynewlist = NULL;
|
||||||
|
PyObject *myargs = NULL;
|
||||||
|
PyObject *signalname = NULL;
|
||||||
|
|
||||||
len = PyTuple_Size(args);
|
signalname = PyString_FromString("have-data::buffer");
|
||||||
|
|
||||||
if (len < 1) {
|
if (PyList_SetItem(mylist, 0, signalname)) {
|
||||||
PyErr_SetString(PyExc_TypeError, "Probe requires at least 1 arg");
|
Py_DECREF(mylist);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
callback = PySequence_GetItem(args, 0);
|
|
||||||
if (!PyCallable_Check(callback)) {
|
mynewlist = PySequence_InPlaceConcat(mylist, args);
|
||||||
PyErr_SetString(PyExc_TypeError, "callback is not callable");
|
|
||||||
|
Py_DECREF(mylist);
|
||||||
|
|
||||||
|
if (!mynewlist)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
myargs = PyList_AsTuple(mynewlist);
|
||||||
|
|
||||||
|
Py_DECREF(mynewlist);
|
||||||
|
|
||||||
|
if (!myargs)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
method = PyObject_GetAttrString((PyObject*)self, "connect");
|
||||||
|
|
||||||
|
if (!method) {
|
||||||
|
Py_DECREF(mylist);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
cbargs = PySequence_GetSlice(args, 1, len);
|
|
||||||
if (cbargs == NULL)
|
|
||||||
return NULL;
|
|
||||||
data = Py_BuildValue("(ON)", callback, cbargs);
|
|
||||||
if (data == NULL)
|
|
||||||
return NULL;
|
|
||||||
sigid = gst_pad_add_buffer_probe (GST_PAD (self->obj), (GCallback) probe_handler_marshal, data);
|
|
||||||
|
|
||||||
return PyLong_FromUnsignedLong(sigid);
|
GST_OBJECT_LOCK (pad);
|
||||||
|
|
||||||
|
rv = PyObject_CallObject(method, myargs);
|
||||||
|
if (rv)
|
||||||
|
GST_PAD_DO_BUFFER_SIGNALS (pad)++;
|
||||||
|
|
||||||
|
GST_OBJECT_UNLOCK (pad);
|
||||||
|
|
||||||
|
Py_DECREF(myargs);
|
||||||
|
Py_DECREF(method);
|
||||||
|
|
||||||
|
return rv;
|
||||||
}
|
}
|
||||||
%%
|
%%
|
||||||
override-slot GstPadTemplate.tp_getattr
|
override-slot GstPadTemplate.tp_getattr
|
||||||
|
|
|
@ -457,18 +457,21 @@ class PadProbePipeTest(TestCase):
|
||||||
|
|
||||||
handle = None
|
handle = None
|
||||||
self._num_times_called = 0
|
self._num_times_called = 0
|
||||||
def buffer_probe(pad, buffer):
|
def buffer_probe(pad, buffer, data):
|
||||||
self._num_times_called += 1
|
self._num_times_called += 1
|
||||||
pad.remove_buffer_probe(handle)
|
pad.remove_buffer_probe(handle)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
pad = self.fakesrc.get_pad('src')
|
pad = self.fakesrc.get_pad('src')
|
||||||
handle = pad.add_buffer_probe(buffer_probe)
|
data = []
|
||||||
|
handle = pad.add_buffer_probe(buffer_probe, data)
|
||||||
self.pipeline.set_state(gst.STATE_PLAYING)
|
self.pipeline.set_state(gst.STATE_PLAYING)
|
||||||
m = self.pipeline.get_bus().poll(gst.MESSAGE_EOS, -1)
|
m = self.pipeline.get_bus().poll(gst.MESSAGE_EOS, -1)
|
||||||
assert m
|
assert m
|
||||||
assert self._num_times_called == 1
|
assert self._num_times_called == 1
|
||||||
self.pipeline.set_state(gst.STATE_NULL)
|
self.pipeline.set_state(gst.STATE_NULL)
|
||||||
|
assert sys.getrefcount(buffer_probe) == 2
|
||||||
|
assert sys.getrefcount(data) == 2
|
||||||
# FIXME: having m going out of scope doesn't seem to be enough
|
# FIXME: having m going out of scope doesn't seem to be enough
|
||||||
# to get it gc collected, and it keeps a ref to the pipeline.
|
# to get it gc collected, and it keeps a ref to the pipeline.
|
||||||
# Look for a way to not have to do this explicitly
|
# Look for a way to not have to do this explicitly
|
||||||
|
|
Loading…
Reference in a new issue