Пример #1
0
class TestPipeline(TestCase):

    def setUp(self):
        TestCase.setUp(self)
        gst.debug("start")
        self.pipeline = Pipeline()
        self.monitor = SignalMonitor(self.pipeline, 'action-added',
                                     'action-removed', 'factory-added',
                                     'factory-removed', 'state-changed')
        self.assertEquals(self.monitor.action_added_count, 0)
        self.assertEquals(self.monitor.action_added_collect, [])

    def tearDown(self):
        self.pipeline.setState(STATE_NULL)
        self.pipeline.release()
        self.monitor.disconnectFromObj(self.pipeline)
        del self.pipeline
        del self.monitor
        TestCase.tearDown(self)

    def testAddRemoveActionSimple(self):
        """ Simple add/remove of Actions """
        ac1 = BogusAction()

        # add the action to the pipeline
        res = self.pipeline.addAction(ac1)
        # the returned value should be the given action
        self.assertEquals(res, ac1)
        # it should now be in the list of actions...
        self.failUnlessEqual(self.pipeline.actions, [ac1])
        # ... and the action should be set to that pipeline
        self.failUnlessEqual(ac1.pipeline, self.pipeline)
        # the 'action-added' signal should be triggered once
        self.assertEquals(self.monitor.action_added_count, 1)
        # And it contained our action
        self.assertEquals(self.monitor.action_added_collect, [(ac1, )])

        # if we try to add that action again, it should be silently ignored
        res = self.pipeline.addAction(ac1)
        self.assertEquals(res, ac1)
        # the list of actions shouldn't have changed
        self.failUnlessEqual(self.pipeline.actions, [ac1])
        # it shouldn't have changed the pipeline set on action
        self.failUnlessEqual(ac1.pipeline, self.pipeline)
        # the 'action-added' signal should NOT have been triggered again
        self.assertEquals(self.monitor.action_added_count, 1)

        # And now to remove it
        self.pipeline.removeAction(ac1)
        # the 'action-removed' signal should have been triggered once..
        self.assertEquals(self.monitor.action_removed_count, 1)
        # .. with the action as an argument
        self.assertEquals(self.monitor.action_removed_collect, [(ac1, )])
        # And there should be no actions left on the pipeline
        self.assertEquals(self.pipeline.actions, [])

    def testAddRemoveActionAdvanced(self):
        """ Advanced add/remove of Actions """
        ac1 = BogusAction()
        ac2 = BogusAction()
        p2 = Pipeline()

        res = self.pipeline.addAction(ac1)
        self.assertEquals(self.pipeline.actions, [ac1])

        # we can't add an action to two pipelines at the same time
        self.failUnlessRaises(PipelineError, p2.addAction, ac1)

        self.pipeline.removeAction(ac1)
        self.assertEquals(self.pipeline.actions, [])

        res = self.pipeline.setAction(ac1)
        self.assertEquals(res, ac1)
        self.assertEquals(self.pipeline.actions, [ac1])
        # calling setAction while a similar action is already set should
        # return the existing action and not change anything else
        res = self.pipeline.setAction(ac2)
        self.assertEquals(res, ac1)
        self.assertEquals(self.pipeline.actions, [ac1])

        # we can't remove active actions while in PAUSED/PLAYING
        self.pipeline.setState(STATE_PAUSED)
        ac1.state = STATE_ACTIVE
        self.assertEquals(self.pipeline.getState(), STATE_PAUSED)
        self.failUnlessRaises(PipelineError, self.pipeline.removeAction, ac1)

        # but we can remove deactivated actions while in PAUSED/PLAYING
        self.pipeline.setState(STATE_PAUSED)
        ac1.state = STATE_NOT_ACTIVE
        self.assertEquals(self.pipeline.getState(), STATE_PAUSED)
        self.pipeline.removeAction(ac1)

        # we can add actions while in PAUSED/PLAYING
        res = self.pipeline.addAction(ac2)
        self.assertEquals(res, ac2)
        self.assertEquals(self.pipeline.actions, [ac2])

        self.pipeline.removeAction(ac2)
        p2.release()

    def testStateChange(self):
        loop = gobject.MainLoop()

        bag = {"last_state": None}
        def state_changed_cb(pipeline, state, bag, loop):
            bag["last_state"] = state
            loop.quit()

        self.pipeline.connect('state-changed', state_changed_cb, bag, loop)

        # playing
        self.pipeline.setState(STATE_PLAYING)
        loop.run()
        self.failUnlessEqual(bag["last_state"], STATE_PLAYING)
        self.failUnlessEqual(self.pipeline.getState(), STATE_PLAYING)
        self.assertEquals(self.monitor.state_changed_count, 1)

        # playing again
        self.pipeline.setState(STATE_PLAYING)
        self.assertEquals(self.monitor.state_changed_count, 1)

        # ready
        self.pipeline.setState(STATE_READY)
        loop.run()
        self.failUnlessEqual(bag["last_state"], STATE_READY)
        self.failUnlessEqual(self.pipeline.getState(), STATE_READY)
        self.assertEquals(self.monitor.state_changed_count, 2)

        # PLAYING
        self.pipeline.play()
        loop.run()
        self.failUnlessEqual(bag["last_state"], STATE_PLAYING)
        self.failUnlessEqual(self.pipeline.getState(), STATE_PLAYING)
        self.assertEquals(self.monitor.state_changed_count, 3)

        # PAUSE
        self.pipeline.pause()
        loop.run()
        self.failUnlessEqual(bag["last_state"], STATE_PAUSED)
        self.failUnlessEqual(self.pipeline.getState(), STATE_PAUSED)
        self.assertEquals(self.monitor.state_changed_count, 4)

        self.pipeline.stop()
        loop.run()
        self.failUnlessEqual(bag["last_state"], STATE_READY)
        self.failUnlessEqual(self.pipeline.getState(), STATE_READY)
        self.assertEquals(self.monitor.state_changed_count, 5)

    def testGetReleaseBinForFactoryStream(self):
        factory = VideoTestSourceFactory()
        stream = VideoStream(gst.Caps('video/x-raw-rgb; video/x-raw-yuv'),
                'src0')
        factory.addOutputStream(stream)

        # try to get a cached instance
        self.failUnlessRaises(PipelineError,
                self.pipeline.getBinForFactoryStream, factory, stream, False)

        # create a bin
        bin1 = self.pipeline.getBinForFactoryStream(factory, stream, True)
        self.failUnless(isinstance(bin1, gst.Element))
        # return the cached instance
        bin2 = self.pipeline.getBinForFactoryStream(factory, stream, True)
        self.failUnlessEqual(id(bin1), id(bin2))

        self.pipeline.releaseBinForFactoryStream(factory, stream)
        self.pipeline.releaseBinForFactoryStream(factory, stream)

        # the bin has been destroyed at this point
        self.failUnlessRaises(PipelineError,
                self.pipeline.releaseBinForFactoryStream, factory, stream)

        # we should get a new instance
        bin2 = self.pipeline.getBinForFactoryStream(factory, stream, True)
        self.pipeline.releaseBinForFactoryStream(factory, stream)

    def testGetReleaseTeeForFactoryStream(self):
        factory = VideoTestSourceFactory()
        stream = VideoStream(gst.Caps('video/x-raw-rgb; video/x-raw-yuv'),
                'src')
        factory.addOutputStream(stream)

        self.failUnlessRaises(PipelineError,
            self.pipeline.getTeeForFactoryStream, factory, stream, True)

        # getBinForFactoryStream(factory, stream) must be called before
        self.failUnlessRaises(PipelineError,
            self.pipeline.getTeeForFactoryStream, factory, stream, True)

        # create the bin
        bin1 = self.pipeline.getBinForFactoryStream(factory, stream, True)

        # try to get a cached tee
        self.failUnlessRaises(PipelineError,
            self.pipeline.getTeeForFactoryStream, factory, stream, False)

        # create tee
        tee1 = self.pipeline.getTeeForFactoryStream(factory, stream, True)
        self.failUnless(isinstance(tee1, gst.Element))

        # get the cached instance
        tee2 = self.pipeline.getTeeForFactoryStream(factory, stream, True)
        self.failUnlessEqual(id(tee1), id(tee2))

        # release
        self.pipeline.releaseTeeForFactoryStream(factory, stream)

        # there's still a tee alive, so we can't release the bin
        #self.failUnlessRaises(PipelineError,
        #        self.pipeline.releaseBinForFactoryStream, factory, stream)

        self.pipeline.releaseTeeForFactoryStream(factory, stream)
        self.failUnlessRaises(PipelineError,
                self.pipeline.releaseTeeForFactoryStream, factory, stream)

        # should always fail with a sink bin
        factory2 = FakeSinkFactory()
        stream2 = VideoStream(gst.Caps('video/x-raw-rgb; video/x-raw-yuv'),
                'src')
        factory2.addInputStream(stream2)

        self.failUnlessRaises(PipelineError,
            self.pipeline.getTeeForFactoryStream, factory2, stream2, True)
        self.pipeline.releaseBinForFactoryStream(factory, stream)

    def testGetReleaseQueueForFactoryStream(self):
        factory = FakeSinkFactory()
        stream = VideoStream(gst.Caps('any'), 'sink')
        factory.addInputStream(stream)

        self.failUnlessRaises(PipelineError,
            self.pipeline.getQueueForFactoryStream, factory, stream, True)

        # getBinForFactoryStream(factory, stream) must be called before
        self.failUnlessRaises(PipelineError,
            self.pipeline.getQueueForFactoryStream, factory, stream, True)

        # create the bin
        bin1 = self.pipeline.getBinForFactoryStream(factory, stream, True)

        # try to get a cached queue
        self.failUnlessRaises(PipelineError,
            self.pipeline.getQueueForFactoryStream, factory, stream, False)

        # create queue
        queue1 = self.pipeline.getQueueForFactoryStream(factory, stream, True)
        self.failUnless(isinstance(queue1, gst.Element))

        gst.debug("pouet")

        # get the cached instance
        queue2 = self.pipeline.getQueueForFactoryStream(factory, stream, True)
        self.failUnlessEqual(id(queue1), id(queue2))

        # release
        self.pipeline.releaseQueueForFactoryStream(factory, stream)

        gst.debug("pouet")

        # there's still a queue alive, so we can't release the bin
        self.failUnlessRaises(PipelineError,
                self.pipeline.releaseBinForFactoryStream, factory, stream)

        self.pipeline.releaseQueueForFactoryStream(factory, stream)

        gst.debug("pouet2")
        self.failUnlessRaises(PipelineError,
                self.pipeline.releaseQueueForFactoryStream, factory, stream)

        # should always fail with a src bin
        factory2 = VideoTestSourceFactory()
        stream2 = VideoStream(gst.Caps('any'), 'src')
        factory2.addOutputStream(stream2)

        bin1 = self.pipeline.getBinForFactoryStream(factory, stream, True)
        self.failUnlessRaises(PipelineError,
            self.pipeline.getQueueForFactoryStream, factory2, stream2, True)
        self.pipeline.releaseBinForFactoryStream(factory, stream)

        self.pipeline.releaseBinForFactoryStream(factory, stream)
        self.assertEquals(factory.current_bins, 0)
