[pitivi] preset: Make use of GstEncodingTargets to handle our encoding targets



commit 17bcf8ff865aedc86a8f23729c1e0dc7a548cd2a
Author: Thibault Saunier <thibault saunier osg samsung com>
Date:   Fri Jan 6 10:53:07 2017 -0300

    preset: Make use of GstEncodingTargets to handle our encoding targets
    
    Instead of using our own grown implementation of 'RenderingPreset' use
    upstream GStreamer `GstEncodingTarget` API which is more complete,
    more powerful, cleaner and allows us to share the profiles with
    the rest of the world, in particular with gst-transcoder which is
    starting to ship profiles for most common use cases.
    
    This also allows us to start using GstPreset to set encoders/muxer
    properties which in turn makes everything much more powerful.
    
    + utils:ui: Return whether setting value in combo_set_value worked
    
    Differential Revision: https://phabricator.freedesktop.org/D1596

 pitivi/preset.py                          |  128 +++++++++++++++++------------
 pitivi/project.py                         |    4 +
 pitivi/render.py                          |  102 ++++++++++++++---------
 pitivi/utils/ui.py                        |   18 ++++-
 tests/__init__.py                         |    3 +-
 tests/test-encoding-targets/test/test.gep |   61 ++++++++++++++
 tests/test_render.py                      |  125 +++++++++++++++++++++++++---
 7 files changed, 335 insertions(+), 106 deletions(-)
---
diff --git a/pitivi/preset.py b/pitivi/preset.py
index 22efa38..953574a 100644
--- a/pitivi/preset.py
+++ b/pitivi/preset.py
@@ -23,6 +23,7 @@ from gettext import gettext as _
 from gi.repository import Gio
 from gi.repository import GObject
 from gi.repository import Gst
+from gi.repository import GstPbutils
 from gi.repository import Gtk
 
 from pitivi.configure import get_audiopresets_dir
@@ -56,7 +57,7 @@ class PresetManager(GObject.Object, Loggable):
         "preset-loaded": (GObject.SignalFlags.RUN_LAST, None, ()),
     }
 
-    def __init__(self, default_path, user_path, system):
+    def __init__(self, default_path=None, user_path=None, system=None):
         GObject.Object.__init__(self)
         Loggable.__init__(self)
 
@@ -111,12 +112,15 @@ class PresetManager(GObject.Object, Loggable):
     def _presetChangedCb(self, combo):
         """Handles the selection of a preset."""
         # Check whether the user selected a preset or editing the preset name.
+        self.select_preset(combo)
+        self.updateMenuActions()
+
+    def select_preset(self, combo):
         preset_name = combo.get_active_id()
         if preset_name:
             # The user selected a preset.
             self.restorePreset(preset_name)
             self.emit("preset-loaded")
-        self.updateMenuActions()
 
     def _addPresetCb(self, unused_action, unused_param):
         preset_name = self.getNewPresetName()
@@ -131,8 +135,6 @@ class PresetManager(GObject.Object, Loggable):
     def _savePresetCb(self, unused_action, unused_param):
         entry = self.combo.get_child()
         preset_name = entry.get_text()
-        if not self.cur_preset:
-            self.createPreset(preset_name)
         self.saveCurrentPreset(preset_name)
         self.updateMenuActions()
 
@@ -308,6 +310,9 @@ class PresetManager(GObject.Object, Loggable):
 
     def saveCurrentPreset(self, new_name=None):
         """Updates the current preset values from the widgets and saves it."""
+
+        if not self.cur_preset:
+            self.createPreset(preset_name)
         if new_name:
             self._renameCurrentPreset(new_name)
         values = self.presets[self.cur_preset]
@@ -478,60 +483,77 @@ class AudioPresetManager(PresetManager):
             "sample-rate": project.audiorate}
 
 
-class RenderPresetManager(PresetManager):
+class EncodingTargetManager(PresetManager):
+    """Manager of EncodingTargets used as render presets.
 
-    def __init__(self, system, encoders):
-        default_path = get_renderpresets_dir()
-        user_path = os.path.join(xdg_data_home(), 'render_presets')
-        PresetManager.__init__(self, default_path, user_path, system)
-        self.encoders = encoders
+    Uses the GstEncodingTarget API to discover and access the EncodingProfiles.
 
-    def _deserializePreset(self, parser):
-        container = parser["container"]
-        acodec = parser["acodec"]
-        vcodec = parser["vcodec"]
+    Attributes:
+        _project (Project): The project.
+    """
 
