[pitivi] preset: Make use of GstEncodingTargets to handle our encoding targets
- From: Thibault Saunier <tsaunier src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pitivi] preset: Make use of GstEncodingTargets to handle our encoding targets
- Date: Fri, 3 Feb 2017 21:47:20 +0000 (UTC)
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]