Пример #2
0
class Project(Signallable, Loggable):
    """The base class for PiTiVi projects

    @ivar name: The name of the project
    @type name: C{str}
    @ivar description: A description of the project
    @type description: C{str}
    @ivar sources: The sources used by this project
    @type sources: L{SourceList}
    @ivar timeline: The timeline
    @type timeline: L{Timeline}
    @ivar pipeline: The timeline's pipeline
    @type pipeline: L{Pipeline}
    @ivar factory: The timeline factory
    @type factory: L{TimelineSourceFactory}
    @ivar format: The format under which the project is currently stored.
    @type format: L{FormatterClass}
    @ivar loaded: Whether the project is fully loaded or not.
    @type loaded: C{bool}

    Signals:
     - C{loaded} : The project is now fully loaded.
    """

    __signals__ = {
        "settings-changed" : ['old', 'new'],
        "project-changed" : [],
        }

    def __init__(self, name="", uri=None, **kwargs):
        """
        @param name: the name of the project
        @param uri: the uri of the project
        """
        Loggable.__init__(self)
        self.log("name:%s, uri:%s", name, uri)
        self.name = name
        self.settings = None
        self.description = ""
        self.uri = uri
        self.urichanged = False
        self.format = None
        self.sources = SourceList()
        self.sources.connect("source-added", self._sourceAddedCb)
        self.sources.connect("source-removed", self._sourceRemovedCb)

        self._dirty = False

        self.timeline = Timeline()

        self.factory = TimelineSourceFactory(self.timeline)
        self.pipeline = Pipeline()
        self.view_action = ViewAction()
        self.view_action.addProducers(self.factory)
        self.seeker = Seeker(80)

        self.settings = ExportSettings()
        self._videocaps = self.settings.getVideoCaps()

    def release(self):
        self.pipeline.release()
        self.pipeline = None

    #{ Settings methods

    def getSettings(self):
        """
        return the currently configured settings.
        """
        self.debug("self.settings %s", self.settings)
        return self.settings

    def setSettings(self, settings):
        """
        Sets the given settings as the project's settings.
        @param settings: The new settings for the project.
        @type settings: ExportSettings
        """
        assert settings
        self.log("Setting %s as the project's settings", settings)
        oldsettings = self.settings
        self.settings = settings
        self._projectSettingsChanged()
        self.emit('settings-changed', oldsettings, settings)

    #}

    #{ Save and Load features

    def setModificationState(self, state):
        self._dirty = state
        if state:
            self.emit('project-changed')

    def hasUnsavedModifications(self):
        return self._dirty

    def _projectSettingsChanged(self):
        settings = self.getSettings()
        self._videocaps = settings.getVideoCaps()
        if self.timeline:
            self.timeline.updateVideoCaps(self._videocaps)

        for fact in self.sources.getSources():
            fact.setFilterCaps(self._videocaps)
        if self.pipeline.getState() != gst.STATE_NULL:
            self.pipeline.stop()
            self.pipeline.pause()

    def _sourceAddedCb(self, sourcelist, factory):
        factory.setFilterCaps(self._videocaps)

    def _sourceRemovedCb(self, sourclist, uri, factory):
        self.timeline.removeFactory(factory)
