Backport from 0.8 branch and added new .defs file from GStreamer 0.9

Original commit message from CVS:
Backport from 0.8 branch and added new .defs file from GStreamer 0.9
This commit is contained in:
Edward Hervey 2005-06-17 10:59:47 +00:00
parent f2997e137d
commit c68afb5f53
35 changed files with 5033 additions and 2786 deletions

347
ChangeLog
View file

@ -1,3 +1,350 @@
2005-06-17 Edward Hervey <bilboed@bilboed.com>
* Backport of bugfixes/new features from 0.8 branch to HEAD
* gst/gst.defs:
* gst/gst-types.defs:
New defs files generated from 0.9 GStreamer
PORTING-TODO will follow
2005-06-17 Andy Wingo <wingo@pobox.com>
* gst/gstcaps.override (_wrap_gst_caps_tp_richcompare): Increment
the refcount of the return value in all cases, even for Py_True
and Py_False. Lines of code per day hovering around 1.3.
2005-06-14 Edward Hervey <bilboed@bilboed.com>
* gst/gstcaps.override: (_wrap_gst_caps_tp_str):
reverting previous fix, was incorrect
* configure.ac:
0.8.1.2 pre-release
* gst/gst.override: (probe_handler_marshal):
bye bye probe memleak
2005-06-14 Edward Hervey <bilboed@bilboed.com>
* gst/Makefile.am: (defs_DATA) (GST_OVERRIDES):
* gst/gst.defs:
* gst/gst.override:
* gst/gst-extrafuncs.defs:
* gst/gstbuffer.override:
* gst/gstevent.override:
* gst/gst-types.defs: (Buffer) (Event):
Added support for element flags (list, set, unset)
Added gst.Buffer duration field
Added gst.Event fields
2005-06-14 Andy Wingo <wingo@pobox.com>
* gst/gstcaps.override (_wrap_gst_caps_tp_richcompare): Fixes for
== and != when the second arg is not a caps.
* testsuite/test_caps.py (CapsTest.testComparisons): Fix name.
2005-06-14 Edward Hervey <bilboed@bilboed.com>
* gst/gstcaps.override: (_wrap_gst_caps_tp_str):
proper repr of GstCaps : <GstCaps at <adress> : '<caps.to_string>'>
2005-06-09 Edward Hervey <bilboed@bilboed.com>
* examples/gst/gstfile.py: (Discoverer.discover):
whoops, pipeline was being run twice :)
2005-06-09 Zaheer Abbas Merali <zaheerabbas at merali dot org>
* examples/gst/gstfile.py: fix typo
2005-06-08 Edward Hervey <bilboed@bilboed.com>
* examples/gst/Makefile.am:
* examples/gst/gstfile.py:
renamed gst-file.py to gstfile.py and made the Discoverer class useable
in other applications/scripts
* examples/gst/audioconcat.py:
Example application that uses the Discoverer and the gnonlin elements to
concatenate several audio files to one single one
2005-06-07 Edward Hervey <bilboed@bilboed.com>
* examples/gst/Makefile.am:
* examples/gst/gst-file.py:
New example that shows how to use gst-python in an object-oriented way,
and how to override existing methods
2005-06-02 Edward Hervey <edward@fluendo.com>
* gst/gstbuffer.override (_wrap_gst_buffer_set_data):
gcc 4.x bugfixes
2005-05-04 Benjamin Otte <in7y118@public.uni-hamburg.de>
* gst/gst.defs:
factory.create doesn't need a name
* gst/gst.override:
implement tp_str and tp_repr gst.Object and gst.PluginFeature
* gst/gstelement.override:
implement factory.get_pad_templates and make element.link_pads
take NULL, pads or strings as the pad argument
* gst/gstpad.override:
implement tp_getattr for GstPadTemplate
2005-05-03 Zaheer Abbas Merali <zaheerabbas at merali dot org>
* gst/pygstvalue.c: (pygst_value_from_pyobject):
stupid, stupid, stupid
2005-05-03 Benjamin Otte <in7y118@public.uni-hamburg.de>
* gst/gstcaps.override:
fix uninitialized warning and refator function
2005-05-03 Benjamin Otte <in7y118@public.uni-hamburg.de>
* gst/pygstvalue.c:
* gst/gst-argtypes.c:
* gst/gst.override:
define NO_IMPORT_PYGOBJECT to have a correct extern declaration
reenable _pygst_element_init and ifdef it correctly
* gst/arg-types.py:
check errors
* gst/gstcaps.override:
- remove list append functionality. Sets don't support the +
operator
- implement richcompare and coerce, nonzero, or, xor and subtract
number functions
* testsuite/test_caps.py:
add tests for the new stuff
2005-05-03 Thomas Vander Stichele <thomas at apestaart dot org>
* testsuite/Makefile.am:
* testsuite/common.py:
rework common so that it behaves a bit more sanely
make the inner workings more clear
2005-05-03 Thomas Vander Stichele <thomas at apestaart dot org>
* configure.ac:
add Wall and Werror just like other modules
* gst/gst.override:
* gst/gstelement.override:
fix compile problems for Wall Werror
* testsuite/common.py:
* Makefile.am:
* gst/Makefile.am:
* gst/ltihooks.py:
* ltihooks.py:
move ltihooks to root; having it in gst was a bad hack and
breaks distcheck
2005-05-03 Thomas Vander Stichele <thomas at apestaart dot org>
* testsuite/common.py: ltihooks only needed for uninstalled (?)
2005-05-03 Thomas Vander Stichele <thomas at apestaart dot org>
* testsuite/runtests.py: a much nicer way of getting all the tests
2005-05-03 Benjamin Otte <in7y118@public.uni-hamburg.de>
* testsuite/Makefile.am:
run tests with GST_DEBUG=*:0 so they don't produce any debugging
output
* testsuite/test_element.py:
don't disable stderr
* gst/gstbuffer.override:
initialize size correctly
2005-05-03 Thomas Vander Stichele <thomas at apestaart dot org>
* testsuite/runtests.py: also return exit code 1 in case of errors
2005-05-02 Edward Hervey <bilboed@bilboed.com>
* testsuite/common.py: Added case for 64bit-system that don't have
the dl module
2005-05-02 Benjamin Otte <in7y118@public.uni-hamburg.de>
* gst/Makefile.am:
running the code generator depends on the files it uses
* gst/arg-types.py:
add GstCaps parsing
* gst/gstelement.override:
make element.link take an optional caps parameter, deprecate
element.link_filtered
* testsuite/test_caps.py:
replace caps.get_strcuture(i) calls with caps[i], the deprecation
warnings are annoying
2005-05-01 Thomas Vander Stichele <thomas at apestaart dot org>
* testsuite/test_interface.py:
do not do stuff in a testsuite that isn't guaranteed to be there.
could do with some way of detecting and skipping.
2005-05-01 Benjamin Otte <in7y118@public.uni-hamburg.de>
* gst/gst-types.c:
remove, it's called gst-argtypes.c these days
* gst/common.h:
* gst/gst-argtypes.c: (pygst_caps_from_pyobject):
add function to transform a PyObject to a GstCaps*
* gst/gstcaps.override:
implement more sequence functions, deprecate caps.get_structure() in
favour of caps[i] and make the structure-by-reference stuff work
* gst/gststructure.override:
make structures from caps work by reference but don't die a horrible
death if the caps get removed before the structure is
* testsuite/Makefile.am:
run with G_DEBUG=fatal_warnings so we crash on g_warnings in C code
* testsuite/test_caps.py:
add some checks
2005-04-30 Benjamin Otte <otte@gnome.org>
* gst/gst.override:
pyg_register_class_init is new API, so only use it when available.
If not available gst.element_register won't work either, but I just
won't care about that right now.
2005-04-30 Benjamin Otte <otte@gnome.org>
* gst/arg-types.py:
ref the GstData we get in conversions as pygst_data_from_pyobject
doesn't do that
* gst/gst-types.c: (pygst_data_from_pyobject),
(pygst_data_to_pyobject), (PyGstData_from_value),
(PyGstData_to_value):
make GstData <=> GValue conversions use the correct G_VALUE_TYPE and
don't copy the values, but just ref them. Avoids mad buffer copying
* gst/common.h:
include GstData <=> PyObject conversion funcs
* gst/gst.override:
- wrap gst.element_register
- add _pygst_get_plugin to query the plugin used by Python.
Necessary preparation for a Python plugin loader. Returns NULL if
no plugin, in that case gst.element_register registers the element
as part of the running app.
- add a class initializer for gst.Element subtypes, that checks the
variables __gsttemplates__ and __gstdetails__ and makes the correct
gst_element_class_set_element_details and
gst_element_class_add_pad_templates calls from them. Note: This
has issues when virtual methods are enabled.
- add _pygst_element_check_error. This functions checks if Python
code raised an exception and if so calls gst_element_error on the
element and clears the error. Very useful in
loop/chain/get-functions
* gst/gstbuffer.override:
- implement buffer.copy_on_write()
- implement sq_(ass_)[item|slice] functions for GstBuffer
- fix PyBufferProcs to allow writing when buffer is writable
* gst/gstpad.override:
- wrap gst.Pad to call gst_pad_new or gst_pad_new_from_template
automatically based on arguments
- don't attach private pad data to the element private, use
g_object_set_data instead
- make attaching functions to pads use closures. Allows garbage
collecting pads where handlers have been set
- fix _repr function to only print parent element's name if parent
exists
* gst/gstmodule.c: (init_gst):
register GstData <=> GValue conversion
2005-04-28 Benjamin Otte <in7y118@public.uni-hamburg.de>
* gst/gstbuffer.override:
Let's unref the buffer we created - prevents gst.Buffer from leaking
2005-04-27 Benjamin Otte <in7y118@public.uni-hamburg.de>
* gst/gstbuffer.override:
throw TypeError if bufsize < data size. Fixes testsuite
2005-04-26 Johan Dahlin <jdahlin@async.com.br>
* testsuite/runtests.py: Return exit code 1 if we have any failures
2005-04-26 Benjamin Otte <in7y118@public.uni-hamburg.de>
* gst/gstbuffer.override:
make Buffer() not use memory it doesn't own
* testsuite/test_buffer.py:
check that it doesn't
2005-04-19 Edward Hervey <bilboed@bilboed.com>
* gst/gststructure.override: (_wrap_gst_structure_get_double):
Forgot to convert the returned value to a PyFloat...
2005-04-19 Edward Hervey <bilboed@bilboed.com>
* gst/gststructure.override: (_wrap_gst_structure_get_double):
Added overriding function for getting double values from a gst.Structure
2005-04-16 Thomas Vander Stichele <thomas at apestaart dot org>
* configure.ac: bumped back required version of pygtk; we can backport
or copy over the code generator for what we need
2005-04-16 Jan Schmidt <thaytan@mad.scientist.com>
* configure.ac:
Bump GStreamer core requirement to 0.8.9 for
gst_pad_set_active_recursive.
* gst/gst.defs:
Wrap gst_pad_set_active_recursive
2005-04-14 Edward Hervey <bilboed@bilboed.com>
* gst/gst.defs:
Added definitions for methods of GstObject, GstElement, GstBin and Clock
so you can create derived classes with more functionnalities.
* configure.ac: (ACLOCAL):
bumped required version of PYGTK to 2.6.0
2005-04-14 Andy Wingo <wingo@pobox.com>
* gst/__init__.py (Value, Fourcc, IntRange, DoubleRange)
(Fraction): New classes.
* gst/gst.override (_wrap_gst_tag_list_get)
(_wrap_gst_tag_list_get_value_index)
(tag_foreach_func_dict)
(_wrap_gst_tag_list_subscript): Use gstvalue procs.
(init): Initialize the pygstvalue stuff (which involves accessing
definitions from gst/__init__.py).
* gst/gststructure.override (_wrap_gst_structure_ass_subscript):
Just use the pygstvalue functions to do conversion. As it is right
now, it's an incompatible change with the previous behavior,
because it doesn't try to parse field values like "(fourcc)XVID".
We can restore that previous behaviour if there is a need for it.
(_wrap_gst_structure_subscript): Use gstvalue functions to do the
conversion.
* gst/Makefile.am: Add in pygstvalue.[ch].
* gst/pygstvalue.h:
* gst/pygstvalue.c: New files.
(pygst_value_as_pyobject): Wraps pyg_value_as_pyobject, handling
gstvalues as well.
(pygst_value_init_for_pyobject): Sniffs a pyobject for a type and
initializes a GValue appropriately.
(pygst_value_from_pyobject): Wraps pyg_value_from_pyobject,
handling gvalues as well.
* testsuite/test_struct.py (StructureTest.testStructureChange)
(StructureTest.testGstValue): Add some tests for GstValue wrapping.
2005-04-25 Benjamin Otte <in7y118@public.uni-hamburg.de>
* gst/gstbuffer.override:

View file

@ -11,6 +11,7 @@ SUBDIRS = \
DIST_SUBDIRS = $(UNCONDDIRS) docs
EXTRA_DIST = \
ltihooks.py \
gst-python.spec.in \
gst-python.spec \
README-docs \

2
common

@ -1 +1 @@
Subproject commit 495d6e30b3e513aebbc98467707c609c49ea654d
Subproject commit d6e46b214fac0ecb46010ff522af2f7653e1c18e

View file

@ -26,7 +26,8 @@ dnl required versions of other packages
AC_SUBST(PYGTK_REQ, 2.4.0)
AC_SUBST(GLIB_REQ, 2.0.0)
AC_SUBST(GTK_REQ, 2.0.0)
AC_SUBST(GST_REQ, 0.8.8.1)
AC_SUBST(GST_REQ, 0.8.9)
AC_DISABLE_STATIC
AC_PROG_LIBTOOL
@ -145,6 +146,19 @@ esac])
AM_CONDITIONAL(BUILD_DOCS, test "x$BUILD_DOCS" = "xyes")
dnl decide on error flags
dnl if we support -Wall, set it unconditionally
AS_COMPILER_FLAG(-Wall,
PYGST_ERROR_CFLAGS="-Wall",
PYGST_ERROR_CFLAGS="")
dnl if we're in nano >= 1, add -Werror if supported
if test "x$GST_CVS" = "xyes"
then
AS_COMPILER_FLAG(-Werror, PYGST_ERROR_CFLAGS="$PYGST_ERROR_CFLAGS -Werror")
fi
PYGST_CFLAGS="$PYGST_ERROR_CFLAGS"
AC_SUBST(PYGST_CFLAGS)
dnl add debugging options ...
changequote(,)dnl

View file

@ -6,6 +6,8 @@ examples_DATA = \
filesrc.py \
gst123 \
play.py \
vorbisplay.py
vorbisplay.py \
gstfile.py \
audioconcat.py
EXTRA_DIST = $(examples_DATA)

128
examples/gst/audioconcat.py Normal file
View file