-        if acodec not in [fact.get_name() for fact in self.encoders.aencoders]:
-            raise DeserializeException("Audio codec not available: %s" % acodec)
-        if vcodec not in [fact.get_name() for fact in self.encoders.vencoders]:
-            raise DeserializeException("Video codec not available: %s" % vcodec)
-        if container not in [fact.get_name() for fact in self.encoders.muxers]:
-            raise DeserializeException("Container not available: %s" % vcodec)
+    __gsignals__ = {
+        "profile-selected": (GObject.SignalFlags.RUN_LAST, None, (GstPbutils.EncodingProfile,)),
+    }
 
-        try:
-            width = parser["width"]
-            height = parser["height"]
-        except:
-            width = 0
-            height = 0
+    def __init__(self, project):
+        PresetManager.__init__(self)
+        self._project = project
 
-        framerate_num = parser["framerate-num"]
-        framerate_denom = parser["framerate-denom"]
-        framerate = Gst.Fraction(framerate_num, framerate_denom)
+    def _add_target(self, target):
+        profiles = target.get_profiles()
+        for profile in profiles:
+            name = target.get_name().split(';')[0]
+            if len(profiles) != 1 and profile.get_name().lower() != 'default':
+                name += '_' + profile.get_name()
 
-        channels = parser["channels"]
-        sample_rate = parser["sample-rate"]
+            self._addPreset(name, profile)
 
-        return {
-            "container": container,
-            "acodec": acodec,
-            "vcodec": vcodec,
-            "width": width,
-            "height": height,
-            "frame-rate": framerate,
-            "channels": channels,
-            "sample-rate": sample_rate,
-        }
+    def loadAll(self):
+        """Loads profiles from GstEncodingTarget and add them to self.combo.
 
-    def _serializePreset(self, preset):
-        return {
-            "container": str(preset["container"]),
-            "acodec": str(preset["acodec"]),
-            "vcodec": str(preset["vcodec"]),
-            "width": int(preset["width"]),
-            "height": int(preset["height"]),
-            "framerate-num": preset["frame-rate"].num,
-            "framerate-denom": preset["frame-rate"].denom,
-            "channels": preset["channels"],
-            "sample-rate": int(preset["sample-rate"]),
-        }
+        Override from PresetManager
+        """
+        for target in GstPbutils.encoding_list_all_targets():
+            if target.get_category() != GstPbutils.ENCODING_CATEGORY_FILE_EXTENSION:
+                self._add_target(target)
+
+    def saveCurrentPreset(self, new_name):
+        """PresetManager override, saves currently selected profile on disk.
+
+        Override from PresetManager
+
+        Args:
+            new_name (str): The name to save current Gst.EncodingProfile as.
+        """
+        if not self.combo.get_parent().valid:
+            self.error("Current encoding target name is not valid")
+            return
+
+        target = GstPbutils.EncodingTarget.new(new_name, "user-defined",
+                                               new_name,
+                                               [self._project.container_profile])
+        target.save()
+
+        self._add_target(target)
+
+    def select_preset(self, combo):
+        """Selects preset from currently active row in @combo.
+
+        Override from PresetManager
+
+        Args:
+            combo (str): The Gtk.ComboBox to retrieve selected GstEncodingProfile from.
+        """
+        active_iter = combo.get_active_iter()
+        if active_iter:
+            # The user selected a preset.
+            profile = combo.props.model.get_value(active_iter, 1)
+            self.emit("profile-selected", profile)
+
+    def restorePreset(self, values):
+        """Raises NotImplemented as it does not make sense for that class.
+
+        Override from PresetManager
+        """
+        raise NotImplementedError
diff --git a/pitivi/project.py b/pitivi/project.py
index d4f634d..d97f0d4 100644
--- a/pitivi/project.py
+++ b/pitivi/project.py
@@ -879,6 +879,8 @@ class Project(Loggable, GES.Project):
             if caps:
                 self.audio_profile.set_format(caps)
             self.audio_profile.set_preset_name(value)
+            # Gst.Preset can be set exclusively through EncodingTagets for now.
+            self.audio_profile.set_preset(None)
             self._emitChange("rendering-settings-changed", "aencoder", value)
 
     @property
@@ -892,6 +894,8 @@ class Project(Loggable, GES.Project):
             if caps:
                 self.video_profile.set_format(caps)
             self.video_profile.set_preset_name(value)
+            # Gst.Preset can be set exclusively through EncodingTagets for now.
+            self.video_profile.set_preset(None)
             self._emitChange("rendering-settings-changed", "vencoder", value)
 
     @property