Пример #3
0
class Project(Signallable, Loggable):
    """The base class for PiTiVi projects

    @ivar name: The name of the project
    @type name: C{str}
    @ivar description: A description of the project
    @type description: C{str}
    @ivar sources: The sources used by this project
    @type sources: L{SourceList}
    @ivar timeline: The timeline
    @type timeline: L{Timeline}
    @ivar pipeline: The timeline's pipeline
    @type pipeline: L{Pipeline}
    @ivar factory: The timeline factory
    @type factory: L{TimelineSourceFactory}
    @ivar format: The format under which the project is currently stored.
    @type format: L{FormatterClass}
    @ivar loaded: Whether the project is fully loaded or not.
    @type loaded: C{bool}

    Signals:
     - C{loaded} : The project is now fully loaded.
    """

    __signals__ = {
        "settings-changed" : ['old', 'new'],
        }

    def __init__(self, name="", uri=None, **kwargs):
        """
        name : the name of the project
        uri : the uri of the project
        """
        Loggable.__init__(self)
        self.log("name:%s, uri:%s", name, uri)
        self.name = name
        self.settings = None
        self.description = ""
        self.uri = uri
        self.urichanged = False
        self.format = None
        self.sources = SourceList()
        self.sources.connect("source-added", self._sourceAddedCb)
        self.sources.connect("source-removed", self._sourceRemovedCb)

        self._dirty = False

        self.timeline = Timeline()

        self.factory = TimelineSourceFactory(self.timeline)
        self.pipeline = Pipeline()
        self.view_action = ViewAction()
        self.view_action.addProducers(self.factory)
        self.seeker = Seeker(80)

        self.getCapsFromSettings()

    def getCapsFromSettings(self):
        settings = self.getSettings()
        #formatstr = "video/x-raw-rgb,width=(int)%d,height=(int)%d;"\
        #    "video/x-raw-yuv,width=(int)%d,height=(int)%d"
        #capstr = formatstr % (
        #    settings.videowidth,
        #    settings.videoheight,
        #    settings.videowidth,
        #    settings.videoheight)
        #self._videocaps = gst.Caps(capstr)
        self._videocaps = settings.getVideoCaps()

    def release(self):
        self.pipeline.release()
        self.pipeline = None

    #{ Settings methods

    def getSettings(self):
        """
        return the currently configured settings.
        If no setting have been explicitely set, some smart settings will be
        chosen.
        """
        self.debug("self.settings %s", self.settings)
        return self.settings or self.getAutoSettings()

    def setSettings(self, settings):
        """
        Sets the given settings as the project's settings.
        If settings is None, the current settings will be unset
        """
        self.log("Setting %s as the project's settings", settings)
        oldsettings = self.settings
        self.settings = settings
        self._projectSettingsChanged()
        self.emit('settings-changed', oldsettings, settings)

    def unsetSettings(self, unused_settings):
        """ Remove the currently configured settings."""
        self.setSettings(None)

    def getAutoSettings(self):
        """
        Computes and returns smart settings for the project.
        If the project only has one source, it will be that source's settings.
        If it has more than one, it will return the largest setting that suits
        all contained sources.
        """
        settings = ExportSettings()
        if not self.timeline:
            self.warning("project doesn't have a timeline, returning default settings")
            return settings

        # FIXME: this is ugly, but rendering for now assumes at most one audio
        # and one video tracks
        have_audio = have_video = False
        for track in self.timeline.tracks:
            if isinstance(track.stream, VideoStream) and track.duration != 0:
                have_video = True
            elif isinstance(track.stream, AudioStream) and track.duration != 0:
                have_audio = True

        if not have_audio:
            settings.aencoder = None

        if not have_video:
            settings.vencoder = None

        return settings

    #}

    #{ Save and Load features

    def save(self, location=None, overwrite=False):
        """
        Save the project to the given location.

        @param location: The location to write to. If not specified, the
        current project location will be used (if set).
        @type location: C{URI}
        @param overwrite: Whether to overwrite existing location.
        @type overwrite: C{bool}
        """
        # import here to break circular import
        from pitivi.formatters.format import save_project
        from pitivi.formatters.base import FormatterError

        self.log("saving...")
        location = location or self.uri

        if location == None:
            raise FormatterError("Location unknown")

        save_project(self, location or self.uri, self.format,
                     overwrite)

        self.uri = location

    def setModificationState(self, state):
        self._dirty = state

    def hasUnsavedModifications(self):
        return self._dirty

    def _projectSettingsChanged(self):
        self.getCapsFromSettings()
        for fact in self.sources.getSources():
            fact.setFilterCaps(self._videocaps)
        if self.pipeline.getState() != gst.STATE_NULL:
            self.pipeline.stop()
            self.pipeline.pause()

    def _sourceAddedCb(self, sourcelist, factory):
        factory.setFilterCaps(self._videocaps)

    def _sourceRemovedCb(self, sourclist, uri, factory):
        self.timeline.removeFactory(factory)