@ -0,0 +1,128 @@
#!/usr/bin/env python
# audio concat tool
# takes in one or more audio files and creates one audio file of the combination
# Uses the gnonlin elements (http://gnonlin.sf.net/)
import os
import sys
import gobject
import gst
from gstfile import Discoverer, time_to_string
class AudioSource(gst.Bin):
"""A bin for audio sources with proper audio converters"""
def __init__(self, filename, caps):
gst.Bin.__init__(self)
self.filename = filename
self.outcaps = caps
self.filesrc = gst.element_factory_make("filesrc")
self.filesrc.set_property("location", self.filename)
self.dbin = gst.element_factory_make("decodebin")
self.ident = gst.element_factory_make("identity")
self.audioconvert = gst.element_factory_make("audioconvert")
self.audioscale = gst.element_factory_make("audioscale")
self.add_many(self.filesrc, self.dbin, self.ident,
self.audioconvert, self.audioscale)
self.filesrc.link(self.dbin)
self.audioconvert.link(self.audioscale)
self.audioscale.link(self.ident, caps)
self.add_ghost_pad(self.ident.get_pad("src"), "src")
self.dbin.connect("new-decoded-pad", self._new_decoded_pad_cb)
def _new_decoded_pad_cb(self, dbin, pad, is_last):
if not "audio" in pad.get_caps().to_string():
return
pad.link(self.audioconvert.get_pad("sink"))
gobject.type_register(AudioSource)
class AudioConcat(gst.Thread):
"""A Gstreamer thread that concatenates a series of audio files to another audio file"""
def __init__(self, infiles, outfile, audioenc="rawvorbisenc", muxer="oggmux"):
gst.Thread.__init__(self)
self.infiles = infiles
self.outfile = outfile
self.audioenc = gst.element_factory_make(audioenc)
if not self.audioenc:
raise NameError, str(audioenc + " audio encoder is not available")
self.muxer = gst.element_factory_make(muxer)
if not self.muxer:
raise NameError, str(muxer + " muxer is not available")
self.filesink = gst.element_factory_make("filesink")
self.filesink.set_property("location", self.outfile)
self.timeline = gst.element_factory_make("gnltimeline")
self.audiocomp = gst.element_factory_make("gnlcomposition", "audiocomp")
self.audioconvert = gst.element_factory_make("audioconvert")
self.add_many(self.timeline, self.audioconvert,
self.audioenc, self.muxer, self.filesink)
## identity perfect stream check !
identity = gst.element_factory_make("identity")
identity.set_property("check-perfect", True)
self.add(identity)
#self.audioconvert.link(self.audioenc)
if not self.audioconvert.link(identity):
print "couldn't link audioconv -> ident"
if not identity.link(self.audioenc):
print "couldn't link ident -> audioenc"
self.audioenc.link(self.muxer)
self.muxer.link(self.filesink)
self.timeline.add(self.audiocomp)
caps = gst.caps_from_string("audio/x-raw-int,channels=2,rate=44100,depth=16")
pos = 0L
for infile in self.infiles:
d = Discoverer(infile)
if not d.audiolength:
continue
print "file", infile, "has length", time_to_string(d.audiolength)
asource = AudioSource(infile, caps)
gnlsource = gst.element_factory_make("gnlsource")
gnlsource.set_property("element", asource)
gnlsource.set_property("media-start", 0L)
gnlsource.set_property("media-stop", d.audiolength)
gnlsource.set_property("start", pos)
gnlsource.set_property("stop", pos + d.audiolength)
self.audiocomp.add(gnlsource)
pos += d.audiolength
self.timeline.get_pad("src_audiocomp").link(self.audioconvert.get_pad("sink"))
timelineprobe = gst.Probe(False, self.timelineprobe)
self.timeline.get_pad("src_audiocomp").add_probe(timelineprobe)
def timelineprobe(self, probe, data):
if isinstance(data, gst.Buffer):
print "timeline outputs buffer", data.timestamp, data.duration
else:
print "timeline ouputs event", data.type
return True
gobject.type_register(AudioConcat)
def eos_cb(pipeline):
sys.exit()
if __name__ == "__main__":
if len(sys.argv) < 3:
print "Usage : %s <input file(s)> <output file>" % sys.argv[0]
print "\tCreates an ogg file from all the audio input files"
sys.exit()
if not gst.element_factory_make("gnltimeline"):
print "You need the gnonlin elements installed (http://gnonlin.sf.net/)"
sys.exit()
concat = AudioConcat(sys.argv[1:-1], sys.argv[-1])
concat.connect("eos", eos_cb)
concat.set_state(gst.STATE_PLAYING)
gst.main()

241
examples/gst/gstfile.py Normal file
View file

@ -0,0 +1,241 @@
#!/usr/bin/env python
# gstfile.py
# (c) 2005 Edward Hervey <edward at fluendo dot com>
# Discovers and prints out multimedia information of files
# This example shows how to use gst-python:
# _ in an object-oriented way (Discoverer class)
# _ subclassing a gst.Pipeline
# _ and overidding existing methods (do_iterate())
import os
import sys
import gobject
import gst
def time_to_string(value):
"""
transform a value in nanoseconds into a human-readable string
"""
ms = value / gst.MSECOND
sec = ms / 1000
ms = ms % 1000
min = sec / 60
sec = sec % 60
return "%2dm %2ds %3d" % (min, sec, ms)
class Discoverer(gst.Pipeline):
"""
Discovers information about files
"""
mimetype = None
audiocaps = {}
videocaps = {}
videowidth = 0
videoheight = 0
videorate = 0
audiofloat = False
audiorate = 0
audiodepth = 0
audiowidth = 0
audiochannels = 0
audiolength = 0L
videolength = 0L
is_video = False
is_audio = False
otherstreams = []
finished = False
tags = {}
def __init__(self, filename):
gobject.GObject.__init__(self)
self.mimetype = None
self.audiocaps = {}
self.videocaps = {}
self.videowidth = 0
self.videoheight = 0
self.videorate = 0
self.audiofloat = False
self.audiorate = 0
self.audiodepth = 0
self.audiowidth = 0
self.audiochannels = 0
self.audiolength = 0L
self.videolength = 0L
self.is_video = False
self.is_audio = False
self.otherstreams = []
self.finished = False
self.tags = {}
if not os.path.isfile(filename):
self.finished = True
return
# the initial elements of the pipeline
self.src = gst.element_factory_make("filesrc")
self.src.set_property("location", filename)
self.src.set_property("blocksize", 1000000)
self.dbin = gst.element_factory_make("decodebin")
self.add_many(self.src, self.dbin)
self.src.link(self.dbin)
self.typefind = self.dbin.get_by_name("typefind")
# callbacks
self.typefind.connect("have-type", self._have_type_cb)
self.dbin.connect("new-decoded-pad", self._new_decoded_pad_cb)
self.dbin.connect("unknown-type", self._unknown_type_cb)
self.dbin.connect("found-tag", self._found_tag_cb)
self.discover()
def discover(self):
"""iterate on ourself to find the information on the given file"""
if self.finished:
return
self.set_state(gst.STATE_PLAYING)
while 1:
if not self.iterate():
break
self.set_state(gst.STATE_NULL)
self.finished = True
def print_info(self):
"""prints out the information on the given file"""
if not self.finished:
self.discover()
if not self.mimetype:
print "Unknown media type"
return
print "Mime Type :\t", self.mimetype
if not self.is_video and not self.is_audio:
return
print "Length :\t", time_to_string(max(self.audiolength, self.videolength))
print "\tAudio:", time_to_string(self.audiolength), "\tVideo:", time_to_string(self.videolength)
if self.is_video:
print "Video :"
print "\t%d x %d @ %.2f fps" % (self.videowidth,
self.videoheight,
self.videorate)
if self.tags.has_key("video-codec"):
print "\tCodec :", self.tags.pop("video-codec")
if self.is_audio:
print "Audio :"
if self.audiofloat:
print "\t%d channels(s) : %dHz @ %dbits (float)" % (self.audiochannels,
self.audiorate,
self.audiowidth)
else:
print "\t%d channels(s) : %dHz @ %dbits (int)" % (self.audiochannels,
self.audiorate,
self.audiodepth)
if self.tags.has_key("audio-codec"):
print "\tCodec :", self.tags.pop("audio-codec")
for stream in self.otherstreams:
if not stream == self.mimetype:
print "Other unsuported Multimedia stream :", stream
if self.tags:
print "Additional information :"
for tag in self.tags.keys():
print "%20s :\t" % tag, self.tags[tag]
def _unknown_type_cb(self, dbin, pad, caps):
self.otherstreams.append(caps.to_string())
def _have_type_cb(self, typefind, prob, caps):
self.mimetype = caps.to_string()
def _notify_caps_cb(self, pad, args):
caps = pad.get_negotiated_caps()
if not caps:
return
# the caps are fixed
# We now get the total length of that stream
length = pad.get_peer().query(gst.QUERY_TOTAL, gst.FORMAT_TIME)
# We store the caps and length in the proper location
if "audio" in caps.to_string():
self.audiocaps = caps
self.audiolength = length
self.audiorate = caps[0]["rate"]
self.audiowidth = caps[0]["width"]
self.audiochannels = caps[0]["channels"]
if "x-raw-float" in caps.to_string():
self.audiofloat = True
else:
self.audiodepth = caps[0]["depth"]
if (not self.is_video) or self.videocaps:
self.finished = True
elif "video" in caps.to_string():
self.videocaps = caps
self.videolength = length
self.videowidth = caps[0]["width"]
self.videoheight = caps[0]["height"]
self.videorate = caps[0]["framerate"]
if (not self.is_audio) or self.audiocaps:
self.finished = True
def _new_decoded_pad_cb(self, dbin, pad, is_last):
# Does the file contain got audio or video ?
if "audio" in pad.get_caps().to_string():
self.is_audio = True
elif "video" in pad.get_caps().to_string():
self.is_video = True
if is_last and not self.is_video and not self.is_audio:
self.finished = True
return
# we connect a fakesink to the new pad...
fakesink = gst.element_factory_make("fakesink")
self.add(fakesink)
sinkpad = fakesink.get_pad("sink")
# ... and connect a callback for when the caps are fixed
sinkpad.connect("notify::caps", self._notify_caps_cb)
pad.link(sinkpad)
fakesink.set_state(gst.STATE_PLAYING)
def _found_tag_cb(self, dbin, source, tags):
self.tags.update(tags)
def do_iterate(self):
# this overrides the GstBin 'iterate' method
# if we have finished discovering we stop the iteration
if self.finished:
return False
# else we call the parent class method
return gst.Pipeline.do_iterate(self)
gobject.type_register(Discoverer)
def main(args):
if len(args) < 2:
print 'usage: %s files...' % args[0]
return 2
if len(args[1:]) > 1:
for filename in args[1:]:
print "File :", filename
Discoverer(filename).print_info()
print "\n"
else:
Discoverer(args[1]).print_info()
if __name__ == '__main__':
sys.exit(main(sys.argv))

View file

@ -1,4 +1,4 @@
common_cflags = $(PYGTK_CFLAGS) $(GST_CFLAGS) -fno-strict-aliasing
common_cflags = $(PYGTK_CFLAGS) $(GST_CFLAGS) $(PYGST_CFLAGS) -fno-strict-aliasing
common_libadd = $(GST_LIBS)
common_ldflags = -module -avoid-version
@ -22,20 +22,22 @@ else
play_lib =
endif
defs_DATA = gst-types.defs
defs_DATA = gst-types.defs \
gst-extrafuncs.defs
defsdir = $(pkgdatadir)/2.0/defs
noinst_HEADERS = common.h
noinst_HEADERS = common.h pygstvalue.h
INCLUDES = $(PYTHON_INCLUDES)
EXTRA_DIST = $(defs_DATA) common.h arg-types.py ltihooks.py
EXTRA_DIST = $(defs_DATA) common.h arg-types.py
PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
GEN_FILES = arg-types.py gst-types.defs
# GStreamer bindings
_gst_la_CFLAGS = $(common_cflags)
_gst_la_LIBADD = $(common_libadd)
_gst_la_LDFLAGS = $(common_ldflags) -export-symbols-regex init_gst
_gst_la_SOURCES = gst-argtypes.c gstmodule.c
_gst_la_SOURCES = gst-argtypes.c gstmodule.c pygstvalue.c
nodist__gst_la_SOURCES = gst.c
GST_OVERRIDES = \
gst.override \
@ -43,13 +45,14 @@ GST_OVERRIDES = \
gstbuffer.override \
gstcaps.override \
gstelement.override \
gstevent.override \
gstpad.override \
gststructure.override
GST_DEFS = gst.defs gst-types.defs
GST_DEFS = gst.defs gst-types.defs gst-extrafuncs.defs
CLEANFILES = gst.c
EXTRA_DIST += $(GST_DEFS) $(GST_OVERRIDES)
gst.c: $(GST_DEFS) $(GST_OVERRIDES)
gst.c: $(GST_DEFS) $(GST_OVERRIDES) $(GEN_FILES)
# gst-play bindings
play_la_CFLAGS = $(common_cflags) $(GST_PLAY_CFLAGS)
@ -61,7 +64,7 @@ PLAY_OVERRIDES = play.override
PLAY_DEFS = play.defs
CLEANFILES += play.c
EXTRA_DIST += $(PLAY_DEFS) $(PLAY_OVERRIDES)
play.c: $(PLAY_DEFS) $(PLAY_OVERRIDES)
play.c: $(PLAY_DEFS) $(PLAY_OVERRIDES) $(GEN_FILES)
# GStreamer interfaces bindings
interfaces_la_CFLAGS = $(common_cflags) $(GST_INTERFACES_CFLAGS)
@ -73,9 +76,9 @@ INTERFACES_OVERRIDES = interfaces.override xoverlay.override
INTERFACES_DEFS = interfaces.defs xoverlay.defs xwindowlistener.defs
CLEANFILES += interfaces.c
EXTRA_DIST += $(INTERFACES_DEFS) $(INTERFACES_OVERRIDES)
interfaces.c: $(INTERFACES_DEFS) $(INTERFACES_OVERRIDES)
interfaces.c: $(INTERFACES_DEFS) $(INTERFACES_OVERRIDES) $(GEN_FILES)
.defs.c:
.defs.c:
(cd $(srcdir) \
&& $(PYGTK_CODEGEN) \
--load-types $(srcdir)/arg-types.py \

View file

@ -36,4 +36,40 @@ try:
except ImportError:
pass
class Value:
def __init__(self, type):
assert type in ('fourcc', 'intrange', 'doublerange', 'fraction')
self.type = type
class Fourcc(Value):
def __init__(self, string):
Value.__init__(self, 'fourcc')
self.fourcc = string
def __repr__(self):
return '<gst.Fourcc %s>' % self.fourcc
class IntRange(Value):
def __init__(self, low, high):
Value.__init__(self, 'intrange')
self.low = low
self.high = high
def __repr__(self):
return '<gst.IntRange [%d, %d]>' % (self.low, self.high)
class DoubleRange(Value):
def __init__(self, low, high):
Value.__init__(self, 'doublerange')
self.low = low
self.high = high
def __repr__(self):
return '<gst.DoubleRange [%f, %f]>' % (self.low, self.high)
class Fraction(Value):
def __init__(self, num, denom):
Value.__init__(self, 'fraction')
self.num = num
self.denom = denom
def __repr__(self):
return '<gst.Fraction %d/%d>' % (self.num, self.denom)
from _gst import *

View file

@ -26,13 +26,15 @@ from argtypes import UInt64Arg, Int64Arg, PointerArg, ArgMatcher, ArgType, match
class GstDataPtrArg(ArgType):
normal = (' if (!pygst_data_from_pyobject(py_%(name)s, &%(name)s))\n'
' return NULL;\n')
' return NULL;\n'
' gst_data_ref (%(name)s);\n')
null = (' if (py_%(name)s == Py_None)\n'
' %(name)s = NULL;\n'
' else if (pyst_data_from_pyobject(py_%(name)s, %(name)s_rect))\n'
' %(name)s = &%(name)s_rect;\n'
' else\n'
' return NULL;\n')
' return NULL;\n'
' gst_data_ref (%(name)s);\n')
def write_param(self, ptype, pname, pdflt, pnull, info):
if pnull:
info.varlist.add('GstData', pname + '_data')
@ -84,7 +86,7 @@ class XmlNodeArg(ArgType):
info.add_parselist('O', ['&py'+pname], [pname])
info.arglist.append(pname)
self.names["name"] = pname
info.codebefore.append(self.parm %self.names)
info.codebefore.append(self.parm % self.names)
info.codeafter.append(self.parmp % self.names);
def write_return(self, ptype, ownsreturn, info):
info.varlist.add('PyObject', '*xml = _gst_get_libxml2_module()')
@ -101,12 +103,66 @@ class XmlDocArg(XmlNodeArg):
"xptr":"xmlDocPtr",
"xwrap":"libxml_xmlDocPtrWrap"}
class GstCapsArg(ArgType):
"""GstCaps node generator"""
before = (' %(name)s = pygst_caps_from_pyobject (py_%(name)s, %(namecopy)s);\n'
' if (PyErr_Occurred())\n'
' return NULL;\n')
beforenull = (' if (py_%(name)s == Py_None)\n'
' %(name)s = NULL;\n'
' else\n'
' ' + before)
after = (' if (%(name)s && %(name)s_is_copy)\n'
' gst_caps_free (%(name)s);\n')
def write_param(self, ptype, pname, pdflt, pnull, info):
if ptype == 'const-GstCaps*':
self.write_const_param(pname, pdflt, pnull, info)
elif ptype == 'GstCaps*':
self.write_normal_param(pname, pdflt, pnull, info)
else:
raise RuntimeError, "write_param not implemented for %s" % ptype
def write_const_param(self, pname, pdflt, pnull, info):
info.varlist.add('PyObject', '*py_'+pname)
info.varlist.add('GstCaps', '*'+pname)
info.varlist.add('gboolean', pname+'_is_copy')
info.add_parselist('O', ['&py_'+pname], [pname])
info.arglist.append(pname)
if pnull:
info.codebefore.append (self.beforenull % { 'name' : pname, 'namecopy' : '&'+pname+'_is_copy' })
else:
info.codebefore.append (self.before % { 'name' : pname, 'namecopy' : '&'+pname+'_is_copy' })
info.codeafter.append (self.after % { 'name' : pname, 'namecopy' : '&'+pname+'_is_copy' })
def write_normal_param(self, pname, pdflt, pnull, info):
info.varlist.add('PyObject', '*py_'+pname)
info.varlist.add('GstCaps', '*'+pname)
info.add_parselist('O', ['&py_'+pname], [pname])
info.arglist.append(pname)
if pnull:
info.codebefore.append (self.beforenull % { 'name' : pname, 'namecopy' : 'NULL' })
else:
info.codebefore.append (self.before % { 'name' : pname, 'namecopy' : 'NULL' })
def write_return(self, ptype, ownsreturn, info):
if ptype == 'GstCaps*':
info.varlist.add('GstCaps', '*ret')
copyval = 'FALSE'
elif ptype == 'const-GstCaps*':
info.varlist.add('const GstCaps', '*ret')
copyval = 'TRUE'
else:
raise RuntimeError, "write_return not implemented for %s" % ptype
info.codeafter.append(' return pyg_boxed_new (GST_TYPE_CAPS, ret, '+copyval+', TRUE);')
matcher.register('GstData*', GstDataPtrArg())
matcher.register('GstClockTime', UInt64Arg())
matcher.register('GstClockTimeDiff', Int64Arg())
matcher.register('xmlNodePtr', XmlNodeArg())
matcher.register('xmlDocPtr', XmlDocArg())
matcher.register('GstCaps', GstCapsArg()) #FIXME: does this work?
matcher.register('GstCaps*', GstCapsArg()) #FIXME: does this work?
matcher.register('const-GstCaps*', GstCapsArg())
arg = PointerArg('gpointer', 'G_TYPE_POINTER')
matcher.register('GstClockID', arg)

