[pitivi] render: Handle encoder from encoding target not available



commit f77154e6ae72ec12006d6ce3b8c2256f7de48c6c
Author: Alexandru Băluț <alexandru balut gmail com>
Date:   Fri Oct 2 01:11:33 2020 +0200

    render: Handle encoder from encoding target not available
    
    Fixes #2513

 pitivi/project.py    | 32 +++++++++++++++---------------
 pitivi/render.py     | 56 ++++++++++++++++++++++++++++++----------------------
 tests/test_render.py | 28 ++++++++++++++++++++++++++
 3 files changed, 76 insertions(+), 40 deletions(-)
---
diff --git a/pitivi/project.py b/pitivi/project.py
index 593200cf7..71c811705 100644
--- a/pitivi/project.py
+++ b/pitivi/project.py
@@ -1513,7 +1513,7 @@ class Project(Loggable, GES.Project):
         if container_profile == self.container_profile:
             return True
 
-        muxer = self._get_element_factory_name(container_profile)
+        muxer = self.get_element_factory_name(container_profile)
         if muxer is None:
             muxer = Encoders().default_muxer
         container_profile.set_preset_name(muxer)
@@ -1531,7 +1531,7 @@ class Project(Loggable, GES.Project):
                 if not self._ensure_video_restrictions(profile):
                     return False
 
-                vencoder = self._get_element_factory_name(profile)
+                vencoder = self.get_element_factory_name(profile)
                 if vencoder:
                     profile.set_preset_name(vencoder)
             elif isinstance(profile, GstPbutils.EncodingAudioProfile):
@@ -1542,7 +1542,7 @@ class Project(Loggable, GES.Project):
                 if not self._ensure_audio_restrictions(profile):
                     return False
 
-                aencoder = self._get_element_factory_name(profile)
+                aencoder = self.get_element_factory_name(profile)
                 if aencoder:
                     profile.set_preset_name(aencoder)
             else:
@@ -1563,7 +1563,7 @@ class Project(Loggable, GES.Project):
         return True
 
     def is_profile_subset(self, profile, superset):
-        return self._get_element_factory_name(profile) == self._get_element_factory_name(superset)
+        return self.get_element_factory_name(profile) == self.get_element_factory_name(superset)
 
     def matches_container_profile(self, container_profile):
         if not self.is_profile_subset(container_profile, self.container_profile):
@@ -2151,7 +2151,7 @@ class Project(Loggable, GES.Project):
         self.emit("rendering-settings-changed", key)
         self.set_modification_state(True)
 
-    def _get_element_factory_name(self, profile):
+    def get_element_factory_name(self, profile):
         """Finds a factory for an element compatible with the specified profile.
 
         Args:
@@ -2174,17 +2174,17 @@ class Project(Loggable, GES.Project):
             # The element does not need to support a specific preset.
             # Return the compatible factory with the highest rank.
             return factories[0].get_name()
-
-        # Make sure that if a #Gst.Preset is set we find an
-        # element that can handle that preset.
-        for factory in factories:
-            elem = factory.create()
-            if isinstance(elem, Gst.Preset):
-                if elem.load_preset(preset):
-                    return factory.get_name()
-
-        self.error("Could not find any element with preset %s", preset)
-        return None
+        else:
+            # Make sure that if a #Gst.Preset is set we find an
+            # element that can handle that preset.
+            for factory in factories:
+                elem = factory.create()
+                if isinstance(elem, Gst.Preset):
+                    if elem.load_preset(preset):
+                        return factory.get_name()
+
+            self.error("Could not find any element with preset %s", preset)
+            return None
 
     @staticmethod
     def __factories_compatible_with_profile(profile):
diff --git a/pitivi/render.py b/pitivi/render.py
index 1a8e1e87d..7d090890c 100644
--- a/pitivi/render.py
+++ b/pitivi/render.py
@@ -277,6 +277,13 @@ class PresetsManager(GObject.Object, Loggable):
             if len(encoding_target.get_profiles()) != 1 and profile.get_name().lower() != "default":
                 name += "_" + profile.get_name()
 
+            # Check the GStreamer elements are available.
+            profiles = [profile] + profile.get_profiles()
+            if not all([self.project.get_element_factory_name(p)
+                        for p in profiles]):
+                self.warning("unusable preset: %s", name)
+                continue
+
             preset_item = PresetItem(name, encoding_target, profile)
             self.model.insert_sorted(preset_item, PresetItem.compare_func)
             preset_items.append(preset_item)
@@ -320,27 +327,32 @@ class PresetsManager(GObject.Object, Loggable):
         """
         self.cur_preset_item = preset_item
         writable = bool(preset_item) and \
