gesdemux: Compute sinkpad caps based on formatter mimetypes
authorThibault Saunier <tsaunier@igalia.com>
Thu, 4 Jul 2019 19:58:44 +0000 (15:58 -0400)
committerThibault Saunier <tsaunier@igalia.com>
Fri, 26 Jul 2019 17:48:51 +0000 (13:48 -0400)
Implement lazy loading asset cache so gesdemux use the formatters
assets while GES hasn't been initialized.

And set extensions to temporary files as some formatters require
the information (otio)

ges/ges-asset.c
ges/ges-formatter.c
ges/ges-internal.h
ges/ges.c
ges/python/gesotioformatter.py
plugins/ges/gesdemux.c

index 19d17b5b45350e176b57dfb7daacb1d6c50d6547..60385dc641fff23d79ac3dec6deb465f91e5485c 100644 (file)
@@ -144,8 +144,6 @@ typedef struct
   GESAsset *asset;
 } GESAssetCacheEntry;
 
-/* Also protect all the entries in the cache */
-G_LOCK_DEFINE_STATIC (asset_cache_lock);
 /* We are mapping entries by types and ID, such as:
  *
  * {
@@ -169,8 +167,10 @@ G_LOCK_DEFINE_STATIC (asset_cache_lock);
  * different extractable types.
  **/
 static GHashTable *type_entries_table = NULL;
-#define LOCK_CACHE   (G_LOCK (asset_cache_lock))
-#define UNLOCK_CACHE (G_UNLOCK (asset_cache_lock))
+/* Protect all the entries in the cache */
+static GRecMutex asset_cache_lock;
+#define LOCK_CACHE   (g_rec_mutex_lock (&asset_cache_lock))
+#define UNLOCK_CACHE (g_rec_mutex_unlock (&asset_cache_lock))
 
 static gchar *
 _check_and_update_parameters (GType * extractable_type, const gchar * id,
@@ -470,12 +470,39 @@ _extractable_type_name (GType type)
   }
 }
 
