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