diff --git a/pitivi/render.py b/pitivi/render.py
index 6f9d6bf..b1c9940 100644
--- a/pitivi/render.py
+++ b/pitivi/render.py
@@ -30,7 +30,7 @@ from gi.repository import Gtk
 
 from pitivi import configure
 from pitivi.check import missing_soft_deps
-from pitivi.preset import RenderPresetManager
+from pitivi.preset import EncodingTargetManager
 from pitivi.utils.loggable import Loggable
 from pitivi.utils.misc import path_from_uri
 from pitivi.utils.misc import show_user_manual
@@ -40,6 +40,7 @@ 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
@@ -408,8 +409,8 @@ class RenderDialog(Loggable):
         # {object: sigId}
         self._gstSigId = {}
 
-        self.render_presets = RenderPresetManager(self.app.system, Encoders())
-        self.render_presets.loadAll()
+        self.render_presets = EncodingTargetManager(project)
+        self.render_presets.connect('profile-selected', self._encoding_profile_selected_cb)
 
         # Whether encoders changing are a result of changing the muxer.
         self.muxer_combo_changing = False
@@ -422,6 +423,8 @@ class RenderDialog(Loggable):
         else:
             self.updateFilename(self.project.name)
 
+        self._setting_encoding_profile = False
+
         # We store these so that when the user tries various container formats,
         # (AKA muxers) we select these a/v encoders, if they are compatible with
         # the current container format.
@@ -438,7 +441,7 @@ class RenderDialog(Loggable):
 
         self.window.connect("delete-event", self._deleteEventCb)
         self.project.connect(
-            "rendering-settings-changed", self._settingsChanged)
+            "rendering-settings-changed", self._settings_changed_cb)
 
         # Monitor changes
 
@@ -459,39 +462,46 @@ class RenderDialog(Loggable):
         self.wg.addEdge(self.channels_combo, self.preset_menubutton)
         self.wg.addEdge(self.sample_rate_combo, self.preset_menubutton)
 
-        # Bind widgets to RenderPresetsManager
-        self.render_presets.bindWidget(
-            "container",
-            lambda x: self.muxer_setter(self.muxer_combo, x),
-            lambda: get_combo_value(self.muxer_combo).get_name())
-        self.render_presets.bindWidget(
-            "acodec",
-            lambda x: self.acodec_setter(self.audio_encoder_combo, x),
-            lambda: get_combo_value(self.audio_encoder_combo).get_name())
-        self.render_presets.bindWidget(
-            "vcodec",
-            lambda x: self.vcodec_setter(self.video_encoder_combo, x),
-            lambda: get_combo_value(self.video_encoder_combo).get_name())
-        self.render_presets.bindWidget(
-            "sample-rate",
-            lambda x: self.sample_rate_setter(self.sample_rate_combo, x),
-            lambda: get_combo_value(self.sample_rate_combo))
-        self.render_presets.bindWidget(
-            "channels",
-            lambda x: self.channels_setter(self.channels_combo, x),
-            lambda: get_combo_value(self.channels_combo))
-        self.render_presets.bindWidget(
-            "frame-rate",
-            lambda x: self.framerate_setter(self.frame_rate_combo, x),
-            lambda: get_combo_value(self.frame_rate_combo))
-        self.render_presets.bindWidget(
-            "height",
-            lambda x: setattr(self.project, "videoheight", x),
-            lambda: 0)
-        self.render_presets.bindWidget(
-            "width",
-            lambda x: setattr(self.project, "videowidth", x),
-            lambda: 0)
+    def _encoding_profile_selected_cb(self, unused_target, encoding_profile):
+        self._set_encoding_profile(encoding_profile)
+
+    def _set_encoding_profile(self, encoding_profile, recursing=False):
+        old_profile = self.project.container_profile
+
+        def rollback(self):
+            if recursing:
+                return
+
+            self._set_encoding_profile(old_profile, True)
+
+        def factory(x):
+            return Encoders().factories_by_name.get(getattr(self.project, x))
+
+        self.project.set_container_profile(encoding_profile)
+        self._setting_encoding_profile = True
+
+        if not set_combo_value(self.muxer_combo, factory('muxer')):
+            return rollback()
+
+        self.updateAvailableEncoders()
+        for i, (combo, value) in enumerate([
+                (self.audio_encoder_combo, factory('aencoder')),
+                (self.video_encoder_combo, factory('vencoder')),
+                (self.sample_rate_combo, self.project.audiorate),
+                (self.channels_combo, self.project.audiochannels),
+                (self.frame_rate_combo, self.project.videorate)]):
+            if value is None:
+                self.error("%d - Got no value for combo %s... rolling back",
+                           i, combo)
+                return rollback(self)
+
+            if not set_combo_value(combo, value):
+                self.error("%d - Could not set value %s for combo %s... rolling back",
+                           i, value, combo)
+                return rollback(self)
+
+        self.updateResolution()
+        self._setting_encoding_profile = False
 
     def _updatePresetMenuButton(self, unused_source, unused_target):
         self.render_presets.updateMenuActions()
