[rhythmbox] library: add cbr and vbr encoding styles



commit a73ff98a4b5cb7301a2c50756aab688199a90bd4
Author: Jonathan Matthew <jonathan d14n org>
Date:   Sun Jul 7 20:46:01 2013 +1000

    library: add cbr and vbr encoding styles
    
    These replace the 'custom settings' option in the format settings.
    They expose different sets of properties for editing (mostly
    bitrate for CBR, quality for VBR).  The encoding profile file
    describes the property settings required to configure the encoder
    for the style.
    
    This will be more useful when media player devices have their own
    encoding settings, but for now if you want 128kbps mp3s in your
    library, you can have them.

 data/rhythmbox.gep          |   40 ++++++++-
 lib/rb-gst-media-types.c    |  101 +++++++++++++++++++++----
 lib/rb-gst-media-types.h    |    7 ++-
 sources/rb-library-source.c |  180 ++++++++++++++++++++++++++-----------------
 4 files changed, 238 insertions(+), 90 deletions(-)
---
diff --git a/data/rhythmbox.gep b/data/rhythmbox.gep
index 5da9af5..ec10803 100644
--- a/data/rhythmbox.gep
+++ b/data/rhythmbox.gep
@@ -63,8 +63,38 @@ format = audio/mpeg, mpegversion=4, stream-format=raw
 presence = 1
 
 [rhythmbox-encoder-settings]
