Gst.py 18.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
# -*- Mode: Python; py-indent-offset: 4 -*-
# vim: tabstop=4 shiftwidth=4 expandtab
#
#       Gst.py
#
# Copyright (C) 2012 Thibault Saunier <thibault.saunier@collabora.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the
Simon Farnsworth's avatar
Simon Farnsworth committed
20
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21
# Boston, MA 02110-1301, USA.
22 23 24 25 26 27
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3, or (at your option)
# any later version.

import sys
28
import inspect
29
from ..overrides import override
30
from ..module import get_introspection_module
31

32 33
from gi.repository import GLib

34 35 36 37

if sys.version_info >= (3, 0):
    _basestring = str
    _callable = lambda c: hasattr(c, '__call__')
38
    long = int
39 40 41 42
else:
    _basestring = basestring
    _callable = callable

43 44
Gst = get_introspection_module('Gst')

45 46 47 48 49 50 51 52 53 54 55 56 57
__all__ = []

if Gst._version == '0.10':
    import warnings
    warn_msg = "You have imported the Gst 0.10 module.  Because Gst 0.10 \
was not designed for use with introspection some of the \
interfaces and API will fail.  As such this is not supported \
by the GStreamer development team and we encourage you to \
port your app to Gst 1 or greater. gst-python is the recomended \
python module to use with Gst 0.10"

    warnings.warn(warn_msg, RuntimeWarning)

58 59 60 61 62 63 64 65 66 67 68 69
class Bin(Gst.Bin):
    def __init__(self, name=None):
        Gst.Bin.__init__(self, name=name)

    def add(self, *args):
        for arg in args:
            if not Gst.Bin.add(self, arg):
                raise AddError(arg)

Bin = override(Bin)
__all__.append('Bin')

70 71
class Caps(Gst.Caps):

72 73 74
    def __nonzero__(self):
        return not self.is_empty()

75 76
    def __new__(cls, *args):
        if not args:
77
            return Caps.new_empty()
78
        elif len(args) > 1:
79
            raise TypeError("wrong arguments when creating GstCaps object")
80 81 82 83 84 85 86 87 88 89 90 91 92
        elif isinstance(args[0], str):
            return Caps.from_string(args[0])
        elif isinstance(args[0], Caps):
            return args[0].copy()
        elif isinstance(args[0], Structure):
            res = Caps.new_empty()
            res.append_structure(args[0])
            return res
        elif isinstance(args[0], (list, tuple)):
            res = Caps.new_empty()
            for e in args[0]:
                res.append_structure(e)
            return res
93 94 95

        raise TypeError("wrong arguments when creating GstCaps object")

96 97 98
    def __init__(self, *args, **kwargs):
        return super(Caps, self).__init__()

99 100 101
    def __str__(self):
        return self.to_string()

102 103 104 105 106 107 108 109
    def __getitem__(self, index):
        if index >= self.get_size():
            raise IndexError('structure index out of range')
        return self.get_structure(index)

    def __len__(self):
        return self.get_size()

110 111 112
Caps = override(Caps)
__all__.append('Caps')

113
class Pad(Gst.Pad):
114
    def __init__(self, *args, **kwargs):
115 116
        self._real_chain_func = None
        self._real_event_func = None
117
        self._real_query_func = None
118
        super(Gst.Pad, self).__init__(*args, **kwargs)
119 120 121 122

    def _chain_override(self, pad, parent, buf):
        return self._real_chain_func(pad, buf)

123 124 125 126 127
    def _event_override(self, pad, parent, event):
        return self._real_event_func(pad, event)

    def _query_override(self, pad, parent, query):
        query.mini_object.refcount -= 1
128
        try:
129
            res = self._real_query_func(pad, query)
130 131 132 133
        except TypeError:
            try:
                res = self._real_query_func(pad, parent, query)
            except TypeError:
134
                raise TypeError("Invalid query method %s, 2 or 3 arguments required"
135
                                % self._real_query_func)
136 137 138
        query.mini_object.refcount += 1

        return res
139 140 141 142 143 144 145 146 147

    def set_chain_function(self, func):
        self._real_chain_func = func
        self.set_chain_function_full(self._chain_override, None)

    def set_event_function(self, func):
        self._real_event_func = func
        self.set_event_function_full(self._event_override, None)

148 149
    def set_query_function(self, func):
        self._real_query_func = func
150
        self.set_query_function_full(self._query_override, None)
151 152 153 154 155

    def set_query_function_full(self, func, udata):
        self._real_query_func = func
        self._real_set_query_function_full(self._query_override, None)

