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