Пример #4
0
class Project(Signallable, Loggable):
    """The base class for PiTiVi projects

    @ivar name: The name of the project
    @type name: C{str}
    @ivar description: A description of the project
    @type description: C{str}
    @ivar sources: The sources used by this project
    @type sources: L{SourceList}
    @ivar timeline: The timeline
    @type timeline: L{Timeline}
    @ivar pipeline: The timeline's pipeline
    @type pipeline: L{Pipeline}
    @ivar factory: The timeline factory
    @type factory: L{TimelineSourceFactory}
    @ivar format: The format under which the project is currently stored.
    @type format: L{FormatterClass}
    @ivar loaded: Whether the project is fully loaded or not.
    @type loaded: C{bool}

    Signals:
     - C{loaded} : The project is now fully loaded.
    """

    __signals__ = {
        "settings-changed": ['old', 'new'],
        "project-changed": [],
    }

    def __init__(self, name="", uri=None, **kwargs):
        """
        @param name: the name of the project
        @param uri: the uri of the project
        """
        Loggable.__init__(self)
        self.log("name:%s, uri:%s", name, uri)
        self.name = name
        self.settings = None
        self.description = ""
        self.uri = uri
        self.urichanged = False
        self.format = None
        self.sources = SourceList()
        self.sources.connect("source-added", self._sourceAddedCb)
        self.sources.connect("source-removed", self._sourceRemovedCb)

        self._dirty = False

        self.timeline = Timeline()

        self.factory = TimelineSourceFactory(self.timeline)
        self.pipeline = Pipeline()
        self.view_action = ViewAction()
        self.view_action.addProducers(self.factory)
        self.seeker = Seeker(80)

        self.settings = ExportSettings()
        self._videocaps = self.settings.getVideoCaps()

    def release(self):
        self.pipeline.release()
        self.pipeline = None

    #{ Settings methods

    def getSettings(self):
        """
        return the currently configured settings.
        """
        self.debug("self.settings %s", self.settings)
        return self.settings

    def setSettings(self, settings):
        """
        Sets the given settings as the project's settings.
        @param settings: The new settings for the project.
        @type settings: ExportSettings
        """
        assert settings
        self.log("Setting %s as the project's settings", settings)
        oldsettings = self.settings
        self.settings = settings
        self._projectSettingsChanged()
        self.emit('settings-changed', oldsettings, settings)

    #}

    #{ Save and Load features

    def setModificationState(self, state):
        self._dirty = state
        if state:
            self.emit('project-changed')

    def hasUnsavedModifications(self):
        return self._dirty

    def _projectSettingsChanged(self):
        settings = self.getSettings()
        self._videocaps = settings.getVideoCaps()
        if self.timeline:
            self.timeline.updateVideoCaps(self._videocaps)

        for fact in self.sources.getSources():
            fact.setFilterCaps(self._videocaps)
        if self.pipeline.getState() != gst.STATE_NULL:
            self.pipeline.stop()
            self.pipeline.pause()

    def _sourceAddedCb(self, sourcelist, factory):
        factory.setFilterCaps(self._videocaps)

    def _sourceRemovedCb(self, sourclist, uri, factory):
        self.timeline.removeFactory(factory)