156 157 158 159 160 161 162 163 164
    def query_caps(self, filter=None):
        return Gst.Pad.query_caps(self, filter)

    def link(self, pad):
        ret = Gst.Pad.link(self, pad)
        if ret != Gst.PadLinkReturn.OK:
            raise LinkError(ret)
        return ret

165
Pad._real_set_query_function_full = Gst.Pad.set_query_function_full
166 167 168
Pad = override(Pad)
__all__.append('Pad')

169 170 171 172
class GhostPad(Gst.GhostPad):
    def __init__(self, name, target=None, direction=None):
        if direction is None:
            if target is None:
173
                raise TypeError('you must pass at least one of target '
174
                                'and direction')
175 176 177 178 179 180 181
            direction = target.props.direction

        Gst.GhostPad.__init__(self, name=name, direction=direction)
        self.construct()
        if target is not None:
            self.set_target(target)

182 183 184
    def query_caps(self, filter=None):
        return Gst.GhostPad.query_caps(self, filter)

185 186 187
GhostPad = override(GhostPad)
__all__.append('GhostPad')

188 189 190 191
class IteratorError(Exception):
    pass
__all__.append('IteratorError')

192 193 194 195
class AddError(Exception):
    pass
__all__.append('AddError')

196 197 198 199
class LinkError(Exception):
    pass
__all__.append('LinkError')

200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
class Iterator(Gst.Iterator):
    def __iter__(self):
        while True:
            result, value = self.next()
            if result == Gst.IteratorResult.DONE:
                break

            if result != Gst.IteratorResult.OK:
                raise IteratorError(result)

            yield value

Iterator = override(Iterator)
__all__.append('Iterator')


216 217 218 219 220 221 222 223 224 225 226 227
class ElementFactory(Gst.ElementFactory):

    # ElementFactory
    def get_longname(self):
        return self.get_metadata("long-name")

    def get_description(self):
        return self.get_metadata("description")

    def get_klass(self):
        return self.get_metadata("klass")

228 229 230
    @classmethod
    def make(cls, factory_name, instance_name=None):
        return Gst.ElementFactory.make(factory_name, instance_name)
231 232 233 234 235 236 237 238

class Pipeline(Gst.Pipeline):
    def __init__(self, name=None):
        Gst.Pipeline.__init__(self, name=name)

Pipeline = override(Pipeline)
__all__.append('Pipeline')

239
class Structure(Gst.Structure):
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
    def __new__(cls, *args, **kwargs):
        if not args:
            if kwargs:
                raise TypeError("wrong arguments when creating GstStructure, first argument"
                                " must be the structure name.")
            return Structure.new_empty()
        elif len(args) > 1:
            raise TypeError("wrong arguments when creating GstStructure object")
        elif isinstance(args[0], str):
            if not kwargs:
                return Structure.from_string(args[0])[0]
            struct = Structure.new_empty(args[0])
            for k, v in kwargs.items():
                struct[k] = v

            return struct
        elif isinstance(args[0], Structure):
            return args[0].copy()

        raise TypeError("wrong arguments when creating GstStructure object")

261 262 263
    def __init__(self, *args, **kwargs):
        pass

264 265
    def __getitem__(self, key):
        return self.get_value(key)
266

267 268 269 270 271 272 273 274
    def keys(self):
        keys = set()
        def foreach(fid, value, unused1, udata):
            keys.add(GLib.quark_to_string(fid))
            return True

        self.foreach(foreach, None, None)
        return keys
275

276 277 278
    def __setitem__(self, key, value):
        return self.set_value(key, value)

279 280 281
    def __str__(self):
        return self.to_string()

282 283 284
Structure = override(Structure)
__all__.append('Structure')

285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
ElementFactory = override(ElementFactory)
__all__.append('ElementFactory')

class Fraction(Gst.Fraction):
    def __init__(self, num, denom=1):
        def __gcd(a, b):
            while b != 0:
                tmp = a
                a = b
                b = tmp % b
            return abs(a)

        def __simplify():
            num = self.num
            denom = self.denom

            if num < 0:
                num = -num
                denom = -denom

            # Compute greatest common divisor
            gcd = __gcd(num, denom)
            if gcd != 0:
                num /= gcd
                denom /= gcd

            self.num = num
            self.denom = denom

        self.num = num
        self.denom = denom

        __simplify()
        self.type = "fraction"

    def __repr__(self):
