[pitivi] render: Ensure x264enc input format to be Y444



commit 7397b0f3e55ce6e2d8c6c9f1f4e90651755a5573
Author: Thibault Saunier <thibault saunier osg samsung com>
Date:   Tue Jan 10 10:49:15 2017 -0300

    render: Ensure x264enc input format to be Y444
    
    To avoid the restriction around video width needing to be even.
    
    Good explanation of libx264 restrictions available in
    http://stackoverflow.com/questions/20847674/ffmpeg-libx264-height-not-divisible-by-2/29582287#29582287
    
    Fixes https://phabricator.freedesktop.org/T3182
    
    Differential Revision: https://phabricator.freedesktop.org/D1603

 pitivi/project.py    |   24 ++++++++++++++++++++++++
 pitivi/render.py     |    1 -
 tests/test_render.py |   44 +++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 65 insertions(+), 4 deletions(-)
---
diff --git a/pitivi/project.py b/pitivi/project.py
index d97f0d4..aff722d 100644
--- a/pitivi/project.py
+++ b/pitivi/project.py
@@ -62,6 +62,17 @@ from pitivi.utils.widgets import FractionWidget
 
 DEFAULT_NAME = _("New Project")
 
+# This is a dictionary to allow adding special restrictions when using a specific
+# encoder element.
+# For example x264enc can't encode frame with odd size if its input format is not Y444
+# so in that case we make sure to force Y444 as x264enc input format.
+ENCODERS_RESTRICTIONS_SETTER = {
+    # To avoid restriction of size in x264enc, make sure that the encoder
+    # input color space is Y444 so that we do not have any restriction
+    "x264enc": lambda project, profile: project._set_restriction(
+        profile, "format", "Y444")
+}
+
 
 class ProjectManager(GObject.Object, Loggable):
     """The project manager.
@@ -883,6 +894,14 @@ class Project(Loggable, GES.Project):
             self.audio_profile.set_preset(None)
             self._emitChange("rendering-settings-changed", "aencoder", value)
 
+    def _enforce_video_encoder_restrictions(self, encoder, profile=None):
+        """Enforces @encoder specific restrictions."""
+        if not profile:
+            profile = self.video_profile
+        restriction_setter = ENCODERS_RESTRICTIONS_SETTER.get(encoder)
+        if restriction_setter:
+            restriction_setter(self, profile)
+
     @property
     def vencoder(self):
         return self.video_profile.get_preset_name()
@@ -896,6 +915,7 @@ class Project(Loggable, GES.Project):
             self.video_profile.set_preset_name(value)
             # Gst.Preset can be set exclusively through EncodingTagets for now.
             self.video_profile.set_preset(None)
+            self._enforce_video_encoder_restrictions(value)
             self._emitChange("rendering-settings-changed", "vencoder", value)
 
     @property
@@ -1491,6 +1511,10 @@ class Project(Loggable, GES.Project):
                     value = ref_restrictions[0][fieldname]
                 res = Project._set_restriction(profile, fieldname, value)
 
+        encoder = profile.get_preset_name()
+        if encoder:
+            self._enforce_video_encoder_restrictions(encoder, profile)
+
     def _ensureVideoRestrictions(self, profile=None, ref_restrictions=None):
         values = [
             ("width", 720),
diff --git a/pitivi/render.py b/pitivi/render.py
index b1c9940..50eb150 100644
--- a/pitivi/render.py
+++ b/pitivi/render.py
@@ -40,7 +40,6 @@ from pitivi.utils.ui import audio_rates
 from pitivi.utils.ui import beautify_ETA
 from pitivi.utils.ui import frame_rates
 from pitivi.utils.ui import get_combo_value
-from pitivi.utils.ui import PADDING
 from pitivi.utils.ui import set_combo_value
 from pitivi.utils.widgets import GstElementSettingsDialog
 from pitivi.utils.widgets import TextWidget
diff --git a/tests/test_render.py b/tests/test_render.py
index 49ccc4d..07005f0 100644
--- a/tests/test_render.py
+++ b/tests/test_render.py
@@ -19,18 +19,30 @@
 """Tests for the render module."""
 # pylint: disable=protected-access,no-self-use
 from unittest import mock
+from unittest import skipUnless
 
 from gi.repository import GES
 from gi.repository import Gst
+from gi.repository import GstPbutils
 from gi.repository import Gtk
 
 from pitivi.preset import EncodingTargetManager
 from pitivi.render import Encoders
 from pitivi.render import extension_for_muxer
 from pitivi.utils.ui import get_combo_value
+from pitivi.utils.ui import set_combo_value
 from tests import common
 
 
+def factory_exists(*factories):
+    """Checks if @factories exists."""
+    for factory in factories:
+        if not Gst.ElementFactory.find(factory):
+            return False, "%s not present on the system" % (factory)
+
+    return True, ""
+
+
 class TestRender(common.TestCase):
     """Tests for functions."""
 
@@ -54,7 +66,7 @@ class TestRender(common.TestCase):
                 self.assertIsNotNone(extension_for_muxer(muxer), container_profile)
 
     def create_simple_project(self):
-        """Create a Project with a layer a clip."""
+        """Creates a Project with a layer a clip."""
         timeline_container = common.create_timeline_container()
         app = timeline_container.app
         project = app.project_manager.current_project
@@ -78,7 +90,7 @@ class TestRender(common.TestCase):
         return project
 
     def create_rendering_dialog(self, project):
-        """Create a RenderingDialog ready for testing"""
+        """Creates a RenderingDialog ready for testing"""
         from pitivi.render import RenderDialog
 
         class MockedBuilder(Gtk.Builder):
@@ -106,9 +118,35 @@ class TestRender(common.TestCase):
                 with mock.patch.object(dialog, "_pipeline"):
                     return dialog._renderButtonClickedCb(None)
 
+    @skipUnless(*factory_exists("x264enc", "matroskamux"))
+    def test_encoder_restrictions(self):
+        """Checks the mechanism to respect encoder specific restrictions."""
+        project = self.create_simple_project()
+        dialog = self.create_rendering_dialog(project)
+
+        # Explicitly set the encoder
+        self.assertTrue(set_combo_value(dialog.muxer_combo,
+                                        Gst.ElementFactory.find("matroskamux")))
+        self.assertTrue(set_combo_value(dialog.video_encoder_combo,
+                                        Gst.ElementFactory.find("x264enc")))
+        self.assertEqual(project.video_profile.get_restriction()[0]["format"],
+                         "Y444")
+
+        # Set encoding profile
+        if getattr(GstPbutils.EncodingProfile, "copy"):  # Available only in > 1.11
+            profile = project.container_profile.copy()
+            vprofile, = [p for p in profile.get_profiles()
+                         if isinstance(p, GstPbutils.EncodingVideoProfile)]
+            vprofile.set_restriction(Gst.Caps('video/x-raw'))
+            project.set_container_profile(profile)
+            self.assertEqual(project.video_profile.get_restriction()[0]["format"],
+                             "Y444")
+
     # pylint: disable=too-many-locals
+    @skipUnless(*factory_exists("vorbisenc", "theoraenc", "oggmux",
+                                "opusenc", "vp8enc"))
     def test_loading_preset(self):
-        """Check preset values are properly exposed in the UI."""
+        """Checks preset values are properly exposed in the UI."""
         def find_preset_row_index(combo, name):
             """Finds @name in @combo."""
             for i, row in enumerate(combo.get_model()):


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