+static void
+ges_asset_cache_init_unlocked (void)
+{
+  if (type_entries_table)
+    return;
+
+  type_entries_table = g_hash_table_new_full (g_str_hash, g_str_equal,
+      g_free, (GDestroyNotify) g_hash_table_unref);
+
+  _init_formatter_assets ();
+  _init_standard_transition_assets ();
+}
+
+
+/* WITH LOCK_CACHE */
+static GHashTable *
+_get_type_entries ()
+{
+  if (type_entries_table)
+    return type_entries_table;
+
+  ges_asset_cache_init_unlocked ();
+
+  return type_entries_table;
+}
+
+/* WITH LOCK_CACHE */
 static inline GESAssetCacheEntry *
 _lookup_entry (GType extractable_type, const gchar * id)
 {
   GHashTable *entries_table;
 
-  entries_table = g_hash_table_lookup (type_entries_table,
+  entries_table = g_hash_table_lookup (_get_type_entries (),
       _extractable_type_name (extractable_type));
   if (entries_table)
     return g_hash_table_lookup (entries_table, id);
@@ -608,13 +635,13 @@ ges_asset_cache_put (GESAsset * asset, GTask * task)
   if (!(entry = _lookup_entry (extractable_type, asset_id))) {
     GHashTable *entries_table;
 
-    entries_table = g_hash_table_lookup (type_entries_table,
+    entries_table = g_hash_table_lookup (_get_type_entries (),
         _extractable_type_name (extractable_type));
     if (entries_table == NULL) {
       entries_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
           _free_entries);
 
-      g_hash_table_insert (type_entries_table,
+      g_hash_table_insert (_get_type_entries (),
           g_strdup (_extractable_type_name (extractable_type)), entries_table);
     }
 
@@ -637,18 +664,20 @@ ges_asset_cache_put (GESAsset * asset, GTask * task)
 void
 ges_asset_cache_init (void)
 {
-  type_entries_table = g_hash_table_new_full (g_str_hash, g_str_equal,
-      g_free, (GDestroyNotify) g_hash_table_unref);
-
-  _init_formatter_assets ();
-  _init_standard_transition_assets ();
+  LOCK_CACHE;
+  ges_asset_cache_init_unlocked ();
+  UNLOCK_CACHE;
 }
 
 void
 ges_asset_cache_deinit (void)
 {
+  _deinit_formatter_assets ();
+
+  LOCK_CACHE;
   g_hash_table_destroy (type_entries_table);
   type_entries_table = NULL;
+  UNLOCK_CACHE;
 }
 
 gboolean
@@ -750,17 +779,21 @@ ges_asset_set_proxy (GESAsset * asset, GESAsset * proxy)
     GHashTable *entries_table;
     GESAssetCacheEntry *entry;
 
-    entries_table = g_hash_table_lookup (type_entries_table,
+    LOCK_CACHE;
+    entries_table = g_hash_table_lookup (_get_type_entries (),
         _extractable_type_name (proxy->priv->extractable_type));
     entry = g_hash_table_find (entries_table, (GHRFunc) _lookup_proxied_asset,
         (gpointer) ges_asset_get_id (proxy));
 
     if (!entry) {
+      UNLOCK_CACHE;
       GST_DEBUG_OBJECT (asset, "Not proxying any asset %s", proxy->priv->id);
       return FALSE;
     }
 
     asset = entry->asset;
+    UNLOCK_CACHE;
+
     while (asset->priv->proxies)
       asset = asset->priv->proxies->data;
 
@@ -914,7 +947,7 @@ ges_asset_set_id (GESAsset * asset, const gchar * id)
   }
 
   LOCK_CACHE;
-  entries = g_hash_table_lookup (type_entries_table,
+  entries = g_hash_table_lookup (_get_type_entries (),
       _extractable_type_name (asset->priv->extractable_type));
 
   g_return_if_fail (g_hash_table_lookup_extended (entries, priv->id, &orig_id,
@@ -1369,7 +1402,7 @@ ges_list_assets (GType filter)
   g_return_val_if_fail (g_type_is_a (filter, GES_TYPE_EXTRACTABLE), NULL);
 
   LOCK_CACHE;
-  g_hash_table_iter_init (&types_iter, type_entries_table);
+  g_hash_table_iter_init (&types_iter, _get_type_entries ());
   while (g_hash_table_iter_next (&types_iter, &typename, &assets)) {
     if (g_type_is_a (filter, g_type_from_name ((gchar *) typename)) == FALSE)
       continue;
index 4bbdb0a0c44722f27fdf5e98d7c729b36a1c89c4..2f5d81527d57eb4e515077bb5c754b2714207e01 100644 (file)
 #include "ges-formatter.h"
 #include "ges-internal.h"
 #include "ges.h"
+#ifndef DISABLE_XPTV
+#include "ges-pitivi-formatter.h"
+#endif
+
 #ifdef HAS_PYTHON
 #include <Python.h>
 #include "ges-resources.h"
@@ -44,6 +48,7 @@
 GST_DEBUG_CATEGORY_STATIC (ges_formatter_debug);
 #undef GST_CAT_DEFAULT
 #define GST_CAT_DEFAULT ges_formatter_debug
+static gboolean initialized = FALSE;
 
 /* TODO Add a GCancellable somewhere in the API */
 static void ges_extractable_interface_init (GESExtractableInterface * iface);
@@ -477,9 +482,10 @@ ges_formatter_class_register_metas (GESFormatterClass * class,
   class->version = version;
   class->rank = rank;
 
-  if (ges_is_initialized () && g_type_class_peek (G_OBJECT_CLASS_TYPE (class)))
+  if (g_atomic_int_get (&initialized)
+      && g_type_class_peek (G_OBJECT_CLASS_TYPE (class)))
     gst_object_unref (ges_asset_request (G_OBJECT_CLASS_TYPE (class), NULL,
-            NULL));
+      NULL));
 }
 
 /* Main Formatter methods */
@@ -622,12 +628,34 @@ _init_formatter_assets (void)
     g_once_init_leave (&init_debug, TRUE);
   }
 
-  load_python_formatters ();
+  if (g_atomic_int_compare_and_exchange (&initialized, FALSE, TRUE)) {
+    /* register formatter types with the system */
+#ifndef DISABLE_XPTV
+    g_type_class_ref (GES_TYPE_PITIVI_FORMATTER);
+#endif
+    g_type_class_ref (GES_TYPE_COMMAND_LINE_FORMATTER);
+    g_type_class_ref (GES_TYPE_XML_FORMATTER);
 
+    load_python_formatters ();
+
+    formatters = g_type_children (GES_TYPE_FORMATTER, &n_formatters);
+    _list_formatters (formatters, n_formatters);
+    g_free (formatters);
+  }
+}
 
-  formatters = g_type_children (GES_TYPE_FORMATTER, &n_formatters);
-  _list_formatters (formatters, n_formatters);
-  g_free (formatters);
+void
+_deinit_formatter_assets (void)
+{
+  if (g_atomic_int_compare_and_exchange (&initialized, TRUE, FALSE)) {
+
+#ifndef DISABLE_XPTV
+    g_type_class_unref (g_type_class_peek (GES_TYPE_PITIVI_FORMATTER));
+#endif
+
+    g_type_class_unref (g_type_class_peek (GES_TYPE_COMMAND_LINE_FORMATTER));
+    g_type_class_unref (g_type_class_peek (GES_TYPE_XML_FORMATTER));
+  }
 }
 
 static gint
index e109b4965a037a4141b610c4aa44798cdbf6cc0d..c3ba7dcbe415780e55e6cfef1fda949a80179b02 100644 (file)
@@ -343,6 +343,7 @@ G_GNUC_INTERNAL GstElement * get_element_for_encoding_profile   (GstEncodingProf
 /* Function to initialise GES */
 G_GNUC_INTERNAL void _init_standard_transition_assets        (void);
 G_GNUC_INTERNAL void _init_formatter_assets                  (void);
+G_GNUC_INTERNAL void _deinit_formatter_assets                (void);
 
 /* Utilities */
 G_GNUC_INTERNAL gint element_start_compare                (GESTimelineElement * a,
index eb81fadb167aa6ac57948fff9917dbe169770e7f..2896ac6f70f773fc8f39daeb4296f88ce2295a86 100644 (file)
--- a/ges/ges.c
+++ b/ges/ges.c
@@ -83,6 +83,7 @@ ges_init_post (GOptionContext * context, GOptionGroup * group, gpointer data,
 
   uriasset_klass = g_type_class_ref (GES_TYPE_URI_CLIP_ASSET);
 
+  _init_formatter_assets ();
   if (!_ges_uri_asset_ensure_setup (uriasset_klass)) {
     GST_ERROR ("cannot setup uri asset");
     goto failed;
@@ -99,8 +100,6 @@ ges_init_post (GOptionContext * context, GOptionGroup * group, gpointer data,
   }
   gst_object_unref (nlecomposition_factory);
 
-
-
   /* register clip classes with the system */
 
   g_type_class_ref (GES_TYPE_TEST_CLIP);
@@ -112,13 +111,6 @@ ges_init_post (GOptionContext * context, GOptionGroup * group, gpointer data,
 
   g_type_class_ref (GES_TYPE_GROUP);
 
-  /* register formatter types with the system */
-#ifndef DISABLE_XPTV
-  g_type_class_ref (GES_TYPE_PITIVI_FORMATTER);
-#endif
-  g_type_class_ref (GES_TYPE_COMMAND_LINE_FORMATTER);
-  g_type_class_ref (GES_TYPE_XML_FORMATTER);
-
   /* Register track elements */
   g_type_class_ref (GES_TYPE_EFFECT);
 
@@ -209,15 +201,6 @@ ges_deinit (void)
   g_type_class_unref (g_type_class_peek (GES_TYPE_OVERLAY_TEXT_CLIP));
 
   g_type_class_unref (g_type_class_peek (GES_TYPE_GROUP));
-
-  /* register formatter types with the system */
-#ifndef DISABLE_XPTV
-  g_type_class_unref (g_type_class_peek (GES_TYPE_PITIVI_FORMATTER));
-#endif
-
-  g_type_class_unref (g_type_class_peek (GES_TYPE_COMMAND_LINE_FORMATTER));
-  g_type_class_unref (g_type_class_peek (GES_TYPE_XML_FORMATTER));
-
   /* Register track elements */
   g_type_class_unref (g_type_class_peek (GES_TYPE_EFFECT));
 
index 2815e26b7461d4c460ef59557f194bda0391a158..26924451554774491b512efb6330958f6f5736bd 100644 (file)
@@ -85,7 +85,7 @@ if otio is not None:
     GObject.type_register(GESOtioFormatter)
     known_extensions_mimetype_map = [
         ("otio", "xml", "fcpxml"),
-        ("application/otio", "application/xmeml", "application/fcpxml")
+        ("application/vnd.pixar.opentimelineio+json", "application/vnd.apple-xmeml+xml", "application/vnd.apple-fcp+xml")
     ]
 
     extensions = []
index cfa850805c79ac098d4cf6dc835212095ef6bdbe..a5bcdcab4e23d029b22dc790ddee207f2b10bdce 100644 (file)
 GST_DEBUG_CATEGORY_STATIC (gesdemux);
 #define GST_CAT_DEFAULT gesdemux
 
-static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
-    GST_PAD_SINK,
-    GST_PAD_ALWAYS,
-    GST_STATIC_CAPS ("application/xges"));
-
 G_DECLARE_FINAL_TYPE (GESDemux, ges_demux, GES, Demux, GESBaseBin);
 
 struct _GESDemux
@@ -79,6 +74,92 @@ enum
 
 static GParamSpec *properties[PROP_LAST];
 
+static GstCaps *
+ges_demux_get_sinkpad_caps ()
+{
+  GList *tmp, *formatters;
+  GstCaps *sinkpad_caps = gst_caps_new_empty ();
+
+  formatters = ges_list_assets (GES_TYPE_FORMATTER);
+  for (tmp = formatters; tmp; tmp = tmp->next) {
+    GstCaps *caps;
+    const gchar *mimetype =
+        ges_meta_container_get_string (GES_META_CONTAINER (tmp->data),
+        GES_META_FORMATTER_MIMETYPE);
+    if (!mimetype)
+      continue;
+
+    caps = gst_caps_from_string (mimetype);
+
+    if (!caps) {
+      GST_INFO_OBJECT (tmp->data,
+          "%s - could not create caps from mimetype: %s",
+          ges_meta_container_get_string (GES_META_CONTAINER (tmp->data),
+              GES_META_FORMATTER_NAME), mimetype);
+
+      continue;
+    }
+
+    gst_caps_append (sinkpad_caps, caps);
+  }
+  g_list_free (formatters);
+
+  return sinkpad_caps;
+}
+
+static gchar *
+ges_demux_get_extension (GstStructure * _struct)
+{
+  GList *tmp, *formatters;
+  gchar *ext = NULL;
+
+  formatters = ges_list_assets (GES_TYPE_FORMATTER);
+  for (tmp = formatters; tmp; tmp = tmp->next) {
+    gchar **extensions_a;
+    gint i, n_exts;
+    GstCaps *caps;
+    const gchar *mimetype =
+        ges_meta_container_get_string (GES_META_CONTAINER (tmp->data),
+        GES_META_FORMATTER_MIMETYPE);
+    const gchar *extensions =
+        ges_meta_container_get_string (GES_META_CONTAINER (tmp->data),
+        GES_META_FORMATTER_EXTENSION);
+    if (!mimetype)
+      continue;
+
+    if (!extensions)
+      continue;
+
+    caps = gst_caps_from_string (mimetype);
+    if (!caps) {
+      GST_INFO_OBJECT (tmp->data,
+          "%s - could not create caps from mimetype: %s",
+          ges_meta_container_get_string (GES_META_CONTAINER (tmp->data),
+              GES_META_FORMATTER_NAME), mimetype);
+
+      continue;
+    }
+
+    extensions_a = g_strsplit (extensions, ",", -1);
+    n_exts = g_strv_length (extensions_a);
+    for (i = 0; i < gst_caps_get_size (caps) && i < n_exts; i++) {
+      GstStructure *structure = gst_caps_get_structure (caps, i);
+
+      if (gst_structure_has_name (_struct, gst_structure_get_name (structure))) {
+        ext = g_strdup (extensions_a[i]);
+        g_strfreev (extensions_a);
+        gst_caps_unref (caps);
+        goto done;
+      }
+    }
+    g_strfreev (extensions_a);
+  }
+done:
+  g_list_free (formatters);
+
+  return ext;
+}
+
 static void
 ges_demux_get_property (GObject * object, guint property_id,
     GValue * value, GParamSpec * pspec)
@@ -110,6 +191,7 @@ ges_demux_class_init (GESDemuxClass * self_class)
 {
   GObjectClass *gclass = G_OBJECT_CLASS (self_class);
   GstElementClass *gstelement_klass = GST_ELEMENT_CLASS (self_class);
+  GstCaps *sinkpad_caps = ges_demux_get_sinkpad_caps ();
 
   GST_DEBUG_CATEGORY_INIT (gesdemux, "gesdemux", 0, "ges demux element");
 
@@ -128,13 +210,16 @@ ges_demux_class_init (GESDemuxClass * self_class)
       GES_TYPE_TIMELINE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
   g_object_class_override_property (gclass, PROP_TIMELINE, "timeline");
 
-  gst_element_class_add_pad_template (gstelement_klass,
-      gst_static_pad_template_get (&sink_template));
   gst_element_class_set_static_metadata (gstelement_klass,
       "GStreamer Editing Services based 'demuxer'",
       "Codec/Demux/Editing",
       "Demuxer for complex timeline file formats using GES.",
       "Thibault Saunier <tsaunier@igalia.com");
+
+  gst_element_class_add_pad_template (gstelement_klass,
+      gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
+          sinkpad_caps));
+  gst_caps_unref (sinkpad_caps);
 }
 
 typedef struct
@@ -455,10 +540,22 @@ ges_demux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
 
       xges_buffer = gst_adapter_take_buffer (self->input_adapter, available);
       if (gst_buffer_map (xges_buffer, &map, GST_MAP_READ)) {
+        gint f;
         GError *err = NULL;
+        gchar *template = NULL;
         gchar *filename = NULL, *uri = NULL;
-        GError *error = NULL;
-        gint f = g_file_open_tmp (NULL, &filename, &err);
+        GstCaps *caps = gst_pad_get_current_caps (pad);
+        GstStructure *structure = gst_caps_get_structure (caps, 0);
+        gchar *ext = ges_demux_get_extension (structure);
+
+        gst_caps_unref (caps);
+        if (ext) {
+          template = g_strdup_printf ("XXXXXX.%s", ext);
+          g_free (ext);
+        }
+
+        f = g_file_open_tmp (template, &filename, &err);
+        g_free (template);
 
         if (err) {
           GST_ELEMENT_ERROR (self, RESOURCE, OPEN_WRITE,
@@ -480,8 +577,8 @@ ges_demux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
         uri = gst_filename_to_uri (filename, NULL);
         GST_INFO_OBJECT (self, "Pre loading the timeline.");
 
-        ges_demux_create_timeline (self, uri, &error);
-        if (error)
+        ges_demux_create_timeline (self, uri, &err);
+        if (err)
           goto error;
 
       done:
@@ -493,9 +590,9 @@ ges_demux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
       error:
         ret = FALSE;
         gst_element_post_message (GST_ELEMENT (self),
-            gst_message_new_error (parent, error,
+            gst_message_new_error (parent, err,
                 "Could not create timeline from description"));
-        g_clear_error (&error);
+        g_clear_error (&err);
 
         goto done;
       } else {
@@ -528,7 +625,10 @@ static void
 ges_demux_init (GESDemux * self)
 {
   ges_init ();
-  self->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
+
+  self->sinkpad =
+      gst_pad_new_from_template (gst_element_get_pad_template (GST_ELEMENT
+          (self), "sink"), "sink");
   gst_element_add_pad (GST_ELEMENT (self), self->sinkpad);
 
   self->input_adapter = gst_adapter_new ();