321
        return '<Gst.Fraction %s>' % (str(self))
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339

    def __value__(self):
        return self.num / self.denom

    def __eq__(self, other):
        if isinstance(other, Fraction):
            return self.num * other.denom == other.num * self.denom
        return False

    def __ne__(self, other):
        return not self.__eq__(other)

    def __mul__(self, other):
        if isinstance(other, Fraction):
            return Fraction(self.num * other.num,
                            self.denom * other.denom)
        elif isinstance(other, int):
            return Fraction(self.num * other, self.denom)
340 341
        raise TypeError("%s is not supported, use Gst.Fraction or int." %
                (type(other)))
342 343 344

    __rmul__ = __mul__

345
    def __truediv__(self, other):
346 347 348 349 350
        if isinstance(other, Fraction):
            return Fraction(self.num * other.denom,
                            self.denom * other.num)
        elif isinstance(other, int):
            return Fraction(self.num, self.denom * other)
351 352
        return TypeError("%s is not supported, use Gst.Fraction or int." %
                (type(other)))
353

354 355 356
    __div__ = __truediv__

    def __rtruediv__(self, other):
357 358
        if isinstance(other, int):
            return Fraction(self.denom * other, self.num)
359
        return TypeError("%s is not an int." % (type(other)))
360

361 362
    __rdiv__ = __rtruediv__

363 364 365
    def __float__(self):
        return float(self.num) / float(self.denom)

366 367 368
    def __str__(self):
        return '%d/%d' % (self.num, self.denom)

369 370 371
Fraction = override(Fraction)
__all__.append('Fraction')

372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399

class IntRange(Gst.IntRange):
    def __init__(self, r):
        if not isinstance(r, range):
            raise TypeError("%s is not a range." % (type(r)))

        if (r.start >= r.stop):
            raise TypeError("Range start must be smaller then stop")

        if r.start % r.step != 0:
            raise TypeError("Range start must be a multiple of the step")

        if r.stop % r.step != 0:
            raise TypeError("Range stop must be a multiple of the step")

        self.range = r

    def __repr__(self):
        return '<Gst.IntRange [%d,%d,%d]>' % (self.range.start,
                self.range.stop, self.range.step)

    def __str__(self):
        if self.range.step == 1:
            return '[%d,%d]' % (self.range.start, self.range.stop)
        else:
            return '[%d,%d,%d]' % (self.range.start, self.range.stop,
                    self.range.step)

400 401 402 403 404 405 406
    def __eq__(self, other):
        if isinstance(other, range):
            return self.range == other
        elif isinstance(other, IntRange):
            return self.range == other.range
        return False

407 408 409
if sys.version_info >= (3, 0):
    IntRange = override(IntRange)
    __all__.append('IntRange')
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438


class Int64Range(Gst.Int64Range):
    def __init__(self, r):
        if not isinstance(r, range):
            raise TypeError("%s is not a range." % (type(r)))

        if (r.start >= r.stop):
            raise TypeError("Range start must be smaller then stop")

        if r.start % r.step != 0:
            raise TypeError("Range start must be a multiple of the step")

        if r.stop % r.step != 0:
            raise TypeError("Range stop must be a multiple of the step")

        self.range = r

    def __repr__(self):
        return '<Gst.Int64Range [%d,%d,%d]>' % (self.range.start,
                self.range.stop, self.range.step)

    def __str__(self):
        if self.range.step == 1:
            return '(int64)[%d,%d]' % (self.range.start, self.range.stop)
        else:
            return '(int64)[%d,%d,%d]' % (self.range.start, self.range.stop,
                    self.range.step)

439 440 441 442 443 444
    def __eq__(self, other):
        if isinstance(other, range):
            return self.range == other
        elif isinstance(other, IntRange):
            return self.range == other.range
        return False
445

446 447
class Bitmask(Gst.Bitmask):
    def __init__(self, v):
448 449
        if not isinstance(v, long) and not isinstance(v, int):
            raise TypeError("%s is not an int or long." % (type(v)))
450

451
        self.v = long(v)
452 453 454 455 456 457 458 459 460 461 462 463

    def __str__(self):
        return hex(self.v)

    def __eq__(self, other):
        return self.v == other


Bitmask = override(Bitmask)
__all__.append('Bitmask')


464 465 466
if sys.version_info >= (3, 0):
    Int64Range = override(Int64Range)
    __all__.append('Int64Range')
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558


class DoubleRange(Gst.DoubleRange):
    def __init__(self, start, stop):
        self.start = float(start)
        self.stop = float(stop)

        if (start >= stop):
            raise TypeError("Range start must be smaller then stop")

    def __repr__(self):
        return '<Gst.DoubleRange [%s,%s]>' % (str(self.start), str(self.stop))

    def __str__(self):
        return '(double)[%s,%s]' % (str(self.range.start), str(self.range.stop))


