[pitivi] elements: Let latest GES handle rescaling on project size change



commit 3b6eac1f700fac4ebbdd343414e66814d1aed7d1
Author: Thibault Saunier <tsaunier igalia com>
Date:   Thu Apr 16 17:36:23 2020 -0400

    elements: Let latest GES handle rescaling on project size change
    
    Since 
https://gitlab.freedesktop.org/gstreamer/gst-editing-services/commit/9cf986c71c79c6339bb5900ec29b9045a3df7c46,
    GES handles resetting sources size when project video size is set
    and it also handle keyframes and should be smarted overall. We should
    let GES do its job.
    
    Bump GStreamer dep to 1.17.90

 pitivi/check.py                 |   2 +-
 pitivi/timeline/elements.py     |  23 +++++----
 tests/common.py                 |  55 ++++++++++++++++++++
 tests/test_clipproperties.py    |   3 +-
 tests/test_timeline_elements.py | 109 ++++++++++++++++++++++++++++------------
 5 files changed, 148 insertions(+), 44 deletions(-)
---
diff --git a/pitivi/check.py b/pitivi/check.py
index 193f5301..7dcbb187 100644
--- a/pitivi/check.py
+++ b/pitivi/check.py
@@ -422,7 +422,7 @@ def initialize_modules():
 # a specific version requirement, they have the "None" value.
 
 GST_API_VERSION = "1.0"
