From f51f2f70de1d0cdabf7a4f5067bf9d3d4c9e9e99 Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Thu, 4 Jul 2019 15:58:44 -0400 Subject: [PATCH] gesdemux: Compute sinkpad caps based on formatter mimetypes 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 | 63 ++++++++++++---- ges/ges-formatter.c | 40 +++++++++-- ges/ges-internal.h | 1 + ges/ges.c | 19 +---- ges/python/gesotioformatter.py | 2 +- plugins/ges/gesdemux.c | 128 +++++++++++++++++++++++++++++---- 6 files changed, 199 insertions(+), 54 deletions(-) diff --git a/ges/ges-asset.c b/ges/ges-asset.c index 19d17b5b45..60385dc641 100644 --- a/ges/ges-asset.c +++ b/ges/ges-asset.c @@ -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; diff --git a/ges/ges-formatter.c b/ges/ges-formatter.c index 4bbdb0a0c4..2f5d81527d 100644 --- a/ges/ges-formatter.c +++ b/ges/ges-formatter.c @@ -36,6 +36,10 @@ #include "ges-formatter.h" #include "ges-internal.h" #include "ges.h" +#ifndef DISABLE_XPTV +#include "ges-pitivi-formatter.h" +#endif + #ifdef HAS_PYTHON #include #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 diff --git a/ges/ges-internal.h b/ges/ges-internal.h index e109b4965a..c3ba7dcbe4 100644 --- a/ges/ges-internal.h +++ b/ges/ges-internal.h @@ -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, diff --git a/ges/ges.c b/ges/ges.c index eb81fadb16..2896ac6f70 100644 --- 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)); diff --git a/ges/python/gesotioformatter.py b/ges/python/gesotioformatter.py index 2815e26b74..2692445155 100644 --- a/ges/python/gesotioformatter.py +++ b/ges/python/gesotioformatter.py @@ -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 = [] diff --git a/plugins/ges/gesdemux.c b/plugins/ges/gesdemux.c index cfa850805c..a5bcdcab4e 100644 --- a/plugins/ges/gesdemux.c +++ b/plugins/ges/gesdemux.c @@ -47,11 +47,6 @@ 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 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 ();