@@ -582,12 +592,13 @@ class RenderDialog(Loggable):
         self.__never_use_proxies.props.group = self.__automatically_use_proxies
 
         self.render_presets.setupUi(self.presets_combo, self.preset_menubutton)
+        self.render_presets.loadAll()
 
         icon = os.path.join(configure.get_pixmap_dir(), "pitivi-render-16.png")
         self.window.set_icon_from_file(icon)
         self.window.set_transient_for(self.app.gui)
 
-    def _settingsChanged(self, unused_project, unused_key, unused_value):
+    def _settings_changed_cb(self, unused_project, key, value):
         self.updateResolution()
 
     def __initialize_muxers_model(self):
@@ -998,6 +1009,7 @@ class RenderDialog(Loggable):
 
     def _closeButtonClickedCb(self, unused_button):
         self.debug("Render dialog's Close button clicked")
+        self.project.disconnect_by_func(self._settings_changed_cb)
         self.destroy()
 
     def _deleteEventCb(self, unused_window, unused_event):
@@ -1159,10 +1171,14 @@ class RenderDialog(Loggable):
         self.render_button.set_sensitive(video_enabled or audio_enabled)
 
     def _frameRateComboChangedCb(self, combo):
+        if self._setting_encoding_profile:
+            return
         framerate = get_combo_value(combo)
         self.project.videorate = framerate
 
     def _videoEncoderComboChangedCb(self, combo):
+        if self._setting_encoding_profile:
+            return
         factory = get_combo_value(combo)
         name = factory.get_name()
         self.project.vencoder = name
@@ -1173,16 +1189,24 @@ class RenderDialog(Loggable):
         self._update_valid_video_restrictions(factory)
 
     def _videoSettingsButtonClickedCb(self, unused_button):
+        if self._setting_encoding_profile:
+            return
         factory = get_combo_value(self.video_encoder_combo)
         self._elementSettingsDialog(factory, 'vcodecsettings')
 
     def _channelsComboChangedCb(self, combo):
+        if self._setting_encoding_profile:
+            return
         self.project.audiochannels = get_combo_value(combo)
 
     def _sampleRateComboChangedCb(self, combo):
+        if self._setting_encoding_profile:
+            return
         self.project.audiorate = get_combo_value(combo)
 
     def _audioEncoderChangedComboCb(self, combo):
+        if self._setting_encoding_profile:
+            return
         factory = get_combo_value(combo)
         name = factory.get_name()
         self.project.aencoder = name
@@ -1198,6 +1222,8 @@ class RenderDialog(Loggable):
 
     def _muxerComboChangedCb(self, combo):
         """Handles the changing of the container format combobox."""
+        if self._setting_encoding_profile:
+            return
         factory = get_combo_value(combo)
         self.project.muxer = factory.get_name()
 
diff --git a/pitivi/utils/ui.py b/pitivi/utils/ui.py
index 6f68b63..911bb53 100644
--- a/pitivi/utils/ui.py
+++ b/pitivi/utils/ui.py
@@ -43,6 +43,7 @@ from gi.repository.GstPbutils import DiscovererVideoInfo
 from pitivi.configure import get_pixmap_dir
 from pitivi.utils.loggable import doLog
 from pitivi.utils.loggable import ERROR
+from pitivi.utils.loggable import INFO
 from pitivi.utils.misc import get_proxy_target
 from pitivi.utils.misc import path_from_uri
 
@@ -460,13 +461,24 @@ def model(columns, data):
 
 
 def set_combo_value(combo, value):
-    def select_specific_row(model, unused_path, iter_, unused_data):
-        if value == model.get_value(iter_, 1):
+    def select_specific_row(model, unused_path, iter_, found):
+        model_value = model.get_value(iter_, 1)
+        if value == model_value:
             combo.set_active_iter(iter_)
+            found.append(1)
             return True
         return False
 