DoubleRange = override(DoubleRange)
__all__.append('DoubleRange')


class FractionRange(Gst.FractionRange):
    def __init__(self, start, stop):
        if not isinstance(start, Gst.Fraction):
            raise TypeError("%s is not a Gst.Fraction." % (type(start)))

        if not isinstance(stop, Gst.Fraction):
            raise TypeError("%s is not a Gst.Fraction." % (type(stop)))

        if (float(start) >= float(stop)):
            raise TypeError("Range start must be smaller then stop")

        self.start = start
        self.stop = stop

    def __repr__(self):
        return '<Gst.FractionRange [%s,%s]>' % (str(self.start),
                str(self.stop))

    def __str__(self):
        return '(fraction)[%s,%s]' % (str(self.start), str(self.stop))

FractionRange = override(FractionRange)
__all__.append('FractionRange')


class ValueArray(Gst.ValueArray):
    def __init__(self, array):
        self.array = list(array)

    def __getitem__(self, index):
        return self.array[index]

    def __setitem__(self, index, value):
        self.array[index] = value

    def __len__(self):
        return len(self.array)

    def __str__(self):
        return '<' + ','.join(map(str,self.array)) + '>'

    def __repr__(self):
        return '<Gst.ValueArray %s>' % (str(self))

ValueArray = override(ValueArray)
__all__.append('ValueArray')


class ValueList(Gst.ValueList):
    def __init__(self, array):
        self.array = list(array)

    def __getitem__(self, index):
        return self.array[index]

    def __setitem__(self, index, value):
        self.array[index] = value

    def __len__(self):
        return len(self.array)

    def __str__(self):
        return '{' + ','.join(map(str,self.array)) + '}'

    def __repr__(self):
        return '<Gst.ValueList %s>' % (str(self))

ValueList = override(ValueList)
__all__.append('ValueList')


559 560 561 562 563 564 565 566 567 568
def TIME_ARGS(time):
    if time == Gst.CLOCK_TIME_NONE:
        return "CLOCK_TIME_NONE"

    return "%u:%02u:%02u.%09u" % (time / (Gst.SECOND * 60 * 60),
                                  (time / (Gst.SECOND * 60)) % 60,
                                  (time / Gst.SECOND) % 60,
                                  time % Gst.SECOND)
__all__.append('TIME_ARGS')

569
from . import _gi_gst
570
_gi_gst
571

572 573
# maybe more python and less C some day if core turns a bit more introspection
# and binding friendly in the debug area
574
Gst.trace = _gi_gst.trace
575 576 577 578 579 580 581
Gst.log = _gi_gst.log
Gst.debug = _gi_gst.debug
Gst.info = _gi_gst.info
Gst.warning = _gi_gst.warning
Gst.error = _gi_gst.error
Gst.fixme = _gi_gst.fixme
Gst.memdump = _gi_gst.memdump
582 583

# Make sure PyGst is not usable if GStreamer has not been initialized
584
class NotInitialized(Exception):
585
    pass
586
__all__.append('NotInitialized')
587 588

def fake_method(*args):
589
    raise NotInitialized("Please call Gst.init(argv) before using GStreamer")
590 591


592
real_functions = [o for o in inspect.getmembers(Gst) if isinstance(o[1], type(Gst.init))]
593 594

class_methods = []
595
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))]:
596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637
    class_methods.append((cname_klass,
                         [(o, cname_klass[1].__dict__[o])
                          for o in cname_klass[1].__dict__
                          if isinstance(cname_klass[1].__dict__[o], type(Gst.init))]))

def init_pygst():
    for fname, function in real_functions:
        if fname not in ["init", "init_check", "deinit"]:
            setattr(Gst, fname, function)

    for cname_class, methods in class_methods:
        for mname, method in methods:
            setattr(cname_class[1], mname, method)


def deinit_pygst():
    for fname, func in real_functions:
        if fname not in ["init", "init_check", "deinit"]:
            setattr(Gst, fname, fake_method)
    for cname_class, methods in class_methods:
        for mname, method in methods:
            setattr(cname_class[1], mname, fake_method)

real_init = Gst.init
def init(argv):
    init_pygst()
    return real_init(argv)
Gst.init = init

real_init_check = Gst.init_check
def init_check(argv):
    init_pygst()
    return real_init_check(argv)
Gst.init_check = init_check

real_deinit = Gst.deinit
def deinit():
    deinit_pygst()
    return real_deinit()

Gst.deinit = deinit

638 639
if not Gst.is_initialized():
    deinit_pygst()