Пример #5
0
class TestPipeline(TestCase):
    def setUp(self):
        TestCase.setUp(self)
        gst.debug("start")
        self.pipeline = Pipeline()
        self.monitor = SignalMonitor(self.pipeline, 'action-added',
                                     'action-removed', 'factory-added',
                                     'factory-removed', 'state-changed')
        self.assertEquals(self.monitor.action_added_count, 0)
        self.assertEquals(self.monitor.action_added_collect, [])

    def tearDown(self):
        self.pipeline.setState(STATE_NULL)
        self.pipeline.release()
        self.monitor.disconnectFromObj(self.pipeline)
        del self.pipeline
        del self.monitor
        TestCase.tearDown(self)

    def testAddRemoveActionSimple(self):
        """ Simple add/remove of Actions """
        ac1 = BogusAction()

        # add the action to the pipeline
        res = self.pipeline.addAction(ac1)
        # the returned value should be the given action
        self.assertEquals(res, ac1)
        # it should now be in the list of actions...
        self.failUnlessEqual(self.pipeline.actions, [ac1])
        # ... and the action should be set to that pipeline
        self.failUnlessEqual(ac1.pipeline, self.pipeline)
        # the 'action-added' signal should be triggered once
        self.assertEquals(self.monitor.action_added_count, 1)
        # And it contained our action
        self.assertEquals(self.monitor.action_added_collect, [(ac1, )])

        # if we try to add that action again, it should be silently ignored
        res = self.pipeline.addAction(ac1)
        self.assertEquals(res, ac1)
        # the list of actions shouldn't have changed
        self.failUnlessEqual(self.pipeline.actions, [ac1])
        # it shouldn't have changed the pipeline set on action
        self.failUnlessEqual(ac1.pipeline, self.pipeline)
        # the 'action-added' signal should NOT have been triggered again
        self.assertEquals(self.monitor.action_added_count, 1)

        # And now to remove it
        self.pipeline.removeAction(ac1)
        # the 'action-removed' signal should have been triggered once..
        self.assertEquals(self.monitor.action_removed_count, 1)
        # .. with the action as an argument
        self.assertEquals(self.monitor.action_removed_collect, [(ac1, )])
        # And there should be no actions left on the pipeline
        self.assertEquals(self.pipeline.actions, [])

    def testAddRemoveActionAdvanced(self):
        """ Advanced add/remove of Actions """
        ac1 = BogusAction()
        ac2 = BogusAction()
        p2 = Pipeline()

        res = self.pipeline.addAction(ac1)
        self.assertEquals(self.pipeline.actions, [ac1])

        # we can't add an action to two pipelines at the same time
        self.failUnlessRaises(PipelineError, p2.addAction, ac1)

        self.pipeline.removeAction(ac1)
        self.assertEquals(self.pipeline.actions, [])

        res = self.pipeline.setAction(ac1)
        self.assertEquals(res, ac1)
        self.assertEquals(self.pipeline.actions, [ac1])
        # calling setAction while a similar action is already set should
        # return the existing action and not change anything else
        res = self.pipeline.setAction(ac2)
        self.assertEquals(res, ac1)
        self.assertEquals(self.pipeline.actions, [ac1])

        # we can't remove active actions while in PAUSED/PLAYING
        self.pipeline.setState(STATE_PAUSED)
        ac1.state = STATE_ACTIVE
        self.assertEquals(self.pipeline.getState(), STATE_PAUSED)
        self.failUnlessRaises(PipelineError, self.pipeline.removeAction, ac1)

        # but we can remove deactivated actions while in PAUSED/PLAYING
        self.pipeline.setState(STATE_PAUSED)
        ac1.state = STATE_NOT_ACTIVE
        self.assertEquals(self.pipeline.getState(), STATE_PAUSED)
        self.pipeline.removeAction(ac1)

        # we can add actions while in PAUSED/PLAYING
        res = self.pipeline.addAction(ac2)
        self.assertEquals(res, ac2)
        self.assertEquals(self.pipeline.actions, [ac2])

        self.pipeline.removeAction(ac2)
        p2.release()

    def testStateChange(self):
        loop = gobject.MainLoop()

        bag = {"last_state": None}

        def state_changed_cb(pipeline, state, bag, loop):
            bag["last_state"] = state
            loop.quit()

        self.pipeline.connect('state-changed', state_changed_cb, bag, loop)

        # playing
        self.pipeline.setState(STATE_PLAYING)
        loop.run()
        self.failUnlessEqual(bag["last_state"], STATE_PLAYING)
        self.failUnlessEqual(self.pipeline.getState(), STATE_PLAYING)
        self.assertEquals(self.monitor.state_changed_count, 1)

        # playing again
        self.pipeline.setState(STATE_PLAYING)
        self.assertEquals(self.monitor.state_changed_count, 1)

        # ready
        self.pipeline.setState(STATE_READY)
        loop.run()
        self.failUnlessEqual(bag["last_state"], STATE_READY)
        self.failUnlessEqual(self.pipeline.getState(), STATE_READY)
        self.assertEquals(self.monitor.state_changed_count, 2)

        # PLAYING
        self.pipeline.play()
        loop.run()
        self.failUnlessEqual(bag["last_state"], STATE_PLAYING)
        self.failUnlessEqual(self.pipeline.getState(), STATE_PLAYING)
        self.assertEquals(self.monitor.state_changed_count, 3)

        # PAUSE
        self.pipeline.pause()
        loop.run()
        self.failUnlessEqual(bag["last_state"], STATE_PAUSED)
        self.failUnlessEqual(self.pipeline.getState(), STATE_PAUSED)
        self.assertEquals(self.monitor.state_changed_count, 4)

        self.pipeline.stop()
        loop.run()
        self.failUnlessEqual(bag["last_state"], STATE_READY)
        self.failUnlessEqual(self.pipeline.getState(), STATE_READY)
        self.assertEquals(self.monitor.state_changed_count, 5)

    def testGetReleaseBinForFactoryStream(self):
        factory = VideoTestSourceFactory()
        stream = VideoStream(gst.Caps('video/x-raw-rgb; video/x-raw-yuv'),
                             'src0')
        factory.addOutputStream(stream)

        # try to get a cached instance
        self.failUnlessRaises(PipelineError,
                              self.pipeline.getBinForFactoryStream, factory,
                              stream, False)

        # create a bin
        bin1 = self.pipeline.getBinForFactoryStream(factory, stream, True)
        self.failUnless(isinstance(bin1, gst.Element))
        # return the cached instance
        bin2 = self.pipeline.getBinForFactoryStream(factory, stream, True)
        self.failUnlessEqual(id(bin1), id(bin2))

        self.pipeline.releaseBinForFactoryStream(factory, stream)
        self.pipeline.releaseBinForFactoryStream(factory, stream)

        # the bin has been destroyed at this point
        self.failUnlessRaises(PipelineError,
                              self.pipeline.releaseBinForFactoryStream,
                              factory, stream)

        # we should get a new instance
        bin2 = self.pipeline.getBinForFactoryStream(factory, stream, True)
        self.pipeline.releaseBinForFactoryStream(factory, stream)

    def testGetReleaseTeeForFactoryStream(self):
        factory = VideoTestSourceFactory()
        stream = VideoStream(gst.Caps('video/x-raw-rgb; video/x-raw-yuv'),
                             'src')
        factory.addOutputStream(stream)

        self.failUnlessRaises(PipelineError,
                              self.pipeline.getTeeForFactoryStream, factory,
                              stream, True)

        # getBinForFactoryStream(factory, stream) must be called before
        self.failUnlessRaises(PipelineError,
                              self.pipeline.getTeeForFactoryStream, factory,
                              stream, True)

        # create the bin
        bin1 = self.pipeline.getBinForFactoryStream(factory, stream, True)

        # try to get a cached tee
        self.failUnlessRaises(PipelineError,
                              self.pipeline.getTeeForFactoryStream, factory,
                              stream, False)

        # create tee
        tee1 = self.pipeline.getTeeForFactoryStream(factory, stream, True)
        self.failUnless(isinstance(tee1, gst.Element))

        # get the cached instance
        tee2 = self.pipeline.getTeeForFactoryStream(factory, stream, True)
        self.failUnlessEqual(id(tee1), id(tee2))

        # release
        self.pipeline.releaseTeeForFactoryStream(factory, stream)

        # there's still a tee alive, so we can't release the bin
        #self.failUnlessRaises(PipelineError,
        #        self.pipeline.releaseBinForFactoryStream, factory, stream)

        self.pipeline.releaseTeeForFactoryStream(factory, stream)
        self.failUnlessRaises(PipelineError,
                              self.pipeline.releaseTeeForFactoryStream,
                              factory, stream)

        # should always fail with a sink bin
        factory2 = FakeSinkFactory()
        stream2 = VideoStream(gst.Caps('video/x-raw-rgb; video/x-raw-yuv'),
                              'src')
        factory2.addInputStream(stream2)

        self.failUnlessRaises(PipelineError,
                              self.pipeline.getTeeForFactoryStream, factory2,
                              stream2, True)
        self.pipeline.releaseBinForFactoryStream(factory, stream)

    def testGetReleaseQueueForFactoryStream(self):
        factory = FakeSinkFactory()
        stream = VideoStream(gst.Caps('any'), 'sink')
        factory.addInputStream(stream)

        self.failUnlessRaises(PipelineError,
                              self.pipeline.getQueueForFactoryStream, factory,
                              stream, True)

        # getBinForFactoryStream(factory, stream) must be called before
        self.failUnlessRaises(PipelineError,
                              self.pipeline.getQueueForFactoryStream, factory,
                              stream, True)

        # create the bin
        bin1 = self.pipeline.getBinForFactoryStream(factory, stream, True)

        # try to get a cached queue
        self.failUnlessRaises(PipelineError,
                              self.pipeline.getQueueForFactoryStream, factory,
                              stream, False)

        # create queue
        queue1 = self.pipeline.getQueueForFactoryStream(factory, stream, True)
        self.failUnless(isinstance(queue1, gst.Element))

        gst.debug("pouet")

        # get the cached instance
        queue2 = self.pipeline.getQueueForFactoryStream(factory, stream, True)
        self.failUnlessEqual(id(queue1), id(queue2))

        # release
        self.pipeline.releaseQueueForFactoryStream(factory, stream)

        gst.debug("pouet")

        # there's still a queue alive, so we can't release the bin
        self.failUnlessRaises(PipelineError,
                              self.pipeline.releaseBinForFactoryStream,
                              factory, stream)

        self.pipeline.releaseQueueForFactoryStream(factory, stream)

        gst.debug("pouet2")
        self.failUnlessRaises(PipelineError,
                              self.pipeline.releaseQueueForFactoryStream,
                              factory, stream)

        # should always fail with a src bin
        factory2 = VideoTestSourceFactory()
        stream2 = VideoStream(gst.Caps('any'), 'src')
        factory2.addOutputStream(stream2)

        bin1 = self.pipeline.getBinForFactoryStream(factory, stream, True)
        self.failUnlessRaises(PipelineError,
                              self.pipeline.getQueueForFactoryStream, factory2,
                              stream2, True)
        self.pipeline.releaseBinForFactoryStream(factory, stream)

        self.pipeline.releaseBinForFactoryStream(factory, stream)
        self.assertEquals(factory.current_bins, 0)