-# maps encoder elements to lists of settings to expose
-lamemp3enc = quality
-faac = quality
-vorbisenc = quality
-opusenc = bitrate
+# maps encoder elements and encoding types to lists of settings to expose
+lamemp3enc-vbr = quality
+lamemp3enc-cbr = bitrate
+faac-vbr = quality
+faac-cbr = bitrate
+vorbisenc-vbr = quality
+vorbisenc-cbr = bitrate
+opusenc-cbr = bitrate
+opusenc-vbr = bitrate
+
+# these bits tell us how to set up encoders for different encoding types
+[rhythmbox-encoding-lamemp3enc-vbr]
+target = 0
+
+[rhythmbox-encoding-lamemp3enc-cbr]
+target = 1
+
+[rhythmbox-encoding-vorbisenc-vbr]
+managed = false
+
+[rhythmbox-encoding-vorbisenc-cbr]
+managed = true
+
+[rhythmbox-encoding-faac-vbr]
+rate-control = 0
+
+[rhythmbox-encoding-faac-cbr]
+rate-control = 1
+
+[rhythmbox-encoding-opusenc-vbr]
+cbr = false
+constrained-vbr = true
+
+[rhythmbox-encoding-opusenc-cbr]
+cbr = true
diff --git a/lib/rb-gst-media-types.c b/lib/rb-gst-media-types.c
index 1f26189..2ef7343 100644
--- a/lib/rb-gst-media-types.c
+++ b/lib/rb-gst-media-types.c
@@ -52,6 +52,8 @@ static const char *container_formats[] = {
 static GstEncodingTarget *default_target = NULL;
 static GKeyFile *target_keyfile = NULL;
 
+#define ENCODER_STYLE_SETTINGS_PREFIX "rhythmbox-encoding-"
+
 RBGstMediaType
 rb_gst_get_missing_plugin_type (const GstStructure *structure)
 {
@@ -392,22 +394,43 @@ rb_gst_encoding_profile_set_preset (GstEncodingProfile *profile, const char *pre
        }
 }
 
+static GKeyFile *
+get_target_keyfile (void)
+{
+       if (target_keyfile == NULL) {
+               char *file = get_encoding_target_file ();
+               GError *error = NULL;
+
+               target_keyfile = g_key_file_new ();
+               g_key_file_set_list_separator (target_keyfile, ',');
+               g_key_file_load_from_file (target_keyfile, file, G_KEY_FILE_NONE, &error);
+               if (error != NULL) {
+                       g_warning ("Unable to load encoding target keyfile %s: %s", file, error->message);
+                       g_clear_error (&error);
+               }
+       }
+
+       return target_keyfile;
+}
+
 /**
  * rb_gst_encoding_profile_get_settings:
  * @profile: a #GstEncodingProfile
+ * @style: encoding style (NULL or "cbr" or "vbr")
  *
  * Returns a list of settings for the profile @profile that can usefully
  * be exposed to a user.  This usually means just bitrate/quality settings.
  * This works by finding the name of the encoder element for the profile
  * and retrieving a list specific to that encoder.
  *
- * Return value: (transfer full) (element-type GParamSpec): list of settings
+ * Return value: (transfer full): list of settings
  */
 char **
-rb_gst_encoding_profile_get_settings (GstEncodingProfile *profile)
+rb_gst_encoding_profile_get_settings (GstEncodingProfile *profile, const char *style)
 {
        GstElementFactory *factory;
        char **setting_names;
+       char *key_name;
 
        factory = get_audio_encoder_factory (profile);
        if (factory == NULL) {
@@ -417,24 +440,21 @@ rb_gst_encoding_profile_get_settings (GstEncodingProfile *profile)
        /* look up list of settings;
         * if we don't have one for the encoder, what do we do?  return everything?
         */
-       if (target_keyfile == NULL) {
-               char *file = get_encoding_target_file ();
-               GError *error = NULL;
 
-               target_keyfile = g_key_file_new ();
-               g_key_file_set_list_separator (target_keyfile, ',');
-               g_key_file_load_from_file (target_keyfile, file, G_KEY_FILE_NONE, &error);
-               if (error != NULL) {
-                       g_warning ("Unable to load encoding target keyfile %s: %s", file, error->message);
-                       g_clear_error (&error);
-               }
+       if (style == NULL) {
+               key_name = g_strdup (gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
+       } else {
+               key_name = g_strdup_printf ("%s-%s",
+                                           gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)),
+                                           style);
        }
 
-       setting_names = g_key_file_get_string_list (target_keyfile,
+       setting_names = g_key_file_get_string_list (get_target_keyfile (),
                                                    "rhythmbox-encoder-settings",
-                                                   gst_plugin_feature_get_name (GST_PLUGIN_FEATURE 
(factory)),
+                                                   key_name,
                                                    NULL,
                                                    NULL);
+       g_free (key_name);
        return setting_names;
 }
 
@@ -465,3 +485,56 @@ rb_gst_encoding_profile_get_presets (GstEncodingProfile *profile)
        return presets;
 }
 
+gboolean
+rb_gst_encoder_set_encoding_style (GstElement *encoder, const char *style)
+{
+       GstElementFactory *factory;
+       char *group_name;
+       char **keys;
+       int i;
+
+       factory = gst_element_get_factory (encoder);
+       group_name = g_strdup_printf (ENCODER_STYLE_SETTINGS_PREFIX "%s-%s",
+                                     gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)),
+                                     style);
+       rb_debug ("applying settings from %s", group_name);
+
+       keys = g_key_file_get_keys (get_target_keyfile (), group_name, NULL, NULL);
+       if (keys == NULL) {
+               rb_debug ("nothing to apply");
+               g_free (group_name);
+               return FALSE;
+       }
+
+       for (i = 0; keys[i] != NULL; i++) {
+               GParamSpec *pspec;
+               GValue v = {0,};
+               char *value;
+
+               pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (encoder), keys[i]);
+               if (pspec == NULL) {
+                       rb_debug ("couldn't find property %s", keys[i]);
+                       continue;
+               }
+
+               value = g_key_file_get_string (get_target_keyfile (), group_name, keys[i], NULL);
+               if (value == NULL) {
+                       rb_debug ("couldn't get value for property %s", keys[i]);
+                       continue;
+               }
+
+               g_value_init (&v, pspec->value_type);
+               if (gst_value_deserialize (&v, value)) {
+                       rb_debug ("applying value \"%s\" to property %s", value, keys[i]);
+                       g_object_set_property (G_OBJECT (encoder), keys[i], &v);
+               } else {
+                       rb_debug ("couldn't deserialize value \"%s\" for property %s", value, keys[i]);
+               }
+
+               g_value_unset (&v);
+       }
+
+       g_strfreev (keys);
+       g_free (group_name);
+       return TRUE;
+}
diff --git a/lib/rb-gst-media-types.h b/lib/rb-gst-media-types.h
index fd1840e..56e4380 100644
--- a/lib/rb-gst-media-types.h
+++ b/lib/rb-gst-media-types.h
@@ -49,6 +49,10 @@ typedef enum {
        MEDIA_TYPE_OTHER
 } RBGstMediaType;
 
