Commit a4566ffb authored by Nicolas Dufresne's avatar Nicolas Dufresne

overrides: Add more GstValue overrides

This patch adds overrides to support IntRange, Int64Range, DoubleRange,
FractionRange, Array and List. For integer ranges, it maps this
to python 'range'. Gst.IntRange() and Gst.Int64Range() are simple cast
to let the underlying code know which GType to use. To set such range in
python you will do:

  structure["range"] = Gst.IntRange(range(0,10,2)))

Same for the 64 bit variant. And when you do:

  r = structure.get_value("range")

A range will be returned directly, without the wrapper. For DoubleRange
and FractionRange, there is no native support in python. So the usage
will be:

  structure["range"] = Gst.DoubleRange(0,10.0)
  structure["range"] =
      Gst.FractionRange(Gst.Fraction(1/30), Gst.Fraction(1/5)

When getting this value, Gst.DoubleRange and Gst.FractionRange class are
returned. They both have start/stop members. The naming was taken from
range type.

For Array and List, both uses the native list type, though they can be
constructed from any python sequence. So again, the class is just like
a cast, to let it pick the right GType and python list are being
returned.

  structure["list"] = Gst.ValueList([1,2,3,4])
  structure["array"] = Gst.ValueArray([1,2,3,4)

Using string and tuple could also work. Since Gst.ValueList/Array are
sequence, you can convert one to the other with:

  list = Gst.ValueList([1,2,3,4])
  array = Gst.ValueArray (list)

https://bugzilla.gnome.org/show_bug.cgi?id=753754
parent 7f78ed25
......@@ -269,7 +269,7 @@ class Fraction(Gst.Fraction):
self.type = "fraction"
def __repr__(self):
return '<Gst.Fraction %d/%d>' % (self.num, self.denom)
return '<Gst.Fraction %s>' % (str(self))
def __value__(self):
return self.num / self.denom
......@@ -288,7 +288,8 @@ class Fraction(Gst.Fraction):
self.denom * other.denom)
elif isinstance(other, int):
return Fraction(self.num * other, self.denom)
raise TypeError
raise TypeError("%s is not supported, use Gst.Fraction or int." %
(type(other)))
__rmul__ = __mul__
......@@ -298,23 +299,181 @@ class Fraction(Gst.Fraction):
self.denom * other.num)
elif isinstance(other, int):
return Fraction(self.num, self.denom * other)
return TypeError
return TypeError("%s is not supported, use Gst.Fraction or int." %
(type(other)))
__div__ = __truediv__
def __rtruediv__(self, other):
if isinstance(other, int):
return Fraction(self.denom * other, self.num)
return TypeError
return TypeError("%s is not an int." % (type(other)))
__rdiv__ = __rtruediv__
def __float__(self):
return float(self.num) / float(self.denom)
def __str__(self):
return '%d/%d' % (self.num, self.denom)
Fraction = override(Fraction)
__all__.append('Fraction')
class IntRange(Gst.IntRange):
def __init__(self, r):
if not isinstance(r, range):
raise TypeError("%s is not a range." % (type(r)))
if (r.start >= r.stop):
raise TypeError("Range start must be smaller then stop")
if r.start % r.step != 0:
raise TypeError("Range start must be a multiple of the step")
if r.stop % r.step != 0:
raise TypeError("Range stop must be a multiple of the step")
self.range = r
def __repr__(self):
return '<Gst.IntRange [%d,%d,%d]>' % (self.range.start,
self.range.stop, self.range.step)
def __str__(self):
if self.range.step == 1:
return '[%d,%d]' % (self.range.start, self.range.stop)
else:
return '[%d,%d,%d]' % (self.range.start, self.range.stop,
self.range.step)
IntRange = override(IntRange)
__all__.append('IntRange')
class Int64Range(Gst.Int64Range):
def __init__(self, r):
if not isinstance(r, range):
raise TypeError("%s is not a range." % (type(r)))
if (r.start >= r.stop):
raise TypeError("Range start must be smaller then stop")
if r.start % r.step != 0:
raise TypeError("Range start must be a multiple of the step")
if r.stop % r.step != 0:
raise TypeError("Range stop must be a multiple of the step")
self.range = r
def __repr__(self):
return '<Gst.Int64Range [%d,%d,%d]>' % (self.range.start,
self.range.stop, self.range.step)
def __str__(self):
if self.range.step == 1:
return '(int64)[%d,%d]' % (self.range.start, self.range.stop)
else:
return '(int64)[%d,%d,%d]' % (self.range.start, self.range.stop,
self.range.step)
Int64Range = override(Int64Range)
__all__.append('Int64Range')
class DoubleRange(Gst.DoubleRange):
def __init__(self, start, stop):
self.start = float(start)
self.stop = float(stop)
if (start >= stop):
raise TypeError("Range start must be smaller then stop")
def __repr__(self):
return '<Gst.DoubleRange [%s,%s]>' % (str(self.start), str(self.stop))
def __str__(self):
return '(double)[%s,%s]' % (str(self.range.start), str(self.range.stop))
DoubleRange = override(DoubleRange)
__all__.append('DoubleRange')
class FractionRange(Gst.FractionRange):
def __init__(self, start, stop):
if not isinstance(start, Gst.Fraction):
raise TypeError("%s is not a Gst.Fraction." % (type(start)))
if not isinstance(stop, Gst.Fraction):
raise TypeError("%s is not a Gst.Fraction." % (type(stop)))
if (float(start) >= float(stop)):
raise TypeError("Range start must be smaller then stop")
self.start = start
self.stop = stop
def __repr__(self):
return '<Gst.FractionRange [%s,%s]>' % (str(self.start),
str(self.stop))
def __str__(self):
return '(fraction)[%s,%s]' % (str(self.start), str(self.stop))
FractionRange = override(FractionRange)
__all__.append('FractionRange')
class ValueArray(Gst.ValueArray):
def __init__(self, array):
self.array = list(array)
def __getitem__(self, index):
return self.array[index]
def __setitem__(self, index, value):
self.array[index] = value
def __len__(self):
return len(self.array)
def __str__(self):
return '<' + ','.join(map(str,self.array)) + '>'
def __repr__(self):
return '<Gst.ValueArray %s>' % (str(self))
ValueArray = override(ValueArray)
__all__.append('ValueArray')
class ValueList(Gst.ValueList):
def __init__(self, array):
self.array = list(array)
def __getitem__(self, index):
return self.array[index]
def __setitem__(self, index, value):
self.array[index] = value
def __len__(self):
return len(self.array)
def __str__(self):
return '{' + ','.join(map(str,self.array)) + '}'
def __repr__(self):
return '<Gst.ValueList %s>' % (str(self))
ValueList = override(ValueList)
__all__.append('ValueList')
def TIME_ARGS(time):
if time == Gst.CLOCK_TIME_NONE:
return "CLOCK_TIME_NONE"
......
......@@ -127,11 +127,382 @@ fail:
return -1;
}
static PyObject *
gi_gst_int_range_from_value (const GValue * value)
{
gint min, max, step;
min = gst_value_get_int_range_min (value);
max = gst_value_get_int_range_max (value);
step = gst_value_get_int_range_step (value);
return PyObject_CallFunction ((PyObject *) & PyRange_Type, "iii",
min, max, step);
}
static int
gi_gst_int_range_to_value (GValue * value, PyObject * object)
{
PyObject *range, *min, *max, *step;
range = PyObject_GetAttrString (object, "range");
if (range == NULL)
goto fail;
min = PyObject_GetAttrString (range, "start");
if (min == NULL)
goto fail;
max = PyObject_GetAttrString (range, "stop");
if (max == NULL)
goto fail;
step = PyObject_GetAttrString (range, "step");
if (step == NULL)
goto fail;
gst_value_set_int_range_step (value, PyLong_AsLong (min),
PyLong_AsLong (max), PyLong_AsLong (step));
return 0;
fail:
PyErr_SetString (PyExc_KeyError,
"Object is not compatible with Gst.IntRange");
return -1;
}
static PyObject *
gi_gst_int64_range_from_value (const GValue * value)
{
gint64 min, max, step;
min = gst_value_get_int64_range_min (value);
max = gst_value_get_int64_range_max (value);
step = gst_value_get_int64_range_step (value);
return PyObject_CallFunction ((PyObject *) & PyRange_Type, "LLL",
min, max, step);
}
static int
gi_gst_int64_range_to_value (GValue * value, PyObject * object)
{
PyObject *range, *min, *max, *step;
range = PyObject_GetAttrString (object, "range");
if (range == NULL)
goto fail;
min = PyObject_GetAttrString (range, "start");
if (min == NULL)
goto fail;
max = PyObject_GetAttrString (range, "stop");
if (max == NULL)
goto fail;
step = PyObject_GetAttrString (range, "step");
if (step == NULL)
goto fail;
gst_value_set_int64_range_step (value, PyLong_AsLongLong (min),
PyLong_AsLongLong (max), PyLong_AsLongLong (step));
return 0;
fail:
PyErr_SetString (PyExc_KeyError,
"Object is not compatible with Gst.Int64Range");
return -1;
}
static PyObject *
gi_gst_double_range_from_value (const GValue * value)
{
PyObject *module, *dict, *double_range_type, *double_range;
gdouble min, max;
min = gst_value_get_double_range_min (value);
max = gst_value_get_double_range_max (value);
module = PyImport_ImportModule ("gi.repository.Gst");
if (module == NULL) {
PyErr_SetString (PyExc_KeyError,
"Could not get module for gi.repository.Gst");
return NULL;
}
dict = PyModule_GetDict (module);
Py_DECREF (module);
/* For some reson we need this intermediary step */
module = PyMapping_GetItemString (dict, "_overrides_module");
if (module == NULL) {
PyErr_SetString (PyExc_KeyError,
"Could not get module for _overrides_module");
return NULL;
}
dict = PyModule_GetDict (module);
double_range_type = PyMapping_GetItemString (dict, "DoubleRange");
double_range = PyObject_CallFunction (double_range_type, "dd", min, max);
Py_DECREF (double_range_type);
Py_DECREF (module);
return double_range;
}
static int
gi_gst_double_range_to_value (GValue * value, PyObject * object)
{
PyObject *min, *max;
min = PyObject_GetAttrString (object, "start");
if (min == NULL)
goto fail;
max = PyObject_GetAttrString (object, "stop");
if (max == NULL)
goto fail;
gst_value_set_double_range (value, PyFloat_AsDouble (min),
PyFloat_AsDouble (max));
return 0;
fail:
PyErr_SetString (PyExc_KeyError,
"Object is not compatible with Gst.DoubleRange");
return -1;
}
static PyObject *
gi_gst_fraction_range_from_value (const GValue * value)
{
PyObject *module, *min, *max, *dict, *fraction_range_type, *fraction_range;
const GValue *fraction;
fraction = gst_value_get_fraction_range_min (value);
min = gi_gst_fraction_from_value (fraction);
fraction = gst_value_get_fraction_range_max (value);
max = gi_gst_fraction_from_value (fraction);
module = PyImport_ImportModule ("gi.repository.Gst");
if (module == NULL) {
PyErr_SetString (PyExc_KeyError,
"Could not get module for gi.repository.Gst");
return NULL;
}
dict = PyModule_GetDict (module);
Py_DECREF (module);
/* For some reson we need this intermediary step */
module = PyMapping_GetItemString (dict, "_overrides_module");
if (module == NULL) {
PyErr_SetString (PyExc_KeyError,
"Could not get module for _overrides_module");
return NULL;
}
dict = PyModule_GetDict (module);
fraction_range_type = PyMapping_GetItemString (dict, "FractionRange");
fraction_range = PyObject_CallFunction (fraction_range_type, "OO", min, max);
Py_DECREF (fraction_range_type);
Py_DECREF (module);
return fraction_range;
}
static int
gi_gst_fraction_range_to_value (GValue * value, PyObject * object)
{
PyObject *min, *max;
GValue vmin = G_VALUE_INIT, vmax = G_VALUE_INIT;
min = PyObject_GetAttrString (object, "start");
if (min == NULL)
goto fail;
max = PyObject_GetAttrString (object, "stop");
if (max == NULL)
goto fail;
g_value_init (&vmin, GST_TYPE_FRACTION);
if (gi_gst_fraction_to_value (&vmin, min) < 0)
goto fail;
g_value_init (&vmax, GST_TYPE_FRACTION);
if (gi_gst_fraction_to_value (&vmax, max) < 0) {
g_value_unset (&vmin);
goto fail;
}
gst_value_set_fraction_range (value, &vmin, &vmax);
g_value_unset (&vmin);
g_value_unset (&vmax);
return 0;
fail:
PyErr_SetString (PyExc_KeyError,
"Object is not compatible with Gst.FractionRange");
return -1;
}
static PyObject *
gi_gst_array_from_value (const GValue * value)
{
PyObject *list;
gint i;
list = PyList_New (gst_value_array_get_size (value));
for (i = 0; i < gst_value_array_get_size (value); i++) {
const GValue *v = gst_value_array_get_value (value, i);
PyList_SET_ITEM (list, i, pyg_value_as_pyobject (v, TRUE));
}
return list;
}
static int
gi_gst_array_to_value (GValue * value, PyObject * object)
{
gint len, i;
len = PySequence_Length (object);
for (i = 0; i < len; i++) {
GValue v = G_VALUE_INIT;
GType type;
PyObject *item;
item = PySequence_GetItem (object, i);
if (item == Py_None)
type = G_TYPE_POINTER;
else
type = pyg_type_from_object ((PyObject *) Py_TYPE (item));
if (type == G_TYPE_NONE) {
Py_DECREF (item);
goto fail;
}
g_value_init (&v, type);
if (pyg_value_from_pyobject (&v, item) < 0) {
Py_DECREF (item);
goto fail;
}
gst_value_array_append_and_take_value (value, &v);
Py_DECREF (item);
}
return 0;
fail:
PyErr_SetString (PyExc_KeyError,
"Object is not compatible with Gst.ValueArray");
return -1;
}
static PyObject *
gi_gst_list_from_value (const GValue * value)
{
PyObject *list;
gint i;
list = PyList_New (gst_value_list_get_size (value));
for (i = 0; i < gst_value_list_get_size (value); i++) {
const GValue *v = gst_value_list_get_value (value, i);
PyList_SET_ITEM (list, i, pyg_value_as_pyobject (v, TRUE));
}
return list;
}
static int
gi_gst_list_to_value (GValue * value, PyObject * object)
{
gint len, i;
len = PySequence_Length (object);
for (i = 0; i < len; i++) {
GValue v = G_VALUE_INIT;
GType type;
PyObject *item;
item = PySequence_GetItem (object, i);
if (item == Py_None)
type = G_TYPE_POINTER;
else
type = pyg_type_from_object ((PyObject *) Py_TYPE (item));
if (type == G_TYPE_NONE) {
Py_DECREF (item);
goto fail;
}
g_value_init (&v, type);
if (pyg_value_from_pyobject (&v, item) < 0) {
Py_DECREF (item);
goto fail;
}
gst_value_list_append_and_take_value (value, &v);
Py_DECREF (item);
}
return 0;
fail:
PyErr_SetString (PyExc_KeyError,
"Object is not compatible with Gst.ValueList");
return -1;
}
void
gi_gst_register_types (PyObject * d)
{
pyg_register_gtype_custom (GST_TYPE_FRACTION,
gi_gst_fraction_from_value, gi_gst_fraction_to_value);
pyg_register_gtype_custom (GST_TYPE_INT_RANGE,
gi_gst_int_range_from_value, gi_gst_int_range_to_value);
pyg_register_gtype_custom (GST_TYPE_INT64_RANGE,
gi_gst_int64_range_from_value, gi_gst_int64_range_to_value);
pyg_register_gtype_custom (GST_TYPE_DOUBLE_RANGE,
gi_gst_double_range_from_value, gi_gst_double_range_to_value);
pyg_register_gtype_custom (GST_TYPE_FRACTION_RANGE,
gi_gst_fraction_range_from_value, gi_gst_fraction_range_to_value);
pyg_register_gtype_custom (GST_TYPE_ARRAY,
gi_gst_array_from_value, gi_gst_array_to_value);
pyg_register_gtype_custom (GST_TYPE_LIST,
gi_gst_list_from_value, gi_gst_list_to_value);
#if 0
/* TODO */
pyg_register_gtype_custom (GST_TYPE_DATE_TIME,
gi_gst_date_time_from_value, gi_gst_date_time_to_value);
pyg_register_gtype_custom (GST_TYPE_FLAG_SET,
gi_gst_flag_set_from_value, gi_gst_flag_set_to_value);
pyg_register_gtype_custom (GST_TYPE_BITMASK,
gi_gst_bitmask_from_value, gi_gst_bitmask_to_value);
#endif
}
static int
......
......@@ -3,7 +3,13 @@
# Keep this list sorted!
tests = \
test_gst.py \
test_fraction.py
test_fraction.py \
test_intrange.py \
test_int64range.py \
test_doublerange.py \
test_fractionrange.py \
test_valuearray.py \
test_valuelist.py
EXTRA_DIST = \
__init__.py \
......
......@@ -32,7 +32,8 @@ import gc
import unittest
import gi.overrides
import gi
gi.require_version("Gst", "1.0")
from gi.repository import Gst
......
......@@ -2,7 +2,13 @@ runtests = find_program('runtests.py')
tests = [
['Test gst', 'test_gst.py'],
['Test fractions', 'test_fraction.py']
['Test fractions', 'test_fraction.py'],
['Test integer ranges', 'test_intrange.py'],
['Test 64bit integer ranges', 'test_int64range.py'],
['Test double ranges', 'test_doublerange.py'],
['Test fraction ranges', 'test_fractionrange.py'],
['Test value arrays', 'test_valuearray.py'],
['Test value lists', 'test_valuelist.py']
]
pluginsdirs = []
......
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
#
# gst-python - Python bindings for GStreamer
# Copyright (C) 2007 Johan Dahlin
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
import overrides_hack
overrides_hack
from common import TestCase
import gi
gi.require_version("Gst", "1.0")
from gi.repository import Gst
Gst.init(None)
R = Gst.DoubleRange
class TestDoubleRange(TestCase):
def testConstructor(self):
Gst.init(None)
r = R(1.2, 3.4)
self.assertEquals(r.start, 1.2)
self.assertEquals(r.stop, 3.4)
self.assertRaises(TypeError, R, {}, 2)
self.assertRaises(TypeError, R, 2, ())
self.assertRaises(TypeError, R, 2, 1)
self.assertRaises(TypeError, R)
def testRepr(self):
Gst.init(None)
self.assertEquals(repr(R(1,2)), '<Gst.DoubleRange [1.0,2.0]>')
def testGetValue(self):
Gst.init(None)
st = Gst.Structure.new_empty("video/x-raw")
st["range"] = R(1,2)
value = st["range"]
self.failUnlessEqual(value.start, 1.0)
self.failUnlessEqual(value.stop, 2.0)
......@@ -98,9 +98,12 @@ class TestFraction(TestCase):
def testPropertyMarshalling(self):
Gst.init(None)
obj = Gst.ElementFactory.make("videoparse")
obj = Gst.ElementFactory.make("rawvideoparse")
if not obj:
# no videoparse and I don't know of any elements in core or -base using
obj = Gst.ElementFactory.make("rawvideoparse")
if not obj:
# no (raw)videoparse and I don't know of any elements in core or -base using
# fraction properties. Skip this test.
return
......
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4
#
# gst-python - Python bindings for GStreamer
# Copyright (C) 2007 Johan Dahlin
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
import overrides_hack
overrides_hack
from common import TestCase
import gi
gi.require_version("Gst", "1.0")
from gi.repository import Gst
Gst.init(None)