[pitivi: 6/14] Initial implementation of command line preview option



commit b3dd49fd8d2a5ea64d540aae7f6bd08b263d4bb7
Author: Robert Swain <robert swain collabora co uk>
Date:   Tue Aug 24 12:00:29 2010 +0200

    Initial implementation of command line preview option
    
    Also refactor Renderer class into Actioner for common parts so that the code
    can be reused for a Previewer class

 pitivi/application.py       |   57 +++++++++---
 pitivi/render.py            |  203 ++++++++++++++++++++++++++-----------------
 pitivi/ui/encodingdialog.py |    6 +-
 3 files changed, 170 insertions(+), 96 deletions(-)
---
diff --git a/pitivi/application.py b/pitivi/application.py
index 6700e25..ec9b1ef 100644
--- a/pitivi/application.py
+++ b/pitivi/application.py
@@ -54,7 +54,8 @@ from pitivi.undo import UndoableActionLog, DebugActionLogObserver
 from pitivi.timeline.timeline_undo import TimelineLogObserver
 from pitivi.sourcelist_undo import SourceListLogObserver
 from pitivi.undo import UndoableAction
-from pitivi.render import Renderer
+from pitivi.ui.viewer import PitiviViewer
+from pitivi.render import Renderer, Previewer
 
 # FIXME : Speedup loading time
 # Currently we load everything in one go
