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)
This commit is contained in:
Thibault Saunier 2019-07-04 15:58:44 -04:00
parent 7caa424aaf
commit f51f2f70de
6 changed files with 199 additions and 54 deletions

View file

@ -144,8 +144,6 @@ typedef struct
GESAsset *asset; GESAsset *asset;
} GESAssetCacheEntry; } 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: /* We are mapping entries by types and ID, such as:
* *
* { * {
@ -169,8 +167,10 @@ G_LOCK_DEFINE_STATIC (asset_cache_lock);
* different extractable types. * different extractable types.
**/ **/
static GHashTable *type_entries_table = NULL; static GHashTable *type_entries_table = NULL;
#define LOCK_CACHE (G_LOCK (asset_cache_lock)) /* Protect all the entries in the cache */
#define UNLOCK_CACHE (G_UNLOCK (asset_cache_lock)) 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 * static gchar *
_check_and_update_parameters (GType * extractable_type, const gchar * id, _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 * static inline GESAssetCacheEntry *
_lookup_entry (GType extractable_type, const gchar * id) _lookup_entry (GType extractable_type, const gchar * id)
{ {
GHashTable *entries_table; 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)); _extractable_type_name (extractable_type));
if (entries_table) if (entries_table)
return g_hash_table_lookup (entries_table, id); 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))) { if (!(entry = _lookup_entry (extractable_type, asset_id))) {
GHashTable *entries_table; 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)); _extractable_type_name (extractable_type));
if (entries_table == NULL) { if (entries_table == NULL) {
entries_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, entries_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
_free_entries); _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); g_strdup (_extractable_type_name (extractable_type)), entries_table);
} }
@ -637,18 +664,20 @@ ges_asset_cache_put (GESAsset * asset, GTask * task)
void void
ges_asset_cache_init (void) ges_asset_cache_init (void)
{ {
type_entries_table = g_hash_table_new_full (g_str_hash, g_str_equal, LOCK_CACHE;
g_free, (GDestroyNotify) g_hash_table_unref); ges_asset_cache_init_unlocked ();
UNLOCK_CACHE;
_init_formatter_assets ();
_init_standard_transition_assets ();
} }
void void
ges_asset_cache_deinit (void) ges_asset_cache_deinit (void)
{ {
_deinit_formatter_assets ();
LOCK_CACHE;
g_hash_table_destroy (type_entries_table); g_hash_table_destroy (type_entries_table);
type_entries_table = NULL; type_entries_table = NULL;
UNLOCK_CACHE;
} }
gboolean gboolean
@ -750,17 +779,21 @@ ges_asset_set_proxy (GESAsset * asset, GESAsset * proxy)
GHashTable *entries_table; GHashTable *entries_table;
GESAssetCacheEntry *entry; 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)); _extractable_type_name (proxy->priv->extractable_type));
entry = g_hash_table_find (entries_table, (GHRFunc) _lookup_proxied_asset, entry = g_hash_table_find (entries_table, (GHRFunc) _lookup_proxied_asset,
(gpointer) ges_asset_get_id (proxy)); (gpointer) ges_asset_get_id (proxy));
if (!entry) { if (!entry) {
UNLOCK_CACHE;
GST_DEBUG_OBJECT (asset, "Not proxying any asset %s", proxy->priv->id); GST_DEBUG_OBJECT (asset, "Not proxying any asset %s", proxy->priv->id);
return FALSE; return FALSE;
} }
asset = entry->asset; asset = entry->asset;
UNLOCK_CACHE;
while (asset->priv->proxies) while (asset->priv->proxies)
asset = asset->priv->proxies->data; asset = asset->priv->proxies->data;
@ -914,7 +947,7 @@ ges_asset_set_id (GESAsset * asset, const gchar * id)
} }
LOCK_CACHE; 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)); _extractable_type_name (asset->priv->extractable_type));
g_return_if_fail (g_hash_table_lookup_extended (entries, priv->id, &orig_id, 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); g_return_val_if_fail (g_type_is_a (filter, GES_TYPE_EXTRACTABLE), NULL);
LOCK_CACHE; 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)) { while (g_hash_table_iter_next (&types_iter, &typename, &assets)) {
if (g_type_is_a (filter, g_type_from_name ((gchar *) typename)) == FALSE) if (g_type_is_a (filter, g_type_from_name ((gchar *) typename)) == FALSE)
continue; continue;

View file

@ -36,6 +36,10 @@
#include "ges-formatter.h" #include "ges-formatter.h"
#include "ges-internal.h" #include "ges-internal.h"
#include "ges.h" #include "ges.h"
#ifndef DISABLE_XPTV
#include "ges-pitivi-formatter.h"
#endif
#ifdef HAS_PYTHON #ifdef HAS_PYTHON
#include <Python.h> #include <Python.h>
#include "ges-resources.h" #include "ges-resources.h"
@ -44,6 +48,7 @@
GST_DEBUG_CATEGORY_STATIC (ges_formatter_debug); GST_DEBUG_CATEGORY_STATIC (ges_formatter_debug);
#undef GST_CAT_DEFAULT #undef GST_CAT_DEFAULT
#define GST_CAT_DEFAULT ges_formatter_debug #define GST_CAT_DEFAULT ges_formatter_debug
static gboolean initialized = FALSE;
/* TODO Add a GCancellable somewhere in the API */ /* TODO Add a GCancellable somewhere in the API */
static void ges_extractable_interface_init (GESExtractableInterface * iface); static void ges_extractable_interface_init (GESExtractableInterface * iface);
@ -477,9 +482,10 @@ ges_formatter_class_register_metas (GESFormatterClass * class,
class->version = version; class->version = version;
class->rank = rank; 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, gst_object_unref (ges_asset_request (G_OBJECT_CLASS_TYPE (class), NULL,
NULL)); NULL));
} }
/* Main Formatter methods */ /* Main Formatter methods */
@ -622,12 +628,34 @@ _init_formatter_assets (void)
g_once_init_leave (&init_debug, TRUE); 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); formatters = g_type_children (GES_TYPE_FORMATTER, &n_formatters);
_list_formatters (formatters, n_formatters); _list_formatters (formatters, n_formatters);
g_free (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 static gint

View file

@ -343,6 +343,7 @@ G_GNUC_INTERNAL GstElement * get_element_for_encoding_profile (GstEncodingProf
/* Function to initialise GES */ /* Function to initialise GES */
G_GNUC_INTERNAL void _init_standard_transition_assets (void); G_GNUC_INTERNAL void _init_standard_transition_assets (void);
G_GNUC_INTERNAL void _init_formatter_assets (void); G_GNUC_INTERNAL void _init_formatter_assets (void);
G_GNUC_INTERNAL void _deinit_formatter_assets (void);
/* Utilities */ /* Utilities */
G_GNUC_INTERNAL gint element_start_compare (GESTimelineElement * a, G_GNUC_INTERNAL gint element_start_compare (GESTimelineElement * a,

View file

@ -83,6 +83,7 @@ ges_init_post (GOptionContext * context, GOptionGroup * group, gpointer data,
uriasset_klass = g_type_class_ref (GES_TYPE_URI_CLIP_ASSET); uriasset_klass = g_type_class_ref (GES_TYPE_URI_CLIP_ASSET);
_init_formatter_assets ();
if (!_ges_uri_asset_ensure_setup (uriasset_klass)) { if (!_ges_uri_asset_ensure_setup (uriasset_klass)) {
GST_ERROR ("cannot setup uri asset"); GST_ERROR ("cannot setup uri asset");
goto failed; goto failed;
@ -99,8 +100,6 @@ ges_init_post (GOptionContext * context, GOptionGroup * group, gpointer data,
} }
gst_object_unref (nlecomposition_factory); gst_object_unref (nlecomposition_factory);
/* register clip classes with the system */ /* register clip classes with the system */
g_type_class_ref (GES_TYPE_TEST_CLIP); 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); 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 */ /* Register track elements */
g_type_class_ref (GES_TYPE_EFFECT); 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_OVERLAY_TEXT_CLIP));
g_type_class_unref (g_type_class_peek (GES_TYPE_GROUP)); 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 */ /* Register track elements */
g_type_class_unref (g_type_class_peek (GES_TYPE_EFFECT)); g_type_class_unref (g_type_class_peek (GES_TYPE_EFFECT));

View file

@ -85,7 +85,7 @@ if otio is not None:
GObject.type_register(GESOtioFormatter) GObject.type_register(GESOtioFormatter)
known_extensions_mimetype_map = [ known_extensions_mimetype_map = [
("otio", "xml", "fcpxml"), ("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 = [] extensions = []

View file

@ -47,11 +47,6 @@
GST_DEBUG_CATEGORY_STATIC (gesdemux); GST_DEBUG_CATEGORY_STATIC (gesdemux);
#define GST_CAT_DEFAULT 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); G_DECLARE_FINAL_TYPE (GESDemux, ges_demux, GES, Demux, GESBaseBin);
struct _GESDemux struct _GESDemux
@ -79,6 +74,92 @@ enum
static GParamSpec *properties[PROP_LAST]; 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 static void
ges_demux_get_property (GObject * object, guint property_id, ges_demux_get_property (GObject * object, guint property_id,
GValue * value, GParamSpec * pspec) GValue * value, GParamSpec * pspec)
@ -110,6 +191,7 @@ ges_demux_class_init (GESDemuxClass * self_class)
{ {
GObjectClass *gclass = G_OBJECT_CLASS (self_class); GObjectClass *gclass = G_OBJECT_CLASS (self_class);
GstElementClass *gstelement_klass = GST_ELEMENT_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"); 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); GES_TYPE_TIMELINE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
g_object_class_override_property (gclass, PROP_TIMELINE, "timeline"); 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, gst_element_class_set_static_metadata (gstelement_klass,
"GStreamer Editing Services based 'demuxer'", "GStreamer Editing Services based 'demuxer'",
"Codec/Demux/Editing", "Codec/Demux/Editing",
"Demuxer for complex timeline file formats using GES.", "Demuxer for complex timeline file formats using GES.",
"Thibault Saunier <tsaunier@igalia.com"); "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 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); xges_buffer = gst_adapter_take_buffer (self->input_adapter, available);
if (gst_buffer_map (xges_buffer, &map, GST_MAP_READ)) { if (gst_buffer_map (xges_buffer, &map, GST_MAP_READ)) {
gint f;
GError *err = NULL; GError *err = NULL;
gchar *template = NULL;
gchar *filename = NULL, *uri = NULL; gchar *filename = NULL, *uri = NULL;
GError *error = NULL; GstCaps *caps = gst_pad_get_current_caps (pad);
gint f = g_file_open_tmp (NULL, &filename, &err); 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) { if (err) {
GST_ELEMENT_ERROR (self, RESOURCE, OPEN_WRITE, 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); uri = gst_filename_to_uri (filename, NULL);
GST_INFO_OBJECT (self, "Pre loading the timeline."); GST_INFO_OBJECT (self, "Pre loading the timeline.");
ges_demux_create_timeline (self, uri, &error); ges_demux_create_timeline (self, uri, &err);
if (error) if (err)
goto error; goto error;
done: done:
@ -493,9 +590,9 @@ ges_demux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
error: error:
ret = FALSE; ret = FALSE;
gst_element_post_message (GST_ELEMENT (self), 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")); "Could not create timeline from description"));
g_clear_error (&error); g_clear_error (&err);
goto done; goto done;
} else { } else {
@ -528,7 +625,10 @@ static void
ges_demux_init (GESDemux * self) ges_demux_init (GESDemux * self)
{ {
ges_init (); 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); gst_element_add_pad (GST_ELEMENT (self), self->sinkpad);
self->input_adapter = gst_adapter_new (); self->input_adapter = gst_adapter_new ();