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;
} 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;

View file

@ -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 <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

View 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,

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);
_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));

View 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 = []

View file

@ -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 <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 ();