~ubuntu-branches/ubuntu/trusty/pitivi/trusty

« back to all changes in this revision

Viewing changes to pitivi/factories/base.py

* New upstream pre-release:
  + debian/control:
    - Update dependencies.
* debian/control:
  + Update Standards-Version to 3.8.2.

Show diffs side-by-side

added added

removed removed

Lines of Context:
28
28
from pitivi.log.loggable import Loggable
29
29
from pitivi.elements.singledecodebin import SingleDecodeBin
30
30
from pitivi.signalinterface import Signallable
 
31
from pitivi.stream import match_stream_groups_map, AudioStream
31
32
 
32
33
# FIXME: define a proper hierarchy
33
34
class ObjectFactoryError(Exception):
45
46
 
46
47
    @ivar name: Factory name.
47
48
    @type name: C{str}
48
 
    @ivar displayname: Nicer name for the factory that could be used in an UI.
49
 
    @type displayname: C{str}
50
49
    @ivar input_streams: List of input streams.
51
50
    @type input_streams: C{list}
52
51
    @ivar output_streams: List of output streams.
64
63
    @type bins: List of C{gst.Bin}
65
64
    """
66
65
 
67
 
    def __init__(self, name="", displayname=""):
 
66
    def __init__(self, name=""):
68
67
        Loggable.__init__(self)
69
68
        self.info("name:%s", name)
70
69
        self.parent = None
71
70
        self.name = name
72
 
        self.displayname = displayname
73
71
        self.input_streams = []
74
72
        self.output_streams = []
75
73
        self.duration = gst.CLOCK_TIME_NONE
190
188
        """
191
189
 
192
190
    def __str__(self):
193
 
        return "<%s: %s>" % (self.__class__.__name__, self.displayname or self.name)
 
191
        return "<%s: %s>" % (self.__class__.__name__, self.name)
 
192
 
 
193
    def getInterpolatedProperties(self, stream):
 
194
        return {}
194
195
 
195
196
class SourceFactory(ObjectFactory):
196
197
    """
207
208
        'bin-released': ['bin']
208
209
    }
209
210
 
210
 
    def __init__(self, name='', displayname=''):
211
 
        ObjectFactory.__init__(self, name, displayname)
 
211
    # make this an attribute to inject it from tests
 
212
    singleDecodeBinClass = SingleDecodeBin
 
213
 
 
214
    def __init__(self, uri, name=''):
 
215
        name = name or os.path.basename(unquote(uri))
 
216
        ObjectFactory.__init__(self, name)
 
217
        self.uri = uri
212
218
        self.max_bins = -1
213
219
        self.current_bins = 0
214
220
 
228
234
 
229
235
        compatible_stream = None
230
236
        self.debug("stream %r", output_stream)
 
237
 
231
238
        if output_stream is not None:
232
 
            self.debug("streams %r", self.output_streams)
233
 
            for stream in self.output_streams:
234
 
                if output_stream.isCompatible(stream):
235
 
                    compatible_stream = stream
236
 
                    break
 
239
            self.debug("output_streams:%r", self.output_streams)
 
240
            stream_map = match_stream_groups_map([output_stream], self.output_streams)
 
241
            if output_stream not in stream_map:
 
242
                self.warning("stream not available in map %r", stream_map)
 
243
                raise ObjectFactoryError("can not create stream")
237
244
 
238
 
            if compatible_stream is None:
239
 
                raise ObjectFactoryError('can not create stream')
 
245
            compatible_stream = stream_map[output_stream]
240
246
 
241
247
        if self.max_bins != -1 and self.current_bins == self.max_bins:
242
248
            raise ObjectFactoryError('no bins available')
243
249
 
244
250
        bin = self._makeBin(compatible_stream)
245
251
        bin.factory = self
246
 
        if not bin in self.bins:
247
 
            self.bins.append(bin)
 
252
        self.bins.append(bin)
248
253
        self.current_bins += 1
249
254
        self.emit('bin-created', bin)
250
255
 
251
256
        return bin
252
257
 
253
 
    def _makeBin(self, output_stream=None):
254
 
        raise NotImplementedError()
255
 
 
256
258
    def releaseBin(self, bin):
257
259
        """
