[pitivi] viewer: Avoid messing with the project pipeline when previewing trims



commit 03e15b44e8878d9c24fbd47b9477a695d2a24a50
Author: Alexandru Băluț <alexandru balut gmail com>
Date:   Tue Jan 15 04:36:20 2019 +0100

    viewer: Avoid messing with the project pipeline when previewing trims
    
    Once the viewer container is connected to a project, it does not even
    have to keep a reference to the project pipeline, now that it keeps a
    separate reference to the temporary AssetPipeline used when trimming.
    
    Gets rid of a few fields, and avoids setting the project pipeline to
    state NULL, thus avoiding a flicker when switching state from NULL to
    PAUSED.

 pitivi/viewer/viewer.py | 131 +++++++++++++++++++++++-------------------------
 1 file changed, 63 insertions(+), 68 deletions(-)
---
diff --git a/pitivi/viewer/viewer.py b/pitivi/viewer/viewer.py
index 49cde960..86f6ef17 100644
--- a/pitivi/viewer/viewer.py
+++ b/pitivi/viewer/viewer.py
@@ -27,7 +27,6 @@ from gi.repository import Gtk
 
 from pitivi.settings import GlobalSettings
 from pitivi.utils.loggable import Loggable
-from pitivi.utils.misc import format_ns
 from pitivi.utils.pipeline import AssetPipeline
 from pitivi.utils.ui import SPACING
 from pitivi.utils.widgets import TimeWidget
@@ -84,20 +83,15 @@ class ViewerContainer(Gtk.Box, Loggable):
         self.log("New ViewerContainer")
 
         self.project = None
-        self.pipeline = None
+        self.trim_pipeline = None
         self.docked = True
         self.target = None
         self._compactMode = False
 
-        # Only used for restoring the pipeline position after a live clip trim
-        # preview:
-        self._oldTimelinePos = None
-
         self._haveUI = False
 
         self._createUi()
 
-        self.__owning_pipeline = False
         if not self.settings.viewerDocked:
             self.undock()
 
@@ -111,8 +105,7 @@ class ViewerContainer(Gtk.Box, Loggable):
     def _project_manager_new_project_loaded_cb(self, unused_project_manager, project):
         project.connect("rendering-settings-changed",
                         self._project_rendering_settings_changed_cb)
-        self.project = project
-        self.setPipeline(project.pipeline)
+        self.set_project(project)
 
     def _projectManagerProjectClosedCb(self, unused_project_manager, project):
         if self.project == project:
@@ -128,16 +121,13 @@ class ViewerContainer(Gtk.Box, Loggable):
         self.target.update_aspect_ratio(project)
         self.timecode_entry.setFramerate(project.videorate)
 
-    def setPipeline(self, pipeline, position=None):
-        """Sets the displayed pipeline.
-
-        Properly switches the currently set action to that new Pipeline.
+    def set_project(self, project):
+        """Sets the displayed project.
 
         Args:
