Gst.py: raise an error if we can't unmap the memory
[platform/upstream/gstreamer.git] / gi / overrides / Gst.py
1 # -*- Mode: Python; py-indent-offset: 4 -*-
2 # vim: tabstop=4 shiftwidth=4 expandtab
3 #
4 #       Gst.py
5 #
6 # Copyright (C) 2012 Thibault Saunier <thibault.saunier@collabora.com>
7 #
8 # This program is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU Lesser General Public
10 # License as published by the Free Software Foundation; either
11 # version 2.1 of the License, or (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 # Lesser General Public License for more details.
17 #
18 # You should have received a copy of the GNU Lesser General Public
19 # License along with this program; if not, write to the
20 # Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 # Boston, MA 02110-1301, USA.
22 # This program is free software; you can redistribute it and/or modify
23 # it under the terms of the GNU General Public License as published by
24 # the Free Software Foundation; either version 3, or (at your option)
25 # any later version.
26
27 import sys
28 import inspect
29 import itertools
30 import weakref
31 from ..overrides import override
32 from ..module import get_introspection_module
33
34 from gi.repository import GLib
35
36
37 Gst = get_introspection_module('Gst')
38
39 __all__ = []
40
41 if Gst._version == '0.10':
42     import warnings
43     warn_msg = "You have imported the Gst 0.10 module.  Because Gst 0.10 \
44 was not designed for use with introspection some of the \
45 interfaces and API will fail.  As such this is not supported \
46 by the GStreamer development team and we encourage you to \
47 port your app to Gst 1 or greater. gst-python is the recommended \
48 python module to use with Gst 0.10"
49
50     warnings.warn(warn_msg, RuntimeWarning)
51
52
53 class Element(Gst.Element):
54     @staticmethod
55     def link_many(*args):
56         '''
57         @raises: Gst.LinkError
58         '''
59         for pair in pairwise(args):
60             if not pair[0].link(pair[1]):
61                 raise LinkError(
62                     'Failed to link {} and {}'.format(pair[0], pair[1]))
63
64 Element = override(Element)
65 __all__.append('Element')
66
67
68 class Bin(Gst.Bin):
69     def __init__(self, name=None):
70         Gst.Bin.__init__(self, name=name)
71
72     def add(self, *args):
73         for arg in args:
74             if not Gst.Bin.add(self, arg):
75                 raise AddError(arg)
76
77     def make_and_add(self, factory_name, instance_name=None):
78         '''
79         @raises: Gst.AddError
80         '''
81         elem = Gst.ElementFactory.make(factory_name, instance_name)
82         if not elem:
83             raise AddError(
84                 'No such element: {}'.format(factory_name))
85         self.add(elem)
86         return elem
87
88
89 Bin = override(Bin)
90 __all__.append('Bin')
91
92 class Caps(Gst.Caps):
93
94     def __nonzero__(self):
95         return not self.is_empty()
96
97     def __new__(cls, *args):
98         if not args:
99             return Caps.new_empty()
100         elif len(args) > 1:
101             raise TypeError("wrong arguments when creating GstCaps object")
102         elif isinstance(args[0], str):
103             return Caps.from_string(args[0])
104         elif isinstance(args[0], Caps):
105             return args[0].copy()
106         elif isinstance(args[0], Structure):
107             res = Caps.new_empty()
108             res.append_structure(args[0])
109             return res
110         elif isinstance(args[0], (list, tuple)):
111             res = Caps.new_empty()
112             for e in args[0]:
113                 res.append_structure(e)
114             return res
115
116         raise TypeError("wrong arguments when creating GstCaps object")
117
118     def __init__(self, *args, **kwargs):
119         return super(Caps, self).__init__()
120
121     def __str__(self):
122         return self.to_string()
123
124     def __getitem__(self, index):
125         if index >= self.get_size():
126             raise IndexError('structure index out of range')
127         return self.get_structure(index)
128
129     def __len__(self):
130         return self.get_size()
131
132 Caps = override(Caps)
133 __all__.append('Caps')
134
135 class PadFunc:
136     def __init__(self, func):
137         self.func = func
138
139     def __call__(self, pad, parent, obj):
140         if isinstance(self.func, weakref.WeakMethod):
141             func = self.func()
142         else:
143             func = self.func
144
145         try:
146             res = func(pad, obj)
147         except TypeError:
148             try:
149                 res = func(pad, parent, obj)
150             except TypeError:
151                 raise TypeError("Invalid method %s, 2 or 3 arguments required"
152                                 % func)
153
154         return res
155
156 class Pad(Gst.Pad):
157     def __init__(self, *args, **kwargs):
158         super(Gst.Pad, self).__init__(*args, **kwargs)
159
160     def set_chain_function(self, func):
161         self.set_chain_function_full(PadFunc(func), None)
162
163     def set_event_function(self, func):
164         self.set_event_function_full(PadFunc(func), None)
165
166     def set_query_function(self, func):
167         self.set_query_function_full(PadFunc(func), None)
168
169     def query_caps(self, filter=None):
170         return Gst.Pad.query_caps(self, filter)
171
172     def set_caps(self, caps):
173         if not isinstance(caps, Gst.Caps):
174             raise TypeError("%s is not a Gst.Caps." % (type(caps)))
175
176         if not caps.is_fixed():
177             return False
178
179         event = Gst.Event.new_caps(caps)
180
181         if self.direction == Gst.PadDirection.SRC:
182             res = self.push_event(event)
183         else:
184             res = self.send_event(event)
185
186         return res
187
188     def link(self, pad):
189         ret = Gst.Pad.link(self, pad)
190         if ret != Gst.PadLinkReturn.OK:
191             raise LinkError(ret)
192         return ret
193
194 Pad = override(Pad)
195 __all__.append('Pad')
196
197 class GhostPad(Gst.GhostPad):
198     def __init__(self, name, target=None, direction=None):
199         if direction is None:
200             if target is None:
201                 raise TypeError('you must pass at least one of target '
202                                 'and direction')
203             direction = target.props.direction
204
205         Gst.GhostPad.__init__(self, name=name, direction=direction)
206         self.construct()
207         if target is not None:
208             self.set_target(target)
209
210     def query_caps(self, filter=None):
211         return Gst.GhostPad.query_caps(self, filter)
212
213 GhostPad = override(GhostPad)
214 __all__.append('GhostPad')
215
216 class IteratorError(Exception):
217     pass
218 __all__.append('IteratorError')
219
220 class AddError(Exception):
221     pass
222 __all__.append('AddError')
223
224 class LinkError(Exception):
225     pass
226 __all__.append('LinkError')
227
228 class MapError(Exception):
229     pass
230 __all__.append('MapError')
231
232
233 class Iterator(Gst.Iterator):
234     def __iter__(self):
235         while True:
236             result, value = self.next()
237             if result == Gst.IteratorResult.DONE:
238                 break
239
240             if result != Gst.IteratorResult.OK:
241                 raise IteratorError(result)
242
243             yield value
244
245 Iterator = override(Iterator)
246 __all__.append('Iterator')
247
248
249 class ElementFactory(Gst.ElementFactory):
250
251     # ElementFactory
252     def get_longname(self):
253         return self.get_metadata("long-name")
254
255     def get_description(self):
256         return self.get_metadata("description")
257
258     def get_klass(self):
259         return self.get_metadata("klass")
260
261     @classmethod
262     def make(cls, factory_name, instance_name=None):
263         return Gst.ElementFactory.make(factory_name, instance_name)
264
265 class Pipeline(Gst.Pipeline):
266     def __init__(self, name=None):
267         Gst.Pipeline.__init__(self, name=name)
268
269 Pipeline = override(Pipeline)
270 __all__.append('Pipeline')
271
272 class Structure(Gst.Structure):
273     def __new__(cls, *args, **kwargs):
274         if not args:
275             if kwargs:
276                 raise TypeError("wrong arguments when creating GstStructure, first argument"
277                                 " must be the structure name.")
278             return Structure.new_empty()
279         elif len(args) > 1:
280             raise TypeError("wrong arguments when creating GstStructure object")
281         elif isinstance(args[0], str):
282             if not kwargs:
283                 return Structure.from_string(args[0])[0]
284             struct = Structure.new_empty(args[0])
285             for k, v in kwargs.items():
286                 struct[k] = v
287
288             return struct
289         elif isinstance(args[0], Structure):
290             return args[0].copy()
291
292         raise TypeError("wrong arguments when creating GstStructure object")
293
294     def __init__(self, *args, **kwargs):
295         pass
296
297     def __getitem__(self, key):
298         return self.get_value(key)
299
300     def keys(self):
301         keys = set()
302         def foreach(fid, value, unused1, udata):
303             keys.add(GLib.quark_to_string(fid))
304             return True
305
306         self.foreach(foreach, None, None)
307         return keys
308
309     def __setitem__(self, key, value):
310         return self.set_value(key, value)
311
312     def __str__(self):
313         return self.to_string()
314
315 Structure = override(Structure)
316 __all__.append('Structure')
317
318 ElementFactory = override(ElementFactory)
319 __all__.append('ElementFactory')
320
321 class Fraction(Gst.Fraction):
322     def __init__(self, num, denom=1):
323         def __gcd(a, b):
324             while b != 0:
325                 tmp = a
326                 a = b
327                 b = tmp % b
328             return abs(a)
329
330         def __simplify():
331             num = self.num
332             denom = self.denom
333
334             if num < 0:
335                 num = -num
336                 denom = -denom
337
338             # Compute greatest common divisor
339             gcd = __gcd(num, denom)
340             if gcd != 0:
341                 num /= gcd
342                 denom /= gcd
343
344             self.num = num
345             self.denom = denom
346
347         self.num = num
348         self.denom = denom
349
350         __simplify()
351         self.type = "fraction"
352
353     def __repr__(self):
354         return '<Gst.Fraction %s>' % (str(self))
355
356     def __value__(self):
357         return self.num / self.denom
358
359     def __eq__(self, other):
360         if isinstance(other, Fraction):
361             return self.num * other.denom == other.num * self.denom
362         return False
363
364     def __ne__(self, other):
365         return not self.__eq__(other)
366
367     def __mul__(self, other):
368         if isinstance(other, Fraction):
369             return Fraction(self.num * other.num,
370                             self.denom * other.denom)
371         elif isinstance(other, int):
372             return Fraction(self.num * other, self.denom)
373         raise TypeError("%s is not supported, use Gst.Fraction or int." %
374                 (type(other)))
375
376     __rmul__ = __mul__
377
378     def __truediv__(self, other):
379         if isinstance(other, Fraction):
380             return Fraction(self.num * other.denom,
381                             self.denom * other.num)
382         elif isinstance(other, int):
383             return Fraction(self.num, self.denom * other)
384         return TypeError("%s is not supported, use Gst.Fraction or int." %
385                 (type(other)))
386
387     __div__ = __truediv__
388
389     def __rtruediv__(self, other):
390         if isinstance(other, int):
391             return Fraction(self.denom * other, self.num)
392         return TypeError("%s is not an int." % (type(other)))
393
394     __rdiv__ = __rtruediv__
395
396     def __float__(self):
397         return float(self.num) / float(self.denom)
398
399     def __str__(self):
400         return '%d/%d' % (self.num, self.denom)
401
402 Fraction = override(Fraction)
403 __all__.append('Fraction')
404
405
406 class IntRange(Gst.IntRange):
407     def __init__(self, r):
408         if not isinstance(r, range):
409             raise TypeError("%s is not a range." % (type(r)))
410
411         if (r.start >= r.stop):
412             raise TypeError("Range start must be smaller then stop")
413
414         if r.start % r.step != 0:
415             raise TypeError("Range start must be a multiple of the step")
416
417         if r.stop % r.step != 0:
418             raise TypeError("Range stop must be a multiple of the step")
419
420         self.range = r
421
422     def __repr__(self):
423         return '<Gst.IntRange [%d,%d,%d]>' % (self.range.start,
424                 self.range.stop, self.range.step)
425
426     def __str__(self):
427         if self.range.step == 1:
428             return '[%d,%d]' % (self.range.start, self.range.stop)
429         else:
430             return '[%d,%d,%d]' % (self.range.start, self.range.stop,
431                     self.range.step)
432
433     def __eq__(self, other):
434         if isinstance(other, range):
435             return self.range == other
436         elif isinstance(other, IntRange):
437             return self.range == other.range
438         return False
439
440 if sys.version_info >= (3, 0):
441     IntRange = override(IntRange)
442     __all__.append('IntRange')
443
444
445 class Int64Range(Gst.Int64Range):
446     def __init__(self, r):
447         if not isinstance(r, range):
448             raise TypeError("%s is not a range." % (type(r)))
449
450         if (r.start >= r.stop):
451             raise TypeError("Range start must be smaller then stop")
452
453         if r.start % r.step != 0:
454             raise TypeError("Range start must be a multiple of the step")
455
456         if r.stop % r.step != 0:
457             raise TypeError("Range stop must be a multiple of the step")
458
459         self.range = r
460
461     def __repr__(self):
462         return '<Gst.Int64Range [%d,%d,%d]>' % (self.range.start,
463                 self.range.stop, self.range.step)
464
465     def __str__(self):
466         if self.range.step == 1:
467             return '(int64)[%d,%d]' % (self.range.start, self.range.stop)
468         else:
469             return '(int64)[%d,%d,%d]' % (self.range.start, self.range.stop,
470                     self.range.step)
471
472     def __eq__(self, other):
473         if isinstance(other, range):
474             return self.range == other
475         elif isinstance(other, IntRange):
476             return self.range == other.range
477         return False
478
479 class Bitmask(Gst.Bitmask):
480     def __init__(self, v):
481         if not isinstance(v, int):
482             raise TypeError("%s is not an int." % (type(v)))
483
484         self.v = int(v)
485
486     def __str__(self):
487         return hex(self.v)
488
489     def __eq__(self, other):
490         return self.v == other
491
492
493 Bitmask = override(Bitmask)
494 __all__.append('Bitmask')
495
496
497 if sys.version_info >= (3, 0):
498     Int64Range = override(Int64Range)
499     __all__.append('Int64Range')
500
501
502 class DoubleRange(Gst.DoubleRange):
503     def __init__(self, start, stop):
504         self.start = float(start)
505         self.stop = float(stop)
506
507         if (start >= stop):
508             raise TypeError("Range start must be smaller then stop")
509
510     def __repr__(self):
511         return '<Gst.DoubleRange [%s,%s]>' % (str(self.start), str(self.stop))
512
513     def __str__(self):
514         return '(double)[%s,%s]' % (str(self.range.start), str(self.range.stop))
515
516
517 DoubleRange = override(DoubleRange)
518 __all__.append('DoubleRange')
519
520
521 class FractionRange(Gst.FractionRange):
522     def __init__(self, start, stop):
523         if not isinstance(start, Gst.Fraction):
524             raise TypeError("%s is not a Gst.Fraction." % (type(start)))
525
526         if not isinstance(stop, Gst.Fraction):
527             raise TypeError("%s is not a Gst.Fraction." % (type(stop)))
528
529         if (float(start) >= float(stop)):
530             raise TypeError("Range start must be smaller then stop")
531
532         self.start = start
533         self.stop = stop
534
535     def __repr__(self):
536         return '<Gst.FractionRange [%s,%s]>' % (str(self.start),
537                 str(self.stop))
538
539     def __str__(self):
540         return '(fraction)[%s,%s]' % (str(self.start), str(self.stop))
541
542 FractionRange = override(FractionRange)
543 __all__.append('FractionRange')
544
545
546 class ValueArray(Gst.ValueArray):
547     def __init__(self, array):
548         self.array = list(array)
549
550     def __getitem__(self, index):
551         return self.array[index]
552
553     def __setitem__(self, index, value):
554         self.array[index] = value
555
556     def __len__(self):
557         return len(self.array)
558
559     def __str__(self):
560         return '<' + ','.join(map(str,self.array)) + '>'
561
562     def __repr__(self):
563         return '<Gst.ValueArray %s>' % (str(self))
564
565 ValueArray = override(ValueArray)
566 __all__.append('ValueArray')
567
568
569 class ValueList(Gst.ValueList):
570     def __init__(self, array):
571         self.array = list(array)
572
573     def __getitem__(self, index):
574         return self.array[index]
575
576     def __setitem__(self, index, value):
577         self.array[index] = value
578
579     def __len__(self):
580         return len(self.array)
581
582     def __str__(self):
583         return '{' + ','.join(map(str,self.array)) + '}'
584
585     def __repr__(self):
586         return '<Gst.ValueList %s>' % (str(self))
587
588 ValueList = override(ValueList)
589 __all__.append('ValueList')
590
591 # From https://docs.python.org/3/library/itertools.html
592
593
594 def pairwise(iterable):
595     a, b = itertools.tee(iterable)
596     next(b, None)
597     return zip(a, b)
598
599 class MapInfo:
600     def __init__(self):
601         self.memory = None
602         self.flags = Gst.MapFlags(0)
603         self.size = 0
604         self.maxsize = 0
605         self.data = None
606         self.user_data = None
607         self.__parent__ = None
608
609     def __enter__(self):
610         return self
611
612     def __exit__(self, type, value, tb):
613         self.__parent__.unmap(self)
614
615 __all__.append("MapInfo")
616
617 class Buffer(Gst.Buffer):
618
619     def map_range(self, idx, length, flags):
620         mapinfo = MapInfo()
621         if (_gi_gst.buffer_override_map_range(self, mapinfo, idx, length, int(flags))):
622             mapinfo.__parent__ = self
623             return (mapinfo)
624         raise MapError('MappingError','Buffer mapping was not successfull')
625
626     def map(self, flags):
627         mapinfo = MapInfo()
628         if _gi_gst.buffer_override_map(self, mapinfo, int(flags)):
629             mapinfo.__parent__ = self
630             return mapinfo
631         raise MapError('MappingError','Buffer mapping was not successfull')
632
633     def unmap(self, mapinfo):
634         mapinfo.__parent__ = None
635         if _gi_gst.buffer_override_unmap(self, mapinfo) is not True:
636             raise MapError('UnmappingError','Buffer unmapping was not successfull')
637
638 Buffer = override(Buffer)
639 __all__.append('Buffer')
640
641 class Memory(Gst.Memory):
642
643     def map(self, flags):
644         mapinfo = MapInfo()
645         if (_gi_gst.memory_override_map(self, mapinfo, int(flags))):
646             mapinfo.__parent__ = self
647             return (mapinfo)
648         raise MapError('MappingError','Memory mapping was not successfull')
649
650     def unmap(self, mapinfo):
651         mapinfo.__parent__ = None
652         if _gi_gst.memory_override_unmap(self, mapinfo) is not True:
653             raise MapError('UnmappingError','Memory unmapping was not successfull')
654
655 Memory = override(Memory)
656 __all__.append('Memory')
657
658 def TIME_ARGS(time):
659     if time == Gst.CLOCK_TIME_NONE:
660         return "CLOCK_TIME_NONE"
661
662     return "%u:%02u:%02u.%09u" % (time / (Gst.SECOND * 60 * 60),
663                                   (time / (Gst.SECOND * 60)) % 60,
664                                   (time / Gst.SECOND) % 60,
665                                   time % Gst.SECOND)
666 __all__.append('TIME_ARGS')
667
668 from gi.overrides import _gi_gst
669 _gi_gst
670
671 # maybe more python and less C some day if core turns a bit more introspection
672 # and binding friendly in the debug area
673 Gst.trace = _gi_gst.trace
674 Gst.log = _gi_gst.log
675 Gst.debug = _gi_gst.debug
676 Gst.info = _gi_gst.info
677 Gst.warning = _gi_gst.warning
678 Gst.error = _gi_gst.error
679 Gst.fixme = _gi_gst.fixme
680 Gst.memdump = _gi_gst.memdump
681
682 # Make sure PyGst is not usable if GStreamer has not been initialized
683 class NotInitialized(Exception):
684     pass
685 __all__.append('NotInitialized')
686
687 def fake_method(*args):
688     raise NotInitialized("Please call Gst.init(argv) before using GStreamer")
689
690
691 real_functions = [o for o in inspect.getmembers(Gst) if isinstance(o[1], type(Gst.init))]
692
693 class_methods = []
694 for cname_klass in [o for o in inspect.getmembers(Gst) if isinstance(o[1], type(Gst.Element)) or isinstance(o[1], type(Gst.Caps))]:
695     class_methods.append((cname_klass,
696                          [(o, cname_klass[1].__dict__[o])
697                           for o in cname_klass[1].__dict__
698                           if isinstance(cname_klass[1].__dict__[o], type(Gst.init))]))
699
700 def init_pygst():
701     for fname, function in real_functions:
702         if fname not in ["init", "init_check", "deinit"]:
703             setattr(Gst, fname, function)
704
705     for cname_class, methods in class_methods:
706         for mname, method in methods:
707             setattr(cname_class[1], mname, method)
708
709
710 def deinit_pygst():
711     for fname, func in real_functions:
712         if fname not in ["init", "init_check", "deinit", "is_initialized"]:
713             setattr(Gst, fname, fake_method)
714     for cname_class, methods in class_methods:
715         for mname, method in methods:
716             setattr(cname_class[1], mname, fake_method)
717
718 real_init = Gst.init
719 def init(argv):
720     init_pygst()
721     return real_init(argv)
722 Gst.init = init
723
724 real_init_check = Gst.init_check
725 def init_check(argv):
726     init_pygst()
727     return real_init_check(argv)
728 Gst.init_check = init_check
729
730 real_deinit = Gst.deinit
731 def deinit():
732     deinit_pygst()
733     return real_deinit()
734
735 Gst.deinit = deinit
736
737 if not Gst.is_initialized():
738     deinit_pygst()