-                   len(preset_item.target.get_profiles()) == 1 and \
-                   os.access(preset_item.target.get_path(), os.W_OK)
+            len(preset_item.target.get_profiles()) == 1 and \
+            os.access(preset_item.target.get_path(), os.W_OK)
 
         self.action_remove.set_enabled(writable)
         self.action_save.set_enabled(writable)
 
-    def select_default_preset(self):
-        """Selects the default hardcoded preset."""
-        for item in self.model:
-            if item.name == "youtube":
-                self.select_preset(item)
-                break
+    def initial_preset(self):
+        """Returns the initial preset to be displayed."""
+        has_vcodecsettings = bool(self.project.vcodecsettings)
+        if has_vcodecsettings:
+            # It's a project with previous render settings, see what matches.
+            return self.matching_preset()
+        else:
+            # It's a new project.
+            for item in self.model:
+                if item.name == "youtube":
+                    return item
+
+            return None
 
-    def select_matching_preset(self):
-        """Selects the first preset matching the project's encoders settings."""
+    def matching_preset(self):
+        """Returns the first preset matching the project's encoding profile."""
         for item in self.model:
             if self.project.matches_container_profile(item.profile):
-                self.select_preset(item)
-                return
-
-        self.select_preset(None)
+                return item
+        return None
 
 
 class Encoders(Loggable):
@@ -834,16 +846,11 @@ class RenderDialog(Loggable):
 
         self.presets_manager.connect("profile-updated", self._presets_manager_profile_updated_cb)
 
-        has_vcodecsettings = bool(self.project.vcodecsettings)
-        if has_vcodecsettings:
-            self.presets_manager.select_matching_preset()
-        else:
-            self.presets_manager.select_default_preset()
-            cur_preset_item = self.presets_manager.cur_preset_item
-            if cur_preset_item and self.apply_preset(cur_preset_item):
+        preset_item = self.presets_manager.initial_preset()
+        if preset_item:
+            if self.apply_preset(preset_item):
                 self.apply_vcodecsettings_quality(Quality.MEDIUM)
-            else:
-                self.presets_manager.select_preset(None)
+                self.presets_manager.select_preset(preset_item)
 
         set_icon_and_title(self.preset_icon, self.preset_label, self.presets_manager.cur_preset_item)
         self._update_quality_scale()
@@ -1751,7 +1758,8 @@ class RenderDialog(Loggable):
         if self._setting_encoding_profile:
             return
 
-        self.presets_manager.select_matching_preset()
+        preset_item = self.presets_manager.matching_preset()
+        self.presets_manager.select_preset(preset_item)
 
     def _update_quality_scale(self):
         encoder = get_combo_value(self.video_encoder_combo)
diff --git a/tests/test_render.py b/tests/test_render.py
index 138d38ca2..47aac4dde 100644
--- a/tests/test_render.py
+++ b/tests/test_render.py
@@ -79,6 +79,34 @@ def setup_render_presets(*profiles):
     return setup_wrapper
 
 
+class TestPresetsManager(common.TestCase):
+
+    @skipUnless(*encoding_target_exists("youtube"))
+    @skipUnless(*factory_exists("x264enc"))
+    def test_initial_preset(self):
+        project = common.create_project()
+        manager = PresetsManager(project)
+
+        self.assertEqual(manager.initial_preset().name, "youtube")
+
+    def test_missing_x264(self):
+        # Simulate no encoder being available for the profile's format.
+        targets = GstPbutils.encoding_list_all_targets()
+        for target in targets:
+            for profile in target.get_profiles():
+                for sub_profile in profile.get_profiles():
+                    raw_caps = "audio/non_existing_whatever_it_s_true"
+                    sub_profile.get_format = mock.Mock(return_value=raw_caps)
+
+        with mock.patch.object(GstPbutils, "encoding_list_all_targets") as encoding_list_all_targets:
+            encoding_list_all_targets.return_value = targets
+
+            project = common.create_project()
+            manager = PresetsManager(project)
+
+            self.assertIsNone(manager.initial_preset())
+
+
 class TestQualityAdapter(common.TestCase):
     """Tests for the QualityAdapter class."""
 


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