gstreamer_core/gst/gstpreset.c
branchRCL_3
changeset 30 7e817e7e631c
parent 29 567bb019e3e3
equal deleted inserted replaced
29:567bb019e3e3 30:7e817e7e631c
     1 /* GStreamer
       
     2  * Copyright (C) 2006 Stefan Kost <ensonic@users.sf.net>
       
     3  *
       
     4  * gstpreset.c: helper interface for element presets
       
     5  *
       
     6  * This library is free software; you can redistribute it and/or
       
     7  * modify it under the terms of the GNU Library General Public
       
     8  * License as published by the Free Software Foundation; either
       
     9  * version 2 of the License, or (at your option) any later version.
       
    10  *
       
    11  * This library is distributed in the hope that it will be useful,
       
    12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    14  * Library General Public License for more details.
       
    15  *
       
    16  * You should have received a copy of the GNU Library General Public
       
    17  * License along with this library; if not, write to the
       
    18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
       
    19  * Boston, MA 02111-1307, USA.
       
    20  */
       
    21 /**
       
    22  * SECTION:gstpreset
       
    23  * @short_description: helper interface for element presets
       
    24  *
       
    25  * This interface offers methods to query and manipulate parameter preset sets.
       
    26  * A preset is a bunch of property settings, together with meta data and a name.
       
    27  * The name of a preset serves as key for subsequent method calls to manipulate
       
    28  * single presets.
       
    29  * All instances of one type will share the list of presets. The list is created
       
    30  * on demand, if presets are not used, the list is not created.
       
    31  *
       
    32  * The interface comes with a default implementation that serves most plugins.
       
    33  * Wrapper plugins will override most methods to implement support for the
       
    34  * native preset format of those wrapped plugins.
       
    35  * One method that is useful to be overridden is gst_preset_get_property_names().
       
    36  * With that one can control which properties are saved and in which order.
       
    37  */
       
    38 /* FIXME:
       
    39  * - non racyness
       
    40  *   - we need to avoid two instances writing the preset file
       
    41  *     -> flock(fileno()), http://www.ecst.csuchico.edu/~beej/guide/ipc/flock.html
       
    42  *     -> open exclusive
       
    43  *     -> better save the new file to a tempfile and then rename?
       
    44  *   - we like to know when any other instance makes changes to the keyfile
       
    45  *     - then ui can be updated
       
    46  *     - and we make sure that we don't lose edits
       
    47  *   -> its the same problem actually, once for inside a process, once system-
       
    48  *      wide
       
    49  *     - can we use a lock inside a names shared memory segment?
       
    50  *
       
    51  * - need to add support for GstChildProxy
       
    52  *   we can do this in a next iteration, the format is flexible enough
       
    53  *   http://www.buzztard.org/index.php/Preset_handling_interface
       
    54  *
       
    55  * - should there be a 'preset-list' property to get the preset list
       
    56  *   (and to connect a notify:: to to listen for changes)
       
    57  *   we could use gnome_vfs_monitor_add() to monitor the user preset_file.
       
    58  *
       
    59  * - should there be a 'preset-name' property so that we can set a preset via
       
    60  *   gst-launch, or should we handle this with special syntax in gst-launch:
       
    61  *   gst-launch element preset:<preset-name> property=value ...
       
    62  *   - this would alloow to hanve preset-bundles too (a preset on bins that
       
    63  *     specifies presets for children
       
    64  *
       
    65  * - GstChildProxy suport
       
    66  *   - if we stick with GParamSpec **_list_properties()
       
    67  *     we need to use g_param_spec_set_qdata() to specify the instance on each GParamSpec
       
    68  *     OBJECT_LOCK(obj);  // ChildProxy needs GstIterator support
       
    69  *     num=gst_child_proxy_get_children_count(obj);
       
    70  *     for(i=0;i<num;i++) {
       
    71  *       child=gst_child_proxy_get_child_by_index(obj,i);
       
    72  *       // v1 ----
       
    73  *       g_object_class_list_properties(child,&num);
       
    74  *       // foreach prop
       
    75  *       //   g_param_spec_set_qdata(prop, quark, (gpointer)child);
       
    76  *       //   add to result
       
    77  *       // v2 ----
       
    78  *       // children have to implement preset-iface too tag the returned GParamSpec* with the owner
       
    79  *       props=gst_preset_list_properties(child);
       
    80  *       // add props to result
       
    81  *     }
       
    82  *     OBJECT_UNLOCK(obj);
       
    83  *
       
    84  */
       
    85 
       
    86 #include "gst_private.h"
       
    87 
       
    88 #include "gstpreset.h"
       
    89 
       
    90 #ifdef HAVE_UNISTD_H
       
    91 #include <unistd.h>
       
    92 #endif
       
    93 #include <glib/gstdio.h>
       
    94 
       
    95 #define GST_CAT_DEFAULT preset_debug
       
    96 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
       
    97 
       
    98 /* defines for keyfile usage, this group contains the element type name and
       
    99  * version these presets belong to. */
       
   100 #define PRESET_HEADER "_presets_"
       
   101 
       
   102 /* keys of the preset header section */
       
   103 #define PRESET_HEADER_ELEMENT_NAME "element-name"
       
   104 #define PRESET_HEADER_VERSION "version"
       
   105 
       
   106 static GQuark preset_user_path_quark = 0;
       
   107 static GQuark preset_system_path_quark = 0;
       
   108 static GQuark preset_quark = 0;
       
   109 
       
   110 /*static GQuark property_list_quark = 0;*/
       
   111 
       
   112 /* default iface implementation */
       
   113 
       
   114 static gboolean gst_preset_default_save_presets_file (GstPreset * preset);
       
   115 
       
   116 /*
       
   117  * preset_get_paths:
       
   118  * @preset: a #GObject that implements #GstPreset
       
   119  * @preset_user_path: location for path or %NULL
       
   120  * @preset_system_path: location for path or %NULL
       
   121  *
       
   122  * Fetch the preset_path for user local and system wide settings. Don't free
       
   123  * after use.
       
   124  *
       
   125  * Returns: %FALSE if no paths could be found.
       
   126  */
       
   127 static gboolean
       
   128 preset_get_paths (GstPreset * preset, const gchar ** preset_user_path,
       
   129     const gchar ** preset_system_path)
       
   130 {
       
   131   GType type = G_TYPE_FROM_INSTANCE (preset);
       
   132   gchar *preset_path;
       
   133   const gchar *element_name;
       
   134 
       
   135   /* we use the element name when we must contruct the paths */
       
   136   element_name = G_OBJECT_TYPE_NAME (preset);
       
   137   GST_INFO_OBJECT (preset, "element_name: '%s'", element_name);
       
   138 
       
   139   if (preset_user_path) {
       
   140     /* preset user path requested, see if we have it cached in the qdata */
       
   141     if (!(preset_path = g_type_get_qdata (type, preset_user_path_quark))) {
       
   142       gchar *preset_dir;
       
   143 
       
   144       /* user presets go in '$HOME/.gstreamer-0.10/presets/GstSimSyn.prs' */
       
   145       preset_dir = g_build_filename (g_get_home_dir (),
       
   146           ".gstreamer-" GST_MAJORMINOR, "presets", NULL);
       
   147       GST_INFO_OBJECT (preset, "user_preset_dir: '%s'", preset_dir);
       
   148       preset_path =
       
   149           g_strdup_printf ("%s" G_DIR_SEPARATOR_S "%s.prs", preset_dir,
       
   150           element_name);
       
   151       GST_INFO_OBJECT (preset, "user_preset_path: '%s'", preset_path);
       
   152       /* create dirs */
       
   153       g_mkdir_with_parents (preset_dir, 0755);
       
   154       g_free (preset_dir);
       
   155 
       
   156       /* cache the preset path to the type */
       
   157       g_type_set_qdata (type, preset_user_path_quark, preset_path);
       
   158     }
       
   159     *preset_user_path = preset_path;
       
   160   }
       
   161 
       
   162   if (preset_system_path) {
       
   163     /* preset system path requested, see if we have it cached in the qdata */
       
   164     if (!(preset_path = g_type_get_qdata (type, preset_system_path_quark))) {
       
   165       gchar *preset_dir;
       
   166 
       
   167       /* system presets in '$GST_DATADIR/gstreamer-0.10/presets/GstAudioPanorama.prs' */
       
   168       preset_dir = g_build_filename (GST_DATADIR, "gstreamer-" GST_MAJORMINOR,
       
   169           "presets", NULL);
       
   170       GST_INFO_OBJECT (preset, "system_preset_dir: '%s'", preset_dir);
       
   171       preset_path = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "%s.prs",
       
   172           preset_dir, element_name);
       
   173       GST_INFO_OBJECT (preset, "system_preset_path: '%s'", preset_path);
       
   174       /* create dirs */
       
   175       g_mkdir_with_parents (preset_dir, 0755);
       
   176       g_free (preset_dir);
       
   177 
       
   178       /* cache the preset path to the type */
       
   179       g_type_set_qdata (type, preset_system_path_quark, preset_path);
       
   180     }
       
   181     *preset_system_path = preset_path;
       
   182   }
       
   183   return TRUE;
       
   184 }
       
   185 
       
   186 static gboolean
       
   187 preset_skip_property (GParamSpec * property)
       
   188 {
       
   189   if (((property->flags & G_PARAM_READWRITE) != G_PARAM_READWRITE) ||
       
   190       (property->flags & G_PARAM_CONSTRUCT_ONLY))
       
   191     return TRUE;
       
   192   /* FIXME: skip GST_PARAM_NOT_PRESETABLE, see #522205 */
       
   193   return FALSE;
       
   194 }
       
   195 
       
   196 /* caller must free @preset_version after use */
       
   197 static GKeyFile *
       
   198 preset_open_and_parse_header (GstPreset * preset, const gchar * preset_path,
       
   199     gchar ** preset_version)
       
   200 {
       
   201   GKeyFile *in;
       
   202   GError *error = NULL;
       
   203   gboolean res;
       
   204   const gchar *element_name;
       
   205   gchar *name;
       
   206 
       
   207   in = g_key_file_new ();
       
   208 
       
   209   res = g_key_file_load_from_file (in, preset_path,
       
   210       G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, &error);
       
   211   if (!res || error != NULL)
       
   212     goto load_error;
       
   213 
       
   214   /* element type name and preset name must match or we are dealing with a wrong
       
   215    * preset file */
       
   216   element_name = G_OBJECT_TYPE_NAME (preset);
       
   217   name =
       
   218       g_key_file_get_value (in, PRESET_HEADER, PRESET_HEADER_ELEMENT_NAME,
       
   219       NULL);
       
   220 
       
   221   if (!name || strcmp (name, element_name))
       
   222     goto wrong_name;
       
   223 
       
   224   g_free (name);
       
   225 
       
   226   /* get the version now so that the caller can check it */
       
   227   if (preset_version)
       
   228     *preset_version =
       
   229         g_key_file_get_value (in, PRESET_HEADER, PRESET_HEADER_VERSION, NULL);
       
   230 
       
   231   return in;
       
   232 
       
   233   /* ERRORS */
       
   234 load_error:
       
   235   {
       
   236     GST_WARNING_OBJECT (preset, "Unable to read preset file %s: %s",
       
   237         preset_path, error->message);
       
   238     g_error_free (error);
       
   239     g_key_file_free (in);
       
   240     return NULL;
       
   241   }
       
   242 wrong_name:
       
   243   {
       
   244     GST_WARNING_OBJECT (preset,
       
   245         "Wrong element name in preset file %s. Expected %s, got %s",
       
   246         preset_path, element_name, GST_STR_NULL (name));
       
   247     g_free (name);
       
   248     g_key_file_free (in);
       
   249     return NULL;
       
   250   }
       
   251 }
       
   252 
       
   253 static guint64
       
   254 preset_parse_version (const gchar * str_version)
       
   255 {
       
   256   gint major, minor, micro, nano, num;
       
   257 
       
   258   major = minor = micro = nano = 0;
       
   259 
       
   260   /* parse version (e.g. 0.10.15.1) to guint64 */
       
   261   num = sscanf (str_version, "%d.%d.%d.%d", &major, &minor, &micro, &nano);
       
   262   /* make sure we have atleast "major.minor" */
       
   263   if (num > 1) {
       
   264     guint64 version;
       
   265 
       
   266     version = ((((major << 8 | minor) << 8) | micro) << 8) | nano;
       
   267     GST_DEBUG ("version %s -> %" G_GUINT64_FORMAT, str_version, version);
       
   268     return version;
       
   269   }
       
   270   return G_GUINT64_CONSTANT (0);
       
   271 }
       
   272 
       
   273 static void
       
   274 preset_merge (GKeyFile * system, GKeyFile * user)
       
   275 {
       
   276   gchar *str;
       
   277   gchar **groups, **keys;
       
   278   gsize i, j, num_groups, num_keys;
       
   279 
       
   280   /* copy file comment if there is any */
       
   281   if ((str = g_key_file_get_comment (user, NULL, NULL, NULL))) {
       
   282     g_key_file_set_comment (system, NULL, NULL, str, NULL);
       
   283     g_free (str);
       
   284   }
       
   285 
       
   286   /* get groups in user and copy into system */
       
   287   groups = g_key_file_get_groups (user, &num_groups);
       
   288   for (i = 0; i < num_groups; i++) {
       
   289     /* copy group comment if there is any */
       
   290     if ((str = g_key_file_get_comment (user, groups[i], NULL, NULL))) {
       
   291       g_key_file_set_comment (system, groups[i], NULL, str, NULL);
       
   292       g_free (str);
       
   293     }
       
   294 
       
   295     /* ignore private groups */
       
   296     if (groups[i][0] == '_')
       
   297       continue;
       
   298 
       
   299     /* if group already exists in system, remove and re-add keys from user */
       
   300     if (g_key_file_has_group (system, groups[i])) {
       
   301       g_key_file_remove_group (system, groups[i], NULL);
       
   302     }
       
   303 
       
   304     keys = g_key_file_get_keys (user, groups[i], &num_keys, NULL);
       
   305     for (j = 0; j < num_keys; j++) {
       
   306       /* copy key comment if there is any */
       
   307       if ((str = g_key_file_get_comment (user, groups[i], keys[j], NULL))) {
       
   308         g_key_file_set_comment (system, groups[i], keys[j], str, NULL);
       
   309         g_free (str);
       
   310       }
       
   311       str = g_key_file_get_value (user, groups[i], keys[j], NULL);
       
   312       g_key_file_set_value (system, groups[i], keys[j], str);
       
   313       g_free (str);
       
   314     }
       
   315     g_strfreev (keys);
       
   316   }
       
   317   g_strfreev (groups);
       
   318 }
       
   319 
       
   320 /* reads the user and system presets files and merges them together. This
       
   321  * function caches the GKeyFile on the element type. If there is no existing
       
   322  * preset file, a new in-memory GKeyFile will be created. */
       
   323 static GKeyFile *
       
   324 preset_get_keyfile (GstPreset * preset)
       
   325 {
       
   326   GKeyFile *presets;
       
   327   GType type = G_TYPE_FROM_INSTANCE (preset);
       
   328 
       
   329   /* first see if the have a cached version for the type */
       
   330   if (!(presets = g_type_get_qdata (type, preset_quark))) {
       
   331     const gchar *preset_user_path, *preset_system_path;
       
   332     gchar *str_version_user = NULL, *str_version_system = NULL;
       
   333     gboolean updated_from_system = FALSE;
       
   334     GKeyFile *in_user, *in_system;
       
   335 
       
   336     preset_get_paths (preset, &preset_user_path, &preset_system_path);
       
   337 
       
   338     /* try to load the user and system presets, we do this to get the versions
       
   339      * of both files. */
       
   340     in_user = preset_open_and_parse_header (preset, preset_user_path,
       
   341         &str_version_user);
       
   342     in_system = preset_open_and_parse_header (preset, preset_system_path,
       
   343         &str_version_system);
       
   344 
       
   345     /* compare version to check for merge */
       
   346     if (in_system) {
       
   347       /* keep system presets if there is no user preset or when the system
       
   348        * version is higher than the user version. */
       
   349       if (!in_user) {
       
   350         presets = in_system;
       
   351       } else if (preset_parse_version (str_version_system) >
       
   352           preset_parse_version (str_version_user)) {
       
   353         presets = in_system;
       
   354         updated_from_system = TRUE;
       
   355       }
       
   356     }
       
   357     if (in_user) {
       
   358       if (updated_from_system) {
       
   359         /* merge user on top of system presets */
       
   360         preset_merge (presets, in_user);
       
   361         g_key_file_free (in_user);
       
   362       } else {
       
   363         /* keep user presets */
       
   364         presets = in_user;
       
   365       }
       
   366     }
       
   367     if (!in_user && !in_system) {
       
   368       /* we did not load a user or system presets file, create a new one */
       
   369       presets = g_key_file_new ();
       
   370       g_key_file_set_string (presets, PRESET_HEADER, PRESET_HEADER_ELEMENT_NAME,
       
   371           G_OBJECT_TYPE_NAME (preset));
       
   372     }
       
   373 
       
   374     g_free (str_version_user);
       
   375     g_free (str_version_system);
       
   376 
       
   377     /* attach the preset to the type */
       
   378     g_type_set_qdata (type, preset_quark, (gpointer) presets);
       
   379 
       
   380     if (updated_from_system) {
       
   381       gst_preset_default_save_presets_file (preset);
       
   382     }
       
   383   }
       
   384   return presets;
       
   385 }
       
   386 
       
   387 /* get a list of all supported preset names for an element */
       
   388 static gchar **
       
   389 gst_preset_default_get_preset_names (GstPreset * preset)
       
   390 {
       
   391   GKeyFile *presets;
       
   392   gsize i, num_groups;
       
   393   gchar **groups;
       
   394 
       
   395   /* get the presets from the type */
       
   396   if (!(presets = preset_get_keyfile (preset)))
       
   397     goto no_presets;
       
   398 
       
   399   /* get the groups, which are also the preset names */
       
   400   if (!(groups = g_key_file_get_groups (presets, &num_groups)))
       
   401     goto no_groups;
       
   402 
       
   403   /* remove all private group names starting with '_' from the array */
       
   404   for (i = 0; i < num_groups; i++) {
       
   405     if (groups[i][0] == '_') {
       
   406       /* free private group */
       
   407       g_free (groups[i]);
       
   408       /* move last element of list down */
       
   409       num_groups--;
       
   410       /* move last element into removed element */
       
   411       groups[i] = groups[num_groups];
       
   412       groups[num_groups] = NULL;
       
   413     }
       
   414   }
       
   415   /* sort the array now */
       
   416   g_qsort_with_data (groups, num_groups, sizeof (gchar *),
       
   417       (GCompareDataFunc) strcmp, NULL);
       
   418 
       
   419   return groups;
       
   420 
       
   421   /* ERRORS */
       
   422 no_presets:
       
   423   {
       
   424     GST_WARNING_OBJECT (preset, "Could not load presets");
       
   425     return NULL;
       
   426   }
       
   427 no_groups:
       
   428   {
       
   429     GST_WARNING_OBJECT (preset, "Could not find preset groups");
       
   430     return NULL;
       
   431   }
       
   432 }
       
   433 
       
   434 /* get a list of all property names that are used for presets */
       
   435 static gchar **
       
   436 gst_preset_default_get_property_names (GstPreset * preset)
       
   437 {
       
   438   GParamSpec **props;
       
   439   guint i, j, n_props;
       
   440   GObjectClass *gclass;
       
   441   gchar **result;
       
   442 
       
   443   gclass = G_OBJECT_CLASS (GST_ELEMENT_GET_CLASS (preset));
       
   444 
       
   445   /* get a list of normal properties. 
       
   446    * FIXME, change this for childproxy support. */
       
   447   props = g_object_class_list_properties (gclass, &n_props);
       
   448   if (!props)
       
   449     goto no_properties;
       
   450 
       
   451   /* allocate array big enough to hold the worst case, including a terminating
       
   452    * NULL pointer. */
       
   453   result = g_new (gchar *, n_props + 1);
       
   454 
       
   455   /* now filter out the properties that we can use for presets */
       
   456   GST_DEBUG_OBJECT (preset, "  filtering properties: %u", n_props);
       
   457   for (i = j = 0; i < n_props; i++) {
       
   458     if (preset_skip_property (props[i]))
       
   459       continue;
       
   460 
       
   461     /* copy and increment out pointer */
       
   462     result[j++] = g_strdup (props[i]->name);
       
   463   }
       
   464   result[j] = NULL;
       
   465   g_free (props);
       
   466 
       
   467   return result;
       
   468 
       
   469   /* ERRORS */
       
   470 no_properties:
       
   471   {
       
   472     GST_INFO_OBJECT (preset, "object has no properties");
       
   473     return NULL;
       
   474   }
       
   475 }
       
   476 
       
   477 /* load the presets of @name for the instance @preset. Returns %FALSE if something
       
   478  * failed. */
       
   479 static gboolean
       
   480 gst_preset_default_load_preset (GstPreset * preset, const gchar * name)
       
   481 {
       
   482   GKeyFile *presets;
       
   483   gchar **props;
       
   484   guint i;
       
   485   GObjectClass *gclass;
       
   486 
       
   487   /* get the presets from the type */
       
   488   if (!(presets = preset_get_keyfile (preset)))
       
   489     goto no_presets;
       
   490 
       
   491   /* get the preset name */
       
   492   if (!g_key_file_has_group (presets, name))
       
   493     goto no_group;
       
   494 
       
   495   GST_DEBUG_OBJECT (preset, "loading preset : '%s'", name);
       
   496 
       
   497   /* get the properties that we can configure in this element */
       
   498   if (!(props = gst_preset_get_property_names (preset)))
       
   499     goto no_properties;
       
   500 
       
   501   gclass = G_OBJECT_CLASS (GST_ELEMENT_GET_CLASS (preset));
       
   502 
       
   503   /* for each of the property names, find the preset parameter and try to
       
   504    * configure the property with its value */
       
   505   for (i = 0; props[i]; i++) {
       
   506     gchar *str;
       
   507     GValue gvalue = { 0, };
       
   508     GParamSpec *property;
       
   509 
       
   510     /* check if we have a settings for this element property */
       
   511     if (!(str = g_key_file_get_value (presets, name, props[i], NULL))) {
       
   512       /* the element has a property but the parameter is not in the keyfile */
       
   513       GST_WARNING_OBJECT (preset, "parameter '%s' not in preset", props[i]);
       
   514       continue;
       
   515     }
       
   516 
       
   517     GST_DEBUG_OBJECT (preset, "setting value '%s' for property '%s'", str,
       
   518         props[i]);
       
   519 
       
   520     /* FIXME, change for childproxy to get the property and element.  */
       
   521     if (!(property = g_object_class_find_property (gclass, props[i]))) {
       
   522       /* the parameter was in the keyfile, the element said it supported it but
       
   523        * then the property was not found in the element. This should not happen. */
       
   524       GST_WARNING_OBJECT (preset, "property '%s' not in object", props[i]);
       
   525       g_free (str);
       
   526       continue;
       
   527     }
       
   528 
       
   529     /* try to deserialize the property value from the keyfile and set it as
       
   530      * the object property */
       
   531     g_value_init (&gvalue, property->value_type);
       
   532     if (gst_value_deserialize (&gvalue, str)) {
       
   533       /* FIXME, change for childproxy support */
       
   534       g_object_set_property (G_OBJECT (preset), props[i], &gvalue);
       
   535     } else {
       
   536       GST_WARNING_OBJECT (preset,
       
   537           "deserialization of value '%s' for property '%s' failed", str,
       
   538           props[i]);
       
   539     }
       
   540     g_value_unset (&gvalue);
       
   541     g_free (str);
       
   542   }
       
   543   g_strfreev (props);
       
   544 
       
   545   return TRUE;
       
   546 
       
   547   /* ERRORS */
       
   548 no_presets:
       
   549   {
       
   550     GST_WARNING_OBJECT (preset, "no presets");
       
   551     return FALSE;
       
   552   }
       
   553 no_group:
       
   554   {
       
   555     GST_WARNING_OBJECT (preset, "no preset named '%s'", name);
       
   556     return FALSE;
       
   557   }
       
   558 no_properties:
       
   559   {
       
   560     GST_INFO_OBJECT (preset, "no properties");
       
   561     return FALSE;
       
   562   }
       
   563 }
       
   564 
       
   565 /* save the presets file. A copy of the existing presets file is stored in a
       
   566  * .bak file */
       
   567 static gboolean
       
   568 gst_preset_default_save_presets_file (GstPreset * preset)
       
   569 {
       
   570   GKeyFile *presets;
       
   571   const gchar *preset_path;
       
   572   GError *error = NULL;
       
   573   gchar *bak_file_name;
       
   574   gboolean backup = TRUE;
       
   575   gchar *data;
       
   576   gsize data_size;
       
   577 
       
   578   preset_get_paths (preset, &preset_path, NULL);
       
   579 
       
   580   /* get the presets from the type */
       
   581   if (!(presets = preset_get_keyfile (preset)))
       
   582     goto no_presets;
       
   583 
       
   584   GST_DEBUG_OBJECT (preset, "saving preset file: '%s'", preset_path);
       
   585 
       
   586   /* create backup if possible */
       
   587   bak_file_name = g_strdup_printf ("%s.bak", preset_path);
       
   588   if (g_file_test (bak_file_name, G_FILE_TEST_EXISTS)) {
       
   589     if (g_unlink (bak_file_name)) {
       
   590       backup = FALSE;
       
   591       GST_INFO_OBJECT (preset, "cannot remove old backup file : %s",
       
   592           bak_file_name);
       
   593     }
       
   594   }
       
   595   if (backup) {
       
   596     if (g_rename (preset_path, bak_file_name)) {
       
   597       GST_INFO_OBJECT (preset, "cannot backup file : %s -> %s", preset_path,
       
   598           bak_file_name);
       
   599     }
       
   600   }
       
   601   g_free (bak_file_name);
       
   602 
       
   603   /* update gstreamer version */
       
   604   g_key_file_set_string (presets, PRESET_HEADER, PRESET_HEADER_VERSION,
       
   605       PACKAGE_VERSION);
       
   606 
       
   607   /* get new contents, wee need this to save it */
       
   608   if (!(data = g_key_file_to_data (presets, &data_size, &error)))
       
   609     goto convert_failed;
       
   610 
       
   611   /* write presets */
       
   612   if (!g_file_set_contents (preset_path, data, data_size, &error))
       
   613     goto write_failed;
       
   614 
       
   615   g_free (data);
       
   616 
       
   617   return TRUE;
       
   618 
       
   619   /* ERRORS */
       
   620 no_presets:
       
   621   {
       
   622     GST_WARNING_OBJECT (preset,
       
   623         "no presets, trying to unlink possibly existing preset file: '%s'",
       
   624         preset_path);
       
   625     g_unlink (preset_path);
       
   626     return FALSE;
       
   627   }
       
   628 convert_failed:
       
   629   {
       
   630     GST_WARNING_OBJECT (preset, "can not get the keyfile contents: %s",
       
   631         error->message);
       
   632     g_error_free (error);
       
   633     g_free (data);
       
   634     return FALSE;
       
   635   }
       
   636 write_failed:
       
   637   {
       
   638     GST_WARNING_OBJECT (preset, "Unable to store preset file %s: %s",
       
   639         preset_path, error->message);
       
   640     g_error_free (error);
       
   641     g_free (data);
       
   642     return FALSE;
       
   643   }
       
   644 }
       
   645 
       
   646 /* save the preset with the given name */
       
   647 static gboolean
       
   648 gst_preset_default_save_preset (GstPreset * preset, const gchar * name)
       
   649 {
       
   650   GKeyFile *presets;
       
   651   gchar **props;
       
   652   guint i;
       
   653   GObjectClass *gclass;
       
   654 
       
   655   GST_INFO_OBJECT (preset, "saving new preset: %s", name);
       
   656 
       
   657   /* get the presets from the type */
       
   658   if (!(presets = preset_get_keyfile (preset)))
       
   659     goto no_presets;
       
   660 
       
   661   /* take copies of current gobject properties from preset */
       
   662   if (!(props = gst_preset_get_property_names (preset)))
       
   663     goto no_properties;
       
   664 
       
   665   gclass = G_OBJECT_CLASS (GST_ELEMENT_GET_CLASS (preset));
       
   666 
       
   667   /* loop over the object properties and store the property value in the
       
   668    * keyfile */
       
   669   for (i = 0; props[i]; i++) {
       
   670     GValue gvalue = { 0, };
       
   671     gchar *str;
       
   672     GParamSpec *property;
       
   673 
       
   674     /* FIXME, change for childproxy to get the property and element.  */
       
   675     if (!(property = g_object_class_find_property (gclass, props[i]))) {
       
   676       /* the element said it supported the property but then it does not have
       
   677        * that property. This should not happen. */
       
   678       GST_WARNING_OBJECT (preset, "property '%s' not in object", props[i]);
       
   679       continue;
       
   680     }
       
   681 
       
   682     g_value_init (&gvalue, property->value_type);
       
   683     /* FIXME, change for childproxy */
       
   684     g_object_get_property (G_OBJECT (preset), props[i], &gvalue);
       
   685 
       
   686     if ((str = gst_value_serialize (&gvalue))) {
       
   687       g_key_file_set_string (presets, name, props[i], (gpointer) str);
       
   688       g_free (str);
       
   689     } else {
       
   690       GST_WARNING_OBJECT (preset, "serialization for property '%s' failed",
       
   691           props[i]);
       
   692     }
       
   693     g_value_unset (&gvalue);
       
   694   }
       
   695   GST_INFO_OBJECT (preset, "  saved");
       
   696   g_strfreev (props);
       
   697 
       
   698   /* save updated version */
       
   699   return gst_preset_default_save_presets_file (preset);
       
   700 
       
   701   /* ERRORS */
       
   702 no_presets:
       
   703   {
       
   704     GST_WARNING_OBJECT (preset, "no presets");
       
   705     return FALSE;
       
   706   }
       
   707 no_properties:
       
   708   {
       
   709     GST_INFO_OBJECT (preset, "no properties");
       
   710     return FALSE;
       
   711   }
       
   712 }
       
   713 
       
   714 /* copies all keys and comments from one group to another, deleting the old
       
   715  * group. */
       
   716 static gboolean
       
   717 gst_preset_default_rename_preset (GstPreset * preset, const gchar * old_name,
       
   718     const gchar * new_name)
       
   719 {
       
   720   GKeyFile *presets;
       
   721   gchar *str;
       
   722   gchar **keys;
       
   723   gsize i, num_keys;
       
   724 
       
   725   /* get the presets from the type */
       
   726   if (!(presets = preset_get_keyfile (preset)))
       
   727     goto no_presets;
       
   728 
       
   729   if (!g_key_file_has_group (presets, old_name))
       
   730     goto no_group;
       
   731 
       
   732   /* copy group comment if there is any */
       
   733   if ((str = g_key_file_get_comment (presets, old_name, NULL, NULL))) {
       
   734     g_key_file_set_comment (presets, new_name, NULL, str, NULL);
       
   735     g_free (str);
       
   736   }
       
   737 
       
   738   /* get all keys from the old group and copy them in the new group */
       
   739   keys = g_key_file_get_keys (presets, old_name, &num_keys, NULL);
       
   740   for (i = 0; i < num_keys; i++) {
       
   741     /* copy key comment if there is any */
       
   742     if ((str = g_key_file_get_comment (presets, old_name, keys[i], NULL))) {
       
   743       g_key_file_set_comment (presets, new_name, keys[i], str, NULL);
       
   744       g_free (str);
       
   745     }
       
   746     /* copy key value */
       
   747     str = g_key_file_get_value (presets, old_name, keys[i], NULL);
       
   748     g_key_file_set_value (presets, new_name, keys[i], str);
       
   749     g_free (str);
       
   750   }
       
   751   g_strfreev (keys);
       
   752 
       
   753   /* remove old group */
       
   754   g_key_file_remove_group (presets, old_name, NULL);
       
   755 
       
   756   /* save updated version */
       
   757   return gst_preset_default_save_presets_file (preset);
       
   758 
       
   759   /* ERRORS */
       
   760 no_presets:
       
   761   {
       
   762     GST_WARNING_OBJECT (preset, "no presets");
       
   763     return FALSE;
       
   764   }
       
   765 no_group:
       
   766   {
       
   767     GST_WARNING_OBJECT (preset, "no preset named %s", old_name);
       
   768     return FALSE;
       
   769   }
       
   770 }
       
   771 
       
   772 /* delete a group from the keyfile */
       
   773 static gboolean
       
   774 gst_preset_default_delete_preset (GstPreset * preset, const gchar * name)
       
   775 {
       
   776   GKeyFile *presets;
       
   777 
       
   778   /* get the presets from the type */
       
   779   if (!(presets = preset_get_keyfile (preset)))
       
   780     goto no_presets;
       
   781 
       
   782   /* get the group */
       
   783   if (!g_key_file_has_group (presets, name))
       
   784     goto no_group;
       
   785 
       
   786   /* remove the group */
       
   787   g_key_file_remove_group (presets, name, NULL);
       
   788 
       
   789   /* save updated version */
       
   790   return gst_preset_default_save_presets_file (preset);
       
   791 
       
   792   /* ERRORS */
       
   793 no_presets:
       
   794   {
       
   795     GST_WARNING_OBJECT (preset, "no presets");
       
   796     return FALSE;
       
   797   }
       
   798 no_group:
       
   799   {
       
   800     GST_WARNING_OBJECT (preset, "no preset named %s", name);
       
   801     return FALSE;
       
   802   }
       
   803 }
       
   804 
       
   805 static gboolean
       
   806 gst_preset_default_set_meta (GstPreset * preset, const gchar * name,
       
   807     const gchar * tag, const gchar * value)
       
   808 {
       
   809   GKeyFile *presets;
       
   810   gchar *key;
       
   811 
       
   812   /* get the presets from the type */
       
   813   if (!(presets = preset_get_keyfile (preset)))
       
   814     goto no_presets;
       
   815 
       
   816   key = g_strdup_printf ("_meta/%s", tag);
       
   817   if (value && *value) {
       
   818     g_key_file_set_value (presets, name, key, value);
       
   819   } else {
       
   820     g_key_file_remove_key (presets, name, key, NULL);
       
   821   }
       
   822   g_free (key);
       
   823 
       
   824   /* save updated keyfile */
       
   825   return gst_preset_default_save_presets_file (preset);
       
   826 
       
   827   /* ERRORS */
       
   828 no_presets:
       
   829   {
       
   830     GST_WARNING_OBJECT (preset, "no presets");
       
   831     return FALSE;
       
   832   }
       
   833 }
       
   834 
       
   835 /* the caller must free @value after usage */
       
   836 static gboolean
       
   837 gst_preset_default_get_meta (GstPreset * preset, const gchar * name,
       
   838     const gchar * tag, gchar ** value)
       
   839 {
       
   840   GKeyFile *presets;
       
   841   gchar *key;
       
   842 
       
   843   /* get the presets from the type */
       
   844   if (!(presets = preset_get_keyfile (preset)))
       
   845     goto no_presets;
       
   846 
       
   847   key = g_strdup_printf ("_meta/%s", tag);
       
   848   *value = g_key_file_get_value (presets, name, key, NULL);
       
   849   g_free (key);
       
   850 
       
   851   return TRUE;
       
   852 
       
   853   /* ERRORS */
       
   854 no_presets:
       
   855   {
       
   856     GST_WARNING_OBJECT (preset, "no presets");
       
   857     *value = NULL;
       
   858     return FALSE;
       
   859   }
       
   860 }
       
   861 
       
   862 /* wrapper */
       
   863 
       
   864 /**
       
   865  * gst_preset_get_preset_names:
       
   866  * @preset: a #GObject that implements #GstPreset
       
   867  *
       
   868  * Get a copy of preset names as a NULL terminated string array.
       
   869  *
       
   870  * Returns: list with names, ue g_strfreev() after usage.
       
   871  *
       
   872  * Since: 0.10.20
       
   873  */
       
   874 #ifdef __SYMBIAN32__
       
   875 EXPORT_C
       
   876 #endif
       
   877 
       
   878 gchar **
       
   879 gst_preset_get_preset_names (GstPreset * preset)
       
   880 {
       
   881   g_return_val_if_fail (GST_IS_PRESET (preset), NULL);
       
   882 
       
   883   return (GST_PRESET_GET_INTERFACE (preset)->get_preset_names (preset));
       
   884 }
       
   885 
       
   886 /**
       
   887  * gst_preset_get_property_names:
       
   888  * @preset: a #GObject that implements #GstPreset
       
   889  *
       
   890  * Get a the names of the GObject properties that can be used for presets.
       
   891  *
       
   892  * Returns: an array of property names which should be freed with g_strfreev() after use.
       
   893  *
       
   894  * Since: 0.10.20
       
   895  */
       
   896 #ifdef __SYMBIAN32__
       
   897 EXPORT_C
       
   898 #endif
       
   899 
       
   900 gchar **
       
   901 gst_preset_get_property_names (GstPreset * preset)
       
   902 {
       
   903   g_return_val_if_fail (GST_IS_PRESET (preset), NULL);
       
   904 
       
   905   return (GST_PRESET_GET_INTERFACE (preset)->get_property_names (preset));
       
   906 }
       
   907 
       
   908 /**
       
   909  * gst_preset_load_preset:
       
   910  * @preset: a #GObject that implements #GstPreset
       
   911  * @name: preset name to load
       
   912  *
       
   913  * Load the given preset.
       
   914  *
       
   915  * Returns: %TRUE for success, %FALSE if e.g. there is no preset with that @name
       
   916  *
       
   917  * Since: 0.10.20
       
   918  */
       
   919 #ifdef __SYMBIAN32__
       
   920 EXPORT_C
       
   921 #endif
       
   922 
       
   923 gboolean
       
   924 gst_preset_load_preset (GstPreset * preset, const gchar * name)
       
   925 {
       
   926   g_return_val_if_fail (GST_IS_PRESET (preset), FALSE);
       
   927   g_return_val_if_fail (name, FALSE);
       
   928 
       
   929   return (GST_PRESET_GET_INTERFACE (preset)->load_preset (preset, name));
       
   930 }
       
   931 
       
   932 /**
       
   933  * gst_preset_save_preset:
       
   934  * @preset: a #GObject that implements #GstPreset
       
   935  * @name: preset name to save
       
   936  *
       
   937  * Save the current preset under the given name. If there is already a preset by
       
   938  * this @name it will be overwritten.
       
   939  *
       
   940  * Returns: %TRUE for success, %FALSE
       
   941  *
       
   942  * Since: 0.10.20
       
   943  */
       
   944 #ifdef __SYMBIAN32__
       
   945 EXPORT_C
       
   946 #endif
       
   947 
       
   948 gboolean
       
   949 gst_preset_save_preset (GstPreset * preset, const gchar * name)
       
   950 {
       
   951   g_return_val_if_fail (GST_IS_PRESET (preset), FALSE);
       
   952   g_return_val_if_fail (name, FALSE);
       
   953 
       
   954   return (GST_PRESET_GET_INTERFACE (preset)->save_preset (preset, name));
       
   955 }
       
   956 
       
   957 /**
       
   958  * gst_preset_rename_preset:
       
   959  * @preset: a #GObject that implements #GstPreset
       
   960  * @old_name: current preset name
       
   961  * @new_name: new preset name
       
   962  *
       
   963  * Renames a preset. If there is already a preset by the @new_name it will be
       
   964  * overwritten.
       
   965  *
       
   966  * Returns: %TRUE for success, %FALSE if e.g. there is no preset with @old_name
       
   967  *
       
   968  * Since: 0.10.20
       
   969  */
       
   970 #ifdef __SYMBIAN32__
       
   971 EXPORT_C
       
   972 #endif
       
   973 
       
   974 gboolean
       
   975 gst_preset_rename_preset (GstPreset * preset, const gchar * old_name,
       
   976     const gchar * new_name)
       
   977 {
       
   978   g_return_val_if_fail (GST_IS_PRESET (preset), FALSE);
       
   979   g_return_val_if_fail (old_name, FALSE);
       
   980   g_return_val_if_fail (new_name, FALSE);
       
   981 
       
   982   return (GST_PRESET_GET_INTERFACE (preset)->rename_preset (preset, old_name,
       
   983           new_name));
       
   984 }
       
   985 
       
   986 /**
       
   987  * gst_preset_delete_preset:
       
   988  * @preset: a #GObject that implements #GstPreset
       
   989  * @name: preset name to remove
       
   990  *
       
   991  * Delete the given preset.
       
   992  *
       
   993  * Returns: %TRUE for success, %FALSE if e.g. there is no preset with that @name
       
   994  *
       
   995  * Since: 0.10.20
       
   996  */
       
   997 #ifdef __SYMBIAN32__
       
   998 EXPORT_C
       
   999 #endif
       
  1000 
       
  1001 gboolean
       
  1002 gst_preset_delete_preset (GstPreset * preset, const gchar * name)
       
  1003 {
       
  1004   g_return_val_if_fail (GST_IS_PRESET (preset), FALSE);
       
  1005   g_return_val_if_fail (name, FALSE);
       
  1006 
       
  1007   return (GST_PRESET_GET_INTERFACE (preset)->delete_preset (preset, name));
       
  1008 }
       
  1009 
       
  1010 /**
       
  1011  * gst_preset_set_meta:
       
  1012  * @preset: a #GObject that implements #GstPreset
       
  1013  * @name: preset name
       
  1014  * @tag: meta data item name
       
  1015  * @value: new value
       
  1016  *
       
  1017  * Sets a new @value for an existing meta data item or adds a new item. Meta
       
  1018  * data @tag names can be something like e.g. "comment". Supplying %NULL for the
       
  1019  * @value will unset an existing value.
       
  1020  *
       
  1021  * Returns: %TRUE for success, %FALSE if e.g. there is no preset with that @name
       
  1022  *
       
  1023  * Since: 0.10.20
       
  1024  */
       
  1025 #ifdef __SYMBIAN32__
       
  1026 EXPORT_C
       
  1027 #endif
       
  1028 
       
  1029 gboolean
       
  1030 gst_preset_set_meta (GstPreset * preset, const gchar * name, const gchar * tag,
       
  1031     const gchar * value)
       
  1032 {
       
  1033   g_return_val_if_fail (GST_IS_PRESET (preset), FALSE);
       
  1034   g_return_val_if_fail (name, FALSE);
       
  1035   g_return_val_if_fail (tag, FALSE);
       
  1036 
       
  1037   return GST_PRESET_GET_INTERFACE (preset)->set_meta (preset, name, tag, value);
       
  1038 }
       
  1039 
       
  1040 /**
       
  1041  * gst_preset_get_meta:
       
  1042  * @preset: a #GObject that implements #GstPreset
       
  1043  * @name: preset name
       
  1044  * @tag: meta data item name
       
  1045  * @value: value
       
  1046  *
       
  1047  * Gets the @value for an existing meta data @tag. Meta data @tag names can be
       
  1048  * something like e.g. "comment". Returned values need to be released when done.
       
  1049  *
       
  1050  * Returns: %TRUE for success, %FALSE if e.g. there is no preset with that @name
       
  1051  * or no value for the given @tag
       
  1052  *
       
  1053  * Since: 0.10.20
       
  1054  */
       
  1055 #ifdef __SYMBIAN32__
       
  1056 EXPORT_C
       
  1057 #endif
       
  1058 
       
  1059 gboolean
       
  1060 gst_preset_get_meta (GstPreset * preset, const gchar * name, const gchar * tag,
       
  1061     gchar ** value)
       
  1062 {
       
  1063   g_return_val_if_fail (GST_IS_PRESET (preset), FALSE);
       
  1064   g_return_val_if_fail (name, FALSE);
       
  1065   g_return_val_if_fail (tag, FALSE);
       
  1066   g_return_val_if_fail (value, FALSE);
       
  1067 
       
  1068   return GST_PRESET_GET_INTERFACE (preset)->get_meta (preset, name, tag, value);
       
  1069 }
       
  1070 
       
  1071 /* class internals */
       
  1072 
       
  1073 static void
       
  1074 gst_preset_class_init (GstPresetInterface * iface)
       
  1075 {
       
  1076   iface->get_preset_names = gst_preset_default_get_preset_names;
       
  1077   iface->get_property_names = gst_preset_default_get_property_names;
       
  1078 
       
  1079   iface->load_preset = gst_preset_default_load_preset;
       
  1080   iface->save_preset = gst_preset_default_save_preset;
       
  1081   iface->rename_preset = gst_preset_default_rename_preset;
       
  1082   iface->delete_preset = gst_preset_default_delete_preset;
       
  1083 
       
  1084   iface->set_meta = gst_preset_default_set_meta;
       
  1085   iface->get_meta = gst_preset_default_get_meta;
       
  1086 }
       
  1087 
       
  1088 static void
       
  1089 gst_preset_base_init (gpointer g_class)
       
  1090 {
       
  1091   static gboolean initialized = FALSE;
       
  1092 
       
  1093   if (!initialized) {
       
  1094     /* init default implementation */
       
  1095     GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "preset",
       
  1096         GST_DEBUG_FG_WHITE | GST_DEBUG_BG_BLACK, "preset interface");
       
  1097 
       
  1098     /* create quarks for use with g_type_{g,s}et_qdata() */
       
  1099     preset_quark = g_quark_from_static_string ("GstPreset::presets");
       
  1100     preset_user_path_quark =
       
  1101         g_quark_from_static_string ("GstPreset::user_path");
       
  1102     preset_system_path_quark =
       
  1103         g_quark_from_static_string ("GstPreset::system_path");
       
  1104 
       
  1105 #if 0
       
  1106     property_list_quark = g_quark_from_static_string ("GstPreset::properties");
       
  1107 
       
  1108     /* create interface properties, each element would need to override this
       
  1109      *   g_object_class_override_property(gobject_class, PROP_PRESET_NAME, "preset-name");
       
  1110      * and in _set_property() do
       
  1111      *   case PROP_PRESET_NAME: {
       
  1112      *     gchar *name = g_value_get_string (value);
       
  1113      *     if (name)
       
  1114      *       gst_preset_load_preset(preset, name);
       
  1115      *   } break;
       
  1116      */
       
  1117     g_object_interface_install_property (g_class,
       
  1118         g_param_spec_string ("preset-name",
       
  1119             "preset-name property",
       
  1120             "load given preset", NULL, G_PARAM_WRITABLE));
       
  1121 #endif
       
  1122 
       
  1123     initialized = TRUE;
       
  1124   }
       
  1125 }
       
  1126 #ifdef __SYMBIAN32__
       
  1127 EXPORT_C
       
  1128 #endif
       
  1129 
       
  1130 
       
  1131 GType
       
  1132 gst_preset_get_type (void)
       
  1133 {
       
  1134   static volatile gsize type = 0;
       
  1135 
       
  1136   if (g_once_init_enter (&type)) {
       
  1137     GType _type;
       
  1138     const GTypeInfo info = {
       
  1139       sizeof (GstPresetInterface),
       
  1140       (GBaseInitFunc) gst_preset_base_init,     /* base_init */
       
  1141       NULL,                     /* base_finalize */
       
  1142       (GClassInitFunc) gst_preset_class_init,   /* class_init */
       
  1143       NULL,                     /* class_finalize */
       
  1144       NULL,                     /* class_data */
       
  1145       0,
       
  1146       0,                        /* n_preallocs */
       
  1147       NULL                      /* instance_init */
       
  1148     };
       
  1149     _type = g_type_register_static (G_TYPE_INTERFACE, "GstPreset", &info, 0);
       
  1150     g_once_init_leave (&type, _type);
       
  1151   }
       
  1152   return type;
       
  1153 }