diff --git a/ChangeLog b/ChangeLog index bfe0376344..eb4a01dec4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,46 @@ +2008-05-27 Wim Taymans + + Patch by: Stefan Kost + + * configure.ac: + Add DATADIR for storing presets. + + * docs/gst/gstreamer-docs.sgml: + * docs/gst/gstreamer-sections.txt: + * docs/gst/gstreamer.types.in: + Add GstPreset to docs. + + * gst/Makefile.am: + * gst/gst.h: + * gst/gstpreset.c: (preset_get_paths), (preset_skip_property), + (preset_open_and_parse_header), (preset_parse_version), + (preset_merge), (preset_get_keyfile), + (gst_preset_default_get_preset_names), + (gst_preset_default_get_property_names), + (gst_preset_default_load_preset), + (gst_preset_default_save_presets_file), + (gst_preset_default_save_preset), + (gst_preset_default_rename_preset), + (gst_preset_default_delete_preset), (gst_preset_default_set_meta), + (gst_preset_default_get_meta), (gst_preset_default_randomize), + (gst_preset_default_reset), (gst_preset_get_preset_names), + (gst_preset_get_property_names), (gst_preset_load_preset), + (gst_preset_save_preset), (gst_preset_rename_preset), + (gst_preset_delete_preset), (gst_preset_set_meta), + (gst_preset_get_meta), (gst_preset_class_init), + (gst_preset_base_init), (gst_preset_get_type): + * gst/gstpreset.h: + Add GstPreset to core. Fixes #396779 + + * tests/check/Makefile.am: + * tests/check/gst/gstpreset.c: (gst_preset_test_get_property), + (gst_preset_test_set_property), (gst_preset_test_class_init), + (gst_preset_test_base_init), (gst_preset_test_get_type), + (gst_preset_test_plugin_init), (GST_START_TEST), + (remove_preset_file), (test_setup), (test_teardown), + (gst_preset_suite): + Add GstPreset unit tests. + 2008-05-27 Wim Taymans * gst/gstpad.c: (gst_pad_event_default_dispatch): diff --git a/configure.ac b/configure.ac index a17fdeda38..ad66acdc19 100644 --- a/configure.ac +++ b/configure.ac @@ -524,9 +524,11 @@ GST_LICENSE="LGPL" AC_DEFINE_UNQUOTED(GST_LICENSE, "$GST_LICENSE", [GStreamer license]) AC_SUBST(GST_LICENSE) -dnl define LIBDIR so we can inform people where we live +dnl define LIBDIR, DATADIR so we can inform people where we live AS_AC_EXPAND(LIBDIR, $libdir) AC_DEFINE_UNQUOTED(LIBDIR, "$LIBDIR", [library dir]) +AS_AC_EXPAND(DATADIR, $datadir) +AC_DEFINE_UNQUOTED(DATADIR, "$DATADIR", [data dir]) dnl set location of plugin directory AG_GST_SET_PLUGINDIR diff --git a/docs/gst/gstreamer-docs.sgml b/docs/gst/gstreamer-docs.sgml index 86d0ca0331..c6203c3695 100644 --- a/docs/gst/gstreamer-docs.sgml +++ b/docs/gst/gstreamer-docs.sgml @@ -37,6 +37,7 @@ + @@ -130,6 +131,7 @@ Windows. It is released under the GNU Library General Public License &GstPlugin; &GstPluginFeature; &GstPoll; + &GstPreset; &GstQuery; &GstRegistry; &GstSegment; diff --git a/docs/gst/gstreamer-sections.txt b/docs/gst/gstreamer-sections.txt index b79a90ab6f..f408c45b73 100644 --- a/docs/gst/gstreamer-sections.txt +++ b/docs/gst/gstreamer-sections.txt @@ -1691,6 +1691,26 @@ gst_poll_set_flushing gst_poll_wait +
+gstpreset +GstPreset +GstPreset +gst_preset_get_preset_names +gst_preset_get_property_names +gst_preset_load_preset +gst_preset_save_preset +gst_preset_rename_preset +gst_preset_delete_preset +gst_preset_set_meta +gst_preset_get_meta + +GstPresetInterface +GST_PRESET +GST_IS_PRESET +GST_TYPE_PRESET +GST_PRESET_GET_INTERFACE +gst_preset_get_type +
gstquery diff --git a/docs/gst/gstreamer.types.in b/docs/gst/gstreamer.types.in index cb2855d2f1..1de0bbb9f7 100644 --- a/docs/gst/gstreamer.types.in +++ b/docs/gst/gstreamer.types.in @@ -23,6 +23,7 @@ gst_pad_get_type gst_pad_template_get_type gst_pipeline_get_type gst_plugin_feature_get_type +gst_preset_get_type gst_registry_get_type gst_system_clock_get_type gst_tag_setter_get_type diff --git a/gst/Makefile.am b/gst/Makefile.am index f2dc4fe4c6..7852de4da3 100644 --- a/gst/Makefile.am +++ b/gst/Makefile.am @@ -103,6 +103,7 @@ libgstreamer_@GST_MAJORMINOR@_la_SOURCES = \ gstplugin.c \ gstpluginfeature.c \ gstpoll.c \ + gstpreset.c \ gstquark.c \ gstquery.c \ gstregistry.c \ @@ -187,6 +188,7 @@ gst_headers = \ gstplugin.h \ gstpluginfeature.h \ gstpoll.h \ + gstpreset.h \ gstquery.h \ gstsegment.h \ gststructure.h \ diff --git a/gst/gst.h b/gst/gst.h index 6094c68a19..9c5200a909 100644 --- a/gst/gst.h +++ b/gst/gst.h @@ -55,6 +55,7 @@ #include #include #include +#include #include #include #include diff --git a/gst/gstpreset.c b/gst/gstpreset.c new file mode 100644 index 0000000000..745eee3976 --- /dev/null +++ b/gst/gstpreset.c @@ -0,0 +1,1175 @@ +/* GStreamer + * Copyright (C) 2006 Stefan Kost + * + * gstpreset.c: helper interface for element presets + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +/** + * SECTION:gstpreset + * @short_description: helper interface for element presets + * + * This interface offers methods to query and manipulate parameter preset sets. + * A preset is a bunch of property settings, together with meta data and a name. + * The name of a preset serves as key for subsequent method calls to manipulate + * single presets. + * All instances of one type will share the list of presets. The list is created + * on demand, if presets are not used, the list is not created. + * + * The interface comes with a default implementation that serves most plugins. + * Wrapper plugins will override most methods to implement support for the + * native preset format of those wrapped plugins. + * One method that is useful to be overridden is gst_preset_get_property_names(). + * With that one can control which properties are saved and in which order. + */ +/* FIXME: + * - non racyness + * - we need to avoid two instances writing the preset file + * -> flock(fileno()), http://www.ecst.csuchico.edu/~beej/guide/ipc/flock.html + * -> open exclusive + * -> better save the new file to a tempfile and then rename? + * - we like to know when any other instance makes changes to the keyfile + * - then ui can be updated + * - and we make sure that we don't lose edits + * -> its the same problem actually, once for inside a process, once system- + * wide + * - can we use a lock inside a names shared memory segment? + * + * - need to add support for GstChildProxy + * we can do this in a next iteration, the format is flexible enough + * http://www.buzztard.org/index.php/Preset_handling_interface + * + * - should there be a 'preset-list' property to get the preset list + * (and to connect a notify:: to to listen for changes) + * we could use gnome_vfs_monitor_add() to monitor the user preset_file. + * + * - should there be a 'preset-name' property so that we can set a preset via + * gst-launch, or should we handle this with special syntax in gst-launch: + * gst-launch element preset: property=value ... + * - this would alloow to hanve preset-bundles too (a preset on bins that + * specifies presets for children + * + * - meta presets : presets that load presets for children (childproxy/bin) + * [] + * _child/= + * _child/... + * + */ + +#include "gst_private.h" + +#include "gstpreset.h" + +#include "stdlib.h" +#include +#include + +#define GST_CAT_DEFAULT preset_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +/* defines for keyfile usage, this group contains the element type name and + * version these presets belong to. */ +#define PRESET_HEADER "_presets_" + +/* keys of the preset header section */ +#define PRESET_HEADER_ELEMENT_NAME "element-name" +#define PRESET_HEADER_VERSION "version" + +static GQuark preset_user_path_quark = 0; +static GQuark preset_system_path_quark = 0; +static GQuark preset_quark = 0; + +/*static GQuark property_list_quark = 0;*/ + +/* default iface implementation */ + +static gboolean gst_preset_default_save_presets_file (GstPreset * preset); + +/* + * preset_get_paths: + * @preset: a #GObject that implements #GstPreset + * @preset_user_path: location for path or %NULL + * @preset_system_path: location for path or %NULL + * + * Fetch the preset_path for user local and system wide settings. Don't free + * after use. + * + * Returns: %FALSE if no paths could be found. + */ +static gboolean +preset_get_paths (GstPreset * preset, const gchar ** preset_user_path, + const gchar ** preset_system_path) +{ + GType type = G_TYPE_FROM_INSTANCE (preset); + gchar *preset_path; + const gchar *element_name; + + /* we use the element name when we must contruct the paths */ + element_name = G_OBJECT_TYPE_NAME (preset); + GST_INFO_OBJECT (preset, "element_name: '%s'", element_name); + + if (preset_user_path) { + /* preset user path requested, see if we have it cached in the qdata */ + if (!(preset_path = g_type_get_qdata (type, preset_user_path_quark))) { + gchar *preset_dir; + + /* user presets go in '$HOME/.gstreamer-0.10/presets/GstSimSyn.prs' */ + preset_dir = g_build_filename (g_get_home_dir (), + ".gstreamer-" GST_MAJORMINOR, "presets", NULL); + GST_INFO_OBJECT (preset, "user_preset_dir: '%s'", preset_dir); + preset_path = + g_strdup_printf ("%s" G_DIR_SEPARATOR_S "%s.prs", preset_dir, + element_name); + GST_INFO_OBJECT (preset, "user_preset_path: '%s'", preset_path); + /* create dirs */ + g_mkdir_with_parents (preset_dir, 0755); + g_free (preset_dir); + + /* cache the preset path to the type */ + g_type_set_qdata (type, preset_user_path_quark, preset_path); + } + *preset_user_path = preset_path; + } + + if (preset_system_path) { + /* preset system path requested, see if we have it cached in the qdata */ + if (!(preset_path = g_type_get_qdata (type, preset_system_path_quark))) { + gchar *preset_dir; + + /* system presets in '$DATADIR/gstreamer-0.10/presets/GstAudioPanorama.prs' */ + preset_dir = g_build_filename (DATADIR, "gstreamer-" GST_MAJORMINOR, + "presets", NULL); + GST_INFO_OBJECT (preset, "system_preset_dir: '%s'", preset_dir); + preset_path = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "%s.prs", + preset_dir, element_name); + GST_INFO_OBJECT (preset, "system_preset_path: '%s'", preset_path); + /* create dirs */ + g_mkdir_with_parents (preset_dir, 0755); + g_free (preset_dir); + + /* cache the preset path to the type */ + g_type_set_qdata (type, preset_system_path_quark, preset_path); + } + *preset_system_path = preset_path; + } + return TRUE; +} + +static gboolean +preset_skip_property (GParamSpec * property) +{ + if (((property->flags & G_PARAM_READWRITE) != G_PARAM_READWRITE) || + (property->flags & G_PARAM_CONSTRUCT_ONLY)) + return TRUE; + /* FIXME: skip GST_PARAM_NOT_PRESETABLE, see #522205 */ + return FALSE; +} + +/* caller must free @preset_version after use */ +static GKeyFile * +preset_open_and_parse_header (GstPreset * preset, const gchar * preset_path, + gchar ** preset_version) +{ + GKeyFile *in; + GError *error = NULL; + gboolean res; + const gchar *element_name; + gchar *name; + + in = g_key_file_new (); + + res = g_key_file_load_from_file (in, preset_path, + G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, &error); + if (!res || error != NULL) + goto load_error; + + /* element type name and preset name must match or we are dealing with a wrong + * preset file */ + element_name = G_OBJECT_TYPE_NAME (preset); + name = + g_key_file_get_value (in, PRESET_HEADER, PRESET_HEADER_ELEMENT_NAME, + NULL); + + if (!name || strcmp (name, element_name)) + goto wrong_name; + + g_free (name); + + /* get the version now so that the caller can check it */ + if (preset_version) + *preset_version = + g_key_file_get_value (in, PRESET_HEADER, PRESET_HEADER_VERSION, NULL); + + return in; + + /* ERRORS */ +load_error: + { + GST_WARNING_OBJECT (preset, "Unable to read preset file %s: %s", + preset_path, error->message); + g_error_free (error); + g_key_file_free (in); + return NULL; + } +wrong_name: + { + GST_WARNING_OBJECT (preset, + "Wrong element name in preset file %s. Expected %s, got %s", + preset_path, element_name, GST_STR_NULL (name)); + g_free (name); + g_key_file_free (in); + return NULL; + } +} + +static guint64 +preset_parse_version (const gchar * str_version) +{ + gint major, minor, micro, nano, num; + + major = minor = micro = nano = 0; + + /* parse version (e.g. 0.10.15.1) to guint64 */ + num = sscanf (str_version, "%d.%d.%d.%d", &major, &minor, µ, &nano); + /* make sure we have atleast "major.minor" */ + if (num > 1) { + guint64 version; + + version = ((((major << 8 | minor) << 8) | micro) << 8) | nano; + GST_DEBUG ("version %s -> %" G_GUINT64_FORMAT, str_version, version); + return version; + } + return G_GUINT64_CONSTANT (0); +} + +static void +preset_merge (GKeyFile * system, GKeyFile * user) +{ + gchar *str; + gchar **groups, **keys; + gsize i, j, num_groups, num_keys; + + /* copy file comment if there is any */ + if ((str = g_key_file_get_comment (user, NULL, NULL, NULL))) { + g_key_file_set_comment (system, NULL, NULL, str, NULL); + g_free (str); + } + + /* get groups in user and copy into system */ + groups = g_key_file_get_groups (user, &num_groups); + for (i = 0; i < num_groups; i++) { + /* copy group comment if there is any */ + if ((str = g_key_file_get_comment (user, groups[i], NULL, NULL))) { + g_key_file_set_comment (system, groups[i], NULL, str, NULL); + g_free (str); + } + + /* ignore private groups */ + if (groups[i][0] == '_') + continue; + + /* if group already exists in system, remove and re-add keys from user */ + if (g_key_file_has_group (system, groups[i])) { + g_key_file_remove_group (system, groups[i], NULL); + } + + keys = g_key_file_get_keys (user, groups[i], &num_keys, NULL); + for (j = 0; j < num_keys; j++) { + /* copy key comment if there is any */ + if ((str = g_key_file_get_comment (user, groups[i], keys[j], NULL))) { + g_key_file_set_comment (system, groups[i], keys[j], str, NULL); + g_free (str); + } + str = g_key_file_get_value (user, groups[i], keys[j], NULL); + g_key_file_set_value (system, groups[i], keys[j], str); + g_free (str); + } + g_strfreev (keys); + } + g_strfreev (groups); +} + +/* reads the user and system presets files and merges them together. This + * function caches the GKeyFile on the element type. If there is no existing + * preset file, a new in-memory GKeyFile will be created. */ +static GKeyFile * +preset_get_keyfile (GstPreset * preset) +{ + GKeyFile *presets; + GType type = G_TYPE_FROM_INSTANCE (preset); + + /* first see if the have a cached version for the type */ + if (!(presets = g_type_get_qdata (type, preset_quark))) { + const gchar *preset_user_path, *preset_system_path; + gchar *str_version_user = NULL, *str_version_system = NULL; + gboolean updated_from_system = FALSE; + GKeyFile *in_user, *in_system; + + preset_get_paths (preset, &preset_user_path, &preset_system_path); + + /* try to load the user and system presets, we do this to get the versions + * of both files. */ + in_user = preset_open_and_parse_header (preset, preset_user_path, + &str_version_user); + in_system = preset_open_and_parse_header (preset, preset_system_path, + &str_version_system); + + /* compare version to check for merge */ + if (in_system) { + if (!in_user || preset_parse_version (str_version_system) > + preset_parse_version (str_version_user)) { + /* keep system presets if there is no user preset or when the system + * version is higher than the user version. */ + presets = in_system; + updated_from_system = TRUE; + } + } + if (in_user) { + if (updated_from_system) { + /* merge user on top of system presets */ + preset_merge (presets, in_user); + g_key_file_free (in_user); + } else { + /* keep user presets */ + presets = in_user; + } + } + if (!in_user && !in_system) { + /* we did not load a user or system presets file, create a new one */ + presets = g_key_file_new (); + g_key_file_set_string (presets, PRESET_HEADER, PRESET_HEADER_ELEMENT_NAME, + G_OBJECT_TYPE_NAME (preset)); + } + + g_free (str_version_user); + g_free (str_version_system); + + /* attach the preset to the type */ + g_type_set_qdata (type, preset_quark, (gpointer) presets); + + if (updated_from_system) { + gst_preset_default_save_presets_file (preset); + } + } + return presets; +} + +/* get a list of all supported preset names for an element */ +static gchar ** +gst_preset_default_get_preset_names (GstPreset * preset) +{ + GKeyFile *presets; + gsize i, num_groups; + gchar **groups; + + /* get the presets from the type */ + if (!(presets = preset_get_keyfile (preset))) + goto no_presets; + + /* get the groups, which are also the preset names */ + if (!(groups = g_key_file_get_groups (presets, &num_groups))) + goto no_groups; + + /* remove all private group names starting with '_' from the array */ + for (i = 0; i < num_groups; i++) { + if (groups[i][0] == '_') { + /* free private group */ + g_free (groups[i]); + /* move last element of list down */ + num_groups--; + /* move last element into removed element */ + groups[i] = groups[num_groups]; + groups[num_groups] = NULL; + } + } + /* sort the array now */ + g_qsort_with_data (groups, num_groups, sizeof (gchar *), + (GCompareDataFunc) strcmp, NULL); + + return groups; + + /* ERRORS */ +no_presets: + { + GST_WARNING_OBJECT (preset, "Could not load presets"); + return NULL; + } +no_groups: + { + GST_WARNING_OBJECT (preset, "Could not find preset groups"); + return NULL; + } +} + +/* get a list of all property names that are used for presets */ +static gchar ** +gst_preset_default_get_property_names (GstPreset * preset) +{ + GParamSpec **props; + guint i, j, n_props; + GObjectClass *gclass; + gchar **result; + + gclass = G_OBJECT_CLASS (GST_ELEMENT_GET_CLASS (preset)); + + /* get a list of normal properties. + * FIXME, change this for childproxy support. */ + props = g_object_class_list_properties (gclass, &n_props); + if (!props) + goto no_properties; + + /* allocate array big enough to hold the worst case, including a terminating + * NULL pointer. */ + result = g_new (gchar *, n_props + 1); + + /* now filter out the properties that we can use for presets */ + GST_DEBUG_OBJECT (preset, " filtering properties: %u", n_props); + for (i = j = 0; i < n_props; i++) { + if (preset_skip_property (props[i])) + continue; + + /* copy and increment out pointer */ + result[j++] = g_strdup (props[i]->name); + } + result[j] = NULL; + g_free (props); + + return result; + + /* ERRORS */ +no_properties: + { + GST_INFO_OBJECT (preset, "object has no properties"); + return NULL; + } +} + +/* load the presets of @name for the instance @preset. Returns %FALSE if something + * failed. */ +static gboolean +gst_preset_default_load_preset (GstPreset * preset, const gchar * name) +{ + GKeyFile *presets; + gchar **props; + guint i; + GObjectClass *gclass; + + /* get the presets from the type */ + if (!(presets = preset_get_keyfile (preset))) + goto no_presets; + + /* get the preset name */ + if (!g_key_file_has_group (presets, name)) + goto no_group; + + GST_DEBUG_OBJECT (preset, "loading preset : '%s'", name); + + /* get the properties that we can configure in this element */ + if (!(props = gst_preset_get_property_names (preset))) + goto no_properties; + + gclass = G_OBJECT_CLASS (GST_ELEMENT_GET_CLASS (preset)); + + /* for each of the property names, find the preset parameter and try to + * configure the property with its value */ + for (i = 0; props[i]; i++) { + gchar *str; + GValue gvalue = { 0, }; + GParamSpec *property; + + /* check if we have a settings for this element property */ + if (!(str = g_key_file_get_value (presets, name, props[i], NULL))) { + /* the element has a property but the parameter is not in the keyfile */ + GST_WARNING_OBJECT (preset, "parameter '%s' not in preset", props[i]); + continue; + } + + GST_DEBUG_OBJECT (preset, "setting value '%s' for property '%s'", str, + props[i]); + + /* FIXME, change for childproxy to get the property and element. */ + if (!(property = g_object_class_find_property (gclass, props[i]))) { + /* the parameter was in the keyfile, the element said it supported it but + * then the property was not found in the element. This should not happen. */ + GST_WARNING_OBJECT (preset, "property '%s' not in object", props[i]); + g_free (str); + continue; + } + + /* try to deserialize the property value from the keyfile and set it as + * the object property */ + g_value_init (&gvalue, property->value_type); + if (gst_value_deserialize (&gvalue, str)) { + /* FIXME, change for childproxy support */ + g_object_set_property (G_OBJECT (preset), props[i], &gvalue); + } else { + GST_WARNING_OBJECT (preset, + "deserialization of value '%s' for property '%s' failed", str, + props[i]); + } + g_value_unset (&gvalue); + g_free (str); + } + g_strfreev (props); + + return TRUE; + + /* ERRORS */ +no_presets: + { + GST_WARNING_OBJECT (preset, "no presets"); + return FALSE; + } +no_group: + { + GST_WARNING_OBJECT (preset, "no preset named '%s'", name); + return FALSE; + } +no_properties: + { + GST_INFO_OBJECT (preset, "no properties"); + return FALSE; + } +} + +/* save the presets file. A copy of the existing presets file is stored in a + * .bak file */ +static gboolean +gst_preset_default_save_presets_file (GstPreset * preset) +{ + GKeyFile *presets; + const gchar *preset_path; + GError *error = NULL; + gchar *bak_file_name; + gboolean backup = TRUE; + gchar *data; + gsize data_size; + + preset_get_paths (preset, &preset_path, NULL); + + /* get the presets from the type */ + if (!(presets = preset_get_keyfile (preset))) + goto no_presets; + + GST_DEBUG_OBJECT (preset, "saving preset file: '%s'", preset_path); + + /* create backup if possible */ + bak_file_name = g_strdup_printf ("%s.bak", preset_path); + if (g_file_test (bak_file_name, G_FILE_TEST_EXISTS)) { + if (g_unlink (bak_file_name)) { + backup = FALSE; + GST_INFO_OBJECT (preset, "cannot remove old backup file : %s", + bak_file_name); + } + } + if (backup) { + if (g_rename (preset_path, bak_file_name)) { + GST_INFO_OBJECT (preset, "cannot backup file : %s -> %s", preset_path, + bak_file_name); + } + } + g_free (bak_file_name); + + /* update gstreamer version */ + g_key_file_set_string (presets, PRESET_HEADER, PRESET_HEADER_VERSION, + PACKAGE_VERSION); + + /* get new contents, wee need this to save it */ + if (!(data = g_key_file_to_data (presets, &data_size, &error))) + goto convert_failed; + + /* write presets */ + if (!g_file_set_contents (preset_path, data, data_size, &error)) + goto write_failed; + + g_free (data); + + return TRUE; + + /* ERRORS */ +no_presets: + { + GST_WARNING_OBJECT (preset, + "no presets, trying to unlink possibly existing preset file: '%s'", + preset_path); + unlink (preset_path); + return FALSE; + } +convert_failed: + { + GST_WARNING_OBJECT (preset, "can not get the keyfile contents: %s", + error->message); + g_error_free (error); + g_free (data); + return FALSE; + } +write_failed: + { + GST_WARNING_OBJECT (preset, "Unable to store preset file %s: %s", + preset_path, error->message); + g_error_free (error); + g_free (data); + return FALSE; + } +} + +/* save the preset with the given name */ +static gboolean +gst_preset_default_save_preset (GstPreset * preset, const gchar * name) +{ + GKeyFile *presets; + gchar **props; + guint i; + GObjectClass *gclass; + + GST_INFO_OBJECT (preset, "saving new preset: %s", name); + + /* get the presets from the type */ + if (!(presets = preset_get_keyfile (preset))) + goto no_presets; + + /* take copies of current gobject properties from preset */ + if (!(props = gst_preset_get_property_names (preset))) + goto no_properties; + + gclass = G_OBJECT_CLASS (GST_ELEMENT_GET_CLASS (preset)); + + /* loop over the object properties and store the property value in the + * keyfile */ + for (i = 0; props[i]; i++) { + GValue gvalue = { 0, }; + gchar *str; + GParamSpec *property; + + /* FIXME, change for childproxy to get the property and element. */ + if (!(property = g_object_class_find_property (gclass, props[i]))) { + /* the element said it supported the property but then it does not have + * that property. This should not happen. */ + GST_WARNING_OBJECT (preset, "property '%s' not in object", props[i]); + continue; + } + + g_value_init (&gvalue, property->value_type); + /* FIXME, change for childproxy */ + g_object_get_property (G_OBJECT (preset), props[i], &gvalue); + + if ((str = gst_value_serialize (&gvalue))) { + g_key_file_set_string (presets, name, props[i], (gpointer) str); + g_free (str); + } else { + GST_WARNING_OBJECT (preset, "serialization for property '%s' failed", + props[i]); + } + g_value_unset (&gvalue); + } + GST_INFO_OBJECT (preset, " saved"); + g_strfreev (props); + + /* save updated version */ + return gst_preset_default_save_presets_file (preset); + + /* ERRORS */ +no_presets: + { + GST_WARNING_OBJECT (preset, "no presets"); + return FALSE; + } +no_properties: + { + GST_INFO_OBJECT (preset, "no properties"); + return FALSE; + } +} + +/* copies all keys and comments from one group to another, deleting the old + * group. */ +static gboolean +gst_preset_default_rename_preset (GstPreset * preset, const gchar * old_name, + const gchar * new_name) +{ + GKeyFile *presets; + gchar *str; + gchar **keys; + gsize i, num_keys; + + /* get the presets from the type */ + if (!(presets = preset_get_keyfile (preset))) + goto no_presets; + + if (!g_key_file_has_group (presets, old_name)) + goto no_group; + + /* copy group comment if there is any */ + if ((str = g_key_file_get_comment (presets, old_name, NULL, NULL))) { + g_key_file_set_comment (presets, new_name, NULL, str, NULL); + g_free (str); + } + + /* get all keys from the old group and copy them in the new group */ + keys = g_key_file_get_keys (presets, old_name, &num_keys, NULL); + for (i = 0; i < num_keys; i++) { + /* copy key comment if there is any */ + if ((str = g_key_file_get_comment (presets, old_name, keys[i], NULL))) { + g_key_file_set_comment (presets, new_name, keys[i], str, NULL); + g_free (str); + } + /* copy key value */ + str = g_key_file_get_value (presets, old_name, keys[i], NULL); + g_key_file_set_value (presets, new_name, keys[i], str); + g_free (str); + } + g_strfreev (keys); + + /* remove old group */ + g_key_file_remove_group (presets, old_name, NULL); + + /* save updated version */ + return gst_preset_default_save_presets_file (preset); + + /* ERRORS */ +no_presets: + { + GST_WARNING_OBJECT (preset, "no presets"); + return FALSE; + } +no_group: + { + GST_WARNING_OBJECT (preset, "no preset named %s", old_name); + return FALSE; + } +} + +/* delete a group from the keyfile */ +static gboolean +gst_preset_default_delete_preset (GstPreset * preset, const gchar * name) +{ + GKeyFile *presets; + + /* get the presets from the type */ + if (!(presets = preset_get_keyfile (preset))) + goto no_presets; + + /* get the group */ + if (!g_key_file_has_group (presets, name)) + goto no_group; + + /* remove the group */ + g_key_file_remove_group (presets, name, NULL); + + /* save updated version */ + return gst_preset_default_save_presets_file (preset); + + /* ERRORS */ +no_presets: + { + GST_WARNING_OBJECT (preset, "no presets"); + return FALSE; + } +no_group: + { + GST_WARNING_OBJECT (preset, "no preset named %s", name); + return FALSE; + } +} + +static gboolean +gst_preset_default_set_meta (GstPreset * preset, const gchar * name, + const gchar * tag, const gchar * value) +{ + GKeyFile *presets; + gchar *key; + + /* get the presets from the type */ + if (!(presets = preset_get_keyfile (preset))) + goto no_presets; + + key = g_strdup_printf ("_meta/%s", tag); + if (value && *value) { + g_key_file_set_value (presets, name, key, value); + } else { + g_key_file_remove_key (presets, name, key, NULL); + } + g_free (key); + + /* save updated keyfile */ + return gst_preset_default_save_presets_file (preset); + + /* ERRORS */ +no_presets: + { + GST_WARNING_OBJECT (preset, "no presets"); + return FALSE; + } +} + +/* the caller must free @value after usage */ +static gboolean +gst_preset_default_get_meta (GstPreset * preset, const gchar * name, + const gchar * tag, gchar ** value) +{ + GKeyFile *presets; + gchar *key; + + /* get the presets from the type */ + if (!(presets = preset_get_keyfile (preset))) + goto no_presets; + + key = g_strdup_printf ("_meta/%s", tag); + *value = g_key_file_get_value (presets, name, key, NULL); + g_free (key); + + return TRUE; + + /* ERRORS */ +no_presets: + { + GST_WARNING_OBJECT (preset, "no presets"); + *value = NULL; + return FALSE; + } +} + +#if 0 +static void +gst_preset_default_randomize (GstPreset * preset) +{ + GList *properties; + GType base, parent; + + if ((properties = gst_preset_get_property_names (preset))) { + GParamSpec *property; + GList *node; + gdouble rnd; + + for (node = properties; node; node = g_list_next (node)) { + property = g_object_class_find_property (G_OBJECT_CLASS + (GST_ELEMENT_GET_CLASS (preset)), node->data); + + rnd = ((gdouble) rand ()) / (RAND_MAX + 1.0); + + /* get base type */ + base = property->value_type; + while ((parent = g_type_parent (base))) + base = parent; + GST_INFO ("set random value for property: %s (type is %s)", + property->name, g_type_name (base)); + + switch (base) { + case G_TYPE_BOOLEAN:{ + g_object_set (preset, property->name, (gboolean) (2.0 * rnd), NULL); + } + break; + case G_TYPE_INT:{ + const GParamSpecInt *int_property = G_PARAM_SPEC_INT (property); + + g_object_set (preset, property->name, + (gint) (int_property->minimum + ((int_property->maximum - + int_property->minimum) * rnd)), NULL); + } break; + case G_TYPE_UINT:{ + const GParamSpecUInt *uint_property = G_PARAM_SPEC_UINT (property); + + g_object_set (preset, property->name, + (guint) (uint_property->minimum + ((uint_property->maximum - + uint_property->minimum) * rnd)), NULL); + } break; + case G_TYPE_DOUBLE:{ + const GParamSpecDouble *double_property = + G_PARAM_SPEC_DOUBLE (property); + + g_object_set (preset, property->name, + (gdouble) (double_property->minimum + ((double_property->maximum - + double_property->minimum) * rnd)), NULL); + } break; + case G_TYPE_ENUM:{ + const GParamSpecEnum *enum_property = G_PARAM_SPEC_ENUM (property); + const GEnumClass *enum_class = enum_property->enum_class; + + g_object_set (preset, property->name, + (gulong) (enum_class->minimum + ((enum_class->maximum - + enum_class->minimum) * rnd)), NULL); + } break; + default: + GST_WARNING ("incomplete implementation for GParamSpec type '%s'", + G_PARAM_SPEC_TYPE_NAME (property)); + } + } + /* FIXME: handle childproxy properties as well */ + } +} + +static void +gst_preset_default_reset (GstPreset * preset) +{ + GList *properties; + + if ((properties = gst_preset_get_property_names (preset))) { + GParamSpec *property; + GList *node; + GValue gvalue = { 0, }; + + for (node = properties; node; node = g_list_next (node)) { + property = g_object_class_find_property (G_OBJECT_CLASS + (GST_ELEMENT_GET_CLASS (preset)), node->data); + + g_value_init (&gvalue, property->value_type); + g_param_value_set_default (property, &gvalue); + g_object_set_property (G_OBJECT (preset), property->name, &gvalue); + g_value_unset (&gvalue); + } + /* FIXME: handle childproxy properties as well */ + } +} +#endif + +/* wrapper */ + +/** + * gst_preset_get_preset_names: + * @preset: a #GObject that implements #GstPreset + * + * Get a copy of preset names as a NULL terminated string array. + * + * Returns: list with names, ue g_strfreev() after usage. + */ +gchar ** +gst_preset_get_preset_names (GstPreset * preset) +{ + g_return_val_if_fail (GST_IS_PRESET (preset), NULL); + + return (GST_PRESET_GET_INTERFACE (preset)->get_preset_names (preset)); +} + +/** + * gst_preset_get_property_names: + * @preset: a #GObject that implements #GstPreset + * + * Get a the names of the GObject properties that can be used for presets. + * + * Returns: an array of property names which should be freed with g_strfreev() after use. + */ +gchar ** +gst_preset_get_property_names (GstPreset * preset) +{ + g_return_val_if_fail (GST_IS_PRESET (preset), NULL); + + return (GST_PRESET_GET_INTERFACE (preset)->get_property_names (preset)); +} + +/** + * gst_preset_load_preset: + * @preset: a #GObject that implements #GstPreset + * @name: preset name to load + * + * Load the given preset. + * + * Returns: %TRUE for success, %FALSE if e.g. there is no preset with that @name + */ +gboolean +gst_preset_load_preset (GstPreset * preset, const gchar * name) +{ + g_return_val_if_fail (GST_IS_PRESET (preset), FALSE); + g_return_val_if_fail (name, FALSE); + + return (GST_PRESET_GET_INTERFACE (preset)->load_preset (preset, name)); +} + +/** + * gst_preset_save_preset: + * @preset: a #GObject that implements #GstPreset + * @name: preset name to save + * + * Save the current preset under the given name. If there is already a preset by + * this @name it will be overwritten. + * + * Returns: %TRUE for success, %FALSE + */ +gboolean +gst_preset_save_preset (GstPreset * preset, const gchar * name) +{ + g_return_val_if_fail (GST_IS_PRESET (preset), FALSE); + g_return_val_if_fail (name, FALSE); + + return (GST_PRESET_GET_INTERFACE (preset)->save_preset (preset, name)); +} + +/** + * gst_preset_rename_preset: + * @preset: a #GObject that implements #GstPreset + * @old_name: current preset name + * @new_name: new preset name + * + * Renames a preset. If there is already a preset by the @new_name it will be + * overwritten. + * + * Returns: %TRUE for success, %FALSE if e.g. there is no preset with @old_name + */ +gboolean +gst_preset_rename_preset (GstPreset * preset, const gchar * old_name, + const gchar * new_name) +{ + g_return_val_if_fail (GST_IS_PRESET (preset), FALSE); + g_return_val_if_fail (old_name, FALSE); + g_return_val_if_fail (new_name, FALSE); + + return (GST_PRESET_GET_INTERFACE (preset)->rename_preset (preset, old_name, + new_name)); +} + +/** + * gst_preset_delete_preset: + * @preset: a #GObject that implements #GstPreset + * @name: preset name to remove + * + * Delete the given preset. + * + * Returns: %TRUE for success, %FALSE if e.g. there is no preset with that @name + */ +gboolean +gst_preset_delete_preset (GstPreset * preset, const gchar * name) +{ + g_return_val_if_fail (GST_IS_PRESET (preset), FALSE); + g_return_val_if_fail (name, FALSE); + + return (GST_PRESET_GET_INTERFACE (preset)->delete_preset (preset, name)); +} + +/** + * gst_preset_set_meta: + * @preset: a #GObject that implements #GstPreset + * @name: preset name + * @tag: meta data item name + * @value: new value + * + * Sets a new @value for an existing meta data item or adds a new item. Meta + * data @tag names can be something like e.g. "comment". Supplying %NULL for the + * @value will unset an existing value. + * + * Returns: %TRUE for success, %FALSE if e.g. there is no preset with that @name + */ +gboolean +gst_preset_set_meta (GstPreset * preset, const gchar * name, const gchar * tag, + const gchar * value) +{ + g_return_val_if_fail (GST_IS_PRESET (preset), FALSE); + g_return_val_if_fail (name, FALSE); + g_return_val_if_fail (tag, FALSE); + + return GST_PRESET_GET_INTERFACE (preset)->set_meta (preset, name, tag, value); +} + +/** + * gst_preset_get_meta: + * @preset: a #GObject that implements #GstPreset + * @name: preset name + * @tag: meta data item name + * @value: value + * + * Gets the @value for an existing meta data @tag. Meta data @tag names can be + * something like e.g. "comment". Returned values need to be released when done. + * + * Returns: %TRUE for success, %FALSE if e.g. there is no preset with that @name + * or no value for the given @tag + */ +gboolean +gst_preset_get_meta (GstPreset * preset, const gchar * name, const gchar * tag, + gchar ** value) +{ + g_return_val_if_fail (GST_IS_PRESET (preset), FALSE); + g_return_val_if_fail (name, FALSE); + g_return_val_if_fail (tag, FALSE); + g_return_val_if_fail (value, FALSE); + + return GST_PRESET_GET_INTERFACE (preset)->get_meta (preset, name, tag, value); +} + +/* class internals */ + +static void +gst_preset_class_init (GstPresetInterface * iface) +{ + iface->get_preset_names = gst_preset_default_get_preset_names; + iface->get_property_names = gst_preset_default_get_property_names; + + iface->load_preset = gst_preset_default_load_preset; + iface->save_preset = gst_preset_default_save_preset; + iface->rename_preset = gst_preset_default_rename_preset; + iface->delete_preset = gst_preset_default_delete_preset; + + iface->set_meta = gst_preset_default_set_meta; + iface->get_meta = gst_preset_default_get_meta; +} + +static void +gst_preset_base_init (gpointer g_class) +{ + static gboolean initialized = FALSE; + + if (!initialized) { + /* init default implementation */ + GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "preset", + GST_DEBUG_FG_WHITE | GST_DEBUG_BG_BLACK, "preset interface"); + + /* create quarks for use with g_type_{g,s}et_qdata() */ + preset_quark = g_quark_from_static_string ("GstPreset::presets"); + preset_user_path_quark = + g_quark_from_static_string ("GstPreset::user_path"); + preset_system_path_quark = + g_quark_from_static_string ("GstPreset::system_path"); + +#if 0 + property_list_quark = g_quark_from_static_string ("GstPreset::properties"); + + /* create interface properties, each element would need to override this + * g_object_class_override_property(gobject_class, PROP_PRESET_NAME, "preset-name"); + * and in _set_property() do + * case PROP_PRESET_NAME: { + * gchar *name = g_value_get_string (value); + * if (name) + * gst_preset_load_preset(preset, name); + * } break; + */ + g_object_interface_install_property (g_class, + g_param_spec_string ("preset-name", + "preset-name property", + "load given preset", NULL, G_PARAM_WRITABLE)); +#endif + + initialized = TRUE; + } +} + +GType +gst_preset_get_type (void) +{ + static GType type = 0; + + if (type == 0) { + const GTypeInfo info = { + sizeof (GstPresetInterface), + (GBaseInitFunc) gst_preset_base_init, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) gst_preset_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + 0, + 0, /* n_preallocs */ + NULL /* instance_init */ + }; + type = g_type_register_static (G_TYPE_INTERFACE, "GstPreset", &info, 0); + } + return type; +} diff --git a/gst/gstpreset.h b/gst/gstpreset.h new file mode 100644 index 0000000000..f84677a5dc --- /dev/null +++ b/gst/gstpreset.h @@ -0,0 +1,80 @@ +/* GStreamer + * Copyright (C) 2006 Stefan Kost + * + * gstpreset.h: helper interface header for element presets + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_PRESET_H__ +#define __GST_PRESET_H__ + +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_PRESET (gst_preset_get_type()) +#define GST_PRESET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PRESET, GstPreset)) +#define GST_IS_PRESET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PRESET)) +#define GST_PRESET_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GST_TYPE_PRESET, GstPresetInterface)) + + +typedef struct _GstPreset GstPreset; /* dummy object */ +typedef struct _GstPresetInterface GstPresetInterface; + +struct _GstPresetInterface +{ + GTypeInterface parent; + + gchar** (*get_preset_names) (GstPreset *preset); + + gchar** (*get_property_names) (GstPreset *preset); + + gboolean (*load_preset) (GstPreset *preset, const gchar *name); + gboolean (*save_preset) (GstPreset *preset, const gchar *name); + gboolean (*rename_preset) (GstPreset *preset, const gchar *old_name, + const gchar *new_name); + gboolean (*delete_preset) (GstPreset *preset, const gchar *name); + + gboolean (*set_meta) (GstPreset *preset, const gchar *name, + const gchar *tag, const gchar *value); + gboolean (*get_meta) (GstPreset *preset, const gchar *name, + const gchar *tag, gchar **value); + /*< private >*/ + gpointer _gst_reserved[GST_PADDING]; +}; + +GType gst_preset_get_type(void); + +gchar** gst_preset_get_preset_names (GstPreset *preset); + +gchar** gst_preset_get_property_names (GstPreset *preset); + +gboolean gst_preset_load_preset (GstPreset *preset, const gchar *name); +gboolean gst_preset_save_preset (GstPreset *preset, const gchar *name); +gboolean gst_preset_rename_preset (GstPreset *preset, const gchar *old_name, + const gchar *new_name); +gboolean gst_preset_delete_preset (GstPreset *preset, const gchar *name); + +gboolean gst_preset_set_meta (GstPreset *preset, const gchar *name, + const gchar *tag, const gchar *value); +gboolean gst_preset_get_meta (GstPreset *preset, const gchar *name, + const gchar *tag, gchar **value); + +G_END_DECLS + +#endif /* __GST_PRESET_H__ */ diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am index 878ceb3efb..20ddedab3a 100644 --- a/tests/check/Makefile.am +++ b/tests/check/Makefile.am @@ -52,6 +52,7 @@ REGISTRY_CHECKS = \ gst/gstindex \ gst/gstinterface \ gst/gstplugin \ + gst/gstpreset \ gst/gstquery \ gst/gstregistry \ gst/gsturi \ diff --git a/tests/check/gst/gstpreset.c b/tests/check/gst/gstpreset.c new file mode 100644 index 0000000000..120aeffc0b --- /dev/null +++ b/tests/check/gst/gstpreset.c @@ -0,0 +1,293 @@ +/* GStreamer + * Copyright (C) 2008 Nokia Corporation and its subsidary(-ies) + * contact: + * + * gstpreset.c: Unit test for GstPreset + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +static GType gst_preset_test_get_type (void); + +#define GST_TYPE_PRESET_TEST (gst_preset_test_get_type ()) +#define GST_PRESET_TEST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PRESET_TEST, GstPresetTest)) +#define GST_PRESET_TEST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_PRESET_TEST, GstPresetTestClass)) +#define GST_IS_PRESET_TEST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PRESET_TEST)) +#define GST_IS_PRESET_TEST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PRESET_TEST)) +#define GST_PRESET_TEST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_PRESET_TEST, GstPresetTestClass)) +#define GST_PRESET_TEST_NAME "preset-test" + +enum +{ + PROP_TEST = 1, +}; + +typedef struct _GstPresetTest +{ + GstElement parent; + + gint test; +} GstPresetTest; + +typedef struct _GstPresetTestClass +{ + GstElementClass parent_class; +} GstPresetTestClass; + +static void +gst_preset_test_get_property (GObject * object, guint property_id, + GValue * value, GParamSpec * pspec) +{ + GstPresetTest *self = GST_PRESET_TEST (object); + + switch (property_id) { + case PROP_TEST: + g_value_set_int (value, self->test); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gst_preset_test_set_property (GObject * object, guint property_id, + const GValue * value, GParamSpec * pspec) +{ + GstPresetTest *self = GST_PRESET_TEST (object); + + switch (property_id) { + case PROP_TEST: + self->test = g_value_get_int (value); + break; + } +} + +static void +gst_preset_test_class_init (GObjectClass * klass) +{ + klass->set_property = gst_preset_test_set_property; + klass->get_property = gst_preset_test_get_property; + + g_object_class_install_property (klass, PROP_TEST, + g_param_spec_int ("test", + "test prop", + "test parameter for preset test", + G_MININT, G_MAXINT, 0, G_PARAM_READWRITE)); +} + +static void +gst_preset_test_base_init (GstPresetTestClass * klass) +{ + static const GstElementDetails details = { + "Element for unit tests", + "Testing", + "Use in unit tests", + "Stefan Kost " + }; + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + gst_element_class_set_details (element_class, &details); +} + +static GType +gst_preset_test_get_type (void) +{ + static GType type = 0; + + if (type == 0) { + const GTypeInfo info = { + sizeof (GstPresetTestClass), + (GBaseInitFunc) gst_preset_test_base_init, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) gst_preset_test_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GstPresetTest), + 0, /* n_preallocs */ + NULL, /* instance_init */ + NULL /* value_table */ + }; + const GInterfaceInfo preset_interface_info = { + NULL, /* interface_init */ + NULL, /* interface_finalize */ + NULL /* interface_data */ + }; + type = g_type_register_static (GST_TYPE_ELEMENT, "GstPresetTest", &info, 0); + g_type_add_interface_static (type, GST_TYPE_PRESET, &preset_interface_info); + } + return type; +} + +static gboolean +gst_preset_test_plugin_init (GstPlugin * plugin) +{ + gst_element_register (plugin, GST_PRESET_TEST_NAME, GST_RANK_NONE, + GST_TYPE_PRESET_TEST); + return TRUE; +} + + +GST_START_TEST (test_check) +{ + GstElement *elem; + + elem = gst_element_factory_make (GST_PRESET_TEST_NAME, NULL); + fail_unless (GST_IS_PRESET (elem)); + + gst_object_unref (elem); +} + +GST_END_TEST; + +GST_START_TEST (test_load) +{ + GstElement *elem; + gboolean res; + + elem = gst_element_factory_make (GST_PRESET_TEST_NAME, NULL); + res = gst_preset_load_preset (GST_PRESET (elem), "does-not-exist"); + fail_unless (!res); + + gst_object_unref (elem); +} + +GST_END_TEST; + +GST_START_TEST (test_add) +{ + GstElement *elem; + gboolean res; + gint val; + + elem = gst_element_factory_make (GST_PRESET_TEST_NAME, NULL); + g_object_set (elem, "test", 5, NULL); + + res = gst_preset_save_preset (GST_PRESET (elem), "test"); + fail_unless (res); + + res = gst_preset_load_preset (GST_PRESET (elem), "test"); + fail_unless (res); + g_object_get (elem, "test", &val, NULL); + fail_unless (val == 5); + + gst_object_unref (elem); +} + +GST_END_TEST; + + +GST_START_TEST (test_del) +{ + GstElement *elem; + gboolean res; + + elem = gst_element_factory_make (GST_PRESET_TEST_NAME, NULL); + res = gst_preset_save_preset (GST_PRESET (elem), "test"); + fail_unless (res); + + res = gst_preset_delete_preset (GST_PRESET (elem), "test"); + fail_unless (res); + + res = gst_preset_load_preset (GST_PRESET (elem), "test"); + fail_unless (!res); + + gst_object_unref (elem); +} + +GST_END_TEST; + +GST_START_TEST (test_two_instances) +{ + GstElement *elem1, *elem2; + gboolean res; + gint val; + + elem1 = gst_element_factory_make (GST_PRESET_TEST_NAME, NULL); + g_object_set (elem1, "test", 5, NULL); + + res = gst_preset_save_preset (GST_PRESET (elem1), "test"); + fail_unless (res); + + elem2 = gst_element_factory_make (GST_PRESET_TEST_NAME, NULL); + res = gst_preset_load_preset (GST_PRESET (elem2), "test"); + fail_unless (res); + g_object_get (elem2, "test", &val, NULL); + fail_unless (val == 5); + + gst_object_unref (elem1); + gst_object_unref (elem2); +} + +GST_END_TEST; + + +static void +remove_preset_file (void) +{ + gchar *preset_file_name; + + preset_file_name = g_build_filename (g_get_home_dir (), + ".gstreamer-" GST_MAJORMINOR, "presets", "GstPresetTest.prs", NULL); + g_unlink (preset_file_name); + g_free (preset_file_name); +} + +static void +test_setup (void) +{ + remove_preset_file (); + gst_plugin_register_static (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "gst-test", + "preset test plugin", + gst_preset_test_plugin_init, + VERSION, GST_LICENSE, PACKAGE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN); +} + +static void +test_teardown (void) +{ + remove_preset_file (); +} + + +static Suite * +gst_preset_suite (void) +{ + Suite *s = suite_create ("GstPreset"); + TCase *tc = tcase_create ("preset"); + + suite_add_tcase (s, tc); + tcase_add_test (tc, test_check); + tcase_add_test (tc, test_load); + tcase_add_test (tc, test_add); + tcase_add_test (tc, test_del); + tcase_add_test (tc, test_two_instances); + tcase_add_unchecked_fixture (tc, test_setup, test_teardown); + + return s; +} + +GST_CHECK_MAIN (gst_preset);