+/* encoding styles */
+#define RB_ENCODING_STYLE_CBR          "cbr"
+#define RB_ENCODING_STYLE_VBR          "vbr"
+
 char *         rb_gst_caps_to_media_type (const GstCaps *caps);
 GstCaps *      rb_gst_media_type_to_caps (const char *media_type);
 
@@ -70,9 +74,10 @@ char *               rb_gst_encoding_profile_get_media_type (GstEncodingProfile *profile);
 
 gboolean       rb_gst_media_type_is_lossless (const char *media_type);
 
-char **                rb_gst_encoding_profile_get_settings (GstEncodingProfile *profile);
+char **                rb_gst_encoding_profile_get_settings (GstEncodingProfile *profile, const char *style);
 char **                rb_gst_encoding_profile_get_presets (GstEncodingProfile *profile);
 void           rb_gst_encoding_profile_set_preset (GstEncodingProfile *profile, const char *preset);
+gboolean       rb_gst_encoder_set_encoding_style (GstElement *element, const char *style);
 
 GstElement *   rb_gst_encoding_profile_get_encoder (GstEncodingProfile *profile);
 
diff --git a/sources/rb-library-source.c b/sources/rb-library-source.c
index 9ebd4a3..bbca1db 100644
--- a/sources/rb-library-source.c
+++ b/sources/rb-library-source.c
@@ -126,16 +126,16 @@ typedef struct {
        char *path;
 } LibraryPathElement;
 