View file

@ -23,6 +23,7 @@
#define __COMMON_H__
#include <Python.h>
#include <gst/gst.h>
#include "pygobject.h"
@ -32,14 +33,21 @@
typedef struct {
PyGObject *pad;
PyObject *link_function;
PyObject *event_function;
PyObject *chain_function;
PyObject *get_function;
GClosure *link_function;
GClosure *event_function;
GClosure *chain_function;
GClosure *get_function;
GClosure *getcaps_function;
} PyGstPadPrivate;
typedef struct {
PyObject *func, *data;
} PyGstCustomNotify;
/* from gst-types.c */
gboolean pygst_data_from_pyobject(PyObject *object, GstData **data);
PyObject *pygst_data_to_pyobject(GstData *data);
GstCaps *pygst_caps_from_pyobject (PyObject *object, gboolean *copy);
#endif /* __COMMON_H__ */

View file

@ -19,6 +19,10 @@
* Author: Johan Dahlin <johan@gnome.org>
*/
/* define this for all source files that don't run init_pygobject()
* before including pygobject.h */
#define NO_IMPORT_PYGOBJECT
#include <gst/gst.h>
#include "common.h"
@ -61,6 +65,44 @@ PyGstData_to_value(GValue *value, PyObject *object)
return 0;
}
/* This function will return a copy, unless the following is all TRUE:
* - The given PyObject contains a GstCaps already
* - The copy parameter is non-NULL
* - New years is the first of January
* If copy is non-NULL, it is set to TRUE if a copy was made.
* If the PyObject could not be converted to a caps, a TypeError is raised
* and NULL is returned.
*/
GstCaps *
pygst_caps_from_pyobject (PyObject *object, gboolean *copy)
{
if (pyg_boxed_check(object, GST_TYPE_CAPS)) {
GstCaps *caps = pyg_boxed_get(object, GstCaps);
if (copy) {
*copy = FALSE;
return caps;
} else {
return gst_caps_copy (caps);
}
} else if (pyg_boxed_check(object, GST_TYPE_STRUCTURE)) {
GstStructure *structure = pyg_boxed_get(object, GstStructure);
if (copy)
*copy = TRUE;
return gst_caps_new_full (gst_structure_copy (structure), NULL);
} else if (PyString_Check (object)) {
GstCaps *caps = gst_caps_from_string (PyString_AsString (object));
if (!caps) {
PyErr_SetString(PyExc_TypeError, "could not convert string to GstCaps");
return NULL;
}
if (copy)
*copy = TRUE;
return caps;
}
PyErr_SetString(PyExc_TypeError, "could not convert to GstCaps");
return NULL;
}
void
_pygst_register_boxed_types(PyObject *moddict)
{

25
gst/gst-extrafuncs.defs Normal file
View file

@ -0,0 +1,25 @@
; -*- scheme -*-
(define-method flags
(of-object "GstElement")
(c-name "GST_FLAGS")
(return-type "GstElementFlags")
)
(define-method set_flag
(of-object "GstObject")
(c-name "GST_FLAG_SET")
(return-type "none")
(parameters
'("GstObjectFlags" "flags")
)
)
(define-method unset_flag
(of-object "GstObject")
(c-name "GST_FLAG_UNSET")
(return-type "none")
(parameters
'("GstObjectFlags" "flag")
)
)

View file

@ -1,70 +0,0 @@
/* gst-python
* Copyright (C) 2004 Johan Dahlin
*
* 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 <johan@gnome.org>
*/
#include <gst/gst.h>
#include <pygobject.h>
gboolean
pygst_data_from_pyobject(PyObject *object, GstData **data)
{
if (pyg_boxed_check(object, GST_TYPE_DATA)) {
*data = pyg_boxed_get(object, GstData);
return TRUE;
} else if (pyg_boxed_check(object, GST_TYPE_BUFFER)) {
*data = GST_DATA (pyg_boxed_get(object, GstBuffer));
return TRUE;
} else if (pyg_boxed_check(object, GST_TYPE_EVENT)) {
*data = GST_DATA (pyg_boxed_get(object, GstEvent));
return TRUE;
}
PyErr_Clear();
PyErr_SetString(PyExc_TypeError, "could not convert to GstData");
return FALSE;
}
static PyObject *
PyGstData_from_value(const GValue *value)
{
GstData *data = (GstData *)g_value_get_boxed(value);
return pyg_boxed_new(GST_TYPE_DATA, data, TRUE, TRUE);
}
static int
PyGstData_to_value(GValue *value, PyObject *object)
{
GstData* data;
if (!pygst_data_from_pyobject(object, &data))
return -1;
g_value_set_boxed(value, data);
return 0;
}
void
_pygst_register_boxed_types(PyObject *moddict)
{
pyg_register_boxed_custom(GST_TYPE_DATA,
PyGstData_from_value,
PyGstData_to_value);
}

View file

@ -6,9 +6,6 @@
(parent "GObject")
(c-name "GstObject")
(gtype-id "GST_TYPE_OBJECT")
(fields
'("guint32" "flags")
)
)
(define-object Index
@ -39,6 +36,13 @@
(gtype-id "GST_TYPE_CLOCK")
)
(define-object Bus
(in-module "Gst")
(parent "GstObject")
(c-name "GstBus")
(gtype-id "GST_TYPE_BUS")
)
(define-object Pad
(in-module "Gst")
(parent "GstObject")
@ -46,13 +50,6 @@
(gtype-id "GST_TYPE_PAD")
)
(define-object GhostPad
(in-module "Gst")
(parent "GstPad")
(c-name "GstGhostPad")
(gtype-id "GST_TYPE_GHOST_PAD")
)
(define-object PadTemplate
(in-module "Gst")
(parent "GstObject")
@ -95,13 +92,6 @@
(gtype-id "GST_TYPE_QUEUE")
)
(define-object RealPad
(in-module "Gst")
(parent "GstPad")
(c-name "GstRealPad")
(gtype-id "GST_TYPE_REAL_PAD")
)
(define-object Registry
(in-module "Gst")
(parent "GObject")
@ -130,11 +120,11 @@
(gtype-id "GST_TYPE_SYSTEM_CLOCK")
)
(define-object Thread
(define-object Task
(in-module "Gst")
(parent "GstBin")
(c-name "GstThread")
(gtype-id "GST_TYPE_THREAD")
(parent "GstObject")
(c-name "GstTask")
(gtype-id "GST_TYPE_TASK")
)
(define-object TypeFindFactory
@ -151,69 +141,49 @@
(gtype-id "GST_TYPE_XML")
)
::
:: MiniObject types
::
(define-object Buffer
(in-module "Gst")
(parent "GstMiniObject")
(c-name "GstBuffer")
(gtype-id "GST_TYPE_BUFFER")
)
(define-object Event
(in-module "Gst")
(parent "GstMiniObject")
(c-name "GstEvent")
(gtype-id "GST_TYPE_EVENT")
)
(define-object Message
(in-module "Gst")
(parent "GstMiniObject")
(c-name "GstMessage")
(gtype-id "GST_TYPE_MESSAGE")
)
(define-object Query
(in-module "Gst")
(parent "GstMiniObject")
(c-name "GstQuery")
(gtype-id "GST_TYPE_QUERY")
)
;;
;; Boxed types
;;
(define-boxed Buffer
(in-module "Gst")
(c-name "GstBuffer")
(gtype-id "GST_TYPE_BUFFER")
(copy-func "gst_buffer_copy")
(release-func "gst_data_unref")
(fields
;; GstData fields
'("GType" "data_type")
'("guint16" "flags")
;; GstBuffer fields
'("guint" "size")
'("guint" "maxsize")
'("guint64" "offset")
'("guint64" "offset_end")
'("GstClockTime" "timestamp")
)
)
(define-boxed Caps
(in-module "Gst")
(c-name "GstCaps")
(gtype-id "GST_TYPE_CAPS")
)
(define-boxed Probe
(in-module "Gst")
(c-name "GstProbe")
(gtype-id "GST_TYPE_PROBE")
)
; Defined in arg-types.py
;(define-boxed Data
; (in-module "Gst")
; (c-name "GstData")
; (gtype-id "GST_TYPE_DATA")
; (copy-func "gst_data_copy")
; (release-func "gst_data_free")
;)
(define-boxed Event
(in-module "Gst")
(c-name "GstEvent")
(gtype-id "GST_TYPE_EVENT")
)
(define-boxed GError
(in-module "Gst")
(c-name "GError")
(gtype-id "GST_TYPE_G_ERROR")
(copy-func "g_error_copy")
(release-func "g_error_free")
(fields
'("GQuark" "domain")
'("gint" "code")
'("gchar*" "message"))
)
(define-boxed Plugin
(in-module "Gst")
(parent "GObject")
@ -235,7 +205,6 @@
(gtype-id "GST_TYPE_TAG_LIST")
)
;; Enumerations and flags ...
(define-enum BinFlags
@ -243,37 +212,59 @@
(c-name "GstBinFlags")
(gtype-id "GST_TYPE_BIN_FLAGS")
(values
'("flag-manager" "GST_BIN_FLAG_MANAGER")
'("self-schedulable" "GST_BIN_SELF_SCHEDULABLE")
'("flag-prefer-cothreads" "GST_BIN_FLAG_PREFER_COTHREADS")
'("flag-fixed-clock" "GST_BIN_FLAG_FIXED_CLOCK")
'("flag-last" "GST_BIN_FLAG_LAST")
'("t" "GST_BIN_FLAG_LAST")
)
)
(define-enum BufferFlag
(define-flags BufferFlag
(in-module "Gst")
(c-name "GstBufferFlag")
(gtype-id "GST_TYPE_BUFFER_FLAG")
(values
'("readonly" "GST_BUFFER_READONLY")
'("subbuffer" "GST_BUFFER_SUBBUFFER")
'("original" "GST_BUFFER_ORIGINAL")
'("dontfree" "GST_BUFFER_DONTFREE")
'("key-unit" "GST_BUFFER_KEY_UNIT")
'("dontkeep" "GST_BUFFER_DONTKEEP")
'("flag-last" "GST_BUFFER_FLAG_LAST")
'("readonly" "GST_BUFFER_FLAG_READONLY")
'("original" "GST_BUFFER_FLAG_ORIGINAL")
'("preroll" "GST_BUFFER_FLAG_PREROLL")
'("discont" "GST_BUFFER_FLAG_DISCONT")
'("in-caps" "GST_BUFFER_FLAG_IN_CAPS")
'("gap" "GST_BUFFER_FLAG_GAP")
'("delta-unit" "GST_BUFFER_FLAG_DELTA_UNIT")
'("last" "GST_BUFFER_FLAG_LAST")
)
)
(define-enum ClockEntryStatus
(define-enum BusFlags
(in-module "Gst")
(c-name "GstClockEntryStatus")
(gtype-id "GST_TYPE_CLOCK_ENTRY_STATUS")
(c-name "GstBusFlags")
(gtype-id "GST_TYPE_BUS_FLAGS")
(values
'("ok" "GST_CLOCK_ENTRY_OK")
'("early" "GST_CLOCK_ENTRY_EARLY")
'("restart" "GST_CLOCK_ENTRY_RESTART")
'("ushing" "GST_BUS_FLUSHING")
'("ag-last" "GST_BUS_FLAG_LAST")
)
)
(define-enum BusSyncReply
(in-module "Gst")
(c-name "GstBusSyncReply")
(gtype-id "GST_TYPE_BUS_SYNC_REPLY")
(values
'("drop" "GST_BUS_DROP")
'("pass" "GST_BUS_PASS")
'("async" "GST_BUS_ASYNC")
)
)
(define-enum ClockReturn
(in-module "Gst")
(c-name "GstClockReturn")
(gtype-id "GST_TYPE_CLOCK_RETURN")
(values
'("ok" "GST_CLOCK_OK")
'("early" "GST_CLOCK_EARLY")
'("unscheduled" "GST_CLOCK_UNSCHEDULED")
'("busy" "GST_CLOCK_BUSY")
'("badtime" "GST_CLOCK_BADTIME")
'("error" "GST_CLOCK_ERROR")
'("unsupported" "GST_CLOCK_UNSUPPORTED")
)
)
@ -287,19 +278,6 @@
)
)
(define-enum ClockReturn
(in-module "Gst")
(c-name "GstClockReturn")
(gtype-id "GST_TYPE_CLOCK_RETURN")
(values
'("stopped" "GST_CLOCK_STOPPED")
'("timeout" "GST_CLOCK_TIMEOUT")
'("early" "GST_CLOCK_EARLY")
'("error" "GST_CLOCK_ERROR")
'("unsupported" "GST_CLOCK_UNSUPPORTED")
)
)
(define-flags ClockFlags
(in-module "Gst")
(c-name "GstClockFlags")
@ -310,29 +288,6 @@
'("do-periodic-sync" "GST_CLOCK_FLAG_CAN_DO_PERIODIC_SYNC")
'("do-periodic-async" "GST_CLOCK_FLAG_CAN_DO_PERIODIC_ASYNC")
'("set-resolution" "GST_CLOCK_FLAG_CAN_SET_RESOLUTION")
'("set-speed" "GST_CLOCK_FLAG_CAN_SET_SPEED")
)
)
(define-flags CPUFlags
(in-module "Gst")
(c-name "GstCPUFlags")
(gtype-id "GST_TYPE_CPU_FLAGS")
(values
'("mmx" "GST_CPU_FLAG_MMX")
'("sse" "GST_CPU_FLAG_SSE")
'("mmxext" "GST_CPU_FLAG_MMXEXT")
'("3dnow" "GST_CPU_FLAG_3DNOW")
)
)
(define-enum DataFlags
(in-module "Gst")
(c-name "GstDataFlags")
(gtype-id "GST_TYPE_DATA_FLAGS")
(values
'("readonly" "GST_DATA_READONLY")
'("flag-last" "GST_DATA_FLAG_LAST")
)
)
@ -341,17 +296,10 @@
(c-name "GstElementFlags")
(gtype-id "GST_TYPE_ELEMENT_FLAGS")
(values
'("complex" "GST_ELEMENT_COMPLEX")
'("decoupled" "GST_ELEMENT_DECOUPLED")
'("thread-suggested" "GST_ELEMENT_THREAD_SUGGESTED")
'("infinite-loop" "GST_ELEMENT_INFINITE_LOOP")
'("new-loopfunc" "GST_ELEMENT_NEW_LOOPFUNC")
'("event-aware" "GST_ELEMENT_EVENT_AWARE")
'("use-threadsafe-properties" "GST_ELEMENT_USE_THREADSAFE_PROPERTIES")
'("scheduler-private1" "GST_ELEMENT_SCHEDULER_PRIVATE1")
'("scheduler-private2" "GST_ELEMENT_SCHEDULER_PRIVATE2")
'("locked-state" "GST_ELEMENT_LOCKED_STATE")
'("in-error" "GST_ELEMENT_IN_ERROR")
'("is-sink" "GST_ELEMENT_IS_SINK")
'("flag-last" "GST_ELEMENT_FLAG_LAST")
)
)
@ -442,17 +390,11 @@
'("unknown" "GST_EVENT_UNKNOWN")
'("eos" "GST_EVENT_EOS")
'("flush" "GST_EVENT_FLUSH")
'("empty" "GST_EVENT_EMPTY")
'("discontinuous" "GST_EVENT_DISCONTINUOUS")
'("qos" "GST_EVENT_QOS")
'("seek" "GST_EVENT_SEEK")
'("seek-segment" "GST_EVENT_SEEK_SEGMENT")
'("segment-done" "GST_EVENT_SEGMENT_DONE")
'("size" "GST_EVENT_SIZE")
'("rate" "GST_EVENT_RATE")
'("filler" "GST_EVENT_FILLER")
'("ts-offset" "GST_EVENT_TS_OFFSET")
'("interrupt" "GST_EVENT_INTERRUPT")
'("navigation" "GST_EVENT_NAVIGATION")
'("tag" "GST_EVENT_TAG")
)
@ -548,6 +490,7 @@
(values
'("none" "GST_ASSOCIATION_FLAG_NONE")
'("key-unit" "GST_ASSOCIATION_FLAG_KEY_UNIT")
'("delta-unit" "GST_ASSOCIATION_FLAG_DELTA_UNIT")
'("last" "GST_ASSOCIATION_FLAG_LAST")
)
)
@ -615,14 +558,71 @@
)
)
(define-enum IteratorResult
(in-module "Gst")
(c-name "GstIteratorResult")
(gtype-id "GST_TYPE_ITERATOR_RESULT")
(values
'("done" "GST_ITERATOR_DONE")
'("ok" "GST_ITERATOR_OK")
'("resync" "GST_ITERATOR_RESYNC")
'("error" "GST_ITERATOR_ERROR")
)
)
(define-enum IteratorItem
(in-module "Gst")
(c-name "GstIteratorItem")
(gtype-id "GST_TYPE_ITERATOR_ITEM")
(values
'("skip" "GST_ITERATOR_ITEM_SKIP")
'("pass" "GST_ITERATOR_ITEM_PASS")
'("end" "GST_ITERATOR_ITEM_END")
)
)
(define-flags MessageType
(in-module "Gst")
(c-name "GstMessageType")
(gtype-id "GST_TYPE_MESSAGE_TYPE")
(values
'("unknown" "GST_MESSAGE_UNKNOWN")
'("eos" "GST_MESSAGE_EOS")
'("error" "GST_MESSAGE_ERROR")
'("warning" "GST_MESSAGE_WARNING")
'("info" "GST_MESSAGE_INFO")
'("tag" "GST_MESSAGE_TAG")
'("buffering" "GST_MESSAGE_BUFFERING")
'("state-changed" "GST_MESSAGE_STATE_CHANGED")
'("step-done" "GST_MESSAGE_STEP_DONE")
'("new-clock" "GST_MESSAGE_NEW_CLOCK")
'("structure-change" "GST_MESSAGE_STRUCTURE_CHANGE")
'("stream-status" "GST_MESSAGE_STREAM_STATUS")
'("application" "GST_MESSAGE_APPLICATION")
'("any" "GST_MESSAGE_ANY")
)
)
(define-flags MiniObjectFlags
(in-module "Gst")
(c-name "GstMiniObjectFlags")
(gtype-id "GST_TYPE_MINI_OBJECT_FLAGS")
(values
'("readonly" "GST_MINI_OBJECT_FLAG_READONLY")
'("static" "GST_MINI_OBJECT_FLAG_STATIC")
'("last" "GST_MINI_OBJECT_FLAG_LAST")
)
)
(define-enum ObjectFlags
(in-module "Gst")
(c-name "GstObjectFlags")
(gtype-id "GST_TYPE_OBJECT_FLAGS")
(values
'("destroyed" "GST_DESTROYED")
'("floating" "GST_FLOATING")
'("object-flag-last" "GST_OBJECT_FLAG_LAST")
'("disposing" "GST_OBJECT_DISPOSING")
'("destroyed" "GST_OBJECT_DESTROYED")
'("floating" "GST_OBJECT_FLOATING")
'("flag-last" "GST_OBJECT_FLAG_LAST")
)
)
@ -631,10 +631,39 @@
(c-name "GstPadLinkReturn")
(gtype-id "GST_TYPE_PAD_LINK_RETURN")
(values
'("nosched" "GST_PAD_LINK_NOSCHED")
'("noformat" "GST_PAD_LINK_NOFORMAT")
'("refused" "GST_PAD_LINK_REFUSED")
'("delayed" "GST_PAD_LINK_DELAYED")
'("wrong-direction" "GST_PAD_LINK_WRONG_DIRECTION")
'("was-linked" "GST_PAD_LINK_WAS_LINKED")
'("ok" "GST_PAD_LINK_OK")
'("done" "GST_PAD_LINK_DONE")
)
)
(define-enum FlowReturn
(in-module "Gst")
(c-name "GstFlowReturn")
(gtype-id "GST_TYPE_FLOW_RETURN")
(values
'("ok" "GST_FLOW_OK")
'("resend" "GST_FLOW_RESEND")
'("error" "GST_FLOW_ERROR")
'("not-connected" "GST_FLOW_NOT_CONNECTED")
'("not-negotiated" "GST_FLOW_NOT_NEGOTIATED")
'("wrong-state" "GST_FLOW_WRONG_STATE")
'("unexpected" "GST_FLOW_UNEXPECTED")
'("not-supported" "GST_FLOW_NOT_SUPPORTED")
)
)
(define-enum ActivateMode
(in-module "Gst")
(c-name "GstActivateMode")
(gtype-id "GST_TYPE_ACTIVATE_MODE")
(values
'("none" "GST_ACTIVATE_NONE")
'("push" "GST_ACTIVATE_PUSH")
'("pull" "GST_ACTIVATE_PULL")
)
)
@ -654,8 +683,10 @@
(c-name "GstPadFlags")
(gtype-id "GST_TYPE_PAD_FLAGS")
(values
'("disabled" "GST_PAD_DISABLED")
'("negotiating" "GST_PAD_NEGOTIATING")
'("blocked" "GST_PAD_BLOCKED")
'("flushing" "GST_PAD_FLUSHING")
'("in-getcaps" "GST_PAD_IN_GETCAPS")
'("in-setcaps" "GST_PAD_IN_SETCAPS")
'("flag-last" "GST_PAD_FLAG_LAST")
)
)
@ -696,6 +727,16 @@
)
)
(define-enum PipelineFlags
(in-module "Gst")
(c-name "GstPipelineFlags")
(gtype-id "GST_TYPE_PIPELINE_FLAGS")
(values
'("fixed-clock" "GST_PIPELINE_FLAG_FIXED_CLOCK")
'("last" "GST_PIPELINE_FLAG_LAST")
)
)
(define-enum PluginError
(in-module "Gst")
(c-name "GstPluginError")
@ -720,6 +761,9 @@
'("start" "GST_QUERY_START")
'("segment-end" "GST_QUERY_SEGMENT_END")
'("rate" "GST_QUERY_RATE")
'("seeking" "GST_QUERY_SEEKING")
'("convert" "GST_QUERY_CONVERT")
'("formats" "GST_QUERY_FORMATS")
)
)
@ -754,20 +798,7 @@
(c-name "GstSchedulerFlags")
(gtype-id "GST_TYPE_SCHEDULER_FLAGS")
(values
'("fixed-clock" "GST_SCHEDULER_FLAG_FIXED_CLOCK")
'("last" "GST_SCHEDULER_FLAG_LAST")
)
)
(define-enum SchedulerState
(in-module "Gst")
(c-name "GstSchedulerState")
(gtype-id "GST_TYPE_SCHEDULER_STATE")
(values
'("none" "GST_SCHEDULER_STATE_NONE")
'("running" "GST_SCHEDULER_STATE_RUNNING")
'("stopped" "GST_SCHEDULER_STATE_STOPPED")
'("error" "GST_SCHEDULER_STATE_ERROR")
'("t" "GST_SCHEDULER_FLAG_LAST")
)
)
@ -800,15 +831,14 @@
)
)
(define-enum ThreadState
(define-enum TaskState
(in-module "Gst")
(c-name "GstThreadState")
(gtype-id "GST_TYPE_THREAD_STATE")
(c-name "GstTaskState")
(gtype-id "GST_TYPE_TASK_STATE")
(values
'("state-spinning" "GST_THREAD_STATE_SPINNING")
'("state-reaping" "GST_THREAD_STATE_REAPING")
'("mutex-locked" "GST_THREAD_MUTEX_LOCKED")
'("flag-last" "GST_THREAD_FLAG_LAST")
'("started" "GST_TASK_STARTED")
'("stopped" "GST_TASK_STOPPED")
'("paused" "GST_TASK_PAUSED")
)
)
@ -859,14 +889,15 @@
)
)
(define-enum Result
(define-enum Rank
(in-module "Gst")
(c-name "GstResult")
(gtype-id "GST_TYPE_RESULT")
(c-name "GstRank")
(gtype-id "GST_TYPE_RANK")
(values
'("ok" "GST_RESULT_OK")
'("nok" "GST_RESULT_NOK")
'("not-impl" "GST_RESULT_NOT_IMPL")
'("none" "GST_RANK_NONE")
'("marginal" "GST_RANK_MARGINAL")
'("secondary" "GST_RANK_SECONDARY")
'("primary" "GST_RANK_PRIMARY")
)
)

File diff suppressed because it is too large Load diff

View file

@ -22,6 +22,10 @@
*/
%%
headers
/* define this for all source files that don't run init_pygobject()
* before including pygobject.h */
#define NO_IMPORT_PYGOBJECT
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
@ -36,7 +40,13 @@ headers
#include <gst/gsttaginterface.h>
#include <gst/gstprobe.h>
extern gboolean pygst_data_from_pyobject (PyObject *object, GstData **data);
#include "pygstvalue.h"
/* These headers have been included directly to get around multiple
* GetAttrString calls */
#include <compile.h>
#include <frameobject.h>
PyObject *PyGstExc_LinkError = NULL;
GSList *mainloops = NULL;
@ -65,16 +75,161 @@ _pygst_main(void)
g_main_loop_run (loop);
}
/* This function checks if a former Python code threw an exception and if
* so, transforms this exception into an error on the given GstElement.
* This allows code run on the element to just raise an exception instead of
* returning GStreamer specific return values.
* The exception is cleared afterwards.
*/
gboolean
_pygst_element_check_error (GstElement *element)
{
PyObject *type, *value, *traceback, *lineno, *msg, *typemsg;
PyFrameObject *frame;
if (!PyErr_Occurred())
return FALSE;
PyErr_Fetch (&type, &value, &traceback);
if (traceback) {
frame = (PyFrameObject *) PyObject_GetAttrString (traceback, "tb_frame");
lineno = PyObject_GetAttrString (traceback, "tb_lineno");
} else {
frame = NULL;
lineno = NULL;
}
msg = PyObject_Str (value);
typemsg = PyObject_Str (type);
if (msg && PyString_Check (msg)) {
gst_element_error_full (element, GST_LIBRARY_ERROR,
GST_LIBRARY_ERROR_FAILED,
g_strdup (PyString_AsString (msg)),
typemsg ? g_strconcat (PyString_AsString (typemsg),
": ", PyString_AsString (msg), NULL)
: g_strdup (PyString_AsString (msg)),
frame ? PyString_AsString(frame->f_code->co_filename) : "???",
frame ? PyString_AsString(frame->f_code->co_name) : "???",
lineno ? PyInt_AsLong (lineno) : 0);
} else {
gst_element_error_full (element, GST_LIBRARY_ERROR,
GST_LIBRARY_ERROR_TOO_LAZY,
NULL, NULL,
frame ? PyString_AsString(frame->f_code->co_filename) : "???",
frame ? PyString_AsString(frame->f_code->co_name) : "???",
lineno ? PyInt_AsLong (lineno) : 0);
}
PyErr_Clear ();
Py_XDECREF (frame);
Py_XDECREF (lineno);
Py_DECREF (msg);
Py_DECREF (typemsg);
return TRUE;
}
#ifdef pyg_register_class_init
PyTypeObject PyGstPadTemplate_Type;
static int
add_templates (gpointer gclass, PyObject *templates)
{
gint i, len;
PyGObject *templ;
if (pygobject_check(templates, &PyGstPadTemplate_Type)) {
gst_element_class_add_pad_template (gclass, GST_PAD_TEMPLATE (pygobject_get (templates)));
return 0;
}
if (!PyTuple_Check(templates)) {
PyErr_SetString(PyExc_TypeError, "__gsttemplates__ attribute neither a tuple nor a GstPadTemplate!");
return -1;
}
len = PyTuple_Size(templates);
if (len == 0)
return 0;
for (i = 0; i < len; i++) {
templ = (PyGObject*) PyTuple_GetItem(templates, i);
if (!pygobject_check(templ, &PyGstPadTemplate_Type)) {
PyErr_SetString(PyExc_TypeError, "entries for __gsttemplates__ must be of type GstPadTemplate");
return -1;
}
}
for (i = 0; i < len; i++) {
templ = (PyGObject*) PyTuple_GetItem(templates, i);
gst_element_class_add_pad_template (gclass, GST_PAD_TEMPLATE (templ->obj));
}
return 0;
}
static int
_pygst_element_set_details (gpointer gclass, PyObject *details)
{
GstElementDetails gstdetails = { 0, };
if (!PyTuple_Check (details)) {
PyErr_SetString(PyExc_TypeError, "__gstdetails__ must be a tuple");
return -1;
}
if (PyTuple_Size (details) != 4) {
PyErr_SetString(PyExc_TypeError, "__gstdetails__ must be contain 4 elements");
return -1;
}
if (!PyArg_ParseTuple (details, "ssss", &gstdetails.longname, &gstdetails.klass,
&gstdetails.description, &gstdetails.author)) {
PyErr_SetString (PyExc_TypeError, "__gstdetails__ must be contain 4 strings");
return -1;
}
gst_element_class_set_details (gclass, &gstdetails);
return 0;
}
static int
_pygst_element_init (gpointer gclass, PyTypeObject *pyclass)
{
PyObject *templates, *details;
templates = PyDict_GetItemString(pyclass->tp_dict, "__gsttemplates__");
if (templates) {
if (add_templates(gclass, templates) != 0)
return -1;
} else {
PyErr_Clear();
}
details = PyDict_GetItemString(pyclass->tp_dict, "__gstdetails__");
if (details) {
if (_pygst_element_set_details (gclass, details) != 0)
return -1;
PyDict_DelItemString(pyclass->tp_dict, "__gstdetails__");
} else {
PyErr_Clear();
}
return 0;
}
#endif
%%
include
gstbin.override
gstbuffer.override
gstcaps.override
gstelement.override
gstevent.override
gstpad.override
gststructure.override
%%
init
{
/* FIXME: new in pygtk-2.6 */
#ifdef pyg_register_class_init
pyg_register_class_init (GST_TYPE_ELEMENT, _pygst_element_init);
#endif
if (!pygst_value_init())
return;
}
%%
modulename gst
%%
@ -126,7 +281,7 @@ tag_foreach_func_dict (const GstTagList *list,
for (i = 0; i < count; i++) {
gvalue = gst_tag_list_get_value_index(GST_TAG_LIST(list), tag, i);
value = pyg_value_as_pyobject(gvalue, TRUE);
value = pygst_value_as_pyobject(gvalue, TRUE);
key = g_strdup (tag);
PyDict_SetItemString(dict, key, value);
g_free (key);
@ -184,7 +339,7 @@ _wrap_gst_tag_list_subscript(PyGObject *self, PyObject *py_key)
} else if (count == 1) {
const GValue *gvalue;
gvalue = gst_tag_list_get_value_index(GST_TAG_LIST(self->obj), key, 0);
v = pyg_value_as_pyobject(gvalue, TRUE);
v = pygst_value_as_pyobject(gvalue, TRUE);
} else {
PyErr_SetString(PyExc_TypeError, "lists are currently unspported");
}
@ -235,7 +390,7 @@ _wrap_gst_tag_list_get(PyGObject *self, PyObject *args)
PyErr_SetString(PyExc_KeyError, key);
} else if (count == 1) {
gvalue = gst_tag_list_get_value_index(GST_TAG_LIST(self->obj), key, 0);
val = pyg_value_as_pyobject(gvalue, TRUE);
val = pygst_value_as_pyobject(gvalue, TRUE);
} else {
PyErr_SetString(PyExc_TypeError, "lists are currently unspported");
}
@ -338,7 +493,73 @@ _wrap_gst_tag_list_get_value_index (PyGObject *self,
tag,
index);
return pyg_value_as_pyobject(gvalue, FALSE);
return pygst_value_as_pyobject(gvalue, FALSE);
}
%%
override-slot GstObject.tp_repr
static PyObject *
_wrap_gst_object_tp_repr(PyObject *self)
{
gchar *repr;
PyObject *ret;
GstObject *object = GST_OBJECT (pygobject_get (self));
repr = g_strdup_printf ("<%s object (%s) at 0x%lx>",
self->ob_type->tp_name,
GST_OBJECT_NAME(object) ? GST_OBJECT_NAME(object) : "unnamed",
(long)self);
ret = PyString_FromString(repr);
g_free (repr);
return ret;
}
%%
override-slot GstObject.tp_str
static PyObject *
_wrap_gst_object_tp_str(PyObject *self)
{
gchar *repr, *path;
PyObject *ret;
GstObject *object = GST_OBJECT (pygobject_get (self));
path = gst_object_get_path_string (object);
repr = g_strdup_printf ("%s (%s)",
path, self->ob_type->tp_name);
ret = PyString_FromString(repr);
g_free (repr);
g_free (path);
return ret;
}
%%
override-slot GstPluginFeature.tp_repr
static PyObject *
_wrap_gst_plugin_feature_tp_repr(PyObject *self)
{
gchar *repr;
PyObject *ret;
GstPluginFeature *feature = GST_PLUGIN_FEATURE (pygobject_get (self));
repr = g_strdup_printf ("<%s %s @ 0x%lx>",
self->ob_type->tp_name, gst_plugin_feature_get_name (feature),
(long) self);
ret = PyString_FromString(repr);
g_free (repr);
return ret;
}
%%
override-slot GstPluginFeature.tp_str
static PyObject *
_wrap_gst_plugin_feature_tp_str(PyObject *self)
{
gchar *repr;
PyObject *ret;
GstPluginFeature *feature = GST_PLUGIN_FEATURE (pygobject_get (self));
repr = g_strdup_printf ("<%s %s (%d)>",
self->ob_type->tp_name, gst_plugin_feature_get_name (feature),
gst_plugin_feature_get_rank (feature));
ret = PyString_FromString(repr);
g_free (repr);
return ret;
}
%%
override gst_type_find_factory_get_caps noargs
@ -627,6 +848,7 @@ probe_handler_marshal(GstProbe *probe, GstData **data, gpointer user_data)
PyGILState_STATE state;
PyObject *callback, *args;
PyObject *ret;
PyObject *py_data;
PyObject *py_user_data;
gboolean res;
gint len, i;
@ -637,10 +859,17 @@ probe_handler_marshal(GstProbe *probe, GstData **data, gpointer user_data)
py_user_data = (PyObject *) user_data;
if (GST_IS_BUFFER(*data))
py_data = pyg_boxed_new(GST_TYPE_BUFFER, *data, TRUE, TRUE);
else if (GST_IS_EVENT(*data))
py_data = pyg_boxed_new(GST_TYPE_EVENT, *data, TRUE, TRUE);
else
py_data = pyg_boxed_new(GST_TYPE_DATA, *data, TRUE, TRUE);
callback = PyTuple_GetItem(py_user_data, 0);
args = Py_BuildValue("(NN)",
pyg_boxed_new(GST_TYPE_PROBE, probe, TRUE, TRUE),
pyg_boxed_new(GST_TYPE_DATA, *data, TRUE, TRUE));
py_data);
len = PyTuple_Size(py_user_data);
for (i = 1; i < len; ++i) {
@ -657,6 +886,7 @@ probe_handler_marshal(GstProbe *probe, GstData **data, gpointer user_data)
res = PyObject_IsTrue(ret);
Py_DECREF(ret);
}
Py_DECREF(args);
pyg_gil_state_release(state);
return res;
@ -765,3 +995,50 @@ _wrap_gst_plugin_get_version(PyGObject *self)
g_strfreev(items);
return tuple;
}
%%
override gst_element_register kwargs
static GstPlugin *
_pygst_get_plugin(void)
{
PyObject *dict = NULL, *module = NULL, *pyplugin = NULL;
GstPlugin *ret;
if (!(module = PyImport_ImportModule ("gst")))
goto err;
if (!(dict = PyModule_GetDict (module)))
goto err;
if (!(pyplugin = PyDict_GetItemString (dict, "__plugin__")))
goto err;
ret = pyg_boxed_get (pyplugin, GstPlugin);
Py_DECREF (module);
return ret;
err:
Py_XDECREF (module);
PyErr_Clear ();
return NULL;
}
static PyObject *
_wrap_gst_element_register(PyObject *self, PyObject *args, PyObject *kwargs)
{
static char *kwlist[] = { "type", "elementname", "rank", NULL };
PyObject *py_type = NULL;
guint rank = GST_RANK_NONE;
char *elementname = NULL;
int ret;
GType type;
/* FIXME: can we make the name optional, too? Anyone know a good default? */
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Os|I:element_register", kwlist,
&py_type, &elementname, &rank))
return NULL;
if ((type = pyg_type_from_object(py_type)) == 0)
return NULL;
ret = gst_element_register(_pygst_get_plugin(), elementname, rank, type);
return PyBool_FromLong(ret);
}

