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.
26 #include "encoding-target.h"
33 * [_gstencodingtarget_]
35 * category : <category>
36 * description : <description> #translatable
38 * [profile-<profile1name>]
40 * description : <description> #optional
44 * [streamprofile-<id>]
45 * parent : <encodingprofile.name>[,<encodingprofile.name>..]
46 * type : <type> # "audio", "video", "text"
49 * restriction : <restriction>
50 * presence : <presence>
52 * variableframerate : <variableframerate>
56 * Location of profile files
58 * $GST_DATADIR/gstreamer-GST_MAJORMINOR/encoding-profile
59 * $HOME/gstreamer-GST_MAJORMINOR/encoding-profile
62 * $(target.category)/$(target.name).gstprof
64 * Naming restrictions:
65 * lowercase ASCII letter for the first character
66 * Same for all other characters + numerics + hyphens
70 #define GST_ENCODING_TARGET_HEADER "_gstencodingtarget_"
72 struct _GstEncodingTarget
85 G_DEFINE_TYPE (GstEncodingTarget, gst_encoding_target, GST_TYPE_MINI_OBJECT);
88 gst_encoding_target_init (GstEncodingTarget * target)
90 /* Nothing to initialize */
94 gst_encoding_target_finalize (GstEncodingTarget * target)
96 GST_DEBUG ("Finalizing");
99 g_free (target->name);
100 if (target->category)
101 g_free (target->category);
102 if (target->description)
103 g_free (target->description);
105 g_list_foreach (target->profiles, (GFunc) gst_mini_object_unref, NULL);
106 g_list_free (target->profiles);
110 gst_encoding_target_class_init (GstMiniObjectClass * klass)
113 (GstMiniObjectFinalizeFunction) gst_encoding_target_finalize;
117 * gst_encoding_target_get_name:
118 * @target: a #GstEncodingTarget
122 * Returns: (transfer none): The name of the @target.
125 gst_encoding_target_get_name (GstEncodingTarget * target)
131 * gst_encoding_target_get_category:
132 * @target: a #GstEncodingTarget
136 * Returns: (transfer none): The category of the @target.
139 gst_encoding_target_get_category (GstEncodingTarget * target)
141 return target->category;
145 * gst_encoding_target_get_description:
146 * @target: a #GstEncodingTarget
150 * Returns: (transfer none): The description of the @target.
153 gst_encoding_target_get_description (GstEncodingTarget * target)
155 return target->description;
159 * gst_encoding_target_get_profiles:
160 * @target: a #GstEncodingTarget
164 * Returns: (transfer none) (element-type Gst.EncodingProfile): A list of
165 * #GstEncodingProfile(s) this @target handles.
168 gst_encoding_target_get_profiles (GstEncodingTarget * target)
170 return target->profiles;
174 * gst_encoding_target_get_profile:
175 * @target: a #GstEncodingTarget
176 * @name: the name of the profile to retrieve
180 * Returns: (transfer full): The matching #GstEncodingProfile, or %NULL.
183 gst_encoding_target_get_profile (GstEncodingTarget * target, const gchar * name)
187 g_return_val_if_fail (GST_IS_ENCODING_TARGET (target), NULL);
188 g_return_val_if_fail (name != NULL, NULL);
190 for (tmp = target->profiles; tmp; tmp = tmp->next) {
191 GstEncodingProfile *tprof = (GstEncodingProfile *) tmp->data;
193 if (!g_strcmp0 (gst_encoding_profile_get_name (tprof), name)) {
194 gst_encoding_profile_ref (tprof);
202 static inline gboolean
203 validate_name (const gchar * name)
211 /* First character can only be a lower case ASCII character */
212 if (!g_ascii_isalpha (name[0]) || !g_ascii_islower (name[0]))
215 /* All following characters can only by:
216 * either a lower case ASCII character
219 for (i = 1; i < len; i++) {
220 /* if uppercase ASCII letter, return */
221 if (g_ascii_isupper (name[i]))
223 /* if a digit, continue */
224 if (g_ascii_isdigit (name[i]))
226 /* if an hyphen, continue */
229 /* remaining should only be ascii letters */
230 if (!g_ascii_isalpha (name[i]))
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
242 * @description: (transfer none): A description of #GstEncodingTarget in the
244 * @profiles: (transfer none) (element-type Gst.EncodingProfile): A #GList of
245 * #GstEncodingProfile.
247 * Creates a new #GstEncodingTarget.
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
255 * Returns: (transfer full): The newly created #GstEncodingTarget or %NULL if
256 * there was an error.
260 gst_encoding_target_new (const gchar * name, const gchar * category,
261 const gchar * description, const GList * profiles)
263 GstEncodingTarget *res;
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);
270 if (!validate_name (name))
272 if (!validate_name (category))
273 goto invalid_category;
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);
281 GstEncodingProfile *prof = (GstEncodingProfile *) profiles->data;
284 g_list_append (res->profiles, gst_encoding_profile_ref (prof));
285 profiles = profiles->next;
292 GST_ERROR ("Invalid name for encoding target : '%s'", name);
298 GST_ERROR ("Invalid name for encoding category : '%s'", category);
304 * gst_encoding_target_add_profile:
305 * @target: the #GstEncodingTarget to add a profile to
306 * @profile: (transfer full): the #GstEncodingProfile to add
308 * Adds the given @profile to the @target.
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
316 * Returns: %TRUE if the profile was added, else %FALSE.
320 gst_encoding_target_add_profile (GstEncodingTarget * target,
321 GstEncodingProfile * profile)
325 g_return_val_if_fail (GST_IS_ENCODING_TARGET (target), FALSE);
326 g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), FALSE);
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;
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");
339 target->profiles = g_list_append (target->profiles, profile);
345 serialize_stream_profiles (GKeyFile * out, GstEncodingProfile * sprof,
346 const gchar * profilename, guint id)
348 gchar *sprofgroupname;
350 const GstCaps *format, *restriction;
351 const gchar *preset, *name, *description;
353 sprofgroupname = g_strdup_printf ("streamprofile-%s-%d", profilename, id);
355 /* Write the parent profile */
356 g_key_file_set_value (out, sprofgroupname, "parent", profilename);
358 g_key_file_set_value (out, sprofgroupname, "type",
359 gst_encoding_profile_get_type_nick (sprof));
361 format = gst_encoding_profile_get_format (sprof);
363 tmpc = gst_caps_to_string (format);
364 g_key_file_set_value (out, sprofgroupname, "format", tmpc);
368 name = gst_encoding_profile_get_name (sprof);
370 g_key_file_set_string (out, sprofgroupname, "name", name);
372 description = gst_encoding_profile_get_description (sprof);
374 g_key_file_set_string (out, sprofgroupname, "description", description);
376 preset = gst_encoding_profile_get_preset (sprof);
378 g_key_file_set_string (out, sprofgroupname, "preset", preset);
380 restriction = gst_encoding_profile_get_restriction (sprof);
382 tmpc = gst_caps_to_string (restriction);
383 g_key_file_set_value (out, sprofgroupname, "restriction", tmpc);
386 g_key_file_set_integer (out, sprofgroupname, "presence",
387 gst_encoding_profile_get_presence (sprof));
389 if (GST_IS_ENCODING_VIDEO_PROFILE (sprof)) {
390 GstEncodingVideoProfile *vp = (GstEncodingVideoProfile *) sprof;
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));
398 g_free (sprofgroupname);
402 /* Serialize the top-level profiles
403 * Note: They don't have to be containerprofiles */
405 serialize_encoding_profile (GKeyFile * out, GstEncodingProfile * prof)
407 gchar *profgroupname;
410 const gchar *profname, *profdesc, *profpreset, *proftype;
411 const GstCaps *profformat, *profrestriction;
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);
420 profgroupname = g_strdup_printf ("profile-%s", profname);
422 g_key_file_set_string (out, profgroupname, "name", profname);
424 g_key_file_set_value (out, profgroupname, "type",
425 gst_encoding_profile_get_type_nick (prof));
428 g_key_file_set_locale_string (out, profgroupname, "description",
429 setlocale (LC_ALL, NULL), profdesc);
431 gchar *tmpc = gst_caps_to_string (profformat);
432 g_key_file_set_string (out, profgroupname, "format", tmpc);
436 g_key_file_set_string (out, profgroupname, "preset", profpreset);
438 /* stream profiles */
439 if (GST_IS_ENCODING_CONTAINER_PROFILE (prof)) {
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;
446 if (!serialize_stream_profiles (out, sprof, profname, i))
450 g_free (profgroupname);
455 serialize_target (GKeyFile * out, GstEncodingTarget * target)
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",
462 g_key_file_set_string (out, GST_ENCODING_TARGET_HEADER, "description",
463 target->description);
465 for (tmp = target->profiles; tmp; tmp = tmp->next) {
466 GstEncodingProfile *prof = (GstEncodingProfile *) tmp->data;
467 if (!serialize_encoding_profile (out, prof))
475 * parse_encoding_profile:
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
482 static GstEncodingProfile *
483 parse_encoding_profile (GKeyFile * in, gchar * parentprofilename,
484 gchar * profilename, gsize nbgroups, gchar ** groups)
486 GstEncodingProfile *sprof = NULL;
488 gchar *proftype, *format, *preset, *restriction, *pname, *description;
489 GstCaps *formatcaps = NULL;
490 GstCaps *restrictioncaps = NULL;
491 gboolean variableframerate;
493 gsize i, nbencprofiles;
495 GST_DEBUG ("parentprofilename : %s , profilename : %s",
496 parentprofilename, profilename);
498 if (parentprofilename) {
499 gboolean found = FALSE;
502 g_key_file_get_string_list (in, profilename, "parent",
503 &nbencprofiles, NULL);
504 if (!parent || !nbencprofiles) {
508 /* Check if this streamprofile is used in <profilename> */
509 for (i = 0; i < nbencprofiles; i++) {
510 if (!g_strcmp0 (parent[i], parentprofilename)) {
518 GST_DEBUG ("Stream profile '%s' isn't used in profile '%s'",
519 profilename, parentprofilename);
524 pname = g_key_file_get_value (in, profilename, "name", NULL);
526 /* First try to get localized 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);
533 /* Parse the remaining fields */
534 proftype = g_key_file_get_value (in, profilename, "type", NULL);
536 GST_WARNING ("Missing 'type' field for streamprofile %s", profilename);
540 format = g_key_file_get_value (in, profilename, "format", NULL);
542 formatcaps = gst_caps_from_string (format);
546 preset = g_key_file_get_value (in, profilename, "preset", NULL);
548 restriction = g_key_file_get_value (in, profilename, "restriction", NULL);
550 restrictioncaps = gst_caps_from_string (restriction);
551 g_free (restriction);
554 presence = g_key_file_get_integer (in, profilename, "presence", NULL);
555 pass = g_key_file_get_integer (in, profilename, "pass", NULL);
557 g_key_file_get_boolean (in, profilename, "variableframerate", NULL);
559 /* Build the streamprofile ! */
560 if (!g_strcmp0 (proftype, "container")) {
561 GstEncodingProfile *pprof;
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);
571 gst_encoding_container_profile_add_profile (
572 (GstEncodingContainerProfile *) sprof, pprof);
576 } else if (!g_strcmp0 (proftype, "video")) {
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,
584 } else if (!g_strcmp0 (proftype, "audio")) {
586 (GstEncodingProfile *) gst_encoding_audio_profile_new (formatcaps,
587 preset, restrictioncaps, presence);
589 GST_ERROR ("Unknown profile format '%s'", proftype);
592 gst_caps_unref (restrictioncaps);
594 gst_caps_unref (formatcaps);
599 g_free (description);
608 static GstEncodingTarget *
609 parse_keyfile (GKeyFile * in, gchar * targetname, gchar * categoryname,
612 GstEncodingTarget *res = NULL;
613 GstEncodingProfile *prof;
617 res = gst_encoding_target_new (targetname, categoryname, description, NULL);
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);
625 gst_encoding_target_add_profile (res, prof);
634 g_free (categoryname);
636 g_free (description);
642 load_file_and_read_header (const gchar * path, gchar ** targetname,
643 gchar ** categoryname, gchar ** description, GError ** error)
648 in = g_key_file_new ();
650 GST_DEBUG ("path:%s", path);
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)
659 g_key_file_get_value (in, GST_ENCODING_TARGET_HEADER, "name", error);
664 g_key_file_get_value (in, GST_ENCODING_TARGET_HEADER, "category", NULL);
666 g_key_file_get_value (in, GST_ENCODING_TARGET_HEADER, "description",
673 GST_WARNING ("Unable to read GstEncodingTarget file %s: %s",
674 path, (*error)->message);
675 g_key_file_free (in);
681 GST_WARNING ("Wrong header in file %s: %s", path, (*error)->message);
682 g_key_file_free (in);
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.
692 * Opens the provided file and returns the contained #GstEncodingTarget.
696 * Returns: (transfer full): The #GstEncodingTarget contained in the file, else
701 gst_encoding_target_load_from (const gchar * path, GError ** error)
704 gchar *targetname, *categoryname, *description;
705 GstEncodingTarget *res = NULL;
707 in = load_file_and_read_header (path, &targetname, &categoryname,
708 &description, error);
712 res = parse_keyfile (in, targetname, categoryname, description);
714 g_key_file_free (in);
722 * returned list contents must be freed
725 get_matching_filenames (gchar * path, gchar * filename)
729 const gchar *subdirname;
731 topdir = g_dir_open (path, 0, NULL);
732 if (G_UNLIKELY (topdir == NULL))
735 while ((subdirname = g_dir_read_name (topdir))) {
736 gchar *ltmp = g_build_filename (path, subdirname, NULL);
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);
749 g_dir_close (topdir);
754 static GstEncodingTarget *
755 gst_encoding_target_subload (gchar * path, const gchar * category,
756 gchar * lfilename, GError ** error)
758 GstEncodingTarget *target = NULL;
763 filename = g_build_filename (path, category, lfilename, NULL);
764 target = gst_encoding_target_load_from (filename, error);
767 GList *tmp, *tries = get_matching_filenames (path, lfilename);
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);
775 g_list_foreach (tries, (GFunc) g_free, NULL);
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.
789 * Searches for the #GstEncodingTarget with the given name, loads it
792 * If the category name is specified only targets from that category will be
797 * Returns: (transfer full): The #GstEncodingTarget if available, else %NULL.
800 gst_encoding_target_load (const gchar * name, const gchar * category,
803 gchar *lfilename, *tldir;
804 GstEncodingTarget *target = NULL;
806 g_return_val_if_fail (name != NULL, NULL);
808 if (!validate_name (name))
811 if (category && !validate_name (category))
812 goto invalid_category;
814 lfilename = g_strdup_printf ("%s.gstprofile", name);
816 /* Try from local profiles */
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);
823 if (target == NULL) {
824 /* Try from system-wide profiles */
826 g_build_filename (GST_DATADIR, "gstreamer-" GST_MAJORMINOR,
827 "encoding-profile", NULL);
828 target = gst_encoding_target_subload (tldir, category, lfilename, error);
838 GST_ERROR ("Invalid name for encoding target : '%s'", name);
843 GST_ERROR ("Invalid name for encoding category : '%s'", category);
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.
854 * Saves the @target to the provided location.
858 * Returns: %TRUE if the target was correctly saved, else %FALSE.
862 gst_encoding_target_save_to (GstEncodingTarget * target, const gchar * path,
869 g_return_val_if_fail (GST_IS_ENCODING_TARGET (target), FALSE);
870 g_return_val_if_fail (path != NULL, FALSE);
872 /* FIXME : Check path is valid and writable
873 * FIXME : Strip out profiles already present in system target */
875 /* Get unique name... */
877 /* Create output GKeyFile */
878 out = g_key_file_new ();
880 if (!serialize_target (out, target))
881 goto serialize_failure;
883 if (!(data = g_key_file_to_data (out, &data_size, error)))
886 if (!g_file_set_contents (path, data, data_size, error))
889 g_key_file_free (out);
896 GST_ERROR ("Failure serializing target");
897 g_key_file_free (out);
903 GST_ERROR ("Failure converting keyfile: %s", (*error)->message);
904 g_key_file_free (out);
911 GST_ERROR ("Unable to write file %s: %s", path, (*error)->message);
912 g_key_file_free (out);
919 * gst_encoding_target_save:
920 * @target: a #GstEncodingTarget
921 * @error: If an error occured, this field will be filled in.
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.
929 * Returns: %TRUE if the target was correctly saved, else %FALSE.
933 gst_encoding_target_save (GstEncodingTarget * target, GError ** error)
939 g_return_val_if_fail (GST_IS_ENCODING_TARGET (target), FALSE);
940 g_return_val_if_fail (target->category != NULL, FALSE);
942 lfilename = g_strdup_printf ("%s.gstprofile", target->name);
944 g_build_filename (g_get_home_dir (), ".gstreamer-0.10",
945 "encoding-profile", target->category, lfilename, NULL);
948 res = gst_encoding_target_save_to (target, filename, error);