[pitivi] transformation: Place videos ourself on the final scene



commit 285c4ffdb14305629411192e0ae52ce3256b43db
Author: Thibault Saunier <tsaunier gnome org>
Date:   Wed Aug 3 07:33:42 2016 -0400

    transformation: Place videos ourself on the final scene
    
    * Make sure to keep video aspect ratio when rescaling
    * Properly compute video position in the scene so that it is centered
      and 'black bars' are present on the side (but those are not taken
      into account by the transformation box)
    
    Fixes T7371
    
    Reviewed-by: Alex Băluț <alexandru balut gmail com>
    Differential Revision: https://phabricator.freedesktop.org/D1240

 pitivi/clipproperties.py        |    9 +----
 pitivi/project.py               |    3 +-
 pitivi/timeline/elements.py     |   82 +++++++++++++++++++++++++++++++++++++++
 tests/test_timeline_elements.py |   56 ++++++++++++++++++++++++++
 tests/test_timeline_timeline.py |    3 +-
 5 files changed, 143 insertions(+), 10 deletions(-)
---
diff --git a/pitivi/clipproperties.py b/pitivi/clipproperties.py
index cb10d02..84c76f1 100644
--- a/pitivi/clipproperties.py
+++ b/pitivi/clipproperties.py
@@ -541,7 +541,6 @@ class TransformationProperties(Gtk.Expander, Loggable):
         self.source = None
         self._selected_clip = None
         self.spin_buttons = {}
-        self.default_values = {}
         self.set_label(_("Transformation"))
 
         self.builder = Gtk.Builder()
@@ -577,7 +576,7 @@ class TransformationProperties(Gtk.Expander, Loggable):
 
     def _defaultValuesCb(self, unused_widget):
         for name, spinbtn in list(self.spin_buttons.items()):
-            spinbtn.set_value(self.default_values[name])
+            spinbtn.set_value(self.source.ui.default_position[name])
 
     def __sourcePropertyChangedCb(self, unused_source, unused_element, param):
         try:
@@ -594,12 +593,6 @@ class TransformationProperties(Gtk.Expander, Loggable):
         for name, spinbtn in list(self.spin_buttons.items()):
             res, value = self.source.get_child_property(name)
             assert res
-            if name == "width":
-                self.default_values[name] = self._project.videowidth
-            elif name == "height":
-                self.default_values[name] = self._project.videoheight
-            else:
-                self.default_values[name] = 0
             spinbtn.set_value(value)
 
     def __setupSpinButton(self, widget_name, property_name):
diff --git a/pitivi/project.py b/pitivi/project.py
index e3587d7..04f17d1 100644
--- a/pitivi/project.py
+++ b/pitivi/project.py
@@ -509,6 +509,7 @@ class ProjectManager(GObject.Object, Loggable):
         project.pipeline.connect("died", self._projectPipelineDiedCb)
         project.setModificationState(False)
         self.emit("new-project-loaded", self.current_project)
+        project.loaded = True
         self.time_loaded = time.time()
 
         return True
@@ -587,6 +588,7 @@ class ProjectManager(GObject.Object, Loggable):
             self.debug("Project is obsolete %s", project.props.uri)
             return
         self.emit("new-project-loaded", project)
+        project.loaded = True
         self.time_loaded = time.time()
 
 
@@ -1122,7 +1124,6 @@ class Project(Loggable, GES.Project):
         self._ensureTracks()
         self.ges_timeline.props.auto_transition = True
         self._ensureLayer()
-        self.loaded = True
 
         if self.scenario is not None:
             return
diff --git a/pitivi/timeline/elements.py b/pitivi/timeline/elements.py
index d436c25..73f5e31 100644
--- a/pitivi/timeline/elements.py
+++ b/pitivi/timeline/elements.py
@@ -618,9 +618,85 @@ class VideoBackground(Gtk.Box):
 
 
 class VideoSource(TimelineElement):
+    """Widget representing a GES.VideoSource.
+
+    Attributes:
+        default_position (dict): The default position (x, y, width, height)
+                                 of the VideoSource.
+    """
 
     __gtype_name__ = "PitiviVideoSource"
 