View file

@ -26,7 +26,6 @@ headers
static int gst_buffer_getreadbuffer (PyGObject *self,
int index,
const void **ptr);
static int gst_buffer_length (PyGObject *self);
static int gst_buffer_getwritebuf (PyGObject *self,
int index,
const void **ptr);
@ -42,7 +41,7 @@ _wrap_gst_buffer_new(PyGBoxed *self, PyObject *args, PyObject *kwargs)
{
static char *kwlist[] = { "data", "buffer_size", NULL };
char *data = NULL;
int size;
int size = 0;
int buf_size = -1;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|z#i:GstBuffer.__init__", kwlist,
@ -53,10 +52,16 @@ _wrap_gst_buffer_new(PyGBoxed *self, PyObject *args, PyObject *kwargs)
PyErr_SetString(PyExc_TypeError, "buffer size must be >= 0");
return -1;
}
if (buf_size < 0)
buf_size = size;
if (buf_size < size) {
PyErr_SetString(PyExc_TypeError, "buffer size must be >= data size");
return -1;
}
self->gtype = GST_TYPE_BUFFER;
self->free_on_dealloc = FALSE;
self->free_on_dealloc = TRUE;
self->boxed = gst_buffer_new_and_alloc(buf_size > size ? buf_size : size);
self->boxed = gst_buffer_new_and_alloc(buf_size);
if (!self->boxed) {
PyErr_SetString(PyExc_RuntimeError, "could not create GstBuffer object");
@ -77,7 +82,7 @@ static PyObject*
_wrap_gst_buffer_get_data(PyObject *self)
{
GstBuffer *buf = pyg_boxed_get(self, GstBuffer);
return PyString_FromStringAndSize(GST_BUFFER_DATA(buf),
return PyString_FromStringAndSize((gchar *) GST_BUFFER_DATA(buf),
GST_BUFFER_SIZE(buf));
}
%%
@ -103,7 +108,7 @@ _wrap_gst_buffer_set_data(PyObject *self, PyObject *args, PyObject *kwargs)
return NULL;
}
GST_BUFFER_SIZE(buf) = PyString_Size(data);
GST_BUFFER_DATA(buf) = g_new0(char, GST_BUFFER_SIZE(buf));
GST_BUFFER_DATA(buf) = (guint8 *) g_new0(char, GST_BUFFER_SIZE(buf));
memcpy(GST_BUFFER_DATA(buf),
PyString_AsString(data),
@ -174,12 +179,33 @@ _wrap_gst_buffer__set_timestamp(PyGBoxed *self, PyObject *value, void *closure)
return 0;
}
%%
override-attr GstBuffer.duration
static PyObject *
_wrap_gst_buffer__get_duration(PyGObject *self, void *closure)
{
return PyInt_FromLong(GST_BUFFER(self->obj)->duration);
}
static int
_wrap_gst_buffer__set_duration(PyGBoxed *self, PyObject *value, void *closure)
{
gint val;
val = PyInt_AsLong(value);
if (PyErr_Occurred())
return -1;
pyg_boxed_get(self, GstBuffer)->duration = val;
return 0;
}
%%
override-slot GstBuffer.tp_str
static PyObject *
_wrap_gst_buffer_tp_str(PyGObject *self)
{
return PyString_FromStringAndSize(GST_BUFFER_DATA(self->obj),
GST_BUFFER_SIZE(self->obj));
GstBuffer *buf = pyg_boxed_get(self, GstBuffer);
return PyString_FromStringAndSize((const gchar*) GST_BUFFER_DATA(buf),
(gint) GST_BUFFER_SIZE(buf));
}
%%
override-slot GstBuffer.tp_as_buffer
@ -193,64 +219,164 @@ static PyBufferProcs _wrap_gst_buffer_tp_as_buffer = {
static int
gst_buffer_getreadbuffer(PyGObject *self, int index, const void **ptr)
{
GstBuffer *buf = pyg_boxed_get(self, GstBuffer);
if ( index != 0 ) {
PyErr_SetString(PyExc_SystemError,
"accessing non-existent GstBuffer segment");
return -1;
}
*ptr = GST_BUFFER_DATA(self->obj);
return GST_BUFFER_SIZE(self->obj);
*ptr = GST_BUFFER_DATA(buf);
return GST_BUFFER_SIZE(buf);
}
static int
gst_buffer_getsegcount(PyGObject *self, int *lenp)
{
GstBuffer *buf = pyg_boxed_get(self, GstBuffer);
if (lenp)
*lenp = GST_BUFFER_SIZE(self->obj);
*lenp = GST_BUFFER_SIZE(buf);
return 1;
}
static int
gst_buffer_getcharbuf(PyGObject *self, int index, const char **ptr)
{
return gst_buffer_getreadbuffer (self, index, (const void **) ptr);
}
static int
gst_buffer_getwritebuf(PyGObject *self, int index, const void **ptr)
{
GstBuffer *buf = pyg_boxed_get(self, GstBuffer);
if ( index != 0 ) {
PyErr_SetString(PyExc_SystemError,
"accessing non-existent GstBuffer segment");
return -1;
}
*ptr = GST_BUFFER_DATA(self->obj);
return GST_BUFFER_SIZE(self->obj);
}
if (!gst_buffer_is_writable (buf)) {
PyErr_SetString(PyExc_TypeError,
"buffer is not writable");
return -1;
}
static int
gst_buffer_getwritebuf(PyGObject *self, int index, const void **ptr)
{
PyErr_SetString(PyExc_TypeError,
"Cannot use GstBuffer as modifiable buffer");
return -1;
*ptr = GST_BUFFER_DATA(buf);
return GST_BUFFER_SIZE(buf);
}
%%
override-slot GstBuffer.tp_as_sequence
/* FIXME: should buffer parts be buffers or strings? */
static int
pygst_buffer_length(PyObject *self)
{
return GST_BUFFER_SIZE(pygobject_get (self));
}
static PyObject *
pygst_buffer_slice(PyObject *self, int start, int end)
{
GstBuffer *buf = GST_BUFFER (pygobject_get (self));
if (start < 0)
start = 0;
if (end < 0)
end = 0;
if (end > GST_BUFFER_SIZE(buf))
end = GST_BUFFER_SIZE(buf);
if (end <= start) {
PyErr_SetString(PyExc_IndexError, "buffer index out of range");
return NULL;
}
return PyString_FromStringAndSize((gchar *) GST_BUFFER_DATA (buf) + start, end - start);
}
static PyObject *
pygst_buffer_item(PyObject *self, int index)
{
return pygst_buffer_slice (self, index, index + 1);
}
static int
pygst_buffer_ass_slice (PyObject *self, int start, int end, PyObject *val)
{
GstBuffer *buf = GST_BUFFER (pygobject_get (self));
const void *data;
int len;
if (!gst_buffer_is_writable (buf)) {
PyErr_SetString(PyExc_TypeError, "buffer is not writable");
return -1;
}
/* FIXME: policy? */
if (start < 0 || end <= start || end > GST_BUFFER_SIZE (buf)) {
PyErr_SetString(PyExc_IndexError, "buffer index out of range");
return -1;
}
end -= start;
if (PyObject_AsReadBuffer(val, &data, &len))
return -1;
if (len > end)
len = end;
memcpy (GST_BUFFER_DATA (buf) + start, data, len);
return 0;
}
static int
pygst_buffer_ass_item (PyObject *self, int index, PyObject *val)
{
GstBuffer *buf = GST_BUFFER (pygobject_get (self));
const void *data;
int len;
if (!gst_buffer_is_writable (buf)) {
PyErr_SetString(PyExc_TypeError, "buffer is not writable");
return -1;
}
if (index < 0 || index > GST_BUFFER_SIZE (buf)) {
PyErr_SetString(PyExc_IndexError, "buffer index out of range");
return -1;
}
if (PyObject_AsReadBuffer(val, &data, &len))
return -1;
/* FIXME: how do we handle this? */
if (len > GST_BUFFER_SIZE (buf) - index)
len = GST_BUFFER_SIZE (buf) - index;
memcpy (GST_BUFFER_DATA (buf) + index, data, len);
return 0;
}
static PySequenceMethods _wrap_gst_buffer_tp_as_sequence = {
(inquiry)gst_buffer_length, /* sq_length */
pygst_buffer_length, /* sq_length */
NULL, /* sq_concat */
NULL, /* sq_repeat */
NULL, /* sq_item */
NULL, /* sq_slice */
NULL, /* sq_ass_item */
NULL, /* sq_ass_slice */
pygst_buffer_item, /* sq_item */
pygst_buffer_slice, /* sq_slice */
pygst_buffer_ass_item, /* sq_ass_item */
pygst_buffer_ass_slice, /* sq_ass_slice */
NULL, /* sq_contains */
NULL, /* sq_inplace_concat */
NULL, /* sq_inplace_repeat */
};
static int
gst_buffer_length(PyGObject *self)
%%
define GstBuffer.copy_on_write
static PyObject *
_wrap_gst_buffer_copy_on_write (PyObject *self)
{
return GST_BUFFER_SIZE(self->obj);
GstBuffer *buf = pyg_boxed_get(self, GstBuffer);
if (gst_buffer_is_writable (buf)) {
Py_INCREF (self);
return self;
}
buf = gst_buffer_copy (buf);
self = pyg_boxed_new (GST_TYPE_BUFFER, buf, FALSE, TRUE);
return self;
}
/* %%

View file

@ -1,6 +1,7 @@
/* -*- Mode: C; c-basic-offset: 4 -*- */
/* gst-python
* Copyright (C) 2005 Johan Dahlin
* 2005 Benjamin Otte <otte@gnome.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@ -18,13 +19,78 @@
* Boston, MA 02111-1307, USA.
*
* Author: Johan Dahlin <johan@gnome.org>
* Benjamin Otte <otte@gnome.org>
*/
%%
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)
@ -40,89 +106,325 @@ _wrap_gst_caps_new_empty(PyGBoxed *self, PyObject *args, PyObject *kwargs)
if (len == 0) {
/* 0 length creates a new empty caps */
self->boxed = gst_caps_new_empty();
goto beach;
} else if (len == 1) {
/* 1 length is either a string or a structure */
item = PyTuple_GetItem(args, 0);
if (PyString_Check(item)) {
self->boxed = gst_caps_from_string(PyString_AsString(item));
goto beach;
} else if (!pyg_boxed_check(item, GST_TYPE_STRUCTURE)) {
PyErr_SetString(PyExc_TypeError, "argument must be a string or a GstStructure");
return -1;
}
}
/* it's either one GstStructure or several whatevers */
self->boxed = gst_caps_new_empty();
for (i = 0; i < len; i++)
{
item = PyTuple_GetItem(args, i);
if (!pyg_boxed_check(item, GST_TYPE_STRUCTURE))
{
PyErr_SetString(PyExc_TypeError, "argument must be a GstStructure");
gst_caps_free(self->boxed);
return -1;
/* 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_free (self->boxed);
self->boxed = NULL;
break;
}
gst_caps_append (self->boxed, append);
}
gst_caps_append_structure(self->boxed, pyg_boxed_get(item, GstStructure));
}
beach:
if (!self->boxed) {
PyErr_SetString(PyExc_RuntimeError, "could not create GstCaps object");
PyErr_SetString(PyExc_TypeError, "wrong arguemntes when creating GstCaps object");
return -1;
}
return 0;
}
%%
override gst_caps_get_structure kwargs
static PyObject *
_wrap_gst_caps_get_structure(PyObject *self, PyObject *args, PyObject *kwargs)
{
static char *kwlist[] = { "index", NULL };
int index;
GstStructure *ret;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i:GstCaps.get_structure", kwlist, &index))
return NULL;
ret = gst_caps_get_structure(pyg_boxed_get(self, GstCaps), index);
/* pyg_boxed_new handles NULL checking */
return pyg_boxed_new(GST_TYPE_STRUCTURE, ret, FALSE, FALSE);
%%
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_free (tmp);
if (!ret)
return FALSE;
tmp = gst_caps_subtract (caps2, caps1);
ret = gst_caps_is_empty (tmp);
gst_caps_free (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_free (caps2);
return NULL;
}
if (caps2 && caps2_is_copy)
gst_caps_free (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_free (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_free (_union);
gst_caps_free (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_free (caps1);
if (caps2 && !caps2_is_copy)
gst_caps_free (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
caps_length(PyGObject *self)
pygst_caps_sq_length(PyObject *self)
{
return gst_caps_get_size((GstCaps*)self->obj);
GstCaps *caps = pyg_boxed_get (self, GstCaps);
return gst_caps_get_size(caps);
}
static PyObject *
caps_item(PyGObject *self, int i)
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((GstCaps*)self->obj)) {
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((GstCaps*)self->obj, i);
return pyg_boxed_new(GST_TYPE_STRUCTURE, structure, TRUE, TRUE);
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 = {
(inquiry)caps_length, /* mp_length */
NULL,
NULL,
(intargfunc)caps_item,
NULL,
NULL,
NULL,
NULL,
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_caps_free (boxed->boxed);
}
self->ob_type->tp_free((PyObject *)self);
}
%%
override-slot GstCaps.tp_str
static PyObject *
_wrap_gst_caps_tp_str(PyGObject *self)

View file

@ -188,14 +188,23 @@ override gst_element_link kwargs
static PyObject *
_wrap_gst_element_link(PyGObject *self, PyObject *args, PyObject *kwargs)
{
static char *kwlist[] = { "dest", NULL };
static char *kwlist[] = { "dest", "filtercaps", NULL };
PyGObject *dest;
PyObject *py_caps = NULL;
int ret;
GstCaps *caps = NULL;
gboolean caps_is_copy;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!:GstElement.link",
kwlist, &PyGstElement_Type, &dest))
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|O:GstElement.link",
kwlist, &PyGstElement_Type, &dest, &py_caps))
return NULL;
ret = gst_element_link(GST_ELEMENT(self->obj), GST_ELEMENT(dest->obj));
if (py_caps == NULL)
caps = NULL;
else
caps = pygst_caps_from_pyobject (py_caps, &caps_is_copy);
ret = gst_element_link_filtered(GST_ELEMENT(self->obj), GST_ELEMENT(dest->obj), caps);
if (caps && caps_is_copy)
gst_caps_free (caps);
if (!ret) {
PyErr_Format(PyGstExc_LinkError,
"failed to link %s with %s",
@ -212,52 +221,47 @@ static PyObject *
_wrap_gst_element_link_filtered(PyGObject *self, PyObject *args,
PyObject *kwargs)
{
static char *kwlist[] = { "dest", "filtercaps", NULL };
PyGObject *dest;
PyObject *py_filtercaps;
int ret;
GstCaps *filtercaps = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
"O!O:GstElement.link_filtered",
kwlist, &PyGstElement_Type,
&dest, &py_filtercaps))
return NULL;
if (pyg_boxed_check(py_filtercaps, GST_TYPE_CAPS))
filtercaps = pyg_boxed_get(py_filtercaps, GstCaps);
else {
PyErr_SetString(PyExc_TypeError, "filtercaps should be a GstCaps");
return NULL;
}
ret = gst_element_link_filtered(GST_ELEMENT(self->obj),
GST_ELEMENT(dest->obj),
filtercaps);
if (!ret) {
PyErr_Format(PyGstExc_LinkError,
"failed to link %s with %s",
GST_ELEMENT_NAME(self->obj),
GST_ELEMENT_NAME(dest->obj));
return NULL;
}
return PyBool_FromLong(ret);
if (PyErr_Warn(PyExc_DeprecationWarning, "element.link_filtered is deprecated, use element.link") < 0)
return NULL;
return _wrap_gst_element_link (self, args, kwargs);
}
%%
override gst_element_link_pads kwargs
static gboolean
pad_name_from_object (PyObject *object, const gchar **name)
{
if (object == Py_None) {
*name = NULL;
return TRUE;
} else if (PyString_Check (object)) {
*name = PyString_AsString (object);
return TRUE;
} else if (pygobject_check (object, &PyGstPad_Type)) {
*name = gst_object_get_name (GST_OBJECT (pygobject_get (object)));
return TRUE;
}
PyErr_SetString(PyExc_TypeError, "argument could not be converted to a pad");
return FALSE;
}
static PyObject *
_wrap_gst_element_link_pads(PyGObject *self, PyObject *args, PyObject *kwargs)
{
static char *kwlist[] = { "srcpadname", "dest", "destpadname", NULL };
char *srcpadname, *destpadname;
const char *srcpadname, *destpadname;
PyGObject *dest;
PyObject *srcpad, *destpad;
int ret;
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
"sO!s:GstElement.link_pads", kwlist,
&srcpadname, &PyGstElement_Type, &dest,
&destpadname))
"OO!O:GstElement.link_pads", kwlist,
&srcpad, &PyGstElement_Type, &dest,
&destpad))
return NULL;
if (!pad_name_from_object (srcpad, &srcpadname) ||
!pad_name_from_object (destpad, &destpadname))
return NULL;
ret = gst_element_link_pads(GST_ELEMENT(self->obj), srcpadname,
GST_ELEMENT(dest->obj), destpadname);
if (!ret) {
@ -374,3 +378,19 @@ _wrap_gst_element_send_event(PyGObject *self, PyObject *args, PyObject *kwargs)
return PyBool_FromLong(ret);
}
%%
override gst_element_factory_get_pad_templates
static PyObject *
_wrap_gst_element_factory_get_pad_templates(PyGObject *self)
{
const GList *pads;
PyObject *list;
pads = gst_element_factory_get_pad_templates(GST_ELEMENT_FACTORY(self->obj));
list = PyList_New(0);
for (; pads; pads = g_list_next (pads))
PyList_Append(list, pygobject_new(pads->data));
return list;
}

