*/
/**
* SECTION:gstpreset
+ * @title: GstPreset
* @short_description: helper interface for element presets
*
* This interface offers methods to query and manipulate parameter preset sets.
* 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.
+ * When implementing support for read-only presets, one should set the vmethods
+ * for gst_preset_save_preset() and gst_preset_delete_preset() to %NULL.
+ * Applications can use gst_preset_is_editable() to check for that.
*
- * The default implementation supports presets located in a system directory,
+ * The default implementation supports presets located in a system directory,
* application specific directory and in the users home directory. When getting
- * a list of presets individual presets are read and overlaid in 1) system,
+ * a list of presets individual presets are read and overlaid in 1) system,
* 2) application and 3) user order. Whenever an earlier entry is newer, the
- * later entries will be updated.
- *
+ * later entries will be updated. Since 1.8 you can also provide extra paths
+ * where to find presets through the GST_PRESET_PATH environment variable.
+ * Presets found in those paths will be considered as "app presets".
*/
/* FIXME:
* - non racyness
static GQuark preset_system_path_quark = 0;
static GQuark preset_quark = 0;
-/*static GQuark property_list_quark = 0;*/
-
/* the application can set a custom path that is checked in addition to standard
* system and user dirs. This helps to develop new presets first local to the
* application.
/*
* preset_get_paths:
* @preset: a #GObject that implements #GstPreset
- * @preset_user_path: location for path or %NULL
- * @preset_app_path: location for path or %NULL
- * @preset_system_path: location for path or %NULL
+ * @preset_user_path: (out) (allow-none): location for path or %NULL
+ * @preset_app_path: (out) (allow-none): location for path or %NULL
+ * @preset_system_path: (out) (allow-none): location for path or %NULL
*
* Fetch the preset_path for user local, application specific and system wide
* settings. Don't free after use.
/* ERRORS */
load_error:
{
- GST_WARNING_OBJECT (preset, "Unable to read preset file %s: %s",
+ GST_INFO_OBJECT (preset, "Unable to read preset file %s: %s",
preset_path, error->message);
g_error_free (error);
g_key_file_free (in);
g_strfreev (groups);
}
+typedef struct
+{
+ GKeyFile *preset;
+ guint64 version;
+} PresetAndVersion;
+
+static gint
+compare_preset_and_version (gconstpointer a, gconstpointer b,
+ gpointer user_data)
+{
+ const PresetAndVersion *pa = a, *pb = b;
+
+ if (pa->version > pb->version)
+ return -1;
+ if (pa->version < pb->version)
+ return 1;
+ else
+ return 0;
+}
+
/* 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. */
guint64 version = G_GUINT64_CONSTANT (0);
gboolean merged = FALSE;
GKeyFile *in_user, *in_app = NULL, *in_system;
-
- preset_get_paths (preset, &preset_user_path, &preset_app_path,
- &preset_system_path);
+ GQueue in_env = G_QUEUE_INIT;
+ gboolean have_env = FALSE;
+ const gchar *envvar;
/* try to load the user, app and system presets, we do this to get the
* versions of all files. */
+ preset_get_paths (preset, &preset_user_path, &preset_app_path,
+ &preset_system_path);
in_user = preset_open_and_parse_header (preset, preset_user_path,
&version_user);
+
if (preset_app_path) {
in_app = preset_open_and_parse_header (preset, preset_app_path,
&version_app);
}
+
+ envvar = g_getenv ("GST_PRESET_PATH");
+ if (envvar) {
+ gint i;
+ gchar **preset_dirs = g_strsplit (envvar, G_SEARCHPATH_SEPARATOR_S, -1);
+
+ for (i = 0; preset_dirs[i]; i++) {
+ gchar *preset_path = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "%s.prs",
+ preset_dirs[i], G_OBJECT_TYPE_NAME (preset));
+ GKeyFile *env_file;
+ guint64 env_version;
+
+ env_file = preset_open_and_parse_header (preset, preset_path,
+ &env_version);
+ g_free (preset_path);
+ if (env_file) {
+ PresetAndVersion *pv = g_new (PresetAndVersion, 1);
+ pv->preset = env_file;
+ pv->version = env_version;
+ g_queue_push_tail (&in_env, pv);
+ have_env = TRUE;
+ }
+ }
+ g_strfreev (preset_dirs);
+ }
+
in_system = preset_open_and_parse_header (preset, preset_system_path,
&version_system);
presets = in_system;
version = version_system;
}
+
+ if (have_env) {
+ GList *l;
+
+ /* merge the ones from the environment paths. If any of them has a
+ * higher version, take that as the "master" version. Lower versions are
+ * then just merged in. */
+ g_queue_sort (&in_env, compare_preset_and_version, NULL);
+ /* highest version to lowest */
+ for (l = in_env.head; l; l = l->next) {
+ PresetAndVersion *pv = l->data;
+
+ if (version > pv->version) {
+ preset_merge (presets, pv->preset);
+ g_key_file_free (pv->preset);
+ } else {
+ if (presets)
+ g_key_file_free (presets);
+ presets = pv->preset;
+ version = pv->version;
+ }
+ g_free (pv);
+ }
+ g_queue_clear (&in_env);
+ }
+
if (in_app) {
- /* if system version is higher, merge */
+ /* if system/env version is higher, merge */
if (version > version_app) {
preset_merge (presets, in_app);
g_key_file_free (in_app);
if (presets)
g_key_file_free (presets);
presets = in_app;
- version = version_system;
+ version = version_app;
}
}
if (in_user) {
- /* if system or app version is higher, merge */
+ /* if system/env or app version is higher, merge */
if (version > version_user) {
preset_merge (presets, in_user);
g_key_file_free (in_user);
return presets;
}
+static gint
+compare_strings (gchar ** a, gchar ** b, gpointer user_data)
+{
+ return g_strcmp0 (*a, *b);
+}
+
/* get a list of all supported preset names for an element */
static gchar **
gst_preset_default_get_preset_names (GstPreset * preset)
groups[num_groups] = NULL;
}
}
+ if (!num_groups) {
+ GST_INFO_OBJECT (preset, "Empty preset file");
+ g_strfreev (groups);
+ return NULL;
+ }
+
/* sort the array now */
g_qsort_with_data (groups, num_groups, sizeof (gchar *),
- (GCompareDataFunc) strcmp, NULL);
+ (GCompareDataFunc) compare_strings, NULL);
return groups;
/* 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]);
+ GST_INFO_OBJECT (preset, "parameter '%s' not in preset", props[i]);
continue;
}
continue;
}
+ if (property->flags & G_PARAM_DEPRECATED) {
+ GST_INFO_OBJECT (preset, "Not saving property %s as it is deprecated",
+ property->name);
+ continue;
+ }
+
g_value_init (&gvalue, property->value_type);
if (is_child_proxy) {
gst_child_proxy_get_property ((GstChildProxy *) preset, props[i],
* @preset: a #GObject that implements #GstPreset
* @name: preset name
* @tag: meta data item name
- * @value: new value
+ * @value: (allow-none): 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
/**
* gst_preset_set_app_dir:
- * @app_dir: the application specific preset dir
+ * @app_dir: (type filename): the application specific preset dir
*
* Sets an extra directory as an absolute path that should be considered when
- * looking for presets. Any presets in the application dir will shadow the
+ * looking for presets. Any presets in the application dir will shadow the
* system presets.
*
* Returns: %TRUE for success, %FALSE if the dir already has been set
* Gets the directory for application specific presets if set by the
* application.
*
- * Returns: the directory or %NULL, don't free or modify the string
+ * Returns: (nullable) (type filename): the directory or %NULL, don't free or modify
+ * the string
*/
const gchar *
gst_preset_get_app_dir (void)
return preset_app_dir;
}
+/**
+ * gst_preset_is_editable:
+ * @preset: a #GObject that implements #GstPreset
+ *
+ * Check if one can add new presets, change existing ones and remove presets.
+ *
+ * Returns: %TRUE if presets are editable or %FALSE if they are static
+ *
+ * Since: 1.6
+ */
+gboolean
+gst_preset_is_editable (GstPreset * preset)
+{
+ GstPresetInterface *iface = GST_PRESET_GET_INTERFACE (preset);
+
+ return iface->save_preset && iface->delete_preset;
+}
+
/* class internals */
static void
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