+    def __init__(self, element, timeline):
+        super().__init__(element, timeline)
+
+        project = self.timeline.app.project_manager.current_project
+        project.connect("video-size-changed",
+                        self._project_video_size_changed_cb)
+
+        self.__retrieve_project_size()
+        self.default_position = self._get_default_position()
+
+        if project.loaded:
+            self.__apply_default_position()
+
+    def __retrieve_project_size(self):
+        project = self.timeline.app.project_manager.current_project
+
+        self._project_width = project.videowidth
+        self._project_height = project.videoheight
+
+    def _project_video_size_changed_cb(self, project):
+        using_defaults = True
+        for name, default_value in self.default_position.items():
+            res, value = self._ges_elem.get_child_property(name)
+            assert res
+            if value != default_value:
+                using_defaults = False
+                break
+
+        self.__retrieve_project_size()
+        self.default_position = self._get_default_position()
+        if using_defaults:
+            self.debug("Applying default position")
+            self.__apply_default_position()
+        else:
+            self.debug("Not using defaults")
+
+    def __apply_default_position(self):
+        video_source = self._ges_elem
+        for name, value in self.default_position.items():
+            video_source.set_child_property(name, value)
+
+    def _get_default_position(self):
+        video_source = self._ges_elem
+        sinfo = video_source.get_asset().get_stream_info()
+
+        # Find the biggest size of the video inside the
+        # final view (project size) keeping the aspect ratio
+        scale = max(self._project_width / asset_width,
+                    self._project_height / asset_height)
+        if asset_width * scale > self._project_width or \
+                asset_height * scale > self._project_height:
+            # But make sure it is never bigger than the project!
+            scale = min(self._project_width / asset_width,
+                        self._project_height / asset_height)
+
+        asset_width = sinfo.get_width()
+        asset_height = sinfo.get_height()
+
+        width = asset_width * scale
+        height = asset_height * scale
+        x = max(0, (self._project_width - width) / 2)
+        y = max(0, (self._project_height - height) / 2)
+
+        self.debug("video scale is %f -> %dx%d", scale, width, height)
+
+        return {"posx": round(x),
+                "posy": round(y),
+                "width": round(width),
+                "height": round(height)}
+
     def _getBackground(self):
         return VideoBackground()
 
@@ -634,6 +710,12 @@ class TitleSource(VideoSource):
             if spec.name == "alpha":
                 return spec
 
+    def _get_default_position(self):
+        return {"posx": 0,
+                "posy": 0,
+                "width": self._project_width,
+                "height": self._project_height}
+
 
 class VideoUriSource(VideoSource):
 
diff --git a/tests/test_timeline_elements.py b/tests/test_timeline_elements.py
index 9aa240e..fe15138 100644
--- a/tests/test_timeline_elements.py
+++ b/tests/test_timeline_elements.py
@@ -89,3 +89,59 @@ class TestKeyframeCurve(BaseTestTimeline):
         # Make sure this does not raise any exception
         timeline = self.createTimeline()
         timeline.parent._keyframe_cb(None, None)
+
+
+class TestVideoSourceScaling(BaseTestTimeline):
+    def test_video_source_scaling(self):
+        timeline = self.createTimeline()
+        project = timeline.app.project_manager.current_project
+
+        clip = self.addClipsSimple(timeline, 1)[0]
+
+        video_source = clip.find_track_element(None, GES.VideoUriSource)
+        sinfo = video_source.get_asset().get_stream_info()
+
+        width = video_source.get_child_property("width")[1]
+        height = video_source.get_child_property("height")[1]
+        self.assertEqual(sinfo.get_width(), 960)
+        self.assertEqual(sinfo.get_height(), 400)
+        self.assertEqual(project.videowidth, sinfo.get_width())
+        self.assertEqual(project.videoheight, sinfo.get_height())
+        self.assertEqual(project.videowidth, width)
+        self.assertEqual(project.videoheight, height)
+
+        project.videowidth = sinfo.get_width() * 2
+        project.videoheight = sinfo.get_height() * 2
+        width = video_source.get_child_property("width")[1]
+        height = video_source.get_child_property("height")[1]
+        self.assertEqual(project.videowidth, width)
+        self.assertEqual(project.videoheight, height)
+
+        project.videowidth = 150
+        project.videoheight = 200
+        width = video_source.get_child_property("width")[1]
+        height = video_source.get_child_property("height")[1]
+
+        expected_width = project.videowidth
+        expected_height = int(sinfo.get_height() * (project.videowidth / sinfo.get_width()))
+        self.assertEqual(width, expected_width)
+        self.assertEqual(height, expected_height)
+
+        video_source.set_child_property("posx", 50)
+        width = video_source.get_child_property("width")[1]
+        height = video_source.get_child_property("height")[1]
+        self.assertEqual(width, expected_width)
+        self.assertEqual(height, expected_height)
+
+        project.videowidth = 1920
+        project.videoheight = 1080
+        self.assertEqual(width, expected_width)
+        self.assertEqual(height, expected_height)
+
+        expected_default_position = {
+            "width": 1920,
+            "height": 800,
+            "posx": 0,
+            "posy": 140}
+        self.assertEqual(video_source.ui.default_position,
+                         expected_default_position)
diff --git a/tests/test_timeline_timeline.py b/tests/test_timeline_timeline.py
index c44d81e..d492180 100644
--- a/tests/test_timeline_timeline.py
+++ b/tests/test_timeline_timeline.py
@@ -45,7 +45,8 @@ class BaseTestTimeline(common.TestCase):
         timeline_container.setProject(project)
 
         timeline = timeline_container.timeline
-        timeline.get_parent = mock.MagicMock()
+        timeline.app.project_manager.current_project = project
+        timeline.get_parent = mock.MagicMock(return_value=timeline_container)
 
         timeline.app.settings.leftClickAlsoSeeks = False
 


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