49
gst/gstevent.override Normal file
View file

@ -0,0 +1,49 @@
/* -*- Mode: C; ; c-file-style: "python" -*- */
/* gst-python
* Copyright (C) 2005 Edward Hervey
*
* 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 <johan@gnome.org>
*/
%%
override-attr GstEvent.data_type
static PyObject*
_wrap_gst_event__get_data_type(PyGObject *self, void *closure)
{
return pyg_type_wrapper_new(GST_DATA_TYPE(GST_DATA(self->obj)));
}
%%
override-attr GstEvent.flags
static PyObject*
_wrap_gst_event__get_flags(PyGObject *self, void *closure)
{
return PyInt_FromLong(GST_DATA_FLAGS(self->obj));
}
%%
override-attr GstEvent.type
static PyObject*
_wrap_gst_event__get_type(PyGObject *self, void *closure)
{
return pyg_enum_from_gtype(GST_TYPE_EVENT_TYPE, GST_EVENT_TYPE(GST_EVENT(self->obj)));
}
%%
override-attr GstEvent.timestamp
static PyObject *
_wrap_gst_event__get_timestamp(PyGObject *self, void *closure)
{
return PyInt_FromLong(GST_EVENT(self->obj)->timestamp);
}

View file

@ -33,6 +33,7 @@
void pygst_register_classes (PyObject *d);
void pygst_add_constants(PyObject *module, const gchar *strip_prefix);
void _pygst_register_boxed_types(PyObject *moddict);
extern PyMethodDef pygst_functions[];
extern GSList *mainloops;
@ -114,6 +115,7 @@ init_gst (void)
g_free (argv);
}
_pygst_register_boxed_types (NULL);
pygobject_register_sinkfunc(GST_TYPE_OBJECT, sink_gstobject);
m = Py_InitModule ("_gst", pygst_functions);