-    combo.props.model.foreach(select_specific_row, None)
+    found = []
+    combo.props.model.foreach(select_specific_row, found)
+
+    if len(found) != 1:
+        doLog(INFO, None, "utils",
+              "Could not set value %s, possible values: %s",
+              (value, [v[0] for v in combo.props.model]))
+        return False
+
+    return True
 
 
 def get_combo_value(combo):
diff --git a/tests/__init__.py b/tests/__init__.py
index 7a83955..07e40b0 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -51,7 +51,8 @@ def setup():
                        GI_TYPELIB_PATH=subproject_paths,
                        GST_PRESET_PATH=[os.path.join(pitivi_dir, "data", "videopresets"),
                                         os.path.join(pitivi_dir, "data", "audiopresets")],
-                       GST_ENCODING_TARGET_PATH=[os.path.join(pitivi_dir, "data", "encoding-profiles")])
+                       GST_ENCODING_TARGET_PATH=[os.path.join(pitivi_dir, "tests", "test-encoding-targets"),
+                                                 os.path.join(pitivi_dir, "data", "encoding-profiles")])
     os.environ.setdefault('PITIVI_TOP_LEVEL_DIR', pitivi_dir)
 
     # Make sure the modules are initialized correctly.
diff --git a/tests/test-encoding-targets/test/test.gep b/tests/test-encoding-targets/test/test.gep
new file mode 100644
index 0000000..7918f90
--- /dev/null
+++ b/tests/test-encoding-targets/test/test.gep
@@ -0,0 +1,61 @@
+[GStreamer Encoding Target]
+name=test
+category=test
+description=Just a test
+
+[profile-default]
+name=default
+description=Test ogg container
+type=container
+format=application/ogg
+
+[streamprofile-default-0]
+parent=default
+type=audio
+format=audio/x-vorbis
+preset_name='vorbisenc'
+
+[streamprofile-default-1]
+parent=default
+type=video
+format=video/x-theora
+preset_name='theoraenc'
+
+[profile-ogg-vp8-opus]
+name=ogg-vp8-opus
+description=Test ogg container
+type=container
+format=application/ogg
+
+[streamprofile-ogg-vp8-opus-0]
+parent=ogg-vp8-opus
+type=audio
+format=audio/x-opus
+preset_name='opusenc'
+
+
+[streamprofile-ogg-vp8-opus-1]
+parent=ogg-vp8-opus
+type=video
+preset_name='vp8enc'
+format=video/x-vp8
+
+[profile-fullhd]
+name=fullhd
+description=Test full HD videos
+type=container
+format=application/ogg
+
+[streamprofile-fullhd-0]
+parent=fullhd
+type=audio
+format=audio/x-vorbis
+preset_name='vorbisenc'
+
+
+[streamprofile-fullhd-1]
+parent=fullhd
+type=video
+format=video/x-theora
+preset_name='theoraenc'
+restriction=video/x-raw,width=1920,height=1080,framerate=120/1
diff --git a/tests/test_render.py b/tests/test_render.py
index 979c11f..49ccc4d 100644
--- a/tests/test_render.py
+++ b/tests/test_render.py
@@ -24,9 +24,10 @@ from gi.repository import GES
 from gi.repository import Gst
 from gi.repository import Gtk
 
-from pitivi.preset import RenderPresetManager
+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 tests import common
 
 
@@ -40,20 +41,25 @@ class TestRender(common.TestCase):
 
     def test_extensions_presets(self):
         """Checks we associate file extensions to the muxers of the presets."""
+        project = self.create_simple_project()
         with mock.patch("pitivi.preset.xdg_data_home") as xdg_data_home:
             xdg_data_home.return_value = "/pitivi-dir-which-does-not-exist"
-            preset_manager = RenderPresetManager(system=None, encoders=Encoders())
+            preset_manager = EncodingTargetManager(project.app)
             preset_manager.loadAll()
             self.assertTrue(preset_manager.presets)
-            for unused_name, preset in preset_manager.presets.items():
-                muxer = preset["container"]
-                self.assertIsNotNone(extension_for_muxer(muxer), preset)
+            for unused_name, container_profile in preset_manager.presets.items():
+                # Preset name is only set when the project loads it
+                project.set_container_profile(container_profile)
+                muxer = container_profile.get_preset_name()
+                self.assertIsNotNone(extension_for_muxer(muxer), container_profile)
 