258
260
        Release a bin created with L{makeBin}.
262
264
        """
263
265
        bin.set_state(gst.STATE_NULL)
264
266
        self._releaseBin(bin)
 
267
        self.debug("Finally releasing %r", bin)
265
268
        self.current_bins -= 1
266
 
        if bin in self.bins:
267
 
            self.bins.remove(bin)
 
269
        self.bins.remove(bin)
268
270
        self.emit('bin-released', bin)
269
271
        del bin.factory
270
272
 
 
273
    def _makeBin(self, output_stream):
 
274
        if output_stream is None:
 
275
            return self._makeDefaultBin()
 
276
 
 
277
        return self._makeStreamBin(output_stream)
 
278
 
 
279
    def _makeDefaultBin(self):
 
280
        """
 
281
        Return a bin that decodes all the available streams.
 
282
 
 
283
        This is generally used to get an overview of the source media before
 
284
        splitting it in separate streams.
 
285
        """
 
286
        bin = gst.Bin("%s" % self.name)
 
287
        src = gst.element_make_from_uri(gst.URI_SRC, self.uri)
 
288
        try:
 
289
            dbin = gst.element_factory_make("decodebin2")
 
290
        except:
 
291
            dbin = gst.element_factory_make("decodebin")
 
292
        bin.add(src, dbin)
 
293
        src.link(dbin)
 
294
 
 
295
        dbin.connect("new-decoded-pad", self._binNewDecodedPadCb, bin)
 
296
        dbin.connect("removed-decoded-pad", self._binRemovedDecodedPadCb, bin)
 
297
 
 
298
        bin.decodebin = dbin
 
299
        return bin
 
300
 
 
301
    def _binNewDecodedPadCb(self, unused_dbin, pad, unused_is_last, bin):
 
302
        ghost_pad = gst.GhostPad(pad.get_name(), pad)
 
303
        ghost_pad.set_active(True)
 
304
        bin.add_pad(ghost_pad)
 
305
 
 
306
    def _binRemovedDecodedPadCb(self, unused_dbin, pad, bin):
 
307
        ghost_pad = bin.get_pad(pad.get_name())
 
308
        bin.remove_pad(ghost_pad)
 
309
 
271
310
    def _releaseBin(self, bin):
272
 
        # default implementation does nothing
273
 
        pass
 
311
        try:
 
312
            # bin is a bin returned from makeDefaultBin
 
313
            bin.decodebin.disconnect_by_func(self._binNewDecodedPadCb)
 
314
            bin.decodebin.disconnect_by_func(self._binRemovedDecodedPadCb)
 
315
        except TypeError:
 
316
            # bin is a stream bin
 
317
            bin.decodebin.disconnect_by_func(self._singlePadAddedCb)
 
318
            bin.decodebin.disconnect_by_func(self._singlePadRemovedCb)
 
319
 
 
320
        del bin.decodebin
 
321
 
 
322
        if hasattr(bin, "volume"):
 
323
            # only audio bins have a volume element
 
324
            for elt in [bin.aconv, bin.ares, bin.volume]:
 
325
                elt.set_state(gst.STATE_NULL)
 
326
                bin.remove(elt)
 
327
            del bin.volume
 
328
            del bin.aconv
 
329
            del bin.ares
 
330
 
 
331
        if hasattr(bin, "ghostpad"):
 
332
            # singledecodebin found something on this pad
 
333
            bin.ghostpad.set_active(False)
 
334
            bin.remove_pad(bin.ghostpad)
 
335
            del bin.ghostpad
 
336
 
 
337
    def _makeStreamBin(self, output_stream):
 
338
        self.debug("output_stream:%r", output_stream)
 
339
        b = gst.Bin()
 
340
        b.decodebin = self.singleDecodeBinClass(uri=self.uri, caps=output_stream.caps,
 
341
                                           stream=output_stream)
 
342
        b.decodebin.connect("pad-added", self._singlePadAddedCb, b)
 
343
        b.decodebin.connect("pad-removed", self._singlePadRemovedCb, b)
 
344
 
 
345
        if isinstance(output_stream, AudioStream):
 
346
            self.debug("Adding volume element")
 
347
            # add a volume element
 
348
            b.aconv = gst.element_factory_make("audioconvert", "internal-aconv")
 
349
            b.ares = gst.element_factory_make("audioresample", "internal-audioresample")
 
350
            b.volume = gst.element_factory_make("volume", "internal-volume")
 
351
            b.add(b.volume, b.ares, b.aconv)
 
352
            gst.element_link_many(b.aconv, b.ares, b.volume)
 
353
            b.aconv.sync_state_with_parent()
 
354
            b.ares.sync_state_with_parent()
 
355
            b.volume.sync_state_with_parent()
 
356
 
 
357
        b.add(b.decodebin)
 
358
        return b
 
359
 
 
360
    def _singlePadAddedCb(self, dbin, pad, topbin):
 
361
        self.debug("dbin:%r, pad:%r, topbin:%r", dbin, pad, topbin)
 
362
        if hasattr(topbin, "volume"):
 
363
            pad.link(topbin.aconv.get_pad("sink"))
 
364
            topbin.ghostpad = gst.GhostPad("src", topbin.volume.get_pad("src"))
 
365
        else:
 
366
            topbin.ghostpad = gst.GhostPad("src", pad)
 
367
        topbin.ghostpad.set_active(True)
 
368
        topbin.add_pad(topbin.ghostpad)
 
369
 
 
370
    def _singlePadRemovedCb(self, dbin, pad, topbin):
 
371
        self.debug("dbin:%r, pad:%r, topbin:%r", dbin, pad, topbin)
 
372
        topbin.remove_pad(topbin.ghostpad)
 
373
        del topbin.ghostpad
 
374
        if hasattr(topbin, "volume"):
 
375
            pad.unlink(topbin.aconv.get_pad("sink"))
 
376
            for elt in [topbin.volume, topbin.aconv, topbin.ares]:
 
377
                elt.set_state(gst.STATE_NULL)
 
378
            for elt in [topbin.volume, topbin.aconv, topbin.ares]:
 
379
                topbin.remove(elt)
 
380
            del topbin.volume
 
381
            del topbin.aconv
 
382
            del topbin.ares
274
383
 
275
384
    def addInputStream(self, stream):
276
385
        raise AssertionError("source factories can't have input streams")
290
399
        'bin-released': ['bin']
291
400
    }
292
401
 
293
 
    def __init__(self, name='', displayname=''):
294
 
        ObjectFactory.__init__(self, name, displayname)
 
402
    def __init__(self, name=''):
 
403
        ObjectFactory.__init__(self, name)
295
404
        self.max_bins = -1
296
405
        self.current_bins = 0
297
406
 
326
435
 
327
436
        bin = self._makeBin(input_stream)
328
437
        bin.factory = self
329
 
        if not bin in self.bins:
330
 
            self.bins.append(bin)
 
438
        self.bins.append(bin)
331
439
        self.current_bins += 1
332
440
        self.emit('bin-created', bin)
333
441
 
366
474
        """