View file

@ -22,66 +22,169 @@
%%
headers
/* we need to do this until PyClosures get exception handlers */
#ifndef pyg_closure_set_exception_handler
# define pyg_closure_set_exception_handler(ig, nore)
# define EXCEPTION_HANDLER G_GNUC_UNUSED
#else
# define EXCEPTION_HANDLER
#endif
#define SET_PAD_CLOSURE(self, args, kwargs, name) \
static char *kwlist[] = { G_STRINGIFY (name), NULL }; \
PyObject *function; \
GstPad *pad; \
GClosure *closure; \
PyGstPadPrivate *priv; \
\
if (!PyArg_ParseTupleAndKeywords(args, kwargs, \
"O:GstPad.set_" G_STRINGIFY (name), \
kwlist, \
&function)) { \
return NULL; \
} \
\
if (!PyCallable_Check(function)) { \
PyErr_SetString(PyExc_TypeError, G_STRINGIFY (name) " not callable"); \
return NULL; \
} \
\
closure = pyg_closure_new (function, NULL, NULL); \
pyg_closure_set_exception_handler (closure, handle_ ## name ## _exception); \
pygobject_watch_closure((PyObject *)self, closure); \
priv = py_pad_private(self);\
if (priv->name) { \
g_closure_invalidate (priv->name); \
g_closure_unref (priv->name); \
} \
priv->name = closure; \
pad = (GstPad*)pygobject_get(self); \
gst_pad_set_ ## name (pad, call_ ## name); \
\
Py_INCREF(Py_None); \
return Py_None;
static void
free_pad_private (gpointer data)
{
PyGstPadPrivate *private = data;
#define INVALIDATE_CLOSURE(closure) \
if (closure) { \
g_closure_invalidate (closure); \
g_closure_unref (closure); \
closure = NULL; \
}
INVALIDATE_CLOSURE (private->link_function)
INVALIDATE_CLOSURE (private->event_function)
INVALIDATE_CLOSURE (private->chain_function)
INVALIDATE_CLOSURE (private->get_function)
INVALIDATE_CLOSURE (private->getcaps_function)
#undef INVALIDATE_CLOSURE
}
static PyGstPadPrivate*
pad_private(GstPad *pad)
{
return (PyGstPadPrivate*)gst_pad_get_element_private(pad);
PyGstPadPrivate *private;
static GQuark padprivate = 0;
if (!padprivate)
padprivate = g_quark_from_static_string ("PyGst::PadPrivate");
private = g_object_get_qdata (G_OBJECT (pad), padprivate);
if (private == NULL) {
private = g_new0(PyGstPadPrivate, 1);
private->pad = (PyGObject *) pygobject_new (G_OBJECT (pad));
Py_DECREF (private->pad);
g_object_set_qdata_full (G_OBJECT (pad), padprivate, private, free_pad_private);
}
return private;
}
static PyGstPadPrivate*
py_pad_private(PyGObject *pad)
{
PyGstPadPrivate *private;
GstPad *gpad;
gpad = (GstPad*)pygobject_get(pad);
private = (PyGstPadPrivate*)gst_pad_get_element_private(gpad);
if (private == NULL) {
/* FIXME need to free this somewhere */
private = g_new0(PyGstPadPrivate, 1);
Py_INCREF(pad);
private->pad = pad;
gst_pad_set_element_private(gpad, private);
}
return private;
return pad_private ((GstPad *)pygobject_get(pad));
}
%%
ignore
gst_pad_select
gst_pad_selectv
gst_pad_new_from_template
gst_pad_load_and_link
%%
override gst_pad_set_getcaps_function kwargs
static void EXCEPTION_HANDLER
handle_getcaps_function_exception (GValue *ret, guint n, const GValue *params)
{
g_value_set_boxed (ret, gst_pad_get_pad_template_caps (
GST_PAD (g_value_get_object (&params[0]))));
PyErr_Clear ();
}
static GstCaps *
call_getcaps_function (GstPad *pad)
{
GClosure *closure;
GValue ret = { 0, };
GValue args = { 0, };
GstCaps *caps;
g_value_init (&ret, GST_TYPE_CAPS);
g_value_init (&args, GST_TYPE_REAL_PAD);
g_value_set_object (&args, pad);
closure = pad_private(pad)->getcaps_function;
g_closure_invoke (closure, &ret, 1, &args, NULL);
caps = g_value_dup_boxed (&ret);
g_value_unset (&ret);
g_value_unset (&args);
return caps;
}
static PyObject*
_wrap_gst_pad_set_getcaps_function (PyGObject *self,
PyObject *args,
PyObject *kwargs)
{
SET_PAD_CLOSURE (self, args, kwargs, getcaps_function)
}
%%
override gst_pad_set_link_function kwargs
static GstPadLinkReturn
call_link_function (GstPad *pad, GstCaps *caps)
static void EXCEPTION_HANDLER
handle_link_function_exception (GValue *ret, guint n, const GValue *params)
{
PyObject *function;
PyObject *retval;
GstPadLinkReturn ret;
PyGILState_STATE state;
g_value_set_enum (ret, GST_PAD_LINK_REFUSED);
PyErr_Clear ();
}
static GstPadLinkReturn
call_link_function (GstPad *pad, const GstCaps *caps)
{
GClosure *closure;
GValue ret = { 0, };
GValue args[2] = { { 0, }, {0, } };
GstPadLinkReturn i;
function = pad_private(pad)->link_function;
g_value_init (&ret, GST_TYPE_PAD_LINK_RETURN);
g_value_init (&args[0], GST_TYPE_REAL_PAD);
g_value_init (&args[1], GST_TYPE_CAPS);
g_value_set_object (&args[0], pad);
g_value_set_boxed (&args[1], caps);
state = pyg_gil_state_ensure();
closure = pad_private(pad)->link_function;
g_closure_invoke (closure, &ret, 2, args, NULL);
retval = (PyObject*)PyObject_CallFunction (function,
"OO",
pad_private(pad)->pad,
pyg_boxed_new(GST_TYPE_CAPS, caps, TRUE, TRUE));
if (PyErr_Occurred ()) {
PyErr_Print ();
pyg_gil_state_release(state);
return GST_PAD_LINK_REFUSED;
}
ret = PyInt_AsLong(retval);
pyg_gil_state_release(state);
return ret;
i = g_value_get_enum (&ret);
g_value_unset (&ret);
g_value_unset (&args[0]);
g_value_unset (&args[1]);
return i;
}
static PyObject*
@ -89,55 +192,43 @@ _wrap_gst_pad_set_link_function (PyGObject *self,
PyObject *args,
PyObject *kwargs)
{
static char *kwlist[] = { "link_function", NULL };
PyObject *link_function;
GstPad *pad;
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
"O:GstPad.set_link_funcion",
kwlist,
&link_function)) {
return NULL;
}
if (!PyCallable_Check(link_function)) {
PyErr_SetString(PyExc_TypeError, "link_function not callable");
return NULL;
}
Py_INCREF(link_function);
py_pad_private(self)->link_function = link_function;
pad = (GstPad*)pygobject_get(self);
gst_pad_set_link_function(pad, (GstPadLinkFunction)call_link_function);
Py_INCREF(Py_None);
return Py_None;
SET_PAD_CLOSURE (self, args, kwargs, link_function)
}
%%
override gst_pad_set_chain_function kwargs
static void
call_chain_function(GstPad *pad, GstBuffer *buf)
static void EXCEPTION_HANDLER
handle_chain_function_exception (GValue *ret, guint n, const GValue *params)
{
PyObject *function;
PyGILState_STATE state;
GstElement *element = gst_pad_get_parent (g_value_get_object (&params[0]));
if (!_pygst_element_check_error (element))
g_assert_not_reached (); /* only returns FALSE when there's no error */
}
function = pad_private(pad)->chain_function;
state = pyg_gil_state_ensure();
PyObject_CallFunction (function,
"OO",
pad_private(pad)->pad,
pyg_boxed_new(GST_TYPE_BUFFER, buf, TRUE, TRUE));
if (PyErr_Occurred ()) {
PyErr_Print ();
pyg_gil_state_release(state);
return;
static void
call_chain_function(GstPad *pad, GstData *data)
{
GClosure *closure;
GValue args[2] = { { 0, }, { 0, } };
g_value_init (&args[0], GST_TYPE_REAL_PAD);
if (GST_IS_BUFFER (data)) {
g_value_init (&args[1], GST_TYPE_BUFFER);
} else if (GST_IS_EVENT (data)) {
g_value_init (&args[1], GST_TYPE_EVENT);
} else {
g_value_init (&args[1], GST_TYPE_DATA);
}
g_value_set_object (&args[0], pad);
g_value_take_boxed (&args[1], data);
closure = pad_private(pad)->chain_function;
g_closure_invoke (closure, NULL, 2, args, NULL);
pyg_gil_state_release(state);
g_value_unset (&args[0]);
g_value_unset (&args[1]);
}
static PyObject*
@ -145,61 +236,44 @@ _wrap_gst_pad_set_chain_function(PyGObject *self,
PyObject *args,
PyObject *kwargs)
{
static char *kwlist[] = { "chain_function", NULL };
PyObject *chain_function;
GstPad *pad;
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
"O:GstPad.set_chain_funcion",
kwlist,
&chain_function)) {
return NULL;
}
if (!PyCallable_Check(chain_function)) {
PyErr_SetString(PyExc_TypeError, "chain_function not callable");
return NULL;
}
Py_INCREF(chain_function);
py_pad_private(self)->chain_function = chain_function;
pad = (GstPad*)pygobject_get(self);
gst_pad_set_chain_function(pad, (GstPadChainFunction)call_chain_function);
Py_INCREF(Py_None);
return Py_None;
SET_PAD_CLOSURE (self, args, kwargs, chain_function)
}
%%
override gst_pad_set_event_function kwargs
static void EXCEPTION_HANDLER
handle_event_function_exception (GValue *ret, guint n, const GValue *params)
{
GstElement *element = gst_pad_get_parent (g_value_get_object (&params[0]));
if (!_pygst_element_check_error (element))
g_assert_not_reached (); /* only returns FALSE when there's no error */
}
static gboolean
call_event_function (GstPad *pad, GstEvent *event)
{
PyObject *function;
PyObject *retval;
gboolean ret;
PyGILState_STATE state;
GClosure *closure;
GValue ret = { 0, };
GValue args[2] = { { 0, }, { 0, } };
gboolean bool;
function = pad_private(pad)->event_function;
g_value_init (&ret, G_TYPE_BOOLEAN);
g_value_set_boolean (&ret, FALSE);
g_value_init (&args[0], GST_TYPE_REAL_PAD);
g_value_init (&args[1], GST_TYPE_EVENT);
g_value_set_object (&args[0], pad);
g_value_set_boxed (&args[1], event);
closure = pad_private(pad)->event_function;
state = pyg_gil_state_ensure();
g_closure_invoke (closure, &ret, 2, args, NULL);
retval = PyObject_CallFunction (function,
"OO",
pad_private(pad)->pad,
pyg_boxed_new(GST_TYPE_EVENT, event, TRUE, TRUE));
bool = g_value_get_boolean (&ret);
if (PyErr_Occurred ()) {
PyErr_Print ();
pyg_gil_state_release(state);
return FALSE;
}
ret = PyInt_AsLong(retval);
pyg_gil_state_release(state);
return ret;
g_value_unset (&ret);
g_value_unset (&args[0]);
g_value_unset (&args[1]);
return bool;
}
static PyObject*
@ -207,58 +281,41 @@ _wrap_gst_pad_set_event_function (PyGObject *self,
PyObject *args,
PyObject *kwargs)
{
static char *kwlist[] = { "event_function", NULL };
PyObject *event_function;
GstPad *pad;
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
"O:GstPad.set_event_funcion",
kwlist,
&event_function)) {
return NULL;
}
if (!PyCallable_Check(event_function)) {
PyErr_SetString(PyExc_TypeError, "event_function not callable");
return NULL;
}
Py_INCREF(event_function);
py_pad_private(self)->event_function = event_function;
pad = (GstPad*)pygobject_get(self);
gst_pad_set_event_function(pad, (GstPadEventFunction)call_event_function);
Py_INCREF(Py_None);
return Py_None;
SET_PAD_CLOSURE (self, args, kwargs, event_function)
}
%%
override gst_pad_set_get_function kwargs
static void EXCEPTION_HANDLER
handle_get_function_exception (GValue *ret, guint n, const GValue *params)
{
GstElement *element = gst_pad_get_parent (g_value_get_object (&params[0]));
if (!_pygst_element_check_error (element))
g_assert_not_reached (); /* only returns FALSE when there's no error */
}
static GstData*
call_get_function (GstPad *pad)
{
PyObject *function;
PyObject *retval;
GClosure *closure;
GValue ret = { 0, };
GValue args = { 0, };
GstData *data = NULL;
PyGILState_STATE state;
function = pad_private(pad)->get_function;
g_value_init (&ret, GST_TYPE_DATA);
g_value_init (&args, GST_TYPE_REAL_PAD);
g_value_set_object (&args, pad);
closure = pad_private(pad)->get_function;
state = pyg_gil_state_ensure();
g_closure_invoke (closure, &ret, 1, &args, NULL);
retval = PyObject_CallFunction(function, "O", pad_private(pad)->pad);
data = g_value_get_boxed (&ret);
gst_data_ref (data);
if (PyErr_Occurred()) {
PyErr_Print();
goto bail;
} else if (retval == Py_None) {
goto bail;
}
pygst_data_from_pyobject(retval, &data);
bail:
pyg_gil_state_release(state);
g_value_unset (&ret);
g_value_unset (&args);
return data;
}
@ -267,29 +324,7 @@ _wrap_gst_pad_set_get_function (PyGObject *self,
PyObject *args,
PyObject *kwargs)
{
static char *kwlist[] = { "get_function", NULL };
PyObject *get_function;
GstPad *pad;
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
"O:GstPad.set_get_funcion",
kwlist,
&get_function)) {
return NULL;
}
if (!PyCallable_Check(get_function)) {
PyErr_SetString(PyExc_TypeError, "get_function not callable");
return NULL;
}
Py_INCREF(get_function);
py_pad_private(self)->get_function = get_function;
pad = (GstPad*)pygobject_get(self);
gst_pad_set_get_function(pad, (GstPadGetFunction)call_get_function);
Py_INCREF(Py_None);
return Py_None;
SET_PAD_CLOSURE (self, args, kwargs, get_function)
}
%%
override-slot GstPad.tp_repr
@ -305,7 +340,7 @@ _wrap_gst_pad_tp_repr (PyGObject *self)
parent = gst_pad_get_parent (pad);
buf = g_strdup_printf ("<GstPad (%s:%s) at %lx>",
gst_element_get_name (parent),
parent ? gst_element_get_name (parent) : "---",
gst_pad_get_name (pad), (long) self->obj);
retval = PyString_FromString(buf);
@ -457,3 +492,56 @@ _wrap_gst_pad_template_get_caps_by_name(PyGObject *self, PyObject *args, PyObjec
/* pyg_boxed_new handles NULL checking */
return pyg_boxed_new(GST_TYPE_CAPS, ret, TRUE, TRUE);
}
%%
override gst_pad_new kwargs
static int
_wrap_gst_pad_new(PyGObject *self, PyObject *args, PyObject *kwargs)
{
static char *kwlist[] = { "name", "direction", NULL };
static char *kwlist2[] = { "template", "name", NULL };
PyGObject *templ;
char *name = NULL;
PyObject *py_direction = NULL;
GstPadDirection direction;
if (PyArg_ParseTupleAndKeywords(args, kwargs, "sO:GstPad.__init__", kwlist, &name, &py_direction)) {
if (pyg_enum_get_value(GST_TYPE_PAD_DIRECTION, py_direction, (gint *)&direction))
return -1;
self->obj = (GObject *)gst_pad_new(name, direction);
} else {
PyErr_Clear ();
if (PyArg_ParseTupleAndKeywords(args, kwargs, "O!|s:GstPad.__init__", kwlist2, &PyGstPadTemplate_Type, &templ, &name)) {
if (name == NULL)
name = GST_PAD_TEMPLATE_NAME_TEMPLATE (GST_PAD_TEMPLATE(templ->obj));
self->obj = (GObject *)gst_pad_new_from_template(GST_PAD_TEMPLATE(templ->obj), name);
}
}
if (!self->obj) {
PyErr_SetString(PyExc_RuntimeError, "could not create GstPad object");
return -1;
}
pygobject_register_wrapper((PyObject *)self);
return 0;
}
%%
override-slot GstPadTemplate.tp_getattr
#define IS_ATTR(name) (strcmp (name, attr) == 0)
PyObject *
_wrap_gst_pad_template_tp_getattr(PyObject *self, char *attr)
{
GstPadTemplate *templ = GST_PAD_TEMPLATE (pygobject_get (self));
if (IS_ATTR ("__members__")) {
return Py_BuildValue("[ssss]", "name_template", "direction", "presence", "caps" );
} else if (IS_ATTR ("name_template")) {
return PyString_FromString (GST_PAD_TEMPLATE_NAME_TEMPLATE(templ));
} else if (IS_ATTR ("direction")) {
return pyg_enum_from_gtype(GST_TYPE_PAD_DIRECTION, GST_PAD_TEMPLATE_DIRECTION(templ));
} else if (IS_ATTR ("presence")) {
return pyg_enum_from_gtype(GST_TYPE_PAD_PRESENCE, GST_PAD_TEMPLATE_PRESENCE(templ));
} else if (IS_ATTR ("caps")) {
return pyg_boxed_new (GST_TYPE_CAPS, GST_PAD_TEMPLATE_CAPS(templ), TRUE, TRUE);
}
return Py_FindMethod(_PyGstPadTemplate_methods, self, attr);
}

