device: Enforce that elements created by gst_device_create_element() are floating
[platform/upstream/gstreamer.git] / gst / gstpreset.c
index 6f51447..7584c5b 100644 (file)
@@ -20,6 +20,7 @@
  */
 /**
  * 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
@@ -101,8 +106,6 @@ 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 standard
  * system and user dirs. This helps to develop new presets first local to the
  * application.
@@ -116,9 +119,9 @@ 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
+ * @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.
@@ -183,12 +186,12 @@ preset_get_paths (GstPreset * preset, const gchar ** preset_user_path,
 
       /* system presets in '$GST_DATADIR/gstreamer-1.0/presets/GstAudioPanorama.prs' */
 #ifdef G_OS_WIN32
-      gchar *basedir = g_win32_get_package_installation_directory_of_module (_priv_gst_dll_handle);
-      preset_dir = g_build_filename (basedir,
-                                     "share",
-                                     "gstreamer-" GST_API_VERSION,
-                                     "presets",
-                                     NULL);
+      gchar *basedir =
+          g_win32_get_package_installation_directory_of_module
+          (_priv_gst_dll_handle);
+      preset_dir =
+          g_build_filename (basedir, "share", "gstreamer-" GST_API_VERSION,
+          "presets", NULL);
       g_free (basedir);
 #else
       preset_dir = g_build_filename (GST_DATADIR, "gstreamer-" GST_API_VERSION,
@@ -283,7 +286,7 @@ preset_open_and_parse_header (GstPreset * preset, const gchar * preset_path,
   /* 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);
@@ -347,6 +350,26 @@ preset_merge (GKeyFile * system, GKeyFile * user)
   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. */
@@ -365,18 +388,47 @@ preset_get_keyfile (GstPreset * preset)
     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);
 
@@ -385,8 +437,34 @@ preset_get_keyfile (GstPreset * preset)
       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);
@@ -394,11 +472,11 @@ preset_get_keyfile (GstPreset * preset)
         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);
@@ -427,6 +505,12 @@ preset_get_keyfile (GstPreset * preset)
   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)
@@ -455,9 +539,15 @@ 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;
 
@@ -531,6 +621,8 @@ gst_preset_default_get_property_names (GstPreset * preset)
         }
         g_free (props);
       }
+
+      g_object_unref (child);
     }
   }
   if (!result) {
@@ -579,7 +671,7 @@ gst_preset_default_load_preset (GstPreset * preset, const gchar * name)
     /* 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;
     }
 
@@ -764,6 +856,12 @@ gst_preset_default_save_preset (GstPreset * preset, const gchar * name)
       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],
@@ -954,7 +1052,7 @@ no_presets:
  * gst_preset_get_preset_names:
  * @preset: a #GObject that implements #GstPreset
  *
- * Get a copy of preset names as a NULL terminated string array.
+ * Get a copy of preset names as a %NULL terminated string array.
  *
  * Returns: (transfer full) (array zero-terminated=1) (element-type gchar*):
  *     list with names, use g_strfreev() after usage.
@@ -1067,7 +1165,7 @@ gst_preset_delete_preset (GstPreset * preset, const gchar * name)
  * @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
@@ -1113,10 +1211,10 @@ gst_preset_get_meta (GstPreset * preset, const gchar * name, const gchar * tag,
 
 /**
  * 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
@@ -1139,7 +1237,8 @@ gst_preset_set_app_dir (const gchar * 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
+ * Returns: (nullable) (type filename): the directory or %NULL, don't free or modify
+ * the string
  */
 const gchar *
 gst_preset_get_app_dir (void)
@@ -1147,6 +1246,24 @@ 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
@@ -1183,8 +1300,6 @@ gst_preset_base_init (gpointer g_class)
         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