latency: Dot not override already stored events
[platform/upstream/gstreamer.git] / gst / gstpreset.c
index d595abc..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
@@ -281,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);
@@ -345,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. */
@@ -363,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);
 
@@ -383,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);
@@ -392,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);
@@ -425,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)
@@ -453,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;
 
@@ -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],
@@ -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,7 @@ gst_preset_set_app_dir (const gchar * app_dir)
  * Gets the directory for application specific presets if set by the
  * application.
  *
- * Returns: (nullable): the directory or %NULL, don't free or modify
+ * Returns: (nullable) (type filename): the directory or %NULL, don't free or modify
  * the string
  */
 const gchar *
@@ -1148,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