-const LibraryPathElement library_layout_paths[] = {
+static const LibraryPathElement library_layout_paths[] = {
        {N_("Artist/Artist - Album"), "%aa/%aa - %at"},
        {N_("Artist/Album"), "%aa/%at"},
        {N_("Artist - Album"), "%aa - %at"},
        {N_("Album"), "%at"},
        {N_("Artist"), "%aa"},
 };
-const int num_library_layout_paths = G_N_ELEMENTS (library_layout_paths);
+static const int num_library_layout_paths = G_N_ELEMENTS (library_layout_paths);
 
-const LibraryPathElement library_layout_filenames[] = {
+static const LibraryPathElement library_layout_filenames[] = {
        {N_("Number - Title"), "%tN - %tt"},
        {N_("Artist - Title"), "%ta - %tt"},
        {N_("Artist - Number - Title"), "%ta - %tN - %tt"},
@@ -143,9 +143,22 @@ const LibraryPathElement library_layout_filenames[] = {
        {N_("Title"), "%tt"},
        {N_("Number. Artist - Title"), "%tN. %ta - %tt"},
 };
-const int num_library_layout_filenames = G_N_ELEMENTS (library_layout_filenames);
+static const int num_library_layout_filenames = G_N_ELEMENTS (library_layout_filenames);
+
+#define CUSTOM_SETTINGS_PREFIX "rhythmbox-custom-settings"
+#define CBR_SETTINGS_PRESET CUSTOM_SETTINGS_PREFIX "-cbr"
+/* this preset name was used in releases where we only had VBR custom settings */
+#define VBR_SETTINGS_PRESET CUSTOM_SETTINGS_PREFIX
+
+static struct {
+       const char *style;
+       const char *label;
+       const char *preset;
+} library_encoding_styles[] = {
+       { "cbr", N_("Constant bit rate"), CBR_SETTINGS_PRESET },
+       { "vbr", N_("Variable bit rate"), VBR_SETTINGS_PRESET },
+};
 
-#define CUSTOM_SETTINGS_PRESET "rhythmbox-custom-settings"
 
 struct RBLibrarySourcePrivate
 {
@@ -176,8 +189,8 @@ struct RBLibrarySourcePrivate
        GList *import_jobs;
        guint start_import_job_id;
        gulong profile_changed_id;
-       gboolean custom_settings_exists;
        gboolean profile_init;
+       char *preset_name;
        gboolean do_initial_import;
 
        GSettings *settings;
@@ -608,9 +621,78 @@ profile_changed_cb (RBObjectPropertyEditor *editor, RBLibrarySource *source)
        if (source->priv->profile_init)
                return;
 
-       if (source->priv->encoder_element)
+       if (source->priv->encoder_element) {
+               rb_debug ("updating preset %s", source->priv->preset_name);
                gst_preset_save_preset (GST_PRESET (source->priv->encoder_element),
-                                                   CUSTOM_SETTINGS_PRESET);
+                                                   source->priv->preset_name);
+       }
+}
+
+static void
+update_property_editor_for_preset (RBLibrarySource *source, const char *media_type, const char *preset)
+{
+       int i;
+       int style;
+
+       /* figure out if this is a user-settings preset name */
+       style = -1;
+       for (i = 0; i < G_N_ELEMENTS (library_encoding_styles); i++) {
+               if (g_strcmp0 (preset, library_encoding_styles[i].preset) == 0) {
+                       style = i;
+                       break;
+               }
+       }
+
+       /* remove old property editor, if there is one */
+       if (source->priv->encoder_property_editor != NULL) {
+               g_signal_handler_disconnect (source->priv->encoder_property_editor,
+                                            source->priv->profile_changed_id);
+
+               gtk_container_remove (GTK_CONTAINER (source->priv->encoder_property_holder),
+                                     source->priv->encoder_property_editor);
+               source->priv->profile_changed_id = 0;
+               source->priv->encoder_property_editor = NULL;
+               g_free (source->priv->preset_name);
+               source->priv->preset_name = NULL;
+       }
+
+       /* create new property editor, if required */
+       if (style != -1) {
+               GstEncodingProfile *profile;
+               char **profile_settings;
+
+               /* make sure the preset exists so encoder batches can use it */
+               if (gst_preset_load_preset (GST_PRESET (source->priv->encoder_element), preset) == FALSE) {
+                       if (rb_gst_encoder_set_encoding_style (source->priv->encoder_element,
+                                                              library_encoding_styles[style].style)) {
+                               gst_preset_save_preset (GST_PRESET (source->priv->encoder_element),
+                                                       preset);
+                       }
+               }
+
+               profile = rb_gst_get_encoding_profile (media_type);
+               profile_settings =
+                       rb_gst_encoding_profile_get_settings (profile,
+                                                             library_encoding_styles[style].style);
+               source->priv->encoder_property_editor =
+                       rb_object_property_editor_new (G_OBJECT (source->priv->encoder_element),
+                                                      profile_settings);
+               g_strfreev (profile_settings);
+               gst_encoding_profile_unref (profile);
+
+               source->priv->profile_changed_id =
+                       g_signal_connect (source->priv->encoder_property_editor,
+                                         "changed",
+                                         G_CALLBACK (profile_changed_cb),
+                                         source);
+
+               gtk_grid_attach (GTK_GRID (source->priv->encoder_property_holder),
+                                source->priv->encoder_property_editor,
+                                0, 0, 1, 1);
+               gtk_widget_show_all (source->priv->encoder_property_editor);
+
+               source->priv->preset_name = g_strdup (preset);
+       }
 }
 
 static void
@@ -621,19 +703,12 @@ update_presets (RBLibrarySource *source, const char *media_type)
        GstEncodingProfile *profile;
        char **profile_settings;
        char **profile_presets;
+       int i;
 
        source->priv->profile_init = TRUE;
 
        gtk_list_store_clear (GTK_LIST_STORE (source->priv->preset_model));
 
-       if (source->priv->encoder_property_editor != NULL) {
-               g_signal_handler_disconnect (source->priv->encoder_property_editor,
-                                            source->priv->profile_changed_id);
-               gtk_container_remove (GTK_CONTAINER (source->priv->encoder_property_holder),
-                                     source->priv->encoder_property_editor);
-               source->priv->profile_changed_id = 0;
-               source->priv->encoder_property_editor = NULL;
-       }
        if (source->priv->encoder_element != NULL) {
                gst_object_unref (source->priv->encoder_element);
                source->priv->encoder_element = NULL;
@@ -663,40 +738,19 @@ update_presets (RBLibrarySource *source, const char *media_type)
                source->priv->profile_init = FALSE;
                return;
        }
+       source->priv->encoder_element = rb_gst_encoding_profile_get_encoder (profile);
 
-       profile_settings = rb_gst_encoding_profile_get_settings (profile);
-       if (profile_settings != NULL) {
+       for (i = 0; i < G_N_ELEMENTS (library_encoding_styles); i++) {
+               profile_settings = rb_gst_encoding_profile_get_settings (profile, 
library_encoding_styles[i].style);
+               if (profile_settings == NULL)
+                       continue;
 
-               rb_debug ("profile has custom settings");
+               rb_debug ("profile has custom settings for style %s", library_encoding_styles[i].style);
                insert_preset (source,
-                              _("Custom settings"),
-                              CUSTOM_SETTINGS_PRESET,
-                              g_strcmp0 (active_preset, CUSTOM_SETTINGS_PRESET) == 0);
+                              gettext (library_encoding_styles[i].label),
+                              library_encoding_styles[i].preset,
+                              g_strcmp0 (active_preset, library_encoding_styles[i].preset) == 0);
                gtk_widget_set_sensitive (source->priv->preset_menu, TRUE);
-
-               source->priv->encoder_element = rb_gst_encoding_profile_get_encoder (profile);
-               source->priv->custom_settings_exists =
-                       gst_preset_load_preset (GST_PRESET (source->priv->encoder_element),
-                                               CUSTOM_SETTINGS_PRESET);
-
-               source->priv->encoder_property_editor =
-                       rb_object_property_editor_new (G_OBJECT (source->priv->encoder_element),
-                                                      profile_settings);
-               source->priv->profile_changed_id =
-                       g_signal_connect (source->priv->encoder_property_editor,
-                                         "changed",
-                                         G_CALLBACK (profile_changed_cb),
-                                         source);
-
-               gtk_grid_attach (GTK_GRID (source->priv->encoder_property_holder),
-                                source->priv->encoder_property_editor,
-                                0, 0, 1, 1);
-               gtk_widget_show_all (source->priv->encoder_property_editor);
-               gtk_widget_set_no_show_all (source->priv->encoder_property_editor, TRUE);
-               if (g_strcmp0 (active_preset, CUSTOM_SETTINGS_PRESET) != 0) {
-                       gtk_widget_hide (source->priv->encoder_property_editor);
-               }
-               g_strfreev (profile_settings);
        }
 
        /* get list of actual presets for the media type */
@@ -704,7 +758,7 @@ update_presets (RBLibrarySource *source, const char *media_type)
        if (profile_presets) {
                int i;
                for (i = 0; profile_presets[i] != NULL; i++) {
-                       if (g_strcmp0 (profile_presets[i], CUSTOM_SETTINGS_PRESET) == 0)
+                       if (g_str_has_prefix (profile_presets[i], CUSTOM_SETTINGS_PREFIX))
                                continue;
 
                        rb_debug ("profile has preset %s", profile_presets[i]);
@@ -717,6 +771,8 @@ update_presets (RBLibrarySource *source, const char *media_type)
                g_strfreev (profile_presets);
        }
 
+       update_property_editor_for_preset (source, media_type, active_preset);
+
        gst_encoding_profile_unref (profile);
        source->priv->profile_init = FALSE;
 }
@@ -822,7 +878,7 @@ impl_get_config_widget (RBDisplayPage *asource, RBShellPreferences *prefs)
                          "clicked",
                          G_CALLBACK (rb_library_source_location_button_clicked_cb),
                          asource);
-       g_signal_connect (G_OBJECT (source->priv->library_location_entry),
+       g_signal_connect (source->priv->library_location_entry,
                          "focus-out-event",
                          G_CALLBACK (rb_library_source_library_location_cb),
                          asource);
@@ -839,7 +895,7 @@ impl_get_config_widget (RBDisplayPage *asource, RBShellPreferences *prefs)
        source->priv->layout_path_menu = gtk_combo_box_text_new ();
        gtk_box_pack_start (GTK_BOX (tmp), source->priv->layout_path_menu, TRUE, TRUE, 0);
        gtk_label_set_mnemonic_widget (GTK_LABEL (label), source->priv->layout_path_menu);
-       g_signal_connect (G_OBJECT (source->priv->layout_path_menu),
+       g_signal_connect (source->priv->layout_path_menu,
                          "changed",
                          G_CALLBACK (rb_library_source_path_changed_cb),
                          asource);
@@ -853,7 +909,7 @@ impl_get_config_widget (RBDisplayPage *asource, RBShellPreferences *prefs)
        source->priv->layout_filename_menu = gtk_combo_box_text_new ();
        gtk_box_pack_start (GTK_BOX (tmp), source->priv->layout_filename_menu, TRUE, TRUE, 0);
        gtk_label_set_mnemonic_widget (GTK_LABEL (label), source->priv->layout_filename_menu);
-       g_signal_connect (G_OBJECT (source->priv->layout_filename_menu),
+       g_signal_connect (source->priv->layout_filename_menu,
                          "changed",
                          G_CALLBACK (rb_library_source_filename_changed_cb),
                          asource);
@@ -891,7 +947,7 @@ impl_get_config_widget (RBDisplayPage *asource, RBShellPreferences *prefs)
        gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (source->priv->preferred_format_menu), renderer, TRUE);
        gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (source->priv->preferred_format_menu), renderer, 
"text", 1, NULL);
 
-       g_signal_connect (G_OBJECT (source->priv->preferred_format_menu),
+       g_signal_connect (source->priv->preferred_format_menu,
                          "changed",
                          G_CALLBACK (rb_library_source_format_changed_cb),
                          asource);
@@ -902,7 +958,7 @@ impl_get_config_widget (RBDisplayPage *asource, RBShellPreferences *prefs)
        gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (source->priv->preset_menu), renderer, TRUE);
        gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (source->priv->preset_menu), renderer, "text", 0, 
NULL);
 
-       g_signal_connect (G_OBJECT (source->priv->preset_menu),
+       g_signal_connect (source->priv->preset_menu,
                          "changed",
                          G_CALLBACK (rb_library_source_preset_changed_cb),
                          asource);
@@ -912,7 +968,7 @@ impl_get_config_widget (RBDisplayPage *asource, RBShellPreferences *prefs)
 
        source->priv->install_plugins_button = GTK_WIDGET (gtk_builder_get_object (builder, 
"install_plugins_button"));
        gtk_widget_set_no_show_all (source->priv->install_plugins_button, TRUE);
-       g_signal_connect (G_OBJECT (source->priv->install_plugins_button), "clicked", G_CALLBACK 
(rb_library_source_install_plugins_cb), source);
+       g_signal_connect (source->priv->install_plugins_button, "clicked", G_CALLBACK 
(rb_library_source_install_plugins_cb), source);
 
        source->priv->encoder_property_holder = GTK_WIDGET (gtk_builder_get_object (builder, 
"encoder_property_holder"));
 
@@ -1096,23 +1152,7 @@ rb_library_source_preset_changed_cb (GtkWidget *widget, RBLibrarySource *source)
                rb_debug ("no preset selected for media type %s?", media_type);
        }
 
-       /* update custom settings widgets */
-       if (g_strcmp0 (preset, CUSTOM_SETTINGS_PRESET) == 0) {
-               /* make sure the preset exists so encoder batches can use it */
-               if (source->priv->custom_settings_exists == FALSE) {
-                       gst_preset_save_preset (GST_PRESET (source->priv->encoder_element),
-                                               CUSTOM_SETTINGS_PRESET);
-               }
-
-               if (source->priv->encoder_property_editor != NULL) {
-                       gtk_widget_show (source->priv->encoder_property_editor);
-               }
-       } else {
-
-               if (source->priv->encoder_property_editor != NULL) {
-                       gtk_widget_hide (source->priv->encoder_property_editor);
-               }
-       }
+       update_property_editor_for_preset (source, media_type, preset);
 
        /* store selected preset */
        settings = g_settings_get_value (source->priv->encoding_settings, "media-type-presets");


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