Пример #6
0
class Project(Signallable, Loggable):
    """The base class for PiTiVi projects

    @ivar name: The name of the project
    @type name: C{str}
    @ivar description: A description of the project
    @type description: C{str}
    @ivar sources: The sources used by this project
    @type sources: L{SourceList}
    @ivar timeline: The timeline
    @type timeline: L{Timeline}
    @ivar pipeline: The timeline's pipeline
    @type pipeline: L{Pipeline}
    @ivar factory: The timeline factory
    @type factory: L{TimelineSourceFactory}
    @ivar format: The format under which the project is currently stored.
    @type format: L{FormatterClass}
    @ivar loaded: Whether the project is fully loaded or not.
    @type loaded: C{bool}

    Signals:
     - C{loaded} : The project is now fully loaded.
    """

    __signals__ = {
        "settings-changed": ['old', 'new'],
        "project-changed": [],
    }

    def __init__(self, name="", uri=None, **kwargs):
        """
        name : the name of the project
        uri : the uri of the project
        """
        Loggable.__init__(self)
        self.log("name:%s, uri:%s", name, uri)
        self.name = name
        self.settings = None
        self.description = ""
        self.uri = uri
        self.urichanged = False
        self.format = None
        self.sources = SourceList()
        self.sources.connect("source-added", self._sourceAddedCb)
        self.sources.connect("source-removed", self._sourceRemovedCb)

        self._dirty = False

        self.timeline = Timeline()

        self.factory = TimelineSourceFactory(self.timeline)
        self.pipeline = Pipeline()
        self.view_action = ViewAction()
        self.view_action.addProducers(self.factory)
        self.seeker = Seeker(80)

        settings = self.getSettings()
        self._videocaps = settings.getVideoCaps()

    def release(self):
        self.pipeline.release()
        self.pipeline = None

    #{ Settings methods

    def getSettings(self):
        """
        return the currently configured settings.
        If no setting have been explicitely set, some smart settings will be
        chosen.
        """
        self.debug("self.settings %s", self.settings)
        return self.settings or self.getAutoSettings()

    def setSettings(self, settings):
        """
        Sets the given settings as the project's settings.
        If settings is None, the current settings will be unset
        """
        self.log("Setting %s as the project's settings", settings)
        oldsettings = self.settings
        self.settings = settings
        self._projectSettingsChanged()
        self.emit('settings-changed', oldsettings, settings)

    def getAutoSettings(self):
        """
        Computes and returns smart settings for the project.
        If the project only has one source, it will be that source's settings.
        If it has more than one, it will return the largest setting that suits
        all contained sources.
        """
        settings = ExportSettings()
        if not self.timeline:
            self.warning(
                "project doesn't have a timeline, returning default settings")
            return settings

        # FIXME: this is ugly, but rendering for now assumes at most one audio
        # and one video tracks
        have_audio = have_video = False
        for track in self.timeline.tracks:
            if isinstance(track.stream, VideoStream) and track.duration != 0:
                have_video = True
            elif isinstance(track.stream, AudioStream) and track.duration != 0:
                have_audio = True

        if not have_audio:
            settings.aencoder = None

        if not have_video:
            settings.vencoder = None

        return settings

    #}

    #{ Save and Load features

    def save(self, location=None, overwrite=False):
        """
        Save the project to the given location.

        @param location: The location to write to. If not specified, the
        current project location will be used (if set).
        @type location: C{URI}
        @param overwrite: Whether to overwrite existing location.
        @type overwrite: C{bool}
        """
        # import here to break circular import
        from pitivi.formatters.format import save_project
        from pitivi.formatters.base import FormatterError

        self.log("saving...")
        location = location or self.uri

        if location == None:
            raise FormatterError("Location unknown")

        save_project(self, location or self.uri, self.format, overwrite)

        self.uri = location

    def setModificationState(self, state):
        self._dirty = state
        if state:
            self.emit('project-changed')

    def hasUnsavedModifications(self):
        return self._dirty

    def _projectSettingsChanged(self):
        settings = self.getSettings()
        self._videocaps = settings.getVideoCaps()

        for fact in self.sources.getSources():
            fact.setFilterCaps(self._videocaps)
        if self.pipeline.getState() != gst.STATE_NULL:
            self.pipeline.stop()
            self.pipeline.pause()

    def _sourceAddedCb(self, sourcelist, factory):
        factory.setFilterCaps(self._videocaps)

    def _sourceRemovedCb(self, sourclist, uri, factory):
        self.timeline.removeFactory(factory)