[pitivi] render: Pick the default encoder from a list



commit b15312eb9c3982c4bd6fff9ba0e4ddaa9c3287b4
Author: Alexandru Băluț <alexandru balut gmail com>
Date:   Sun Jul 10 00:22:30 2016 +0200

    render: Pick the default encoder from a list
    
    Reviewed-by: Thibault Saunier <tsaunier gnome org>
    Differential Revision: https://phabricator.freedesktop.org/D1167

 data/renderpresets/Blu-ray.json |    2 +-
 pitivi/project.py               |   14 +++-----
 pitivi/render.py                |   77 +++++++++++++++++++++++++++++++++++++--
 tests/test_render.py            |   45 +++++++++++++++++++++++
 4 files changed, 124 insertions(+), 14 deletions(-)
---
diff --git a/data/renderpresets/Blu-ray.json b/data/renderpresets/Blu-ray.json
index e1c4b1b..8098d8a 100644
--- a/data/renderpresets/Blu-ray.json
+++ b/data/renderpresets/Blu-ray.json
@@ -9,5 +9,5 @@
     "framerate-denom": 1001.0,
     "width": 0,
     "depth": 16,
-    "acodec": "faac"
+    "acodec": "voaacenc"
 }
diff --git a/pitivi/project.py b/pitivi/project.py
index f710a3d..e3587d7 100644
--- a/pitivi/project.py
+++ b/pitivi/project.py
@@ -59,9 +59,6 @@ from pitivi.utils.widgets import FractionWidget
 
 
 DEFAULT_NAME = _("New Project")
-DEFAULT_MUXER = "oggmux"
-DEFAULT_VIDEO_ENCODER = "theoraenc"
-DEFAULT_AUDIO_ENCODER = "vorbisenc"
 
 
 class ProjectManager(GObject.Object, Loggable):
@@ -671,8 +668,7 @@ class Project(Loggable, GES.Project):
         self.container_profile = \
             GstPbutils.EncodingContainerProfile.new("pitivi-profile",
                                                     _("Pitivi encoding profile"),
-                                                    Gst.Caps(
-                                                        "application/ogg"),
+                                                    Gst.Caps("application/ogg"),
                                                     None)
         self.video_profile = GstPbutils.EncodingVideoProfile.new(
             Gst.Caps("video/x-theora"), None, Gst.Caps("video/x-raw"), 0)
@@ -682,9 +678,9 @@ class Project(Loggable, GES.Project):
         self.container_profile.add_profile(self.audio_profile)
         self.add_encoding_profile(self.container_profile)
 
-        self.muxer = DEFAULT_MUXER
-        self.vencoder = DEFAULT_VIDEO_ENCODER
-        self.aencoder = DEFAULT_AUDIO_ENCODER
+        self.muxer = Encoders().default_muxer
+        self.vencoder = Encoders().default_video_encoder
+        self.aencoder = Encoders().default_audio_encoder
         self._ensureAudioRestrictions()
         self._ensureVideoRestrictions()
         has_default_settings = not bool(uri) and not bool(scenario)
@@ -1141,7 +1137,7 @@ class Project(Loggable, GES.Project):
             self.muxer = self._getElementFactoryName(
                 Encoders().muxers, container_profile)
             if self.muxer is None:
-                self.muxer = DEFAULT_MUXER
+                self.muxer = Encoders().default_muxer
             for profile in container_profile.get_profiles():
                 if isinstance(profile, GstPbutils.EncodingVideoProfile):
                     self.video_profile = profile
diff --git a/pitivi/render.py b/pitivi/render.py
index 603a731..8cb99cb 100644
--- a/pitivi/render.py
+++ b/pitivi/render.py
@@ -44,7 +44,7 @@ from pitivi.utils.ui import set_combo_value
 from pitivi.utils.widgets import GstElementSettingsDialog
 
 
-class Encoders(object):
+class Encoders(Loggable):
     """Registry of avalaible Muxers, Audio encoders and Video encoders.
 
     Also keeps the avalaible combinations of those.
@@ -59,6 +59,45 @@ class Encoders(object):
             compatible audio encoders ordered by rank.
         compatible_video_encoders (dict): Maps each muxer name to a list of
             compatible video encoders ordered by rank.
+        default_muxer (str): The factory name of the default muxer.
+        default_audio_encoder (str): The factory name of the default audio
+            encoder.
+        default_video_encoder (str): The factory name of the default video
+            encoder.
+    """
+
+    OGG = "oggmux"
+    MKV = "matroskamux"
+    MP4 = "mp4mux"
+    QUICKTIME = "qtmux"
+    WEBM = "webmmux"
+
+    AAC = "voaacenc"
+    AC3 = "avenc_ac3_fixed"
+    OPUS = "opusenc"
+    VORBIS = "vorbisenc"
+
+    JPEG = "jpegenc"
+    THEORA = "theoraenc"
+    VP8 = "vp8enc"
+    X264 = "x264enc"
+
+    SUPPORTED_ENCODERS_COMBINATIONS = [
+        (OGG, VORBIS, THEORA),
+        (OGG, OPUS, THEORA),
+        (WEBM, VORBIS, VP8),
+        (WEBM, OPUS, VP8),
+        (MP4, AAC, X264),
+        (MP4, AC3, X264),
+        (QUICKTIME, AAC, JPEG),
+        (MKV, OPUS, X264),
+        (MKV, VORBIS, X264),
+        (MKV, OPUS, JPEG),
+        (MKV, VORBIS, JPEG)]
+    """The combinations of muxers and encoders which are supported.
+
+    Mirror of GES_ENCODING_TARGET_COMBINATIONS from
+    https://cgit.freedesktop.org/gstreamer/gst-editing-services/tree/tests/validate/geslaunch.py
     """
 
     _instance = None