@@ -223,6 +224,7 @@ class Pitivi(Loggable, Signallable):
 class InteractivePitivi(Pitivi):
     usage = _("""
       %prog [-r OUTPUT_FILE] [PROJECT_FILE]
+      %prog -p [PROJECT_FILE]
       %prog -i [-a] [MEDIA_FILE]...""")
 
     description = _("""Starts the video editor, optionally loading PROJECT_FILE. If
@@ -239,20 +241,28 @@ When -r is specified, the given project file is rendered without opening the GUI
 
     no_ui_help = _("""Run pitivi with no gui""")
     render_help = _("""Render the given project file to OUTPUT_FILE with no GUI.""")
+    preview_help = _("""Preview the given project file without the full UI.""")
 
     def __init__(self):
         Pitivi.__init__(self)
         self.mainloop = gobject.MainLoop()
+        self.actioner = None
 
     def _newProjectLoaded(self, project):
         if self.render_output:
             # create renderer and set output file
-            self.renderer = Renderer(self.current, pipeline=None, outfile=self.output_file)
-            self.renderer.connect("eos", self._eosCb)
+            self.actioner = Renderer(self.current, pipeline=None, outfile=self.output_file)
+        elif self.preview:
+            # create previewer and set ui
+            self.actioner = Previewer(self.current, pipeline=None, ui=self.gui)
+            # hack to make the gtk.HScale seek slider UI behave properly
+            self.gui._durationChangedCb(None, project.timeline.duration)
+        if self.actioner:
+            self.actioner.connect("eos", self._eosCb)
             # on error, all we need to do is shutdown which is the same as we do for EOS
-            self.renderer.connect("error", self._eosCb)
-            # configure the renderer and start rendering!
-            self.renderer.startRender()
+            self.actioner.connect("error", self._eosCb)
+            # configure the actioner and start acting!
+            self.actioner.startAction()
 
     def run(self, argv):
         # check for dependencies
@@ -272,19 +282,17 @@ When -r is specified, the given project file is rendered without opening the GUI
 
         # validate options
         self.render_output = options.render_output
+        self.preview = options.preview
         if options.render_output:
             options.no_ui = True
             n_args += 1
 
-        if options.no_ui:
-            self.gui = None
-        else:
-            # create the ui
-            self.gui = PitiviMainWindow(self)
-            self.gui.show()
+        if options.render_output and options.preview:
+            parser.error("-p and -r cannot be used simultaneously")
+            return
 
-        if options.import_sources and options.render_output:
-            parser.error("-r and -i are incompatible")
+        if options.import_sources and (options.render_output or options.preview):
+            parser.error("-r or -p and -i are incompatible")
             return
 
         if not options.import_sources and options.add_to_timeline:
@@ -296,6 +304,20 @@ When -r is specified, the given project file is rendered without opening the GUI
             parser.error("invalid arguments")
             return
 
+        if options.no_ui:
+            self.gui = None
+        elif options.preview:
+            # init ui for previewing
+            self.gui = PitiviViewer()
+            self.window = gtk.Window()
+            self.window.connect("delete-event", self._deleteCb)
+            self.window.add(self.gui)
+            self.window.show_all()
+        else:
+            # create the ui
+            self.gui = PitiviMainWindow(self)
+            self.gui.show()
+
         if not options.import_sources and args:
             index = 0
             if options.render_output:
@@ -318,9 +340,14 @@ When -r is specified, the given project file is rendered without opening the GUI
         # run the mainloop
         self.mainloop.run()
 
+    def _deleteCb(self, unused_widget, unused_data):
+        self.shutdown()
+
     def _eosCb(self, unused_obj):
         if self.gui is None:
             self.shutdown()
+        elif self.window is not None:
+            self.gui.seek(0)
 
     def shutdown(self):
         if Pitivi.shutdown(self):
@@ -343,6 +370,8 @@ When -r is specified, the given project file is rendered without opening the GUI
                 action="store_true", default=False)
         parser.add_option("-r", "--render", help=self.render_help,
                 dest="render_output", action="store_true", default=False)
+        parser.add_option("-p", "--preview", help=self.preview_help,
+                action="store_true", default=False)
 
         return parser
 
diff --git a/pitivi/render.py b/pitivi/render.py
index 6c752fe..cdabe51 100644
--- a/pitivi/render.py
+++ b/pitivi/render.py
@@ -36,15 +36,18 @@ from pitivi.settings import export_settings_to_render_settings
 from pitivi.stream import VideoStream, AudioStream
 from pitivi.utils import beautify_length
 
-class Renderer(Loggable, Signallable):
-    """ Rendering helper methods """
+class Actioner(Loggable, Signallable):
+    """ Previewer/Renderer helper methods """
 
     __signals__ = {
         "eos" : None,
         "error" : None
         }
 
-    def __init__(self, project, pipeline=None, outfile=None):
+    RENDERER = 0
+    PREVIEWER = 1
+
+    def __init__(self, project, pipeline=None):
         Loggable.__init__(self)
         # grab the Pipeline and settings
         self.project = project
@@ -52,128 +55,170 @@ class Renderer(Loggable, Signallable):
             self.pipeline = pipeline
         else:
             self.pipeline = self.project.pipeline
-        self.outfile = outfile
-        self.detectStreamTypes()
-
-        self.rendering = False
-        self.renderaction = None
+        self.acting = False
+        self.action = None
         self.settings = project.getSettings()
 
-    def detectStreamTypes(self):
-        self.have_video = False
-        self.have_audio = False
-
-        # we can only render TimelineSourceFactory
-        if len(self.pipeline.factories) == 0:
-            timeline_source = self.project.factory
-        else:
-            sources = [factory for factory in self.pipeline.factories.keys()
-                    if isinstance(factory, SourceFactory)]
-            timeline_source = sources[0]
-        assert isinstance(timeline_source, TimelineSourceFactory)
-
-        for track in timeline_source.timeline.tracks:
-            if isinstance(track.stream, AudioStream) and track.duration > 0:
-                self.have_audio = True
-            elif isinstance(track.stream, VideoStream) and \
-                    track.duration > 0:
-                self.have_video = True
-
     def _eosCb(self, unused_pipeline):
         self.debug("eos !")
-        self.rendering = False
-        self.updateUIOnEOS()
-        self.removeRecordAction()
+        if self.actioner != self.PREVIEWER:
+            self.shutdown()
         self.emit("eos")
 
+    def shutdown(self):
+        self.acting = False
+        self.updateUIOnEOS()
+        self.removeAction()
+
     def updateUIOnEOS(self):
         pass
 
     def _errorCb(self, pipeline, error, detail):
         self.debug("error !")
-        self.rendering = False
+        self.acting = False
         self.updateUIOnError()
-        self.removeRecordAction()
+        self.removeAction()
         self.emit("error")
 
     def updateUIOnError(self):
         pass
 
-    def _positionCb(self, unused_pipeline, position):
-        self.debug("%r %r", unused_pipeline, position)
-        fraction = None
-        text = None
-        timediff = time.time() - self.timestarted
-        length = self.project.timeline.duration
-        fraction = float(min(position, length)) / float(length)
-        if timediff > 5.0 and position:
-            # only display ETA after 5s in order to have enough averaging and
-            # if the position is non-null
-            totaltime = (timediff * float(length) / float(position)) - timediff
-            text = beautify_length(int(totaltime * gst.SECOND))
-        self.updatePosition(fraction, text)
-
-    def updatePosition(self, fraction, text):
-        pass
-
     def _changeSourceSettings(self, settings):
         videocaps = settings.getVideoCaps()
         for source in self.project.sources.getSources():
             source.setFilterCaps(videocaps)
 
-    def addRecordAction(self):
-        self.debug("renderaction %r", self.renderaction)
-        if self.renderaction == None:
-            self.pipeline.connect('position', self._positionCb)
+    def addAction(self):
+        self.debug("action %r", self.action)
+        if self.action == None:
+            if self.actioner == self.RENDERER:
+                self.pipeline.connect('position', self._positionCb)
             self.pipeline.connect('eos', self._eosCb)
             self.pipeline.connect('error', self._errorCb)
             self.debug("Setting pipeline to STOP")
             self.pipeline.stop()
-            settings = export_settings_to_render_settings(self.settings,
-                    self.have_video, self.have_audio)
-            self.debug("Creating RenderAction")
+            self.debug("Creating action")
             if len(self.pipeline.factories) == 0:
                 sources = [self.project.factory]
             else:
                 sources = [factory for factory in self.pipeline.factories
                         if isinstance(factory, SourceFactory)]
-            self.renderaction = render_action_for_uri(self.outfile,
-                    settings, *sources)
+            if self.actioner == self.PREVIEWER:
+                self.action = ViewAction()
+                self.action.addProducers(*sources)
+                self.ui.setAction(self.action)
+                self.ui.setPipeline(self.pipeline)
+            elif self.actioner == self.RENDERER:
+                settings = export_settings_to_render_settings(self.settings,
+                        self.have_video, self.have_audio)
+                self.action = render_action_for_uri(self.outfile,
+                        settings, *sources)
+            #else:
+                # BIG FAT ERROR HERE
+
             self.debug("setting action on pipeline")
-            self.pipeline.addAction(self.renderaction)
-            self.debug("Activating render action")
-            self.renderaction.activate()
-            self.debug("Setting all active ViewAction to sync=False")
-            for ac in self.pipeline.actions:
-                if isinstance(ac, ViewAction) and ac.isActive():
-                    ac.setSync(False)
+            self.pipeline.addAction(self.action)
+            self.debug("Activating action")
+            self.action.activate()
+            if self.actioner == self.RENDERER:
+                self.debug("Setting all active ViewAction to sync=False")
+                for ac in self.pipeline.actions:
+                    if isinstance(ac, ViewAction) and ac.isActive():
+                        ac.setSync(False)
             self.debug("Updating all sources to render settings")
             self._changeSourceSettings(self.settings)
             self.debug("setting pipeline to PAUSE")
             self.pipeline.pause()
             self.debug("done")
 
-    def removeRecordAction(self):
-        self.debug("renderaction %r", self.renderaction)
-        if self.renderaction:
+
+    def removeAction(self):
+        self.debug("action %r", self.action)
+        if self.action:
             self.pipeline.stop()
-            self.renderaction.deactivate()
-            self.pipeline.removeAction(self.renderaction)
+            self.action.deactivate()
+            self.pipeline.removeAction(self.action)
             self.debug("putting all active ViewActions back to sync=True")
             for ac in self.pipeline.actions:
                 if isinstance(ac, ViewAction) and ac.isActive():
                     ac.setSync(True)
             self._changeSourceSettings(self.project.getSettings())
             self.pipeline.pause()
-            self.pipeline.disconnect_by_function(self._positionCb)
+            if self.actioner == self.RENDERER:
+                self.pipeline.disconnect_by_function(self._positionCb)
             self.pipeline.disconnect_by_function(self._eosCb)
             self.pipeline.disconnect_by_function(self._errorCb)
-            self.renderaction = None
+            self.action = None
 
-    def startRender(self):
+    def _startAction(self):
+        self.addAction()
+        self.pipeline.play()
+        self.timestarted = time.time()
+        self.acting = True
+
+class Renderer(Actioner):
+    """ Rendering helper methods """
+
+    def __init__(self, project, pipeline=None, outfile=None):
+        self.actioner = self.RENDERER
+        Actioner.__init__(self, project, pipeline)
+        self.detectStreamTypes()
+        self.outfile = outfile
+
+    def setOutfile(self):
+        self.outfile = outfile
+
+    def detectStreamTypes(self):
+        self.have_video = False
+        self.have_audio = False
+
+        # we can only render TimelineSourceFactory
+        if len(self.pipeline.factories) == 0:
+            timeline_source = self.project.factory
+        else:
+            sources = [factory for factory in self.pipeline.factories.keys()
+                    if isinstance(factory, SourceFactory)]
+            timeline_source = sources[0]
+        assert isinstance(timeline_source, TimelineSourceFactory)
+
+        for track in timeline_source.timeline.tracks:
+            if isinstance(track.stream, AudioStream) and track.duration > 0:
+                self.have_audio = True
+            elif isinstance(track.stream, VideoStream) and \
+                    track.duration > 0:
+                self.have_video = True
+
+    def _positionCb(self, unused_pipeline, position):
+        self.debug("%r %r", unused_pipeline, position)
+        fraction = None
+        text = None
+        timediff = time.time() - self.timestarted
+        length = self.project.timeline.duration
+        fraction = float(min(position, length)) / float(length)
+        if timediff > 5.0 and position:
+            # only display ETA after 5s in order to have enough averaging and
+            # if the position is non-null
+            totaltime = (timediff * float(length) / float(position)) - timediff
+            text = beautify_length(int(totaltime * gst.SECOND))
+        self.updatePosition(fraction, text)
+
+    def updatePosition(self, fraction, text):
+        pass
+
+    def startAction(self):
         self.debug("Rendering")
-        if self.outfile and not self.rendering:
-            self.addRecordAction()
-            self.pipeline.play()
-            self.timestarted = time.time()
-            self.rendering = True
+        if not self.acting and self.outfile:
+            self._startAction()
+
+class Previewer(Actioner):
+    """ Previewing helper methods """
+
+    def __init__(self, project, pipeline=None, ui=None):
+        self.actioner = self.PREVIEWER
+        Actioner.__init__(self, project, pipeline)
+        self.ui = ui
+
+    def startAction(self):
+        self.debug("Previewing")
+        if not self.acting and self.ui:
+            self._startAction()
diff --git a/pitivi/ui/encodingdialog.py b/pitivi/ui/encodingdialog.py
index 1356507..47ff5df 100644
--- a/pitivi/ui/encodingdialog.py
+++ b/pitivi/ui/encodingdialog.py
@@ -73,7 +73,7 @@ class EncodingDialog(GladeWindow, Renderer):
     def _shutDown(self):
         self.debug("shutting down")
         # Abort recording
-        self.removeRecordAction()
+        self.removeAction()
         self.destroy()
 
     def _displaySettings(self):
@@ -118,8 +118,8 @@ class EncodingDialog(GladeWindow, Renderer):
             self.progressbar.set_text(_("About %s left") % text)
 
     def _recordButtonClickedCb(self, unused_button):
-        self.startRender()
-        if self.rendering:
+        self.startAction()
+        if self.acting:
             self.cancelbutton.set_label("gtk-cancel")
             self.progressbar.set_text(_("Rendering"))
             self.recordbutton.set_sensitive(False)



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]