1 /* GStreamer encoding profile registry
2 * Copyright (C) 2010 Edward Hervey <edward.hervey@collabora.co.uk>
3 * (C) 2010 Nokia Corporation
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.
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.
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.
23 #include "encoding-target.h"
30 * [_gstencodingtarget_]
32 * category : <category>
33 * description : <description> #translatable
35 * [profile-<profile1name>]
37 * description : <description> #optional
41 * [streamprofile-<id>]
42 * parent : <encodingprofile.name>[,<encodingprofile.name>..]
43 * type : <type> # "audio", "video", "text"
46 * restriction : <restriction>
47 * presence : <presence>
49 * variableframerate : <variableframerate>
52 #define GST_ENCODING_TARGET_HEADER "_gstencodingtarget_"
54 struct _GstEncodingTarget
67 G_DEFINE_TYPE (GstEncodingTarget, gst_encoding_target, GST_TYPE_MINI_OBJECT);
70 gst_encoding_target_init (GstEncodingTarget * target)
72 /* Nothing to initialize */
76 gst_encoding_target_finalize (GstEncodingTarget * target)
78 GST_DEBUG ("Finalizing");
81 g_free (target->name);
83 g_free (target->category);
84 if (target->description)
85 g_free (target->description);
87 g_list_foreach (target->profiles, (GFunc) gst_mini_object_unref, NULL);
88 g_list_free (target->profiles);
92 gst_encoding_target_class_init (GstMiniObjectClass * klass)
95 (GstMiniObjectFinalizeFunction) gst_encoding_target_finalize;
99 * gst_encoding_target_get_name:
100 * @target: a #GstEncodingTarget
104 * Returns: (transfer none): The name of the @target.
107 gst_encoding_target_get_name (GstEncodingTarget * target)
113 * gst_encoding_target_get_category:
114 * @target: a #GstEncodingTarget
118 * Returns: (transfer none): The category of the @target.
121 gst_encoding_target_get_category (GstEncodingTarget * target)
123 return target->category;
127 * gst_encoding_target_get_description:
128 * @target: a #GstEncodingTarget
132 * Returns: (transfer none): The description of the @target.
135 gst_encoding_target_get_description (GstEncodingTarget * target)
137 return target->description;
141 * gst_encoding_target_get_profiles:
142 * @target: a #GstEncodingTarget
146 * Returns: (transfer none) (element-type Gst.EncodingProfile): A list of
147 * #GstEncodingProfile(s) this @target handles.
150 gst_encoding_target_get_profiles (GstEncodingTarget * target)
152 return target->profiles;
156 * gst_encoding_target_get_profile:
157 * @target: a #GstEncodingTarget
158 * @name: the name of the profile to retrieve
162 * Returns: (transfer full): The matching #GstEncodingProfile, or %NULL.
165 gst_encoding_target_get_profile (GstEncodingTarget * target, const gchar * name)
169 g_return_val_if_fail (GST_IS_ENCODING_TARGET (target), NULL);
170 g_return_val_if_fail (name != NULL, NULL);
172 for (tmp = target->profiles; tmp; tmp = tmp->next) {
173 GstEncodingProfile *tprof = (GstEncodingProfile *) tmp->data;
175 if (!g_strcmp0 (gst_encoding_profile_get_name (tprof), name)) {
176 gst_encoding_profile_ref (tprof);
184 static inline gboolean
185 validate_name (const gchar * name)
193 /* First character can only be a lower case ASCII character */
194 if (!g_ascii_isalpha (name[0]) || !g_ascii_islower (name[0]))
197 /* All following characters can only by:
198 * either a lower case ASCII character
201 for (i = 1; i < len; i++) {
202 /* if uppercase ASCII letter, return */
203 if (g_ascii_isupper (name[i]))
205 /* if a digit, continue */
206 if (g_ascii_isdigit (name[i]))
208 /* if an hyphen, continue */
211 /* remaining should only be ascii letters */
212 if (!g_ascii_isalpha (name[i]))
220 * gst_encoding_target_new:
221 * @name: The name of the target.
222 * @category: (transfer none): The name of the category to which this @target
224 * @description: (transfer none): A description of #GstEncodingTarget in the
226 * @profiles: (transfer none) (element-type Gst.EncodingProfile): A #GList of
227 * #GstEncodingProfile.
229 * Creates a new #GstEncodingTarget.
231 * The name and category can only consist of lowercase ASCII letters for the
232 * first character, followed by either lowercase ASCII letters, digits or
237 * Returns: (transfer full): The newly created #GstEncodingTarget or %NULL if
238 * there was an error.
242 gst_encoding_target_new (const gchar * name, const gchar * category,
243 const gchar * description, const GList * profiles)
245 GstEncodingTarget *res;
247 g_return_val_if_fail (name != NULL, NULL);
248 g_return_val_if_fail (category != NULL, NULL);
249 g_return_val_if_fail (description != NULL, NULL);
252 if (!validate_name (name))
254 if (!validate_name (category))
255 goto invalid_category;
257 res = (GstEncodingTarget *) gst_mini_object_new (GST_TYPE_ENCODING_TARGET);
258 res->name = g_strdup (name);
259 res->category = g_strdup (category);
260 res->description = g_strdup (description);
263 GstEncodingProfile *prof = (GstEncodingProfile *) profiles->data;
266 g_list_append (res->profiles, gst_encoding_profile_ref (prof));
267 profiles = profiles->next;
274 GST_ERROR ("Invalid name for encoding target : '%s'", name);
280 GST_ERROR ("Invalid name for encoding category : '%s'", category);
286 * gst_encoding_target_add_profile:
287 * @target: the #GstEncodingTarget to add a profile to
288 * @profile: (transfer full): the #GstEncodingProfile to add
290 * Adds the given @profile to the @target.
292 * The @target will steal a reference to the @profile. If you wish to use
293 * the profile after calling this method, you should increase its reference
298 * Returns: %TRUE if the profile was added, else %FALSE.
302 gst_encoding_target_add_profile (GstEncodingTarget * target,
303 GstEncodingProfile * profile)
307 g_return_val_if_fail (GST_IS_ENCODING_TARGET (target), FALSE);
308 g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), FALSE);
310 /* Make sure profile isn't already controlled by this target */
311 for (tmp = target->profiles; tmp; tmp = tmp->next) {
312 GstEncodingProfile *prof = (GstEncodingProfile *) tmp->data;
314 if (!g_strcmp0 (gst_encoding_profile_get_name (profile),
315 gst_encoding_profile_get_name (prof))) {
316 GST_WARNING ("Profile already present in target");
321 target->profiles = g_list_append (target->profiles, profile);
327 serialize_stream_profiles (GKeyFile * out, GstEncodingProfile * sprof,
328 const gchar * profilename, guint id)
330 gchar *sprofgroupname;
332 const GstCaps *format, *restriction;
333 const gchar *preset, *name, *description;
335 sprofgroupname = g_strdup_printf ("streamprofile-%s-%d", profilename, id);
337 /* Write the parent profile */
338 g_key_file_set_value (out, sprofgroupname, "parent", profilename);
340 g_key_file_set_value (out, sprofgroupname, "type",
341 gst_encoding_profile_get_type_nick (sprof));
343 format = gst_encoding_profile_get_format (sprof);
345 tmpc = gst_caps_to_string (format);
346 g_key_file_set_value (out, sprofgroupname, "format", tmpc);
350 name = gst_encoding_profile_get_name (sprof);
352 g_key_file_set_string (out, sprofgroupname, "name", name);
354 description = gst_encoding_profile_get_description (sprof);
356 g_key_file_set_string (out, sprofgroupname, "description", description);
358 preset = gst_encoding_profile_get_preset (sprof);
360 g_key_file_set_string (out, sprofgroupname, "preset", preset);
362 restriction = gst_encoding_profile_get_restriction (sprof);
364 tmpc = gst_caps_to_string (restriction);
365 g_key_file_set_value (out, sprofgroupname, "restriction", tmpc);
368 g_key_file_set_integer (out, sprofgroupname, "presence",
369 gst_encoding_profile_get_presence (sprof));
371 if (GST_IS_ENCODING_VIDEO_PROFILE (sprof)) {
372 GstEncodingVideoProfile *vp = (GstEncodingVideoProfile *) sprof;
374 g_key_file_set_integer (out, sprofgroupname, "pass",
375 gst_encoding_video_profile_get_pass (vp));
376 g_key_file_set_boolean (out, sprofgroupname, "variableframerate",
377 gst_encoding_video_profile_get_variableframerate (vp));
380 g_free (sprofgroupname);
384 /* Serialize the top-level profiles
385 * Note: They don't have to be containerprofiles */
387 serialize_encoding_profile (GKeyFile * out, GstEncodingProfile * prof)
389 gchar *profgroupname;
392 const gchar *profname, *profdesc, *profpreset, *proftype;
393 const GstCaps *profformat, *profrestriction;
395 profname = gst_encoding_profile_get_name (prof);
396 profdesc = gst_encoding_profile_get_description (prof);
397 profformat = gst_encoding_profile_get_format (prof);
398 profpreset = gst_encoding_profile_get_preset (prof);
399 proftype = gst_encoding_profile_get_type_nick (prof);
400 profrestriction = gst_encoding_profile_get_restriction (prof);
402 profgroupname = g_strdup_printf ("profile-%s", profname);
404 g_key_file_set_string (out, profgroupname, "name", profname);
406 g_key_file_set_value (out, profgroupname, "type",
407 gst_encoding_profile_get_type_nick (prof));
410 g_key_file_set_locale_string (out, profgroupname, "description",
411 setlocale (LC_ALL, NULL), profdesc);
413 gchar *tmpc = gst_caps_to_string (profformat);
414 g_key_file_set_string (out, profgroupname, "format", tmpc);
418 g_key_file_set_string (out, profgroupname, "preset", profpreset);
420 /* stream profiles */
421 if (GST_IS_ENCODING_CONTAINER_PROFILE (prof)) {
423 gst_encoding_container_profile_get_profiles
424 (GST_ENCODING_CONTAINER_PROFILE (prof)), i = 0; tmp;
425 tmp = tmp->next, i++) {
426 GstEncodingProfile *sprof = (GstEncodingProfile *) tmp->data;
428 if (!serialize_stream_profiles (out, sprof, profname, i))
432 g_free (profgroupname);
437 serialize_target (GKeyFile * out, GstEncodingTarget * target)
441 g_key_file_set_string (out, GST_ENCODING_TARGET_HEADER, "name", target->name);
442 g_key_file_set_string (out, GST_ENCODING_TARGET_HEADER, "category",
444 g_key_file_set_string (out, GST_ENCODING_TARGET_HEADER, "description",
445 target->description);
447 for (tmp = target->profiles; tmp; tmp = tmp->next) {
448 GstEncodingProfile *prof = (GstEncodingProfile *) tmp->data;
449 if (!serialize_encoding_profile (out, prof))
457 * parse_encoding_profile:
459 * @parentprofilename: the parent profile name (including 'profile-' or 'streamprofile-' header)
460 * @profilename: the profile name group to parse
461 * @nbgroups: the number of top-level groups
462 * @groups: the top-level groups
464 static GstEncodingProfile *
465 parse_encoding_profile (GKeyFile * in, gchar * parentprofilename,
466 gchar * profilename, gsize nbgroups, gchar ** groups)
468 GstEncodingProfile *sprof = NULL;
470 gchar *proftype, *format, *preset, *restriction, *pname, *description;
471 GstCaps *formatcaps = NULL;
472 GstCaps *restrictioncaps = NULL;
473 gboolean variableframerate;
475 gsize i, nbencprofiles;
477 GST_DEBUG ("parentprofilename : %s , profilename : %s",
478 parentprofilename, profilename);
480 if (parentprofilename) {
481 gboolean found = FALSE;
484 g_key_file_get_string_list (in, profilename, "parent",
485 &nbencprofiles, NULL);
486 if (!parent || !nbencprofiles) {
490 /* Check if this streamprofile is used in <profilename> */
491 for (i = 0; i < nbencprofiles; i++) {
492 if (!g_strcmp0 (parent[i], parentprofilename)) {
500 GST_DEBUG ("Stream profile '%s' isn't used in profile '%s'",
501 profilename, parentprofilename);
506 pname = g_key_file_get_value (in, profilename, "name", NULL);
508 /* First try to get localized description */
510 g_key_file_get_locale_string (in, profilename, "description",
511 setlocale (LC_ALL, NULL), NULL);
512 if (description == NULL)
513 description = g_key_file_get_value (in, profilename, "description", NULL);
515 /* Parse the remaining fields */
516 proftype = g_key_file_get_value (in, profilename, "type", NULL);
518 GST_WARNING ("Missing 'type' field for streamprofile %s", profilename);
522 format = g_key_file_get_value (in, profilename, "format", NULL);
524 formatcaps = gst_caps_from_string (format);
528 preset = g_key_file_get_value (in, profilename, "preset", NULL);
530 restriction = g_key_file_get_value (in, profilename, "restriction", NULL);
532 restrictioncaps = gst_caps_from_string (restriction);
533 g_free (restriction);
536 presence = g_key_file_get_integer (in, profilename, "presence", NULL);
537 pass = g_key_file_get_integer (in, profilename, "pass", NULL);
539 g_key_file_get_boolean (in, profilename, "variableframerate", NULL);
541 /* Build the streamprofile ! */
542 if (!g_strcmp0 (proftype, "container")) {
543 GstEncodingProfile *pprof;
546 (GstEncodingProfile *) gst_encoding_container_profile_new (pname,
547 description, formatcaps, preset);
548 /* Now look for the stream profiles */
549 for (i = 0; i < nbgroups; i++) {
550 if (!g_ascii_strncasecmp (groups[i], "streamprofile-", 13)) {
551 pprof = parse_encoding_profile (in, pname, groups[i], nbgroups, groups);
553 gst_encoding_container_profile_add_profile (
554 (GstEncodingContainerProfile *) sprof, pprof);
558 } else if (!g_strcmp0 (proftype, "video")) {
560 (GstEncodingProfile *) gst_encoding_video_profile_new (formatcaps,
561 preset, restrictioncaps, presence);
562 gst_encoding_video_profile_set_variableframerate ((GstEncodingVideoProfile
563 *) sprof, variableframerate);
564 gst_encoding_video_profile_set_pass ((GstEncodingVideoProfile *) sprof,
566 } else if (!g_strcmp0 (proftype, "audio")) {
568 (GstEncodingProfile *) gst_encoding_audio_profile_new (formatcaps,
569 preset, restrictioncaps, presence);
571 GST_ERROR ("Unknown profile format '%s'", proftype);
574 gst_caps_unref (restrictioncaps);
576 gst_caps_unref (formatcaps);
581 g_free (description);
590 static GstEncodingTarget *
591 parse_keyfile (GKeyFile * in, gchar * targetname, gchar * categoryname,
594 GstEncodingTarget *res = NULL;
595 GstEncodingProfile *prof;
599 res = gst_encoding_target_new (targetname, categoryname, description, NULL);
601 /* Figure out the various profiles */
602 groups = g_key_file_get_groups (in, &nbgroups);
603 for (i = 0; i < nbgroups; i++) {
604 if (!g_ascii_strncasecmp (groups[i], "profile-", 8)) {
605 prof = parse_encoding_profile (in, NULL, groups[i], nbgroups, groups);
607 gst_encoding_target_add_profile (res, prof);
616 g_free (categoryname);
618 g_free (description);
624 load_file_and_read_header (const gchar * path, gchar ** targetname,
625 gchar ** categoryname, gchar ** description, GError ** error)
630 in = g_key_file_new ();
632 GST_DEBUG ("path:%s", path);
635 g_key_file_load_from_file (in, path,
636 G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, error);
637 if (!res || error != NULL)
641 g_key_file_get_value (in, GST_ENCODING_TARGET_HEADER, "name", error);
646 g_key_file_get_value (in, GST_ENCODING_TARGET_HEADER, "category", NULL);
648 g_key_file_get_value (in, GST_ENCODING_TARGET_HEADER, "description",
655 GST_WARNING ("Unable to read GstEncodingTarget file %s: %s",
656 path, (*error)->message);
657 g_key_file_free (in);
663 GST_WARNING ("Wrong header in file %s: %s", path, (*error)->message);
664 g_key_file_free (in);
670 * gst_encoding_target_load_from:
671 * @path: The file to load the #GstEncodingTarget from
672 * @error: If an error occured, this field will be filled in.
674 * Opens the provided file and returns the contained #GstEncodingTarget.
678 * Returns: (transfer full): The #GstEncodingTarget contained in the file, else
683 gst_encoding_target_load_from (const gchar * path, GError ** error)
686 gchar *targetname, *categoryname, *description;
687 GstEncodingTarget *res = NULL;
689 in = load_file_and_read_header (path, &targetname, &categoryname,
690 &description, error);
694 res = parse_keyfile (in, targetname, categoryname, description);
696 g_key_file_free (in);
703 * gst_encoding_target_load:
704 * @name: the name of the #GstEncodingTarget to load.
705 * @error: If an error occured, this field will be filled in.
707 * Searches for the #GstEncodingTarget with the given name, loads it
710 * Warning: NOT IMPLEMENTED.
714 * Returns: (transfer full): The #GstEncodingTarget if available, else %NULL.
718 gst_encoding_target_load (const gchar * name, GError ** error)
720 /* FIXME : IMPLEMENT */
725 * gst_encoding_target_save:
726 * @target: a #GstEncodingTarget
727 * @error: If an error occured, this field will be filled in.
729 * Saves the @target to the default location.
731 * Warning: NOT IMPLEMENTED.
735 * Returns: %TRUE if the target was correctly saved, else %FALSE.
739 gst_encoding_target_save (GstEncodingTarget * target, GError ** error)
741 g_return_val_if_fail (GST_IS_ENCODING_TARGET (target), FALSE);
743 /* FIXME : IMPLEMENT */
748 * gst_encoding_target_save_to:
749 * @target: a #GstEncodingTarget
750 * @path: the location to store the @target at.
751 * @error: If an error occured, this field will be filled in.
753 * Saves the @target to the provided location.
757 * Returns: %TRUE if the target was correctly saved, else %FALSE.
761 gst_encoding_target_save_to (GstEncodingTarget * target, const gchar * path,
768 g_return_val_if_fail (GST_IS_ENCODING_TARGET (target), FALSE);
769 g_return_val_if_fail (path != NULL, FALSE);
771 /* FIXME : Check path is valid and writable
772 * FIXME : Strip out profiles already present in system target */
774 /* Get unique name... */
776 /* Create output GKeyFile */
777 out = g_key_file_new ();
779 if (!serialize_target (out, target))
780 goto serialize_failure;
782 if (!(data = g_key_file_to_data (out, &data_size, error)))
785 if (!g_file_set_contents (path, data, data_size, error))
788 g_key_file_free (out);
795 GST_ERROR ("Failure serializing target");
796 g_key_file_free (out);
802 GST_ERROR ("Failure converting keyfile: %s", (*error)->message);
803 g_key_file_free (out);
810 GST_ERROR ("Unable to write file %s: %s", path, (*error)->message);
811 g_key_file_free (out);