-GST_VERSION = "1.14.1"
+GST_VERSION = "1.17.90"
 GTK_API_VERSION = "3.0"
 GLIB_API_VERSION = "2.0"
 HARD_DEPENDENCIES = [GICheck(version_required="3.20.0"),
diff --git a/pitivi/timeline/elements.py b/pitivi/timeline/elements.py
index 24beb6bb..1b1e35e9 100644
--- a/pitivi/timeline/elements.py
+++ b/pitivi/timeline/elements.py
@@ -854,12 +854,12 @@ class VideoSource(TimelineElement):
         parent.connect("child-removed", self.__parent_child_removed_cb)
 
     def __parent_child_added_cb(self, unused_parent, unused_child):
-        self.__apply_new_size_if_needed()
+        self.__reset_position()
 
     def __parent_child_removed_cb(self, unused_parent, child):
         if child == self.__videoflip:
             self.__videoflip = None
-            self.__apply_new_size_if_needed()
+            self.__reset_position()
             disconnect_all_by_func(child, self.__track_element_deep_notify_cb)
             disconnect_all_by_func(child, self.__track_element_notify_active_cb)
 
@@ -870,17 +870,22 @@ class VideoSource(TimelineElement):
         self._project_height = project.videoheight
 
     def _project_video_size_changed_cb(self, unused_project):
-        self.__apply_new_size_if_needed()
+        # GES handles repositionning clips on project size change, make sure to
+        # take that into account.
+        self.__retrieve_project_size()
+        self.default_position = self._get_default_position()
 
-    def __apply_new_size_if_needed(self):
-        using_defaults = True
+    def __has_default_position(self):
         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
+                return False
+
+        return True
 
+    def __reset_position(self):
+        using_defaults = self.__has_default_position()
         self.__retrieve_project_size()
         self.default_position = self._get_default_position()
         if using_defaults:
@@ -945,11 +950,11 @@ class VideoSource(TimelineElement):
 
     def __track_element_deep_notify_cb(self, unused_source, unused_gstelement,
                                        unused_pspec):
-        self.__apply_new_size_if_needed()
+        self.__reset_position()
 
     def __track_element_notify_active_cb(self, unused_track_element,
                                          unused_pspec):
-        self.__apply_new_size_if_needed()
+        self.__reset_position()
 
     def _get_background(self):
         return VideoBackground()
diff --git a/tests/common.py b/tests/common.py
index 319d33af..ae539bd1 100644
--- a/tests/common.py
+++ b/tests/common.py
@@ -16,6 +16,7 @@
 # License along with this program; if not, see <http://www.gnu.org/licenses/>.
 """Useful objects for testing."""
 # pylint: disable=protected-access
+import collections
 import contextlib
 import gc
 import locale
@@ -185,6 +186,60 @@ class CheckedOperationDuration:
         signal.alarm(0)
 
 
+def setup_project_with_clips(func, assets_names=None):
+    """Sets up a Pitivi instance with the specified assets on the timeline."""
+    if assets_names is None:
+        assets_names = {"1sec_simpsons_trailer.mp4"}
+
+    def wrapper(self):
+        with cloned_sample(*list(assets_names)):
+            self.app = create_pitivi()
+            self.project = self.app.project_manager.new_blank_project()
+            self.timeline = self.project.ges_timeline
+            self.layer = self.timeline.append_layer()
+            self.action_log = self.app.action_log
+            project = self.app.project_manager.current_project
+            self.timeline_container = TimelineContainer(self.app, 
editor_state=self.app.gui.editor.editor_state)
+            self.timeline_container.set_project(project)
+
+            timeline = self.timeline_container.timeline
+            timeline.app.project_manager.current_project = project
+            timeline.get_parent = mock.MagicMock(return_value=self.timeline_container)
+            uris = collections.deque([get_sample_uri(fname) for fname in assets_names])
+            mainloop = create_main_loop()
+
+            def loaded_cb(project, timeline):
+                project.add_uris([uris.popleft()])
+
+            def progress_cb(project, progress, estimated_time):
+                if progress == 100:
+                    if uris:
+                        project.add_uris([uris.popleft()])
+                    else:
+                        mainloop.quit()
+
+            project.connect_after("loaded", loaded_cb)
+            project.connect_after("asset-loading-progress", progress_cb)
+            mainloop.run()
+
+            project.disconnect_by_func(loaded_cb)
+            project.disconnect_by_func(progress_cb)
+
+            assets = project.list_assets(GES.UriClip)
+            self.timeline_container.insert_assets(assets, self.timeline.props.duration)
+
+            func(self)
+
+        del self.app
+        del self.project
+        del self.timeline
+        del self.layer
+        del self.action_log
+        del self.timeline_container
+
+    return wrapper
+
+
 def setup_transformation_box(func):
     def wrapped(self):
         has_container = hasattr(self, "timeline_container")
diff --git a/tests/test_clipproperties.py b/tests/test_clipproperties.py
index b423db6b..cf9caac6 100644
--- a/tests/test_clipproperties.py
+++ b/tests/test_clipproperties.py
@@ -15,7 +15,7 @@
 # You should have received a copy of the GNU Lesser General Public
 # License along with this program; if not, see <http://www.gnu.org/licenses/>.
 """Tests for the pitivi.clipproperties module."""
-# pylint: disable=protected-access,no-self-use
+# pylint: disable=protected-access,no-self-use,import-outside-toplevel,no-member
 from unittest import mock
 
 from gi.repository import GES
@@ -322,7 +322,6 @@ class ClipPropertiesTest(BaseTestUndoTimeline, BaseTestTimeline):
         # Wait until the project creates a layer in the timeline.
         common.create_main_loop().run(until_empty=True)
 
-        # pylint: disable=import-outside-toplevel
         from pitivi.timeline.timeline import TimelineContainer
         timeline_container = TimelineContainer(self.app, editor_state=self.app.gui.editor.editor_state)
         timeline_container.set_project(self.project)
diff --git a/tests/test_timeline_elements.py b/tests/test_timeline_elements.py
index 60a09cdd..affde34d 100644
--- a/tests/test_timeline_elements.py
+++ b/tests/test_timeline_elements.py
@@ -24,12 +24,12 @@ from gi.repository import GES
 from gi.repository import Gst
 from gi.repository import Gtk
 from matplotlib.backend_bases import MouseEvent
+from tests import common
+from tests.test_timeline_timeline import BaseTestTimeline
 
 from pitivi.timeline.elements import GES_TYPE_UI_TYPE
 from pitivi.undo.undo import UndoableActionLog
 from pitivi.utils.timeline import Zoomable
-from tests import common
-from tests.test_timeline_timeline import BaseTestTimeline
 
 
 class TestKeyframeCurve(BaseTestTimeline):
@@ -227,41 +227,16 @@ class TestVideoSource(BaseTestTimeline):
         self.assertEqual(project.videowidth, width)
         self.assertEqual(project.videoheight, height)
 
-        project.videowidth = sinfo.get_width() * 2
-        project.videoheight = sinfo.get_height() * 2
+        project.set_video_properties(sinfo.get_width() * 2, sinfo.get_height() * 2, project.videorate)
         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)