-    def test_launching_rendering(self):
-        """Checks no exception is raised when clicking the render button."""
+    def create_simple_project(self):
+        """Create a Project with a layer a clip."""
         timeline_container = common.create_timeline_container()
         app = timeline_container.app
         project = app.project_manager.current_project
+        if not project.ges_timeline.get_layers():
+            project.ges_timeline.append_layer()
 
         mainloop = common.create_main_loop()
 
@@ -69,11 +75,108 @@ class TestRender(common.TestCase):
         layer.add_asset(project.list_assets(GES.UriClip)[0],
                         0, 0, Gst.CLOCK_TIME_NONE, GES.TrackType.UNKNOWN)
 
-        from pitivi.render import RenderDialog, RenderingProgressDialog
+        return project
+
+    def create_rendering_dialog(self, project):
+        """Create a RenderingDialog ready for testing"""
+        from pitivi.render import RenderDialog
+
+        class MockedBuilder(Gtk.Builder):
+            """Specialized builder suitable for RenderingDialog testing."""
+
+            # pylint: disable=arguments-differ
+            def get_object(self, name):
+                """Get @name widget or a MagicMock for render dialog window."""
+                if name == "render-dialog":
+                    return mock.MagicMock()
+
+                return super().get_object(name)
 
-        with mock.patch.object(Gtk.Builder, "__new__"):
-            dialog = RenderDialog(app, project)
+        with mock.patch.object(Gtk.Builder, "__new__", return_value=MockedBuilder()):
+            return RenderDialog(project.app, project)
+
+    def test_launching_rendering(self):
+        """Checks no exception is raised when clicking the render button."""
+        project = self.create_simple_project()
+        dialog = self.create_rendering_dialog(project)
+
+        from pitivi.render import RenderingProgressDialog
         with mock.patch.object(dialog, "startAction"):
             with mock.patch.object(RenderingProgressDialog, "__new__"):
                 with mock.patch.object(dialog, "_pipeline"):
-                    dialog._renderButtonClickedCb(None)
+                    return dialog._renderButtonClickedCb(None)
+
+    # pylint: disable=too-many-locals
+    def test_loading_preset(self):
+        """Check 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()):
+                if row[0] == name:
+                    return i
+
+            return None
+
+        def preset_changed_cb(combo, changed):
+            """Callback for the 'combo::changed' signal."""
+            changed.append(1)
+
+        project = self.create_simple_project()
+        dialog = self.create_rendering_dialog(project)
+
+        preset_combo = dialog.render_presets.combo
+        changed = []
+        preset_combo.connect("changed", preset_changed_cb, changed)
+
+        test_data = [
+            ("test", {'aencoder': "vorbisenc",
+                      'vencoder': "theoraenc",
+                      'muxer': "oggmux"}),
+            ("test_ogg-vp8-opus", {
+                "aencoder": "opusenc",
+                "vencoder": "vp8enc",
+                "muxer": "oggmux"}),
+            ("test_fullhd", {
+                "aencoder": "vorbisenc",
+                "vencoder": "theoraenc",
+                "muxer": "oggmux",
+                "videowidth": 1920,
+                "videoheight": 1080,
+                "videorate": Gst.Fraction(120, 1)}),
+            ("test_ogg-vp8-opus", {
+                "aencoder": "opusenc",
+                "vencoder": "vp8enc",
+                "muxer": "oggmux"}),
+            ("test_fullhd", {
+                "aencoder": "vorbisenc",
+                "vencoder": "theoraenc",
+                "muxer": "oggmux",
+                "videowidth": 1920,
+                "videoheight": 1080,
+                "videorate": Gst.Fraction(120, 1)}),
+        ]
+
+        attr_dialog_widget_map = {
+            "videorate": dialog.frame_rate_combo,
+            "aencoder": dialog.audio_encoder_combo,
+            "vencoder": dialog.video_encoder_combo,
+            "muxer": dialog.muxer_combo,
+        }
+
+        for preset_name, values in test_data:
+            i = find_preset_row_index(preset_combo, preset_name)
+            self.assertNotEqual(i, None)
+
+            del changed[:]
+            preset_combo.set_active(i)
+            self.assertEqual(changed, [1], "Preset %s" % preset_name)
+
+            for attr, val in values.items():
+                combo = attr_dialog_widget_map.get(attr)
+                if combo:
+                    combo_value = get_combo_value(combo)
+                    if isinstance(combo_value, Gst.ElementFactory):
+                        combo_value = combo_value.get_name()
+                    self.assertEqual(combo_value, val, preset_name)
+
+                self.assertEqual(getattr(project, attr), val)


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