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