encoding-target: Implement save/load feature
[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 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 #include "encoding-target.h"
27
28 /*
29  * File format
30  *
31  * GKeyFile style.
32  *
33  * [_gstencodingtarget_]
34  * name : <name>
35  * category : <category>
36  * description : <description> #translatable
37  *
38  * [profile-<profile1name>]
39  * name : <name>
40  * description : <description> #optional
41  * format : <format>
42  * preset : <preset>
43  *
44  * [streamprofile-<id>]
45  * parent : <encodingprofile.name>[,<encodingprofile.name>..]
46  * type : <type> # "audio", "video", "text"
47  * format : <format>
48  * preset : <preset>
49  * restriction : <restriction>
50  * presence : <presence>
51  * pass : <pass>
52  * variableframerate : <variableframerate>
53  *  */
54
55 /*
56  * Location of profile files
57  *
58  * $GST_DATADIR/gstreamer-GST_MAJORMINOR/encoding-profile
59  * $HOME/gstreamer-GST_MAJORMINOR/encoding-profile
60  *
61  * Naming convention
62  *   $(target.category)/$(target.name).gstprof
63  *
64  * Naming restrictions:
65  *  lowercase ASCII letter for the first character
66  *  Same for all other characters + numerics + hyphens
67  */
68
69
70 #define GST_ENCODING_TARGET_HEADER "_gstencodingtarget_"
71
72 struct _GstEncodingTarget
73 {
74   GstMiniObject parent;
75
76   gchar *name;
77   gchar *category;
78   gchar *description;
79   GList *profiles;
80
81   /*< private > */
82   gchar *keyfile;
83 };
84
85 G_DEFINE_TYPE (GstEncodingTarget, gst_encoding_target, GST_TYPE_MINI_OBJECT);
86
87 static void
88 gst_encoding_target_init (GstEncodingTarget * target)
89 {
90   /* Nothing to initialize */
91 }
92
93 static void
94 gst_encoding_target_finalize (GstEncodingTarget * target)
95 {
96   GST_DEBUG ("Finalizing");
97
98   if (target->name)
99     g_free (target->name);
100   if (target->category)
101     g_free (target->category);
102   if (target->description)
103     g_free (target->description);
104
105   g_list_foreach (target->profiles, (GFunc) gst_mini_object_unref, NULL);
106   g_list_free (target->profiles);
107 }
108
109 static void
110 gst_encoding_target_class_init (GstMiniObjectClass * klass)
111 {
112   klass->finalize =
113       (GstMiniObjectFinalizeFunction) gst_encoding_target_finalize;
114 }
115
116 /**
117  * gst_encoding_target_get_name:
118  * @target: a #GstEncodingTarget
119  *
120  * Since: 0.10.32
121  *
122  * Returns: (transfer none): The name of the @target.
123  */
124 const gchar *
125 gst_encoding_target_get_name (GstEncodingTarget * target)
126 {
127   return target->name;
128 }
129
130 /**
131  * gst_encoding_target_get_category:
132  * @target: a #GstEncodingTarget
133  *
134  * Since: 0.10.32
135  *
136  * Returns: (transfer none): The category of the @target.
137  */
138 const gchar *
139 gst_encoding_target_get_category (GstEncodingTarget * target)
140 {
141   return target->category;
142 }
143
144 /**
145  * gst_encoding_target_get_description:
146  * @target: a #GstEncodingTarget
147  *
148  * Since: 0.10.32
149  *
150  * Returns: (transfer none): The description of the @target.
151  */
152 const gchar *
153 gst_encoding_target_get_description (GstEncodingTarget * target)
154 {
155   return target->description;
156 }
157
158 /**
159  * gst_encoding_target_get_profiles:
160  * @target: a #GstEncodingTarget
161  *
162  * Since: 0.10.32
163  *
164  * Returns: (transfer none) (element-type Gst.EncodingProfile): A list of
165  * #GstEncodingProfile(s) this @target handles.
166  */
167 const GList *
168 gst_encoding_target_get_profiles (GstEncodingTarget * target)
169 {
170   return target->profiles;
171 }
172
173 /**
174  * gst_encoding_target_get_profile:
175  * @target: a #GstEncodingTarget
176  * @name: the name of the profile to retrieve
177  *
178  * Since: 0.10.32
179  *
180  * Returns: (transfer full): The matching #GstEncodingProfile, or %NULL.
181  */
182 GstEncodingProfile *
183 gst_encoding_target_get_profile (GstEncodingTarget * target, const gchar * name)
184 {
185   GList *tmp;
186
187   g_return_val_if_fail (GST_IS_ENCODING_TARGET (target), NULL);
188   g_return_val_if_fail (name != NULL, NULL);
189
190   for (tmp = target->profiles; tmp; tmp = tmp->next) {
191     GstEncodingProfile *tprof = (GstEncodingProfile *) tmp->data;
192
193     if (!g_strcmp0 (gst_encoding_profile_get_name (tprof), name)) {
194       gst_encoding_profile_ref (tprof);
195       return tprof;
196     }
197   }
198
199   return NULL;
200 }
201
202 static inline gboolean
203 validate_name (const gchar * name)
204 {
205   guint i, len;
206
207   len = strlen (name);
208   if (len == 0)
209     return FALSE;
210
211   /* First character can only be a lower case ASCII character */
212   if (!g_ascii_isalpha (name[0]) || !g_ascii_islower (name[0]))
213     return FALSE;
214
215   /* All following characters can only by:
216    * either a lower case ASCII character
217    * or an hyphen
218    * or a numeric */
219   for (i = 1; i < len; i++) {
220     /* if uppercase ASCII letter, return */
221     if (g_ascii_isupper (name[i]))
222       return FALSE;
223     /* if a digit, continue */
224     if (g_ascii_isdigit (name[i]))
225       continue;
226     /* if an hyphen, continue */
227     if (name[i] == '-')
228       continue;
229     /* remaining should only be ascii letters */
230     if (!g_ascii_isalpha (name[i]))
231       return FALSE;
232   }
233
234   return TRUE;
235 }
236
237 /**
238  * gst_encoding_target_new:
239  * @name: The name of the target.
240  * @category: (transfer none): The name of the category to which this @target
241  * belongs.
242  * @description: (transfer none): A description of #GstEncodingTarget in the
243  * current locale.
244  * @profiles: (transfer none) (element-type Gst.EncodingProfile): A #GList of
245  * #GstEncodingProfile.
246  *
247  * Creates a new #GstEncodingTarget.
248  *
249  * The name and category can only consist of lowercase ASCII letters for the
250  * first character, followed by either lowercase ASCII letters, digits or
251  * hyphens ('-').
252  *
253  * Since: 0.10.32
254  *
255  * Returns: (transfer full): The newly created #GstEncodingTarget or %NULL if
256  * there was an error.
257  */
258
259 GstEncodingTarget *
260 gst_encoding_target_new (const gchar * name, const gchar * category,
261     const gchar * description, const GList * profiles)
262 {
263   GstEncodingTarget *res;
264
265   g_return_val_if_fail (name != NULL, NULL);
266   g_return_val_if_fail (category != NULL, NULL);
267   g_return_val_if_fail (description != NULL, NULL);
268
269   /* Validate name */
270   if (!validate_name (name))
271     goto invalid_name;
272   if (!validate_name (category))
273     goto invalid_category;
274
275   res = (GstEncodingTarget *) gst_mini_object_new (GST_TYPE_ENCODING_TARGET);
276   res->name = g_strdup (name);
277   res->category = g_strdup (category);
278   res->description = g_strdup (description);
279
280   while (profiles) {
281     GstEncodingProfile *prof = (GstEncodingProfile *) profiles->data;
282
283     res->profiles =
284         g_list_append (res->profiles, gst_encoding_profile_ref (prof));
285     profiles = profiles->next;
286   }
287
288   return res;
289
290 invalid_name:
291   {
292     GST_ERROR ("Invalid name for encoding target : '%s'", name);
293     return NULL;
294   }
295
296 invalid_category:
297   {
298     GST_ERROR ("Invalid name for encoding category : '%s'", category);
299     return NULL;
300   }
301 }
302
303 /**
304  * gst_encoding_target_add_profile:
305  * @target: the #GstEncodingTarget to add a profile to
306  * @profile: (transfer full): the #GstEncodingProfile to add
307  *
308  * Adds the given @profile to the @target.
309  *
310  * The @target will steal a reference to the @profile. If you wish to use
311  * the profile after calling this method, you should increase its reference
312  * count.
313  *
314  * Since: 0.10.32
315  *
316  * Returns: %TRUE if the profile was added, else %FALSE.
317  **/
318
319 gboolean
320 gst_encoding_target_add_profile (GstEncodingTarget * target,
321     GstEncodingProfile * profile)
322 {
323   GList *tmp;
324
325   g_return_val_if_fail (GST_IS_ENCODING_TARGET (target), FALSE);
326   g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), FALSE);
327
328   /* Make sure profile isn't already controlled by this target */
329   for (tmp = target->profiles; tmp; tmp = tmp->next) {
330     GstEncodingProfile *prof = (GstEncodingProfile *) tmp->data;
331
332     if (!g_strcmp0 (gst_encoding_profile_get_name (profile),
333             gst_encoding_profile_get_name (prof))) {
334       GST_WARNING ("Profile already present in target");
335       return FALSE;
336     }
337   }
338
339   target->profiles = g_list_append (target->profiles, profile);
340
341   return TRUE;
342 }
343
344 static gboolean
345 serialize_stream_profiles (GKeyFile * out, GstEncodingProfile * sprof,
346     const gchar * profilename, guint id)
347 {
348   gchar *sprofgroupname;
349   gchar *tmpc;
350   const GstCaps *format, *restriction;
351   const gchar *preset, *name, *description;
352
353   sprofgroupname = g_strdup_printf ("streamprofile-%s-%d", profilename, id);
354
355   /* Write the parent profile */
356   g_key_file_set_value (out, sprofgroupname, "parent", profilename);
357
358   g_key_file_set_value (out, sprofgroupname, "type",
359       gst_encoding_profile_get_type_nick (sprof));
360
361   format = gst_encoding_profile_get_format (sprof);
362   if (format) {
363     tmpc = gst_caps_to_string (format);
364     g_key_file_set_value (out, sprofgroupname, "format", tmpc);
365     g_free (tmpc);
366   }
367
368   name = gst_encoding_profile_get_name (sprof);
369   if (name)
370     g_key_file_set_string (out, sprofgroupname, "name", name);
371
372   description = gst_encoding_profile_get_description (sprof);
373   if (description)
374     g_key_file_set_string (out, sprofgroupname, "description", description);
375
376   preset = gst_encoding_profile_get_preset (sprof);
377   if (preset)
378     g_key_file_set_string (out, sprofgroupname, "preset", preset);
379
380   restriction = gst_encoding_profile_get_restriction (sprof);
381   if (restriction) {
382     tmpc = gst_caps_to_string (restriction);
383     g_key_file_set_value (out, sprofgroupname, "restriction", tmpc);
384     g_free (tmpc);
385   }
386   g_key_file_set_integer (out, sprofgroupname, "presence",
387       gst_encoding_profile_get_presence (sprof));
388
389   if (GST_IS_ENCODING_VIDEO_PROFILE (sprof)) {
390     GstEncodingVideoProfile *vp = (GstEncodingVideoProfile *) sprof;
391
392     g_key_file_set_integer (out, sprofgroupname, "pass",
393         gst_encoding_video_profile_get_pass (vp));
394     g_key_file_set_boolean (out, sprofgroupname, "variableframerate",
395         gst_encoding_video_profile_get_variableframerate (vp));
396   }
397
398   g_free (sprofgroupname);
399   return TRUE;
400 }
401
402 /* Serialize the top-level profiles
403  * Note: They don't have to be containerprofiles */
404 static gboolean
405 serialize_encoding_profile (GKeyFile * out, GstEncodingProfile * prof)
406 {
407   gchar *profgroupname;
408   const GList *tmp;
409   guint i;
410   const gchar *profname, *profdesc, *profpreset, *proftype;
411   const GstCaps *profformat, *profrestriction;
412
413   profname = gst_encoding_profile_get_name (prof);
414   profdesc = gst_encoding_profile_get_description (prof);
415   profformat = gst_encoding_profile_get_format (prof);
416   profpreset = gst_encoding_profile_get_preset (prof);
417   proftype = gst_encoding_profile_get_type_nick (prof);
418   profrestriction = gst_encoding_profile_get_restriction (prof);
419
420   profgroupname = g_strdup_printf ("profile-%s", profname);
421
422   g_key_file_set_string (out, profgroupname, "name", profname);
423
424   g_key_file_set_value (out, profgroupname, "type",
425       gst_encoding_profile_get_type_nick (prof));
426
427   if (profdesc)
428     g_key_file_set_locale_string (out, profgroupname, "description",
429         setlocale (LC_ALL, NULL), profdesc);
430   if (profformat) {
431     gchar *tmpc = gst_caps_to_string (profformat);
432     g_key_file_set_string (out, profgroupname, "format", tmpc);
433     g_free (tmpc);
434   }
435   if (profpreset)
436     g_key_file_set_string (out, profgroupname, "preset", profpreset);
437
438   /* stream profiles */
439   if (GST_IS_ENCODING_CONTAINER_PROFILE (prof)) {
440     for (tmp =
441         gst_encoding_container_profile_get_profiles
442         (GST_ENCODING_CONTAINER_PROFILE (prof)), i = 0; tmp;
443         tmp = tmp->next, i++) {
444       GstEncodingProfile *sprof = (GstEncodingProfile *) tmp->data;
445
446       if (!serialize_stream_profiles (out, sprof, profname, i))
447         return FALSE;
448     }
449   }
450   g_free (profgroupname);
451   return TRUE;
452 }
453
454 static gboolean
455 serialize_target (GKeyFile * out, GstEncodingTarget * target)
456 {
457   GList *tmp;
458
459   g_key_file_set_string (out, GST_ENCODING_TARGET_HEADER, "name", target->name);
460   g_key_file_set_string (out, GST_ENCODING_TARGET_HEADER, "category",
461       target->category);
462   g_key_file_set_string (out, GST_ENCODING_TARGET_HEADER, "description",
463       target->description);
464
465   for (tmp = target->profiles; tmp; tmp = tmp->next) {
466     GstEncodingProfile *prof = (GstEncodingProfile *) tmp->data;
467     if (!serialize_encoding_profile (out, prof))
468       return FALSE;
469   }
470
471   return TRUE;
472 }
473
474 /**
475  * parse_encoding_profile:
476  * @in: a #GKeyFile
477  * @parentprofilename: the parent profile name (including 'profile-' or 'streamprofile-' header)
478  * @profilename: the profile name group to parse
479  * @nbgroups: the number of top-level groups
480  * @groups: the top-level groups
481  */
482 static GstEncodingProfile *
483 parse_encoding_profile (GKeyFile * in, gchar * parentprofilename,
484     gchar * profilename, gsize nbgroups, gchar ** groups)
485 {
486   GstEncodingProfile *sprof = NULL;
487   gchar **parent;
488   gchar *proftype, *format, *preset, *restriction, *pname, *description;
489   GstCaps *formatcaps = NULL;
490   GstCaps *restrictioncaps = NULL;
491   gboolean variableframerate;
492   gint pass, presence;
493   gsize i, nbencprofiles;
494
495   GST_DEBUG ("parentprofilename : %s , profilename : %s",
496       parentprofilename, profilename);
497
498   if (parentprofilename) {
499     gboolean found = FALSE;
500
501     parent =
502         g_key_file_get_string_list (in, profilename, "parent",
503         &nbencprofiles, NULL);
504     if (!parent || !nbencprofiles) {
505       return NULL;
506     }
507
508     /* Check if this streamprofile is used in <profilename> */
509     for (i = 0; i < nbencprofiles; i++) {
510       if (!g_strcmp0 (parent[i], parentprofilename)) {
511         found = TRUE;
512         break;
513       }
514     }
515     g_strfreev (parent);
516
517     if (!found) {
518       GST_DEBUG ("Stream profile '%s' isn't used in profile '%s'",
519           profilename, parentprofilename);
520       return NULL;
521     }
522   }
523
524   pname = g_key_file_get_value (in, profilename, "name", NULL);
525
526   /* First try to get localized description */
527   description =
528       g_key_file_get_locale_string (in, profilename, "description",
529       setlocale (LC_ALL, NULL), NULL);
530   if (description == NULL)
531     description = g_key_file_get_value (in, profilename, "description", NULL);
532
533   /* Parse the remaining fields */
534   proftype = g_key_file_get_value (in, profilename, "type", NULL);
535   if (!proftype) {
536     GST_WARNING ("Missing 'type' field for streamprofile %s", profilename);
537     return NULL;
538   }
539
540   format = g_key_file_get_value (in, profilename, "format", NULL);
541   if (format) {
542     formatcaps = gst_caps_from_string (format);
543     g_free (format);
544   }
545
546   preset = g_key_file_get_value (in, profilename, "preset", NULL);
547
548   restriction = g_key_file_get_value (in, profilename, "restriction", NULL);
549   if (restriction) {
550     restrictioncaps = gst_caps_from_string (restriction);
551     g_free (restriction);
552   }
553
554   presence = g_key_file_get_integer (in, profilename, "presence", NULL);
555   pass = g_key_file_get_integer (in, profilename, "pass", NULL);
556   variableframerate =
557       g_key_file_get_boolean (in, profilename, "variableframerate", NULL);
558
559   /* Build the streamprofile ! */
560   if (!g_strcmp0 (proftype, "container")) {
561     GstEncodingProfile *pprof;
562
563     sprof =
564         (GstEncodingProfile *) gst_encoding_container_profile_new (pname,
565         description, formatcaps, preset);
566     /* Now look for the stream profiles */
567     for (i = 0; i < nbgroups; i++) {
568       if (!g_ascii_strncasecmp (groups[i], "streamprofile-", 13)) {
569         pprof = parse_encoding_profile (in, pname, groups[i], nbgroups, groups);
570         if (pprof) {
571           gst_encoding_container_profile_add_profile (
572               (GstEncodingContainerProfile *) sprof, pprof);
573         }
574       }
575     }
576   } else if (!g_strcmp0 (proftype, "video")) {
577     sprof =
578         (GstEncodingProfile *) gst_encoding_video_profile_new (formatcaps,
579         preset, restrictioncaps, presence);
580     gst_encoding_video_profile_set_variableframerate ((GstEncodingVideoProfile
581             *) sprof, variableframerate);
582     gst_encoding_video_profile_set_pass ((GstEncodingVideoProfile *) sprof,
583         pass);
584   } else if (!g_strcmp0 (proftype, "audio")) {
585     sprof =
586         (GstEncodingProfile *) gst_encoding_audio_profile_new (formatcaps,
587         preset, restrictioncaps, presence);
588   } else
589     GST_ERROR ("Unknown profile format '%s'", proftype);
590
591   if (restrictioncaps)
592     gst_caps_unref (restrictioncaps);
593   if (formatcaps)
594     gst_caps_unref (formatcaps);
595
596   if (pname)
597     g_free (pname);
598   if (description)
599     g_free (description);
600   if (preset)
601     g_free (preset);
602   if (proftype)
603     g_free (proftype);
604
605   return sprof;
606 }
607
608 static GstEncodingTarget *
609 parse_keyfile (GKeyFile * in, gchar * targetname, gchar * categoryname,
610     gchar * description)
611 {
612   GstEncodingTarget *res = NULL;
613   GstEncodingProfile *prof;
614   gchar **groups;
615   gsize i, nbgroups;
616
617   res = gst_encoding_target_new (targetname, categoryname, description, NULL);
618
619   /* Figure out the various profiles */
620   groups = g_key_file_get_groups (in, &nbgroups);
621   for (i = 0; i < nbgroups; i++) {
622     if (!g_ascii_strncasecmp (groups[i], "profile-", 8)) {
623       prof = parse_encoding_profile (in, NULL, groups[i], nbgroups, groups);
624       if (prof)
625         gst_encoding_target_add_profile (res, prof);
626     }
627   }
628
629   g_strfreev (groups);
630
631   if (targetname)
632     g_free (targetname);
633   if (categoryname)
634     g_free (categoryname);
635   if (description)
636     g_free (description);
637
638   return res;
639 }
640
641 static GKeyFile *
642 load_file_and_read_header (const gchar * path, gchar ** targetname,
643     gchar ** categoryname, gchar ** description, GError ** error)
644 {
645   GKeyFile *in;
646   gboolean res;
647
648   in = g_key_file_new ();
649
650   GST_DEBUG ("path:%s", path);
651
652   res =
653       g_key_file_load_from_file (in, path,
654       G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, error);
655   if (!res || error != NULL)
656     goto load_error;
657
658   *targetname =
659       g_key_file_get_value (in, GST_ENCODING_TARGET_HEADER, "name", error);
660   if (!*targetname)
661     goto empty_name;
662
663   *categoryname =
664       g_key_file_get_value (in, GST_ENCODING_TARGET_HEADER, "category", NULL);
665   *description =
666       g_key_file_get_value (in, GST_ENCODING_TARGET_HEADER, "description",
667       NULL);
668
669   return in;
670
671 load_error:
672   {
673     GST_WARNING ("Unable to read GstEncodingTarget file %s: %s",
674         path, (*error)->message);
675     g_key_file_free (in);
676     return NULL;
677   }
678
679 empty_name:
680   {
681     GST_WARNING ("Wrong header in file %s: %s", path, (*error)->message);
682     g_key_file_free (in);
683     return NULL;
684   }
685 }
686
687 /**
688  * gst_encoding_target_load_from:
689  * @path: The file to load the #GstEncodingTarget from
690  * @error: If an error occured, this field will be filled in.
691  *
692  * Opens the provided file and returns the contained #GstEncodingTarget.
693  *
694  * Since: 0.10.32
695  *
696  * Returns: (transfer full): The #GstEncodingTarget contained in the file, else
697  * %NULL
698  */
699
700 GstEncodingTarget *
701 gst_encoding_target_load_from (const gchar * path, GError ** error)
702 {
703   GKeyFile *in;
704   gchar *targetname, *categoryname, *description;
705   GstEncodingTarget *res = NULL;
706
707   in = load_file_and_read_header (path, &targetname, &categoryname,
708       &description, error);
709   if (!in)
710     goto beach;
711
712   res = parse_keyfile (in, targetname, categoryname, description);
713
714   g_key_file_free (in);
715
716 beach:
717   return res;
718 }
719
720 /**
721  *
722  * returned list contents must be freed
723  */
724 static GList *
725 get_matching_filenames (gchar * path, gchar * filename)
726 {
727   GList *res = NULL;
728   GDir *topdir;
729   const gchar *subdirname;
730
731   topdir = g_dir_open (path, 0, NULL);
732   if (G_UNLIKELY (topdir == NULL))
733     return NULL;
734
735   while ((subdirname = g_dir_read_name (topdir))) {
736     gchar *ltmp = g_build_filename (path, subdirname, NULL);
737
738     if (g_file_test (ltmp, G_FILE_TEST_IS_DIR)) {
739       gchar *tmp = g_build_filename (path, subdirname, filename, NULL);
740       /* Test to see if we have a file named like that in that directory */
741       if (g_file_test (tmp, G_FILE_TEST_EXISTS))
742         res = g_list_append (res, tmp);
743       else
744         g_free (tmp);
745     }
746     g_free (ltmp);
747   }
748
749   g_dir_close (topdir);
750
751   return res;
752 }
753
754 static GstEncodingTarget *
755 gst_encoding_target_subload (gchar * path, const gchar * category,
756     gchar * lfilename, GError ** error)
757 {
758   GstEncodingTarget *target = NULL;
759
760   if (category) {
761     gchar *filename;
762
763     filename = g_build_filename (path, category, lfilename, NULL);
764     target = gst_encoding_target_load_from (filename, error);
765     g_free (filename);
766   } else {
767     GList *tmp, *tries = get_matching_filenames (path, lfilename);
768
769     /* Try to find a file named %s.gstprofile in any subdirectories */
770     for (tmp = tries; tmp; tmp = tmp->next) {
771       target = gst_encoding_target_load_from ((gchar *) tmp->data, NULL);
772       if (target)
773         break;
774     }
775     g_list_foreach (tries, (GFunc) g_free, NULL);
776     if (tries)
777       g_list_free (tries);
778   }
779
780   return target;
781 }
782
783 /**
784  * gst_encoding_target_load:
785  * @name: the name of the #GstEncodingTarget to load.
786  * @category: (allow-none): the name of the target category. Can be %NULL
787  * @error: If an error occured, this field will be filled in.
788  *
789  * Searches for the #GstEncodingTarget with the given name, loads it
790  * and returns it.
791  *
792  * If the category name is specified only targets from that category will be
793  * searched for.
794  *
795  * Since: 0.10.32
796  *
797  * Returns: (transfer full): The #GstEncodingTarget if available, else %NULL.
798  */
799 GstEncodingTarget *
800 gst_encoding_target_load (const gchar * name, const gchar * category,
801     GError ** error)
802 {
803   gchar *lfilename, *tldir;
804   GstEncodingTarget *target = NULL;
805
806   g_return_val_if_fail (name != NULL, NULL);
807
808   if (!validate_name (name))
809     goto invalid_name;
810
811   if (category && !validate_name (category))
812     goto invalid_category;
813
814   lfilename = g_strdup_printf ("%s.gstprofile", name);
815
816   /* Try from local profiles */
817   tldir =
818       g_build_filename (g_get_home_dir (), ".gstreamer-0.10",
819       "encoding-profile", NULL);
820   target = gst_encoding_target_subload (tldir, category, lfilename, error);
821   g_free (tldir);
822
823   if (target == NULL) {
824     /* Try from system-wide profiles */
825     tldir =
826         g_build_filename (GST_DATADIR, "gstreamer-" GST_MAJORMINOR,
827         "encoding-profile", NULL);
828     target = gst_encoding_target_subload (tldir, category, lfilename, error);
829     g_free (tldir);
830   }
831
832   g_free (lfilename);
833
834   return target;
835
836 invalid_name:
837   {
838     GST_ERROR ("Invalid name for encoding target : '%s'", name);
839     return NULL;
840   }
841 invalid_category:
842   {
843     GST_ERROR ("Invalid name for encoding category : '%s'", category);
844     return NULL;
845   }
846 }
847
848 /**
849  * gst_encoding_target_save_to:
850  * @target: a #GstEncodingTarget
851  * @path: the location to store the @target at.
852  * @error: If an error occured, this field will be filled in.
853  *
854  * Saves the @target to the provided location.
855  *
856  * Since: 0.10.32
857  *
858  * Returns: %TRUE if the target was correctly saved, else %FALSE.
859  **/
860
861 gboolean
862 gst_encoding_target_save_to (GstEncodingTarget * target, const gchar * path,
863     GError ** error)
864 {
865   GKeyFile *out;
866   gchar *data;
867   gsize data_size;
868
869   g_return_val_if_fail (GST_IS_ENCODING_TARGET (target), FALSE);
870   g_return_val_if_fail (path != NULL, FALSE);
871
872   /* FIXME : Check path is valid and writable
873    * FIXME : Strip out profiles already present in system target */
874
875   /* Get unique name... */
876
877   /* Create output GKeyFile */
878   out = g_key_file_new ();
879
880   if (!serialize_target (out, target))
881     goto serialize_failure;
882
883   if (!(data = g_key_file_to_data (out, &data_size, error)))
884     goto convert_failed;
885
886   if (!g_file_set_contents (path, data, data_size, error))
887     goto write_failed;
888
889   g_key_file_free (out);
890   g_free (data);
891
892   return TRUE;
893
894 serialize_failure:
895   {
896     GST_ERROR ("Failure serializing target");
897     g_key_file_free (out);
898     return FALSE;
899   }
900
901 convert_failed:
902   {
903     GST_ERROR ("Failure converting keyfile: %s", (*error)->message);
904     g_key_file_free (out);
905     g_free (data);
906     return FALSE;
907   }
908
909 write_failed:
910   {
911     GST_ERROR ("Unable to write file %s: %s", path, (*error)->message);
912     g_key_file_free (out);
913     g_free (data);
914     return FALSE;
915   }
916 }
917
918 /**
919  * gst_encoding_target_save:
920  * @target: a #GstEncodingTarget
921  * @error: If an error occured, this field will be filled in.
922  *
923  * Saves the @target to the default location. Unless the user has write access
924  * to the system-wide encoding target directory, it will be saved in a
925  * user-local directory.
926  *
927  * Since: 0.10.32
928  *
929  * Returns: %TRUE if the target was correctly saved, else %FALSE.
930  **/
931
932 gboolean
933 gst_encoding_target_save (GstEncodingTarget * target, GError ** error)
934 {
935   gchar *filename;
936   gchar *lfilename;
937   gboolean res;
938
939   g_return_val_if_fail (GST_IS_ENCODING_TARGET (target), FALSE);
940   g_return_val_if_fail (target->category != NULL, FALSE);
941
942   lfilename = g_strdup_printf ("%s.gstprofile", target->name);
943   filename =
944       g_build_filename (g_get_home_dir (), ".gstreamer-0.10",
945       "encoding-profile", target->category, lfilename, NULL);
946   g_free (lfilename);
947
948   res = gst_encoding_target_save_to (target, filename, error);
949   g_free (filename);
950
951   return TRUE;
952 }