367
475
        bin.set_state(gst.STATE_NULL)
368
476
        self._releaseBin(bin)
369
 
        if bin in self.bins:
370
 
            self.bins.remove(bin)
 
477
        self.bins.remove(bin)
371
478
        self.current_bins -= 1
372
479
        del bin.factory
373
480
        self.emit('bin-released', bin)
393
500
        'bin-released': ['bin']
394
501
    }
395
502
 
396
 
    def __init__(self, name='', displayname=''):
397
 
        ObjectFactory.__init__(self, name, displayname)
 
503
    def __init__(self, name=''):
 
504
        ObjectFactory.__init__(self, name)
398
505
        self.max_bins = -1
399
506
        self.current_bins = 0
400
507
 
419
526
 
420
527
        bin = self._makeBin(input_stream)
421
528
        bin.factory = self
422
 
        if not bin in self.bins:
423
 
            self.bins.append(bin)
 
529
        self.bins.append(bin)
424
530
        self.current_bins += 1
425
531
        self.emit('bin-created', bin)
426
532
 
459
565
        """
460
566
        bin.set_state(gst.STATE_NULL)
461
567
        self._releaseBin(bin)
462
 
        if bin in self.bins:
463
 
            self.bins.remove(bin)
 
568
        self.bins.remove(bin)
464
569
        self.current_bins -= 1
465
570
        del bin.factory
466
571
        self.emit('bin-released', bin)
479
584
    timeline.
480
585
    """