View file

@ -100,7 +100,7 @@ _wrap_gst_structure_set_value(PyObject *self, PyObject *args, PyObject *kwargs)
}
g_value_init(&value, type);
if (pyg_value_from_pyobject(&value, py_value) != 0) {
if (pygst_value_from_pyobject(&value, py_value) != 0) {
return NULL;
}
gst_structure_set_value(pyg_boxed_get(self, GstStructure), field,
@ -131,6 +131,27 @@ _wrap_gst_structure_get_int(PyObject *self, PyObject *args, PyObject *kwargs)
return Py_None;
}
%%
override gst_structure_get_double kwargs
static PyObject *
_wrap_gst_structure_get_double(PyObject *self, PyObject *args, PyObject *kwargs)
{
static char *kwlist[] = { "fieldname", NULL };
char *fieldname;
gdouble value;
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
"s:GstStructure.get_double",
kwlist, &fieldname))
return NULL;
if (gst_structure_get_double(pyg_boxed_get(self, GstStructure), fieldname, &value))
return PyFloat_FromDouble(value);
/* XXX: Raise exception? */
Py_INCREF(Py_None);
return Py_None;
}
%%
define GstStructure.has_key args
static PyObject*
_wrap_gst_structure_has_key(PyGObject *self, PyObject *args)
@ -163,7 +184,7 @@ _wrap_gst_structure_subscript(PyGObject *self, PyObject *py_key)
const GValue *gvalue;
gvalue = gst_structure_get_value((GstStructure*)self->obj, field);
g_assert(gvalue != NULL);
v = pyg_value_as_pyobject(gvalue, TRUE);
v = pygst_value_as_pyobject(gvalue, TRUE);
} else {
PyErr_SetString(PyExc_KeyError, field);
}
@ -184,37 +205,13 @@ _wrap_gst_structure_ass_subscript(PyGObject *self,
structure = (GstStructure*)self->obj;
key = PyString_AsString(py_key);
if (py_value != NULL) {
if (PyString_Check(py_value)) {
#if 0
GValue *value = NULL;
gst_structure_field_from_string(PyString_AsString(py_value), value);
g_print ("gvalue: %s %s %s\n",
PyString_AsString(py_value),
gst_value_serialize(value),
G_VALUE_TYPE_NAME(value));
gst_structure_set_value(structure, key, value);
#else
GValue value = { 0, };
g_value_init (&value, G_TYPE_STRING);
gst_value_deserialize(&value, PyString_AsString(py_value));
gst_structure_set_value(structure, key, &value);
g_value_unset(&value);
#endif
// gst_structure_set(structure, key, G_TYPE_STRING, PyString_AsString(py_value), NULL);
} else if (PyInt_Check(py_value))
gst_structure_set(structure, key, G_TYPE_INT, PyInt_AsLong(py_value), NULL);
else if (PyFloat_Check(py_value))
gst_structure_set(structure, key, G_TYPE_DOUBLE, PyFloat_AsDouble(py_value), NULL);
#if 0
g_value_init(&value, g_type_from_name("PyObject"));
if (pyg_value_from_pyobject(&value, py_value)) {
PyErr_SetString(PyExc_TypeError, "can't convert value");
GValue v = { 0, };
if (!pygst_value_init_for_pyobject (&v, py_value))
return -1;
if (pygst_value_from_pyobject(&v, py_value))
return -1;
}
gst_structure_set_value(structure, key, &value);
g_value_unset(&value);
#endif
gst_structure_set_value(structure, key, &v);
g_value_unset(&v);
} else {
gst_structure_remove_field(structure, key);
}
@ -245,7 +242,7 @@ pygst_structure_foreach_marshal(GQuark field_id,
state = pyg_gil_state_ensure();
py_field = Py_BuildValue("s", g_quark_to_string(field_id));
py_value = pyg_value_as_pyobject(value, FALSE);
py_value = pygst_value_as_pyobject(value, FALSE);
if (cunote->data)
retobj = PyEval_CallFunction(cunote->func, "(NNO)",
py_field, py_value,
@ -331,3 +328,18 @@ _wrap_gst_structure_from_string(PyObject *self, PyObject *args, PyObject *kwargs
/* pyg_boxed_new handles NULL checking */
return pyg_boxed_new(GST_TYPE_STRUCTURE, ret, TRUE, TRUE);
}
%%
override-slot GstStructure.tp_dealloc
static void
_wrap_gst_structure_tp_dealloc (PyObject *self)
{
PyGBoxed *boxed = (PyGBoxed *) self;
if (boxed->free_on_dealloc && boxed->boxed) {
gst_structure_free (boxed->boxed);
} else if (boxed->boxed) {
pygst_caps_map_remove_structure (self);
}
self->ob_type->tp_free((PyObject *)self);
}

301
gst/pygstvalue.c Normal file
View file

@ -0,0 +1,301 @@
/* gst-python
* Copyright (C) 2004 Andy Wingo
*
* 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: Andy Wingo <wingo@pobox.com>
*/
/* define this for all source files that don't run init_pygobject()
* before including pygobject.h */
#define NO_IMPORT_PYGOBJECT
#include "pygstvalue.h"
static PyObject *gstvalue_class = NULL;
static PyObject *gstfourcc_class = NULL;
static PyObject *gstintrange_class = NULL;
static PyObject *gstdoublerange_class = NULL;
static PyObject *gstfraction_class = NULL;
/**
* pygst_value_as_pyobject:
* @value: the GValue object.
* @copy_boxed: true if boxed values should be copied.
*
* This function creates/returns a Python wrapper object that
* represents the GValue passed as an argument.
*
* Returns: a PyObject representing the value.
*/
PyObject *
pygst_value_as_pyobject(const GValue *value, gboolean copy_boxed)
{
PyObject *ret = pyg_value_as_pyobject(value, copy_boxed);
if (!ret) {
PyErr_Clear();
if (GST_VALUE_HOLDS_FOURCC (value)) {
gchar str[5];
g_snprintf (str, 5, GST_FOURCC_FORMAT,
GST_FOURCC_ARGS (gst_value_get_fourcc (value)));
ret = PyObject_Call (gstfourcc_class, Py_BuildValue ("(s)", str), NULL);
} else if (GST_VALUE_HOLDS_INT_RANGE (value)) {
ret = PyObject_Call
(gstintrange_class,
Py_BuildValue ("ii",
gst_value_get_int_range_min (value),
gst_value_get_int_range_max (value)),
NULL);
} else if (GST_VALUE_HOLDS_DOUBLE_RANGE (value)) {
ret = PyObject_Call
(gstdoublerange_class,
Py_BuildValue ("dd",
gst_value_get_double_range_min (value),
gst_value_get_double_range_max (value)),
NULL);
} else if (GST_VALUE_HOLDS_LIST (value)) {
int i, len;
len = gst_value_list_get_size (value);
ret = PyList_New (len);
for (i=0; i<len; i++) {
PyList_SetItem (ret, i,
pygst_value_as_pyobject
(gst_value_list_get_value (value, i), copy_boxed));
}
} else if (GST_VALUE_HOLDS_FIXED_LIST (value)) {
int i, len;
len = gst_value_list_get_size (value);
ret = PyTuple_New (len);
for (i=0; i<len; i++) {
PyTuple_SetItem (ret, i,
pygst_value_as_pyobject
(gst_value_list_get_value (value, i), copy_boxed));
}
} else if (GST_VALUE_HOLDS_FRACTION (value)) {
ret = PyObject_Call
(gstfraction_class,
Py_BuildValue ("ii",
gst_value_get_fraction_numerator (value),
gst_value_get_fraction_denominator (value)),
NULL);
} else {
gchar buf[256];
g_snprintf (buf, 256, "unknown type: %s", g_type_name (G_VALUE_TYPE (value)));
PyErr_SetString(PyExc_TypeError, buf);
}
}
return ret;
}
#define VALUE_TYPE_CHECK(v, t) \
G_STMT_START{\
if (!G_VALUE_HOLDS (v, t)) {\
gchar errbuf[256];\
g_snprintf (errbuf, 256, "Could not convert %s to %s",\
g_type_name (t), g_type_name (G_VALUE_TYPE (v)));\
PyErr_SetString (PyExc_TypeError, errbuf);\
return -1;\
}}G_STMT_END
gboolean
pygst_value_init_for_pyobject (GValue *value, PyObject *obj)
{
GType t;
if (!(t = pyg_type_from_object ((PyObject*)obj->ob_type))) {
if (PyObject_IsInstance (obj, gstvalue_class)) {
PyErr_Clear ();
if (PyObject_IsInstance (obj, gstfourcc_class))
t = GST_TYPE_FOURCC;
else if (PyObject_IsInstance (obj, gstintrange_class))
t = GST_TYPE_INT_RANGE;
else if (PyObject_IsInstance (obj, gstdoublerange_class))
t = GST_TYPE_DOUBLE_RANGE;
else if (PyObject_IsInstance (obj, gstfraction_class))
t = GST_TYPE_FRACTION;
else {
PyErr_SetString(PyExc_TypeError, "Unexpected gst.Value instance");
return FALSE;
}
} else if (PyTuple_Check (obj)) {
PyErr_Clear ();
t = GST_TYPE_FIXED_LIST;
} else if (PyList_Check (obj)) {
PyErr_Clear ();
t = GST_TYPE_LIST;
} else {
/* pyg_type_from_object already set the error */
return FALSE;
}
}
g_value_init (value, t);
return TRUE;
}
/**
* pygst_value_from_pyobject:
* @value: the GValue object to store the converted value in.
* @obj: the Python object to convert.
*
* This function converts a Python object and stores the result in a
* GValue. The GValue must be initialised in advance with
* g_value_init(). If the Python object can't be converted to the
* type of the GValue, then an error is returned.
*
* Returns: 0 on success, -1 on error.
*/
int
pygst_value_from_pyobject (GValue *value, PyObject *obj)
{
GType f = g_type_fundamental (G_VALUE_TYPE (value));
/* work around a bug in pygtk whereby pyg_value_from_pyobject claims success
for unknown fundamental types without actually doing anything */
if (f < G_TYPE_MAKE_FUNDAMENTAL (G_TYPE_RESERVED_USER_FIRST)
&& pyg_value_from_pyobject (value, obj) == 0) {
return 0;
} else if (PyObject_IsInstance (obj, gstvalue_class)) {
PyErr_Clear ();
if (PyObject_IsInstance (obj, gstfourcc_class)) {
PyObject *pystr;
gchar *str;
VALUE_TYPE_CHECK (value, GST_TYPE_FOURCC);
if (!(pystr = PyObject_GetAttrString (obj, "fourcc")))
return -1;
if (!(str = PyString_AsString (pystr)))
return -1;
g_assert (strlen (str) == 4);
gst_value_set_fourcc (value, GST_STR_FOURCC (str));
} else if (PyObject_IsInstance (obj, gstintrange_class)) {
PyObject *pyval;
long low, high;
VALUE_TYPE_CHECK (value, GST_TYPE_INT_RANGE);
if (!(pyval = PyObject_GetAttrString (obj, "low")))
return -1;
low = PyInt_AsLong (pyval);
g_assert (G_MININT <= low && low <= G_MAXINT);
if (!(pyval = PyObject_GetAttrString (obj, "high")))
return -1;
high = PyInt_AsLong (pyval);
g_assert (G_MININT <= high && high <= G_MAXINT);
gst_value_set_int_range (value, (int)low, (int)high);
} else if (PyObject_IsInstance (obj, gstdoublerange_class)) {
PyObject *pyval;
double low, high;
VALUE_TYPE_CHECK (value, GST_TYPE_DOUBLE_RANGE);
if (!(pyval = PyObject_GetAttrString (obj, "low")))
return -1;
low = PyFloat_AsDouble (pyval);
if (!(pyval = PyObject_GetAttrString (obj, "high")))
return -1;
high = PyFloat_AsDouble (pyval);
gst_value_set_double_range (value, low, high);
} else if (PyObject_IsInstance (obj, gstfraction_class)) {
PyObject *pyval;
long num, denom;
VALUE_TYPE_CHECK (value, GST_TYPE_FRACTION);
if (!(pyval = PyObject_GetAttrString (obj, "num")))
return -1;
num = PyInt_AsLong (pyval);
g_assert (G_MININT <= num && num <= G_MAXINT);
if (!(pyval = PyObject_GetAttrString (obj, "denom")))
return -1;
denom = PyInt_AsLong (pyval);
g_assert (G_MININT <= denom && denom <= G_MAXINT);
gst_value_set_fraction (value, (int)num, (int)denom);
} else {
gchar buf[256];
gchar *str = PyString_AsString (PyObject_Repr(obj));
g_snprintf(buf, 256, "Unknown gst.Value type: %s", str);
PyErr_SetString(PyExc_TypeError, buf);
return -1;
}
return 0;
} else if (PyTuple_Check (obj)) {
gint i, len;
PyErr_Clear ();
VALUE_TYPE_CHECK (value, GST_TYPE_FIXED_LIST);
len = PyTuple_Size (obj);
for (i = 0; i < len; i++) {
PyObject *o;
GValue new = {0,};
o = PyTuple_GetItem (obj, i);
if (!pygst_value_init_for_pyobject (&new, o))
return -1;
if (pygst_value_from_pyobject (&new, o) != 0) {
g_value_unset (&new);
return -1;
}
gst_value_list_append_value (value, &new);
g_value_unset (&new);
}
return 0;
} else if (PyList_Check (obj)) {
gint i, len;
PyErr_Clear ();
VALUE_TYPE_CHECK (value, GST_TYPE_LIST);
len = PyList_Size (obj);
for (i = 0; i < len; i++) {
PyObject *o;
GValue new = {0,};
o = PyList_GetItem (obj, i);
if (!pygst_value_init_for_pyobject (&new, o))
return -1;
if (pygst_value_from_pyobject (&new, o) != 0) {
g_value_unset (&new);
return -1;
}
gst_value_list_append_value (value, &new);
g_value_unset (&new);
}
return 0;
} else {
return -1;
}
}
#define NULL_CHECK(o) if (!o) goto err
gboolean
pygst_value_init(void)
{
PyObject *module, *dict;
if ((module = PyImport_ImportModule("gst")) == NULL)
return FALSE;
dict = PyModule_GetDict (module);
gstvalue_class = (PyObject*)PyDict_GetItemString (dict, "Value");
NULL_CHECK (gstvalue_class);
gstfourcc_class = (PyObject*)PyDict_GetItemString (dict, "Fourcc");
NULL_CHECK (gstfourcc_class);
gstintrange_class = (PyObject*)PyDict_GetItemString (dict, "IntRange");
NULL_CHECK (gstintrange_class);
gstdoublerange_class = (PyObject*)PyDict_GetItemString (dict, "DoubleRange");
NULL_CHECK (gstdoublerange_class);
gstfraction_class = (PyObject*)PyDict_GetItemString (dict, "Fraction");
NULL_CHECK (gstfraction_class);
return TRUE;
err:
PyErr_SetString (PyExc_ImportError,
"Failed to get GstValue classes from gst module");
return FALSE;
}

29
gst/pygstvalue.h Normal file
View file

@ -0,0 +1,29 @@
/* gst-python
* Copyright (C) 2004 Andy Wingo
*
* 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: Andy Wingo <wingo@pobox.com>
*/
#include "common.h"
#include <gst/gst.h>
PyObject *pygst_value_as_pyobject(const GValue *value, gboolean copy_boxed);
gboolean pygst_value_init_for_pyobject (GValue *value, PyObject *obj);
int pygst_value_from_pyobject(GValue *value, PyObject *obj);
gboolean pygst_value_init(void);

View file

@ -31,7 +31,7 @@ tests = \
test_xml.py
check-local: testhelper.la
@PYTHONPATH=$(PYTHONPATH):$(top_builddir):$(top_builddir)/gst/.libs $(PYTHON) $(srcdir)/runtests.py
@G_DEBUG=fatal_warnings GST_DEBUG=*:0 PYTHONPATH=$(top_builddir):$(top_builddir)/gst/.libs:`pwd`:$(top_srcdir):$(PYTHONPATH) $(PYTHON) $(srcdir)/runtests.py
@rm -fr *.pyc
EXTRA_DIST = $(tests) common.py runtests.py test-object.h

View file

@ -1,4 +1,11 @@
import dl
try:
from dl import RTLD_LAZY, RTLD_GLOBAL
except ImportError:
# dl doesn't seem to be available on 64bit systems
try:
from DLFCN import RTLD_LAZY, RTLD_GLOBAL
except ImportError:
pass
import os
import sys
import unittest
@ -13,36 +20,51 @@ except:
print "WARNING: gobject doesn't have threads_init, no threadsafety"
# Don't insert before .
sys.path.insert(1, os.path.join('..'))
# sys.path.insert(1, os.path.join('..'))
# Load GST and make sure we load it from the current build
sys.setdlopenflags(dl.RTLD_LAZY | dl.RTLD_GLOBAL)
sys.setdlopenflags(RTLD_LAZY | RTLD_GLOBAL)
# Hack
sys.argv.append('--gst-debug-no-color')
path = os.path.abspath(os.path.join('..', 'gst'))
import gst
topbuilddir = os.path.abspath(os.path.join('..'))
topsrcdir = os.path.abspath(os.path.join('..'))
if topsrcdir.endswith('_build'):
topsrcdir = os.path.dirname(topsrcdir)
# gst's __init__.py is in topsrcdir/gst
path = os.path.abspath(os.path.join(topsrcdir, 'gst'))
import gst
file = gst.__file__
assert file.startswith(path), 'bad gst path: %s' % file
# gst's interfaces and play are in topbuilddir/gst
path = os.path.abspath(os.path.join(topbuilddir, 'gst'))
try:
import gst.interfaces
assert os.path.basename(gst.interfaces.__file__) != path, 'bad path'
except ImportError:
pass
# hack: we import it from our builddir/gst/.libs instead; ugly
import interfaces
gst.interfaces = interfaces
file = gst.interfaces.__file__
assert file.startswith(path), 'bad gst.interfaces path: %s' % file
try:
import gst.play
assert os.path.basename(gst.play.__file__) != path, 'bad path'
except ImportError:
# hack: we import it from our builddir/gst/.libs instead; ugly
import play
gst.play = play
pass
file = gst.play.__file__
assert file.startswith(path), 'bad gst.play path: %s' % file
# testhelper needs ltihooks
import gst.ltihooks
import ltihooks
import testhelper
# finally remove ltihooks
gst.ltihooks.uninstall()
ltihooks.uninstall()
_stderr = None

View file

@ -6,11 +6,9 @@ import unittest
SKIP_FILES = ['common', 'runtests']
dir = os.path.split(os.path.abspath(__file__))[0]
os.chdir(dir)
def gettestnames():
files = glob.glob('*.py')
dir = os.path.split(os.path.abspath(__file__))[0]
files = [os.path.basename(p) for p in glob.glob('%s/*.py' % dir)]
names = map(lambda x: x[:-3], files)
map(names.remove, SKIP_FILES)
return names
@ -22,4 +20,6 @@ for name in gettestnames():
suite.addTest(loader.loadTestsFromName(name))
testRunner = unittest.TextTestRunner()
testRunner.run(suite)
result = testRunner.run(suite)
if result.failures or result.errors:
sys.exit(1)

View file

@ -1,4 +1,5 @@
import sys
import gc
from common import gobject, gst, unittest
class BufferTest(unittest.TestCase):
@ -9,7 +10,13 @@ class BufferTest(unittest.TestCase):
def testBufferStr(self):
buffer = gst.Buffer('test')
assert str(buffer) == 'test'
def testBufferAlloc(self):
bla = 'mooooooo'
buffer = gst.Buffer(bla + '12345')
gc.collect ()
assert str(buffer) == 'mooooooo12345'
def testBufferBadConstructor(self):
self.assertRaises(TypeError, gst.Buffer, 'test', 0)

View file

@ -4,7 +4,9 @@ from common import gst, unittest
class CapsTest(unittest.TestCase):
def setUp(self):
self.caps = gst.caps_from_string('video/x-raw-yuv,width=10,framerate=5.0;video/x-raw-rgb,width=15,framerate=10.0')
self.structure = self.caps.get_structure(0)
self.structure = self.caps[0]
self.any = gst.Caps("ANY")
self.empty = gst.Caps()
def testCapsMime(self):
mime = self.structure.get_name()
@ -61,6 +63,14 @@ class CapsTest(unittest.TestCase):
assert isinstance(struct['height'], float)
assert struct['height'] == 20.0
def testCapsRefernceStructs(self):
'test that shows why it\'s not a good idea to use structures by reference'
caps = gst.Caps('hi/mom,width=0')
structure = caps[0]
del caps
assert structure['width'] == 0
def testCapsStructureChange(self):
'test if changing the structure of the caps works by reference'
assert self.structure['width'] == 10
@ -80,6 +90,57 @@ class CapsTest(unittest.TestCase):
# This causes segfault!
#self.assertRaises(TypeError, gst.Caps, struct, 10, None)
def testTrueFalse(self):
'test that comparisons using caps work the intended way'
assert self.any # not empty even though it has no structures
assert not self.empty
assert not gst.Caps('EMPTY') # also empty
assert gst.Caps('your/mom')
def testComparisons(self):
assert self.empty < self.any
assert self.empty < self.structure
assert self.empty < self.caps
assert self.caps < self.any
assert self.empty <= self.empty
assert self.caps <= self.caps
assert self.caps <= self.any
assert self.empty == "EMPTY"
assert self.caps != self.any
assert self.empty != self.any
assert self.any > self.empty
assert self.any >= self.empty
def testFilters(self):
name = 'video/x-raw-yuv'
filtercaps = gst.Caps(*[struct for struct in self.caps if struct.get_name() == name])
intersection = self.caps & 'video/x-raw-yuv'
assert filtercaps == intersection
def doSubtract(self, set, subset):
'''mimic the test in GStreamer core's testsuite/caps/subtract.c'''
assert not set - set
assert not subset - subset
assert not subset - set
test = set - subset
assert test
test2 = test | subset
test = test2 - set
assert not test
#our own extensions foolow here
assert subset == set & subset
assert set == set | subset
assert set - subset == set ^ subset
def testSubtract(self):
self.doSubtract(
gst.Caps ("some/mime, _int = [ 1, 2 ], list = { \"A\", \"B\", \"C\" }"),
gst.Caps ("some/mime, _int = 1, list = \"A\""))
self.doSubtract(
gst.Caps ("some/mime, _double = (double) 1.0; other/mime, _int = { 1, 2 }"),
gst.Caps ("some/mime, _double = (double) 1.0"))
if __name__ == "__main__":
unittest.main()

View file

@ -2,7 +2,6 @@
#
# testsuite for gstreamer.Element
import common
from common import gst, unittest
class ElementTest(unittest.TestCase):
@ -44,7 +43,7 @@ class FakeSinkTest(ElementTest):
self.error = True
self.element.connect('error', error_cb)
common.run_silent(self.element.set_state, state)
self.element.set_state (state)
assert self.error, 'error not set'
#assert error_message.find('ERROR') != -1

View file

@ -12,7 +12,10 @@ class Availability(unittest.TestCase):
assert issubclass(gst.interfaces.Mixer, gobject.GInterface)
class FunctionCall(unittest.TestCase):
def testXOverlay(self):
def FIXME_testXOverlay(self):
# obviously a testsuite is not allowed to instantiate this
# since it needs a running X or will fail. find some way to
# deal with that.
element = gst.element_factory_make('xvimagesink')
assert isinstance(element, gst.Element)
assert isinstance(element, gst.interfaces.XOverlay)

View file

@ -5,9 +5,6 @@ class StructureTest(unittest.TestCase):
def setUp(self):
self.struct = gst.structure_from_string('video/x-raw-yuv,width=10,foo="bar",pixel-aspect-ratio=1/2,framerate=5.0')
#def foo(self):
# gst.structure_from_string("foo")
def testName(self):
assert self.struct.get_name() == 'video/x-raw-yuv'
self.struct.set_name('foobar')
@ -37,30 +34,39 @@ class StructureTest(unittest.TestCase):
assert isinstance(self.struct['integer'], int)
assert self.struct['integer'] == 5, self.struct['integer']
def testCreateFourCC(self):
self.struct['fourcc'] = "(fourcc)XVID"
#assert self.struct.has_key('fourcc')
#print self.struct.to_string()
#assert isinstance(self.struct['fourcc'], int)
#assert self.struct['integer'] == 5, self.struct['integer']
def testGstValue(self):
s = self.struct
s['fourcc'] = gst.Fourcc('XVID')
assert s['fourcc'].fourcc == 'XVID'
s['frac'] = gst.Fraction(3,4)
assert s['frac'].num == 3
assert s['frac'].denom == 4
s['intrange'] = gst.IntRange(5,21)
assert s['intrange'].low == 5
assert s['intrange'].high == 21
s['doublerange'] = gst.DoubleRange(6.,21.)
assert s['doublerange'].low == 6.
assert s['doublerange'].high == 21.
s['fixedlist'] = (4, 5, 6)
assert isinstance(s['fixedlist'], tuple)
assert s['fixedlist'] == (4, 5, 6)
s['list'] = [4, 5, 6]
assert isinstance(s['list'], list)
assert s['list'] == [4, 5, 6]
# finally, some recursive tests
s['rflist'] = ([(['a', 'b'], ['c', 'd']),'e'], ['f', 'g'])
assert s['rflist'] == ([(['a', 'b'], ['c', 'd']),'e'], ['f', 'g'])
s['rlist'] = [([(['a', 'b'], ['c', 'd']),'e'], ['f', 'g']), 'h']
assert s['rlist'] == [([(['a', 'b'], ['c', 'd']),'e'], ['f', 'g']), 'h']
def testStructureChange(self):
#assert structure['pixel-aspect-ratio'].numerator == 1
#assert structure['pixel-aspect-ratio'].denominator == 2
#assert float(structure['pixel-aspect-ratio']) == 0.5
#structure['pixel-aspect-ratio'] = gst.Fraction(3, 4)
#assert structure['pixel-aspect-ratio'].numerator == 3
#assert structure['pixel-aspect-ratio'].denominator == 4
#assert float(structure['pixel-aspect-ratio']) == 0.75
assert self.struct['framerate'] == 5.0
self.struct['framerate'] = 10.0
assert self.struct['framerate'] == 10.0
# a list of heights
#structure['height'] = (20, 40, 60)
#assert structure['width'] == (20, 40, 60)
# FIXME: add ranges
self.struct['pixel-aspect-ratio'] = gst.Fraction(4, 2)
assert self.struct['pixel-aspect-ratio'].num == 2
assert self.struct['pixel-aspect-ratio'].denom == 1
if __name__ == "__main__":
unittest.main()