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