preset: allow applications to specify an extra preset dir

An extra application preset dir help to organize presets created for special
purposes. Fixes #660760
API: gst_preset_set_app_dir(), gst_preset_get_app_dir()
This commit is contained in:
Stefan Sauer 2011-12-07 16:01:07 +01:00
parent a6bb6d0fc3
commit f2b5b79eeb
4 changed files with 150 additions and 55 deletions

View file

@ -1999,6 +1999,8 @@ gst_preset_rename_preset
gst_preset_delete_preset
gst_preset_set_meta
gst_preset_get_meta
gst_preset_set_app_dir
gst_preset_get_app_dir
<SUBSECTION Standard>
GST_PRESET
GST_IS_PRESET

View file

@ -34,6 +34,13 @@
* 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.
*
* 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,
* 2) application and 3) user order. Whenever an earlier entry is newer, the
* later entries will be updated.
*
*/
/* FIXME:
* - non racyness
@ -106,11 +113,18 @@ GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
#define PRESET_HEADER_VERSION "version"
static GQuark preset_user_path_quark = 0;
static GQuark preset_app_path_quark = 0;
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 standart
* system and user dirs. This helps to develop new presets first local to the
* application.
*/
static gchar *preset_app_dir = NULL;
/* default iface implementation */
static gboolean gst_preset_default_save_presets_file (GstPreset * preset);
@ -119,16 +133,17 @@ 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_app_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.
* Fetch the preset_path for user local, application specific 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)
const gchar ** preset_app_path, const gchar ** preset_system_path)
{
GType type = G_TYPE_FROM_INSTANCE (preset);
gchar *preset_path;
@ -161,6 +176,22 @@ preset_get_paths (GstPreset * preset, const gchar ** preset_user_path,
*preset_user_path = preset_path;
}
if (preset_app_path) {
if (preset_app_dir) {
if (!(preset_path = g_type_get_qdata (type, preset_system_path_quark))) {
preset_path = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "%s.prs",
preset_app_dir, element_name);
GST_INFO_OBJECT (preset, "application_preset_path: '%s'", preset_path);
/* cache the preset path to the type */
g_type_set_qdata (type, preset_app_path_quark, preset_path);
}
*preset_app_path = preset_path;
} else {
*preset_app_path = NULL;
}
}
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))) {
@ -195,10 +226,30 @@ preset_skip_property (GParamSpec * property)
return FALSE;
}
/* caller must free @preset_version after use */
static guint64
preset_parse_version (const gchar * str_version)
{
guint major, minor, micro, nano;
gint num;
major = minor = micro = nano = 0;
/* parse version (e.g. 0.10.15.1) to guint64 */
num = sscanf (str_version, "%u.%u.%u.%u", &major, &minor, &micro, &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 GKeyFile *
preset_open_and_parse_header (GstPreset * preset, const gchar * preset_path,
gchar ** preset_version)
guint64 * preset_version)
{
GKeyFile *in;
GError *error = NULL;
@ -226,9 +277,12 @@ preset_open_and_parse_header (GstPreset * preset, const gchar * preset_path,
g_free (name);
/* get the version now so that the caller can check it */
if (preset_version)
*preset_version =
if (preset_version) {
gchar *str =
g_key_file_get_value (in, PRESET_HEADER, PRESET_HEADER_VERSION, NULL);
*preset_version = preset_parse_version (str);
g_free (str);
}
return in;
@ -252,27 +306,6 @@ wrong_name:
}
}
static guint64
preset_parse_version (const gchar * str_version)
{
guint major, minor, micro, nano;
gint num;
major = minor = micro = nano = 0;
/* parse version (e.g. 0.10.15.1) to guint64 */
num = sscanf (str_version, "%u.%u.%u.%u", &major, &minor, &micro, &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)
{
@ -331,56 +364,70 @@ preset_get_keyfile (GstPreset * 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;
const gchar *preset_user_path, *preset_app_path, *preset_system_path;
guint64 version_system = G_GUINT64_CONSTANT (0);
guint64 version_app = G_GUINT64_CONSTANT (0);
guint64 version_user = G_GUINT64_CONSTANT (0);
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_system_path);
preset_get_paths (preset, &preset_user_path, &preset_app_path,
&preset_system_path);
/* try to load the user and system presets, we do this to get the versions
* of both files. */
/* try to load the user, app and system presets, we do this to get the
* versions of all files. */
in_user = preset_open_and_parse_header (preset, preset_user_path,
&str_version_user);
&version_user);
if (preset_app_path) {
in_app = preset_open_and_parse_header (preset, preset_app_path,
&version_app);
}
in_system = preset_open_and_parse_header (preset, preset_system_path,
&str_version_system);
&version_system);
/* compare version to check for merge */
if (in_system) {
/* keep system presets if there is no user preset or when the system
* version is higher than the user version. */
if (!in_user) {
presets = in_system;
} else if (preset_parse_version (str_version_system) >
preset_parse_version (str_version_user)) {
presets = in_system;
updated_from_system = TRUE;
version = version_system;
}
if (in_app) {
/* if system version is higher, merge */
if (version > version_app) {
preset_merge (presets, in_app);
g_key_file_free (in_app);
} else {
if (presets)
g_key_file_free (presets);
presets = in_app;
version = version_system;
}
}
if (in_user) {
if (updated_from_system) {
/* merge user on top of system presets */
/* if system or app version is higher, merge */
if (version > version_user) {
preset_merge (presets, in_user);
g_key_file_free (in_user);
merged = TRUE;
} else {
/* keep user presets */
if (presets)
g_key_file_free (presets);
presets = in_user;
version = version_user;
}
}
if (!in_user && !in_system) {
/* we did not load a user or system presets file, create a new one */
if (!presets) {
/* we did not load a user, app 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) {
if (merged) {
gst_preset_default_save_presets_file (preset);
}
}
@ -578,7 +625,7 @@ gst_preset_default_save_presets_file (GstPreset * preset)
gchar *data;
gsize data_size;
preset_get_paths (preset, &preset_path, NULL);
preset_get_paths (preset, &preset_path, NULL, NULL);
/* get the presets from the type */
if (!(presets = preset_get_keyfile (preset)))
@ -1041,6 +1088,46 @@ gst_preset_get_meta (GstPreset * preset, const gchar * name, const gchar * tag,
return GST_PRESET_GET_INTERFACE (preset)->get_meta (preset, name, tag, value);
}
/**
* gst_preset_set_app_dir:
* @app_dir: 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
* system presets.
*
* Returns: %TRUE for success, %FALSE if the dir already has been set
*
* Since: 0.10.36
*/
gboolean
gst_preset_set_app_dir (const gchar * app_dir)
{
g_return_val_if_fail (app_dir, FALSE);
if (!preset_app_dir) {
preset_app_dir = g_strdup (app_dir);
return TRUE;
}
return FALSE;
}
/**
* gst_preset_get_app_dir:
*
* Gets the directory for application specific presets if set by the
* application.
*
* Returns: the directory or %NULL, don't free or modify the string
*
* Since: 0.10.36
*/
const gchar *
gst_preset_get_app_dir (void)
{
return preset_app_dir;
}
/* class internals */
static void
@ -1072,6 +1159,7 @@ gst_preset_base_init (gpointer g_class)
preset_quark = g_quark_from_static_string ("GstPreset::presets");
preset_user_path_quark =
g_quark_from_static_string ("GstPreset::user_path");
preset_app_path_quark = g_quark_from_static_string ("GstPreset::app_path");
preset_system_path_quark =
g_quark_from_static_string ("GstPreset::system_path");

View file

@ -93,6 +93,9 @@ gboolean gst_preset_set_meta (GstPreset *preset, const gchar *name
gboolean gst_preset_get_meta (GstPreset *preset, const gchar *name,
const gchar *tag, gchar **value);
gboolean gst_preset_set_app_dir (const gchar *app_dir);
const gchar *gst_preset_get_app_dir (void);
G_END_DECLS
#endif /* __GST_PRESET_H__ */

View file

@ -835,6 +835,7 @@ EXPORTS
gst_poll_wait
gst_poll_write_control
gst_preset_delete_preset
gst_preset_get_app_dir
gst_preset_get_meta
gst_preset_get_preset_names
gst_preset_get_property_names
@ -842,6 +843,7 @@ EXPORTS
gst_preset_load_preset
gst_preset_rename_preset
gst_preset_save_preset
gst_preset_set_app_dir
gst_preset_set_meta
gst_print_element_args
gst_print_pad_caps