481
586
 
482
 
    def __init__(self, name, displayname, default_duration=None):
483
 
        SourceFactory.__init__(self, name, displayname)
 
587
    def __init__(self, uri, name='', default_duration=None):
 
588
        SourceFactory.__init__(self, uri, name)
484
589
        if default_duration is None:
485
590
            default_duration = 5 * gst.SECOND
486
591
 
501
606
    @type abs_offset_length: C{int}
502
607
    """
503
608
 
504
 
    def __init__(self, name='', displayname='',
 
609
    def __init__(self, uri, name='',
505
610
            offset=0, offset_length=gst.CLOCK_TIME_NONE):
506
611
        self.offset = offset
507
612
        self.offset_length = offset_length
508
613
 
509
 
        SourceFactory.__init__(self, name, displayname)
 
614
        SourceFactory.__init__(self, uri, name)
510
615
 
511
616
    def _getAbsOffset(self):
512
617
        if self.parent is None:
534
639
        return offset_length
535
640
 
536
641
    abs_offset_length = property(_getAbsOffsetLength)
537
 
 
538
 
class URISourceFactoryMixin(object):
539
 
    """
540
 
    Abstract mixin for sources that access an URI.
541
 
    """
542
 
 
543
 
    # make this an attribute to inject it from tests
544
 
    singleDecodeBinClass = SingleDecodeBin
545
 
 
546
 
    def __init__(self, uri):
547
 
        self.uri = uri
548
 
        self.displayname = os.path.basename(unquote(uri))
549
 
 
550
 
    def _makeBin(self, output_stream):
551
 
        if output_stream is None:
552
 
            return self._makeDefaultBin()
553
 
 
554
 
        return self._makeStreamBin(output_stream)
555
 
 
556
 
    def _makeDefaultBin(self):
557
 
        """
558
 
        Return a bin that decodes all the available streams.
559
 
 
560
 
        This is generally used to get an overview of the source media before
561
 
        splitting it in separate streams.
562
 
        """
563
 
        bin = gst.Bin("%s" % self.name)
564
 
        src = gst.element_make_from_uri(gst.URI_SRC, self.uri)
565
 
        try:
566
 
            dbin = gst.element_factory_make("decodebin2")
567
 
        except:
568
 
            dbin = gst.element_factory_make("decodebin")
569
 
        bin.add(src, dbin)
570
 
        src.link(dbin)
571
 
 
572
 
        dbin.connect("new-decoded-pad", self._binNewDecodedPadCb, bin)
573
 
        dbin.connect("removed-decoded-pad", self._binRemovedDecodedPadCb, bin)
574
 
 
575
 
        bin.decodebin = dbin
576
 
        return bin
577
 
 
578
 
    def _binNewDecodedPadCb(self, unused_dbin, pad, unused_is_last, bin):
579
 
        ghost_pad = gst.GhostPad(pad.get_name(), pad)
580
 
        ghost_pad.set_active(True)
581
 
        bin.add_pad(ghost_pad)
582
 
 
583
 
    def _binRemovedDecodedPadCb(self, unused_dbin, pad, bin):
584
 
        ghost_pad = bin.get_pad(pad.get_name())
585
 
        bin.remove_pad(ghost_pad)
586
 
 
587
 
    def _makeStreamBin(self, output_stream):
588
 
        return self.singleDecodeBinClass(uri=self.uri, caps=output_stream.caps,
589
 
                stream=output_stream)
590
 
 
591
 
class LiveURISourceFactory(URISourceFactoryMixin, LiveSourceFactory):
592
 
    """
593
 
    Factory for live sources accessible at a given URI.
594
 
 
595
 
    @see L{LiveSourceFactory}.
596
 
    """
597
 
    def __init__(self, uri, name='', displayname='', default_duration=None):
598
 
        URISourceFactoryMixin.__init__(self, uri)
599
 
        LiveSourceFactory.__init__(self, name, displayname, default_duration)