@@ -67,6 +106,9 @@ class Encoders(object):
         """Returns the singleton instance."""
         if not cls._instance:
             cls._instance = super(Encoders, cls).__new__(cls, *args, **kwargs)
+            # We have to initialize the instance here, otherwise
+            # __init__ is called every time we use Encoders().
+            Loggable.__init__(cls._instance)
             Gst.Registry.get().connect(
                 "feature-added", cls._instance._registry_feature_added_cb)
             cls._instance._load_encoders()
@@ -108,6 +150,10 @@ class Encoders(object):
         for muxer in useless_muxers:
             self.muxers.remove(muxer)
 
+        self.default_muxer, \
+            self.default_audio_encoder, \
+            self.default_video_encoder = self._pick_defaults()
+
     def _find_compatible_encoders(self, encoders, muxer):
         """Returns the list of encoders compatible with the specified muxer."""
         res = []
@@ -130,6 +176,25 @@ class Encoders(object):
                 return True
         return False
 
+    def _pick_defaults(self):
+        """Picks the defaults for new projects.
+
+        Returns:
+            (str, str, str): The muxer, audio encoder, video encoder.
+        """
+        muxer_names = [fact.get_name() for fact in self.muxers]
+        aencoder_names = [fact.get_name() for fact in self.aencoders]
+        vencoder_names = [fact.get_name() for fact in self.vencoders]
+        for muxer, audio, video in self.SUPPORTED_ENCODERS_COMBINATIONS:
+            if muxer not in muxer_names or \
+                    audio not in aencoder_names or \
+                    video not in vencoder_names:
+                continue
+            self.info("Default encoders: %s, %s, %s", muxer, audio, video)
+            return muxer, audio, video
+        self.warning("No good combination of container and encoders available.")
+        return Encoders.OGG, Encoders.VORBIS, Encoders.THEORA
+
     def _registry_feature_added_cb(self, registry, feature):
         # TODO Check what feature has been added and update our lists
         pass
@@ -155,8 +220,12 @@ def beautify_factoryname(factory):
     return " ".join(word for word in name.split())
 
 
-def extension_for_muxer(muxer):
-    """Returns the file extension appropriate for the specified muxer."""
+def extension_for_muxer(muxer_name):
+    """Returns the file extension appropriate for the specified muxer.
+
+    Args:
+        muxer_name (str): The name of the muxer factory.
+    """
     exts = {
         "asfmux": "asf",
         "avimux": "avi",
@@ -185,7 +254,7 @@ def extension_for_muxer(muxer):
         "oggmux": "ogv",
         "qtmux": "mov",
         "webmmux": "webm"}
-    return exts.get(muxer)
+    return exts.get(muxer_name)
 
 
 def factorylist(factories):
diff --git a/tests/test_render.py b/tests/test_render.py
new file mode 100644
index 0000000..da3d789
--- /dev/null
+++ b/tests/test_render.py
@@ -0,0 +1,45 @@
+# -*- coding: utf-8 -*-
+# Pitivi video editor
+# Copyright (c) 2016, Alex Băluț <alexandru balut gmail com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+# Boston, MA 02110-1301, USA.
+"""Tests for the render module."""
+from unittest import mock
+from unittest import TestCase
+
+from pitivi.preset import RenderPresetManager
+from pitivi.render import Encoders
+from pitivi.render import extension_for_muxer
+
+
+class TestRender(TestCase):
+    """Tests for functions."""
+
+    def test_extensions_supported(self):
+        """Checks we associate file extensions to the well supported muxers."""
+        for muxer, unused_audio, unused_video in Encoders.SUPPORTED_ENCODERS_COMBINATIONS:
+            self.assertIsNotNone(extension_for_muxer(muxer), muxer)
+
+    def test_extensions_presets(self):
+        """Checks we associate file extensions to the muxers of the presets."""
+        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)
+            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)


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