+        # GES won't ever break aspect ratio, neither should we!
+        project.set_video_properties(150, 200, project.videorate)
+        self.assertEqual(video_source.get_child_property("width").value, width)
+        self.assertEqual(video_source.get_child_property("height").value, height)
 
     def test_rotation(self):
         """Checks the size of the clips flipped 90 degrees."""
@@ -308,6 +283,76 @@ class TestVideoSource(BaseTestTimeline):
         self.assertEqual(width, 960)
         self.assertEqual(height, 400)
 
+    @common.setup_project_with_clips
+    @common.setup_transformation_box
+    def test_change_set_project_size(self):
+        """Checks the size of the scaled clips after project settings changes."""
+        clip, = self.layer.get_clips()
+
+        def assert_child_props(clip, expectations):
+            # ellipsize position in expectation means 0
+            for prop in ['posx', 'posy']:
+                if prop not in expectations:
+                    expectations[prop] = 0
+            res = {}
+            for propname in expectations.keys():
+                res[propname] = clip.get_child_property(propname).value
+            self.assertEqual(res, expectations)
+
+        source = clip.find_track_element(None, GES.VideoUriSource)
+        sinfo = source.get_asset().get_stream_info()
+        # Check that the clip has its natural size
+        assert_child_props(clip, {"width": sinfo.get_width(), "height": sinfo.get_height()})
+
+        reset_clip_properties_button = self.transformation_box.builder.get_object("clear_button")
+
+        def check_set_pos_and_project_size(new_position, new_project_width,
+                                           new_project_height, expected_position):
+
+            self.timeline_container.timeline.selection.select([clip])
+            reset_clip_properties_button.clicked()
+
+            assert_child_props(source, {"width": self.project.videowidth, "height": 
self.project.videoheight})
+
+            for propname, value in new_position.items():
+                source.set_child_property(propname, value)
+            self.project.set_video_properties(new_project_width, new_project_height, self.project.videorate)
+            assert_child_props(source, expected_position)
+
+        # Rescale to half the size
+        check_set_pos_and_project_size(
+            {},
+            sinfo.get_width() / 2,
+            sinfo.get_height() / 2,
+            {"width": sinfo.get_width() / 2, "height": sinfo.get_height() / 2}
+        )
+
+        # Back to natural size
+        check_set_pos_and_project_size(
+            {},
+            sinfo.get_width(),
+            sinfo.get_height(),
+            {"width": sinfo.get_width(), "height": sinfo.get_height()}
+        )
+
+        # Put the video in the bottom left at its half size and rescale project
+        # to half, meaning video should be 1/4th of its size now
+        check_set_pos_and_project_size(
+            {
+                "width": sinfo.get_width() / 2,
+                "height": sinfo.get_height() / 2,
+                "posx": sinfo.get_width() / 2,
+                "posy": sinfo.get_height() / 2,
+            },
+            sinfo.get_width() / 2, sinfo.get_height() / 2,
+            {
+                "width": sinfo.get_width() / 4,
+                "height": sinfo.get_height() / 4,
+                "posx": sinfo.get_width() / 4,
+                "posy": sinfo.get_height() / 4,
+            },
+        )
+
 
 class TestClip(common.TestCase):
     """Tests for the Clip class."""


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