encoding-target: Fix typo
[platform/upstream/gstreamer.git] / gst-libs / gst / pbutils / encoding-target.c
1 /* GStreamer encoding profile registry
2  * Copyright (C) 2010 Edward Hervey <edward.hervey@collabora.co.uk>
3  *           (C) 2010 Nokia Corporation
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include <locale.h>
22 #include <string.h>
23 #include "encoding-target.h"
24
25 /*
26  * File format
27  *
28  * GKeyFile style.
29  *
30  * [_gstencodingtarget_]
31  * name : <name>
32  * category : <category>
33  * description : <description> #translatable
34  *
35  * [profile-<profile1name>]
36  * name : <name>
37  * description : <description> #optional
38  * format : <format>
39  * preset : <preset>
40  *
41  * [streamprofile-<id>]
42  * parent : <encodingprofile.name>[,<encodingprofile.name>..]
43  * type : <type> # "audio", "video", "text"
44  * format : <format>
45  * preset : <preset>
46  * restriction : <restriction>
47  * presence : <presence>
48  * pass : <pass>
49  * variableframerate : <variableframerate>
50  *  */
51
52 #define GST_ENCODING_TARGET_HEADER "_gstencodingtarget_"
53
54 struct _GstEncodingTarget
55 {
56   GstMiniObject parent;
57
58   gchar *name;
59   gchar *category;
60   gchar *description;
61   GList *profiles;
62
63   /*< private > */
64   gchar *keyfile;
65 };
66
67 G_DEFINE_TYPE (GstEncodingTarget, gst_encoding_target, GST_TYPE_MINI_OBJECT);
68
69 static void
70 gst_encoding_target_init (GstEncodingTarget * target)
71 {
72   /* Nothing to initialize */
73 }
74
75 static void
76 gst_encoding_target_finalize (GstEncodingTarget * target)
77 {
78   GST_DEBUG ("Finalizing");
79
80   if (target->name)
81     g_free (target->name);
82   if (target->category)
83     g_free (target->category);
84   if (target->description)
85     g_free (target->description);
86
87   g_list_foreach (target->profiles, (GFunc) gst_mini_object_unref, NULL);
88   g_list_free (target->profiles);
89 }
90
91 static void
92 gst_encoding_target_class_init (GstMiniObjectClass * klass)
93 {
94   klass->finalize =
95       (GstMiniObjectFinalizeFunction) gst_encoding_target_finalize;
96 }
97
98 /**
99  * gst_encoding_target_get_name:
100  * @target: a #GstEncodingTarget
101  *
102  * Since: 0.10.32
103  *
104  * Returns: (transfer none): The name of the @target.
105  */
106 const gchar *
107 gst_encoding_target_get_name (GstEncodingTarget * target)
108 {
109   return target->name;
110 }
111
112 /**
113  * gst_encoding_target_get_category:
114  * @target: a #GstEncodingTarget
115  *
116  * Since: 0.10.32
117  *
118  * Returns: (transfer none): The category of the @target.
119  */
120 const gchar *
121 gst_encoding_target_get_category (GstEncodingTarget * target)
122 {
123   return target->category;
124 }
125
126 /**
127  * gst_encoding_target_get_description:
128  * @target: a #GstEncodingTarget
129  *
130  * Since: 0.10.32
131  *
132  * Returns: (transfer none): The description of the @target.
133  */
134 const gchar *
135 gst_encoding_target_get_description (GstEncodingTarget * target)
136 {
137   return target->description;
138 }
139
140 /**
141  * gst_encoding_target_get_profiles:
142  * @target: a #GstEncodingTarget
143  *
144  * Since: 0.10.32
145  *
146  * Returns: (transfer none) (element-type Gst.EncodingProfile): A list of
147  * #GstEncodingProfile(s) this @target handles.
148  */
149 const GList *
150 gst_encoding_target_get_profiles (GstEncodingTarget * target)
151 {
152   return target->profiles;
153 }
154
155 static inline gboolean
156 validate_name (const gchar * name)
157 {
158   guint i, len;
159
160   len = strlen (name);
161   if (len == 0)
162     return FALSE;
163
164   /* First character can only be a lower case ASCII character */
165   if (!g_ascii_isalpha (name[0]) || !g_ascii_islower (name[0]))
166     return FALSE;
167
168   /* All following characters can only by:
169    * either a lower case ASCII character
170    * or an hyphen
171    * or a numeric */
172   for (i = 1; i < len; i++) {
173     /* if uppercase ASCII letter, return */
174     if (g_ascii_isupper (name[i]))
175       return FALSE;
176     /* if a digit, continue */
177     if (g_ascii_isdigit (name[i]))
178       continue;
179     /* if an hyphen, continue */
180     if (name[i] == '-')
181       continue;
182     /* remaining should only be ascii letters */
183     if (!g_ascii_isalpha (name[i]))
184       return FALSE;
185   }
186
187   return TRUE;
188 }
189
190 /**
191  * gst_encoding_target_new:
192  * @name: The name of the target.
193  * @category: (transfer none): The name of the category to which this @target
194  * belongs.
195  * @description: (transfer none): A description of #GstEncodingTarget in the
196  * current locale.
197  * @profiles: (transfer none) (element-type Gst.EncodingProfile): A #GList of
198  * #GstEncodingProfile.
199  *
200  * Creates a new #GstEncodingTarget.
201  *
202  * The name and category can only consist of lowercase ASCII letters for the
203  * first character, followed by either lowercase ASCII letters, digits or
204  * hyphens ('-').
205  *
206  * Since: 0.10.32
207  *
208  * Returns: (transfer full): The newly created #GstEncodingTarget or %NULL if
209  * there was an error.
210  */
211
212 GstEncodingTarget *
213 gst_encoding_target_new (const gchar * name, const gchar * category,
214     const gchar * description, const GList * profiles)
215 {
216   GstEncodingTarget *res;
217
218   g_return_val_if_fail (name != NULL, NULL);
219   g_return_val_if_fail (category != NULL, NULL);
220   g_return_val_if_fail (description != NULL, NULL);
221
222   /* Validate name */
223   if (!validate_name (name))
224     goto invalid_name;
225   if (!validate_name (category))
226     goto invalid_category;
227
228   res = (GstEncodingTarget *) gst_mini_object_new (GST_TYPE_ENCODING_TARGET);
229   res->name = g_strdup (name);
230   res->category = g_strdup (category);
231   res->description = g_strdup (description);
232
233   while (profiles) {
234     GstEncodingProfile *prof = (GstEncodingProfile *) profiles->data;
235
236     res->profiles =
237         g_list_append (res->profiles, gst_encoding_profile_ref (prof));
238     profiles = profiles->next;
239   }
240
241   return res;
242
243 invalid_name:
244   {
245     GST_ERROR ("Invalid name for encoding target : '%s'", name);
246     return NULL;
247   }
248
249 invalid_category:
250   {
251     GST_ERROR ("Invalid name for encoding category : '%s'", category);
252     return NULL;
253   }
254 }
255
256 /**
257  * gst_encoding_target_add_profile:
258  * @target: the #GstEncodingTarget to add a profile to
259  * @profile: (transfer full): the #GstEncodingProfile to add
260  *
261  * Adds the given @profile to the @target.
262  *
263  * The @target will steal a reference to the @profile. If you wish to use
264  * the profile after calling this method, you should increase its reference
265  * count.
266  *
267  * Since: 0.10.32
268  *
269  * Returns: %TRUE if the profile was added, else %FALSE.
270  **/
271
272 gboolean
273 gst_encoding_target_add_profile (GstEncodingTarget * target,
274     GstEncodingProfile * profile)
275 {
276   GList *tmp;
277
278   g_return_val_if_fail (GST_IS_ENCODING_TARGET (target), FALSE);
279   g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), FALSE);
280
281   /* Make sure profile isn't already controlled by this target */
282   for (tmp = target->profiles; tmp; tmp = tmp->next) {
283     GstEncodingProfile *prof = (GstEncodingProfile *) tmp->data;
284
285     if (!g_strcmp0 (gst_encoding_profile_get_name (profile),
286             gst_encoding_profile_get_name (prof))) {
287       GST_WARNING ("Profile already present in target");
288       return FALSE;
289     }
290   }
291
292   target->profiles = g_list_append (target->profiles, profile);
293
294   return TRUE;
295 }
296
297 static gboolean
298 serialize_stream_profiles (GKeyFile * out, GstEncodingProfile * sprof,
299     const gchar * profilename, guint id)
300 {
301   gchar *sprofgroupname;
302   gchar *tmpc;
303   const GstCaps *format, *restriction;
304   const gchar *preset, *name, *description;
305
306   sprofgroupname = g_strdup_printf ("streamprofile-%s-%d", profilename, id);
307
308   /* Write the parent profile */
309   g_key_file_set_value (out, sprofgroupname, "parent", profilename);
310
311   g_key_file_set_value (out, sprofgroupname, "type",
312       gst_encoding_profile_get_type_nick (sprof));
313
314   format = gst_encoding_profile_get_format (sprof);
315   if (format) {
316     tmpc = gst_caps_to_string (format);
317     g_key_file_set_value (out, sprofgroupname, "format", tmpc);
318     g_free (tmpc);
319   }
320
321   name = gst_encoding_profile_get_name (sprof);
322   if (name)
323     g_key_file_set_string (out, sprofgroupname, "name", name);
324
325   description = gst_encoding_profile_get_description (sprof);
326   if (description)
327     g_key_file_set_string (out, sprofgroupname, "description", description);
328
329   preset = gst_encoding_profile_get_preset (sprof);
330   if (preset)
331     g_key_file_set_string (out, sprofgroupname, "preset", preset);
332
333   restriction = gst_encoding_profile_get_restriction (sprof);
334   if (restriction) {
335     tmpc = gst_caps_to_string (restriction);
336     g_key_file_set_value (out, sprofgroupname, "restriction", tmpc);
337     g_free (tmpc);
338   }
339   g_key_file_set_integer (out, sprofgroupname, "presence",
340       gst_encoding_profile_get_presence (sprof));
341
342   if (GST_IS_ENCODING_VIDEO_PROFILE (sprof)) {
343     GstEncodingVideoProfile *vp = (GstEncodingVideoProfile *) sprof;
344
345     g_key_file_set_integer (out, sprofgroupname, "pass",
346         gst_encoding_video_profile_get_pass (vp));
347     g_key_file_set_boolean (out, sprofgroupname, "variableframerate",
348         gst_encoding_video_profile_get_variableframerate (vp));
349   }
350
351   g_free (sprofgroupname);
352   return TRUE;
353 }
354
355 /* Serialize the top-level profiles
356  * Note: They don't have to be containerprofiles */
357 static gboolean
358 serialize_encoding_profile (GKeyFile * out, GstEncodingProfile * prof)
359 {
360   gchar *profgroupname;
361   const GList *tmp;
362   guint i;
363   const gchar *profname, *profdesc, *profpreset, *proftype;
364   const GstCaps *profformat, *profrestriction;
365
366   profname = gst_encoding_profile_get_name (prof);
367   profdesc = gst_encoding_profile_get_description (prof);
368   profformat = gst_encoding_profile_get_format (prof);
369   profpreset = gst_encoding_profile_get_preset (prof);
370   proftype = gst_encoding_profile_get_type_nick (prof);
371   profrestriction = gst_encoding_profile_get_restriction (prof);
372
373   profgroupname = g_strdup_printf ("profile-%s", profname);
374
375   g_key_file_set_string (out, profgroupname, "name", profname);
376
377   g_key_file_set_value (out, profgroupname, "type",
378       gst_encoding_profile_get_type_nick (prof));
379
380   if (profdesc)
381     g_key_file_set_locale_string (out, profgroupname, "description",
382         setlocale (LC_ALL, NULL), profdesc);
383   if (profformat) {
384     gchar *tmpc = gst_caps_to_string (profformat);
385     g_key_file_set_string (out, profgroupname, "format", tmpc);
386     g_free (tmpc);
387   }
388   if (profpreset)
389     g_key_file_set_string (out, profgroupname, "preset", profpreset);
390
391   /* stream profiles */
392   if (GST_IS_ENCODING_CONTAINER_PROFILE (prof)) {
393     for (tmp =
394         gst_encoding_container_profile_get_profiles
395         (GST_ENCODING_CONTAINER_PROFILE (prof)), i = 0; tmp;
396         tmp = tmp->next, i++) {
397       GstEncodingProfile *sprof = (GstEncodingProfile *) tmp->data;
398
399       if (!serialize_stream_profiles (out, sprof, profname, i))
400         return FALSE;
401     }
402   }
403   g_free (profgroupname);
404   return TRUE;
405 }
406
407 static gboolean
408 serialize_target (GKeyFile * out, GstEncodingTarget * target)
409 {
410   GList *tmp;
411
412   g_key_file_set_string (out, GST_ENCODING_TARGET_HEADER, "name", target->name);
413   g_key_file_set_string (out, GST_ENCODING_TARGET_HEADER, "category",
414       target->category);
415   g_key_file_set_string (out, GST_ENCODING_TARGET_HEADER, "description",
416       target->description);
417
418   for (tmp = target->profiles; tmp; tmp = tmp->next) {
419     GstEncodingProfile *prof = (GstEncodingProfile *) tmp->data;
420     if (!serialize_encoding_profile (out, prof))
421       return FALSE;
422   }
423
424   return TRUE;
425 }
426
427 /**
428  * parse_encoding_profile:
429  * @in: a #GKeyFile
430  * @parentprofilename: the parent profile name (including 'profile-' or 'streamprofile-' header)
431  * @profilename: the profile name group to parse
432  * @nbgroups: the number of top-level groups
433  * @groups: the top-level groups
434  */
435 static GstEncodingProfile *
436 parse_encoding_profile (GKeyFile * in, gchar * parentprofilename,
437     gchar * profilename, gsize nbgroups, gchar ** groups)
438 {
439   GstEncodingProfile *sprof = NULL;
440   gchar **parent;
441   gchar *proftype, *format, *preset, *restriction, *pname, *description;
442   GstCaps *formatcaps = NULL;
443   GstCaps *restrictioncaps = NULL;
444   gboolean variableframerate;
445   gint pass, presence;
446   gsize i, nbencprofiles;
447
448   GST_DEBUG ("parentprofilename : %s , profilename : %s",
449       parentprofilename, profilename);
450
451   if (parentprofilename) {
452     gboolean found = FALSE;
453
454     parent =
455         g_key_file_get_string_list (in, profilename, "parent",
456         &nbencprofiles, NULL);
457     if (!parent || !nbencprofiles) {
458       return NULL;
459     }
460
461     /* Check if this streamprofile is used in <profilename> */
462     for (i = 0; i < nbencprofiles; i++) {
463       if (!g_strcmp0 (parent[i], parentprofilename)) {
464         found = TRUE;
465         break;
466       }
467     }
468     g_strfreev (parent);
469
470     if (!found) {
471       GST_DEBUG ("Stream profile '%s' isn't used in profile '%s'",
472           profilename, parentprofilename);
473       return NULL;
474     }
475   }
476
477   pname = g_key_file_get_value (in, profilename, "name", NULL);
478
479   /* First try to get localized description */
480   description =
481       g_key_file_get_locale_string (in, profilename, "description",
482       setlocale (LC_ALL, NULL), NULL);
483   if (description == NULL)
484     description = g_key_file_get_value (in, profilename, "description", NULL);
485
486   /* Parse the remaining fields */
487   proftype = g_key_file_get_value (in, profilename, "type", NULL);
488   if (!proftype) {
489     GST_WARNING ("Missing 'type' field for streamprofile %s", profilename);
490     return NULL;
491   }
492
493   format = g_key_file_get_value (in, profilename, "format", NULL);
494   if (format) {
495     formatcaps = gst_caps_from_string (format);
496     g_free (format);
497   }
498
499   preset = g_key_file_get_value (in, profilename, "preset", NULL);
500
501   restriction = g_key_file_get_value (in, profilename, "restriction", NULL);
502   if (restriction) {
503     restrictioncaps = gst_caps_from_string (restriction);
504     g_free (restriction);
505   }
506
507   presence = g_key_file_get_integer (in, profilename, "presence", NULL);
508   pass = g_key_file_get_integer (in, profilename, "pass", NULL);
509   variableframerate =
510       g_key_file_get_boolean (in, profilename, "variableframerate", NULL);
511
512   /* Build the streamprofile ! */
513   if (!g_strcmp0 (proftype, "container")) {
514     GstEncodingProfile *pprof;
515
516     sprof =
517         (GstEncodingProfile *) gst_encoding_container_profile_new (pname,
518         description, formatcaps, preset);
519     /* Now look for the stream profiles */
520     for (i = 0; i < nbgroups; i++) {
521       if (!g_ascii_strncasecmp (groups[i], "streamprofile-", 13)) {
522         pprof = parse_encoding_profile (in, pname, groups[i], nbgroups, groups);
523         if (pprof) {
524           gst_encoding_container_profile_add_profile (
525               (GstEncodingContainerProfile *) sprof, pprof);
526         }
527       }
528     }
529   } else if (!g_strcmp0 (proftype, "video")) {
530     sprof =
531         (GstEncodingProfile *) gst_encoding_video_profile_new (formatcaps,
532         preset, restrictioncaps, presence);
533     gst_encoding_video_profile_set_variableframerate ((GstEncodingVideoProfile
534             *) sprof, variableframerate);
535     gst_encoding_video_profile_set_pass ((GstEncodingVideoProfile *) sprof,
536         pass);
537   } else if (!g_strcmp0 (proftype, "audio")) {
538     sprof =
539         (GstEncodingProfile *) gst_encoding_audio_profile_new (formatcaps,
540         preset, restrictioncaps, presence);
541   } else
542     GST_ERROR ("Unknown profile format '%s'", proftype);
543
544   if (restrictioncaps)
545     gst_caps_unref (restrictioncaps);
546   if (formatcaps)
547     gst_caps_unref (formatcaps);
548
549   if (pname)
550     g_free (pname);
551   if (description)
552     g_free (description);
553   if (preset)
554     g_free (preset);
555   if (proftype)
556     g_free (proftype);
557
558   return sprof;
559 }
560
561 static GstEncodingTarget *
562 parse_keyfile (GKeyFile * in, gchar * targetname, gchar * categoryname,
563     gchar * description)
564 {
565   GstEncodingTarget *res = NULL;
566   GstEncodingProfile *prof;
567   gchar **groups;
568   gsize i, nbgroups;
569
570   res = gst_encoding_target_new (targetname, categoryname, description, NULL);
571
572   /* Figure out the various profiles */
573   groups = g_key_file_get_groups (in, &nbgroups);
574   for (i = 0; i < nbgroups; i++) {
575     if (!g_ascii_strncasecmp (groups[i], "profile-", 8)) {
576       prof = parse_encoding_profile (in, NULL, groups[i], nbgroups, groups);
577       if (prof)
578         gst_encoding_target_add_profile (res, prof);
579     }
580   }
581
582   g_strfreev (groups);
583
584   if (targetname)
585     g_free (targetname);
586   if (categoryname)
587     g_free (categoryname);
588   if (description)
589     g_free (description);
590
591   return res;
592 }
593
594 static GKeyFile *
595 load_file_and_read_header (const gchar * path, gchar ** targetname,
596     gchar ** categoryname, gchar ** description, GError ** error)
597 {
598   GKeyFile *in;
599   gboolean res;
600
601   in = g_key_file_new ();
602
603   GST_DEBUG ("path:%s", path);
604
605   res =
606       g_key_file_load_from_file (in, path,
607       G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, error);
608   if (!res || error != NULL)
609     goto load_error;
610
611   *targetname =
612       g_key_file_get_value (in, GST_ENCODING_TARGET_HEADER, "name", error);
613   if (!*targetname)
614     goto empty_name;
615
616   *categoryname =
617       g_key_file_get_value (in, GST_ENCODING_TARGET_HEADER, "category", NULL);
618   *description =
619       g_key_file_get_value (in, GST_ENCODING_TARGET_HEADER, "description",
620       NULL);
621
622   return in;
623
624 load_error:
625   {
626     GST_WARNING ("Unable to read GstEncodingTarget file %s: %s",
627         path, (*error)->message);
628     g_key_file_free (in);
629     return NULL;
630   }
631
632 empty_name:
633   {
634     GST_WARNING ("Wrong header in file %s: %s", path, (*error)->message);
635     g_key_file_free (in);
636     return NULL;
637   }
638 }
639
640 /**
641  * gst_encoding_target_load_from:
642  * @path: The file to load the #GstEncodingTarget from
643  * @error: If an error occured, this field will be filled in.
644  *
645  * Opens the provided file and returns the contained #GstEncodingTarget.
646  *
647  * Since: 0.10.32
648  *
649  * Returns: (transfer full): The #GstEncodingTarget contained in the file, else
650  * %NULL
651  */
652
653 GstEncodingTarget *
654 gst_encoding_target_load_from (const gchar * path, GError ** error)
655 {
656   GKeyFile *in;
657   gchar *targetname, *categoryname, *description;
658   GstEncodingTarget *res = NULL;
659
660   in = load_file_and_read_header (path, &targetname, &categoryname,
661       &description, error);
662   if (!in)
663     goto beach;
664
665   res = parse_keyfile (in, targetname, categoryname, description);
666
667   g_key_file_free (in);
668
669 beach:
670   return res;
671 }
672
673 /**
674  * gst_encoding_target_load:
675  * @name: the name of the #GstEncodingTarget to load.
676  * @error: If an error occured, this field will be filled in.
677  *
678  * Searches for the #GstEncodingTarget with the given name, loads it
679  * and returns it.
680  *
681  * Warning: NOT IMPLEMENTED.
682  *
683  * Since: 0.10.32
684  *
685  * Returns: (transfer full): The #GstEncodingTarget if available, else %NULL.
686  */
687
688 GstEncodingTarget *
689 gst_encoding_target_load (const gchar * name, GError ** error)
690 {
691   /* FIXME : IMPLEMENT */
692   return NULL;
693 }
694
695 /**
696  * gst_encoding_target_save:
697  * @target: a #GstEncodingTarget
698  * @error: If an error occured, this field will be filled in.
699  *
700  * Saves the @target to the default location.
701  *
702  * Warning: NOT IMPLEMENTED.
703  *
704  * Since: 0.10.32
705  *
706  * Returns: %TRUE if the target was correctly saved, else %FALSE.
707  **/
708
709 gboolean
710 gst_encoding_target_save (GstEncodingTarget * target, GError ** error)
711 {
712   g_return_val_if_fail (GST_IS_ENCODING_TARGET (target), FALSE);
713
714   /* FIXME : IMPLEMENT */
715   return FALSE;
716 }
717
718 /**
719  * gst_encoding_target_save_to:
720  * @target: a #GstEncodingTarget
721  * @path: the location to store the @target at.
722  * @error: If an error occured, this field will be filled in.
723  *
724  * Saves the @target to the provided location.
725  *
726  * Since: 0.10.32
727  *
728  * Returns: %TRUE if the target was correctly saved, else %FALSE.
729  **/
730
731 gboolean
732 gst_encoding_target_save_to (GstEncodingTarget * target, const gchar * path,
733     GError ** error)
734 {
735   GKeyFile *out;
736   gchar *data;
737   gsize data_size;
738
739   g_return_val_if_fail (GST_IS_ENCODING_TARGET (target), FALSE);
740   g_return_val_if_fail (path != NULL, FALSE);
741
742   /* FIXME : Check path is valid and writable
743    * FIXME : Strip out profiles already present in system target */
744
745   /* Get unique name... */
746
747   /* Create output GKeyFile */
748   out = g_key_file_new ();
749
750   if (!serialize_target (out, target))
751     goto serialize_failure;
752
753   if (!(data = g_key_file_to_data (out, &data_size, error)))
754     goto convert_failed;
755
756   if (!g_file_set_contents (path, data, data_size, error))
757     goto write_failed;
758
759   g_key_file_free (out);
760   g_free (data);
761
762   return TRUE;
763
764 serialize_failure:
765   {
766     GST_ERROR ("Failure serializing target");
767     g_key_file_free (out);
768     return FALSE;
769   }
770
771 convert_failed:
772   {
773     GST_ERROR ("Failure converting keyfile: %s", (*error)->message);
774     g_key_file_free (out);
775     g_free (data);
776     return FALSE;
777   }
778
779 write_failed:
780   {
781     GST_ERROR ("Unable to write file %s: %s", path, (*error)->message);
782     g_key_file_free (out);
783     g_free (data);
784     return FALSE;
785   }
786 }