-            pipeline (Pipeline): The Pipeline to switch to.
-            position (Optional[int]): The position to seek to initially.
+            project (Project): The Project to switch to.
         """
-        self.debug("Setting pipeline: %r", pipeline)
+        self.debug("Setting project: %r", project)
         self._disconnectFromPipeline()
 
         if self.target:
@@ -145,21 +135,20 @@ class ViewerContainer(Gtk.Box, Loggable):
             if parent:
                 parent.remove(self.target)
 
-        self.pipeline = pipeline
-        self.pipeline.connect("state-change", self._pipelineStateChangedCb)
-        self.pipeline.connect("position", self._positionCb)
-        self.pipeline.connect("duration-changed", self._durationChangedCb)
+        project.pipeline.connect("state-change", self._pipelineStateChangedCb)
+        project.pipeline.connect("position", self._positionCb)
+        project.pipeline.connect("duration-changed", self._durationChangedCb)
+        self.project = project
 
-        self.__owning_pipeline = False
         self.__createNewViewer()
         self._setUiActive()
 
-        if position:
-            self.pipeline.simple_seek(position)
-        self.pipeline.pause()
+        # This must be done at the end, otherwise the created sink widget
+        # appears in a separate window.
+        project.pipeline.pause()
 
     def __createNewViewer(self):
-        _, sink_widget = self.pipeline.create_sink()
+        _, sink_widget = self.project.pipeline.create_sink()
 
         self.overlay_stack = OverlayStack(self.app, sink_widget)
         self.target = ViewerWidget(self.overlay_stack)
@@ -178,18 +167,14 @@ class ViewerContainer(Gtk.Box, Loggable):
         GLib.timeout_add(1000, self.__viewer_realization_done_cb, None)
 
     def _disconnectFromPipeline(self):
-        if self.pipeline is None:
-            # silently return, there's nothing to disconnect from
+        if self.project is None:
             return
 
-        self.debug("Disconnecting from: %r", self.pipeline)
-        self.pipeline.disconnect_by_func(self._pipelineStateChangedCb)
-        self.pipeline.disconnect_by_func(self._positionCb)
-        self.pipeline.disconnect_by_func(self._durationChangedCb)
-
-        if self.__owning_pipeline:
-            self.pipeline.release()
-        self.pipeline = None
+        pipeline = self.project.pipeline
+        self.debug("Disconnecting from: %r", pipeline)
+        pipeline.disconnect_by_func(self._pipelineStateChangedCb)
+        pipeline.disconnect_by_func(self._positionCb)
+        pipeline.disconnect_by_func(self._durationChangedCb)
 
     def _setUiActive(self, active=True):
         self.debug("active %r", active)
@@ -428,10 +413,10 @@ class ViewerContainer(Gtk.Box, Loggable):
         self.settings.viewerDocked = False
         self.remove(self.buttons_container)
         position = None
-        if self.pipeline:
+        if self.project:
             self.overlay_stack.enable_resize_status(False)
-            position = self.pipeline.getPosition()
-            self.pipeline.setState(Gst.State.NULL)
+            position = self.project.pipeline.getPosition()
+            self.project.pipeline.setState(Gst.State.NULL)
             self.remove(self.target)
             self.__createNewViewer()
         self.buttons_container.set_margin_bottom(SPACING)
@@ -452,9 +437,9 @@ class ViewerContainer(Gtk.Box, Loggable):
         self.external_window.move(self.settings.viewerX, self.settings.viewerY)
         self.external_window.resize(
             self.settings.viewerWidth, self.settings.viewerHeight)
-        if self.pipeline:
-            self.pipeline.pause()
-            self.pipeline.simple_seek(position)
+        if self.project:
+            self.project.pipeline.pause()
+            self.project.pipeline.simple_seek(position)
 
     def __viewer_realization_done_cb(self, unused_data):
         self.overlay_stack.enable_resize_status(True)
@@ -469,10 +454,10 @@ class ViewerContainer(Gtk.Box, Loggable):
         self.settings.viewerDocked = True
 
         position = None
-        if self.pipeline:
+        if self.project:
             self.overlay_stack.enable_resize_status(False)
-            position = self.pipeline.getPosition()
-            self.pipeline.setState(Gst.State.NULL)
+            position = self.project.pipeline.getPosition()
+            self.project.pipeline.setState(Gst.State.NULL)
             self.external_vbox.remove(self.target)
             self.__createNewViewer()
 
@@ -484,9 +469,9 @@ class ViewerContainer(Gtk.Box, Loggable):
         self.show()
 
         self.external_window.hide()
-        if self.pipeline:
-            self.pipeline.pause()
-            self.pipeline.simple_seek(position)
+        if self.project.pipeline:
+            self.project.pipeline.pause()
+            self.project.pipeline.simple_seek(position)
 
     def _toggleFullscreen(self, widget):
         if widget.get_active():
@@ -517,29 +502,32 @@ class ViewerContainer(Gtk.Box, Loggable):
             self.log("Not previewing trim for image or title clip: %s", clip)
             return False
 
-        if self.pipeline == self.app.project_manager.current_project.pipeline:
-            self.debug("Creating temporary pipeline for clip %s, position %s",
-                       clip.props.uri, format_ns(position))
-            self._oldTimelinePos = self.pipeline.getPosition(False)
-            self.pipeline.set_state(Gst.State.NULL)
-            self.setPipeline(AssetPipeline(clip))
-            self.__owning_pipeline = True
+        if self.project.pipeline.getState() == Gst.State.PLAYING:
+            self.project.pipeline.setState(Gst.State.PAUSED)
 
-        self.pipeline.simple_seek(position)
-        return False
+        if self.trim_pipeline and clip is not self.trim_pipeline.clip:
+            # Seems to be the trim preview pipeline for a different clip.
+            self.trim_pipeline.release()
+            self.trim_pipeline = None
+
+        if not self.trim_pipeline:
+            self.debug("Creating temporary pipeline for clip %s", clip.props.uri)
+            self.trim_pipeline = AssetPipeline(clip)
+            video_sink, sink_widget = self.trim_pipeline.create_sink()
+            self.target.switch_widget(sink_widget)
+            self.trim_pipeline.setState(Gst.State.PAUSED)
+
+        self.trim_pipeline.simple_seek(position)
 
     def clipTrimPreviewFinished(self):
         """Switches back to the project pipeline following a clip trimming."""
-        if self.pipeline is not self.app.project_manager.current_project.pipeline:
-            self.debug("Going back to the project's pipeline")
-            self.pipeline.setState(Gst.State.NULL)
-            # Using pipeline.getPosition() here does not work because for some
-            # reason it's a bit off, that's why we need self._oldTimelinePos.
-            self.setPipeline(
-                self.app.project_manager.current_project.pipeline, self._oldTimelinePos)
-            self._oldTimelinePos = None
-
-    def _pipelineStateChangedCb(self, unused_pipeline, state, old_state):
+        if not self.trim_pipeline:
+            return
+        self.target.switch_widget(self.overlay_stack)
+        self.trim_pipeline.release()
+        self.trim_pipeline = None
+
+    def _pipelineStateChangedCb(self, pipeline, state, old_state):
         """Updates the widgets when the playback starts or stops."""
         if state == Gst.State.PLAYING:
             st = Gst.Structure.new_empty("play")
@@ -553,8 +541,8 @@ class ViewerContainer(Gtk.Box, Loggable):
                 if old_state != Gst.State.PAUSED:
                     st = Gst.Structure.new_empty("pause")
                     if old_state == Gst.State.PLAYING:
-                        st.set_value("playback_time",
-                                     self.pipeline.getPosition() / Gst.SECOND)
+                        position_seconds = pipeline.getPosition() / Gst.SECOND
+                        st.set_value("playback_time", position_seconds)
                     self.app.write_action(st)
 
                 self.playpause_button.setPlay()
@@ -589,6 +577,13 @@ class ViewerWidget(Gtk.AspectFrame, Loggable):
         # would show through the non-double-buffered widget!
         self.hide()
 
+    def switch_widget(self, widget):
+        child = self.get_child()
+        if child:
+            self.remove(child)
+        widget.show_all()
+        self.add(widget)
+
     def update_aspect_ratio(self, project):
         """Forces the DAR of the project on the child widget."""
         ratio_fraction = project.getDAR()


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