diff --git a/docs/libs/ges-docs.sgml b/docs/libs/ges-docs.sgml
index eef4b3f635..a5a54479e5 100644
--- a/docs/libs/ges-docs.sgml
+++ b/docs/libs/ges-docs.sgml
@@ -47,6 +47,7 @@ platform as well as Windows. It is released under the GNU Library General Public
+
diff --git a/docs/libs/ges-sections.txt b/docs/libs/ges-sections.txt
index c5a4bae7ad..0e5380ed4c 100644
--- a/docs/libs/ges-sections.txt
+++ b/docs/libs/ges-sections.txt
@@ -297,6 +297,7 @@ GES_TYPE_TIMELINE
GESTimelineLayer
GESTimelineLayerClass
ges_timeline_layer_add_object
+ges_timeline_layer_add_asset
ges_timeline_layer_new
ges_timeline_layer_remove_object
ges_timeline_layer_set_priority
@@ -796,19 +797,10 @@ GESFormatterLoadedMethod
ges_default_formatter_new
ges_formatter_load_from_uri
ges_formatter_save_to_uri
-ges_formatter_new_for_uri
ges_formatter_can_load_uri
ges_formatter_can_save_uri
-ges_formatter_update_source_uri
-ges_formatter_load
-ges_formatter_save
-ges_formatter_set_data
-ges_formatter_clear_data
-ges_formatter_get_data
GESFormatterCanLoadURIMethod
GESFormatterCanSaveURIMethod
-GESFormatterLoadMethod
-GESFormatterSaveMethod
ges_formatter_emit_loaded
@@ -823,22 +815,6 @@ ges_formatter_get_type
GESFormatterPrivate
-
-ges-keyfile-formatter
-GESKeyFileFormatter
-GESKeyfileFormatter
-ges_keyfile_formatter_new
-
-GESKeyfileFormatterClass
-GES_IS_KEYFILE_FORMATTER
-GES_IS_KEYFILE_FORMATTER_CLASS
-GES_KEYFILE_FORMATTER
-GES_KEYFILE_FORMATTER_CLASS
-GES_KEYFILE_FORMATTER_GET_CLASS
-GES_TYPE_KEYFILE_FORMATTER
-ges_keyfile_formatter_get_type
-
-
ges-pitivi-formatter
GESPitiviFormatter
@@ -951,3 +927,25 @@ GES_META_CONTAINER_GET_INTERFACE
GES_TYPE_META_CONTAINER
ges_meta_container_get_ype
+
+
+ges-asset
+GESAsset
+GESAsset
+ges_asset_get_extractable_type
+ges_asset_get_id
+ges_asset_request
+ges_asset_request_async
+ges_asset_request_finish
+ges_asset_extract
+ges_list_assets
+
+GESAssetPrivate
+GES_ASSET
+GES_TYPE_ASSET
+GES_ASSET_CLASS
+GES_IS_ASSET
+GES_IS_ASSET_CLASS
+GES_ASSET_GET_CLASS
+ges_asset_get_type
+
diff --git a/ges/Makefile.am b/ges/Makefile.am
index 0afd3226c2..99117eb631 100644
--- a/ges/Makefile.am
+++ b/ges/Makefile.am
@@ -94,7 +94,8 @@ libges_@GST_API_VERSION@include_HEADERS = \
ges-utils.h
noinst_HEADERS = \
- ges-internal.h
+ ges-internal.h \
+ ges-internal-enums.h
libges_@GST_API_VERSION@_la_CFLAGS = -I$(top_srcdir) $(GST_PBUTILS_CFLAGS) \
$(GST_VIDEO_CFLAGS) $(GST_CONTROLLER_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) \
diff --git a/ges/ges-asset.c b/ges/ges-asset.c
new file mode 100644
index 0000000000..8b7d0703df
--- /dev/null
+++ b/ges/ges-asset.c
@@ -0,0 +1,1008 @@
+/* GStreamer Editing Services
+ *
+ * Copyright (C) 2012 Thibault Saunier
+ * Copyright (C) 2012 Volodymyr Rudyi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/**
+ * SECTION: ges-asset
+ * @short_description: #GESAssets represent usable ressources inside the GStreamer Editing Services
+ *
+ * The Assets in the GStreamer Editing Services represent the ressources
+ * that can be used. You can create assets for any type that implements the #GESExtractable
+ * interface, for example #GESTimelineObjects, #GESFormatter, and #GESTrackObject do implement it.
+ * This means that asssets will represent for example a #GESTimelineFileSources, #GESTrackEffect etc,
+ * and then you can extract objects of those types with the appropriate parameters from the asset
+ * using the #ges_asset_extract method:
+ *
+ * |[
+ * GESAsset *effect_asset;
+ * GESTrackParseLaunchEffect *effect;
+ *
+ * // You create an asset for an effect
+ * effect_asset = ges_asset_request (GES_TYPE_TRACK_PARSE_LAUNCH_EFFECT, "agingtv", NULL);
+ *
+ * // And now you can extract an instance of GESTrackParseLaunchEffect from that asset
+ * effect = GES_TRACK_PARSE_LAUNCH_EFFECT (ges_asset_extract (effect_asset));
+ *
+ * ]|
+ *
+ * In that example, the advantages of having a #GESAsset are that you can know what effects
+ * you are working with and let your user know about the avalaible ones, you can add metadata
+ * to the #GESAsset through the #GESMetaContainer interface and you have a model for your
+ * custom effects. Note that #GESAsset management is making easier thanks to the #GESProject class.
+ *
+ * Each asset are represented by a pair of @extractable_type and @id (string). Actually the @extractable_type
+ * is the type that implements the #GESExtractable interface, that means that for example for a #GESTimelineFileSource,
+ * the type that implements the #GESExtractable interface is #GESTimelineObject.
+ * The identifier represents different things depending on the @extractable_type and you should check
+ * the documentation of each type to know what the ID of #GESAsset actually represents for that type. By default,
+ * we only have one #GESAsset per type, and the @id is the name of the type, but this behaviour is overriden
+ * to be more usefull. For example, for GESTimelineStandardTransitions, the ID is the vtype of the transition
+ * you will extract from it (ie crossfade, box-wipe-rc etc..) For #GESTrackParseLaunchEffect the id is the
+ * @bin-description property of the extracted objects (ie the gst-launch style description of the bin that
+ * will be used).
+ *
+ * Each and every #GESAsset are cached into GES, and you can query those with the #ges_list_assets function.
+ * Also the system will automatically register #GESAssets for #GESFormatters and #GESTimelineStandardTransitions
+ * and standard effects (actually not implemented yet) and you can simply query those calling:
+ * |[
+ * GList *formatter_assets, *tmp;
+ *
+ * // List all the transitions
+ * formatter_assets = ges_list_assets (GES_TYPE_FORMATTER);
+ *
+ * // Print some infos about the formatter GESAsset
+ * for (tmp = formatter_assets; tmp; tmp = tmp->next) {
+ * g_print ("Name of the formatter: %s, file extension it produces: %s",
+ * ges_meta_container_get_string (GES_META_CONTAINER (tmp->data), GES_META_FORMATTER_NAME),
+ * ges_meta_container_get_string (GES_META_CONTAINER (tmp->data), GES_META_FORMATTER_EXTENSION));
+ * }
+ *
+ * g_list_free (transition_assets);
+ *
+ * ]|
+ *
+ * You can request the creation of #GESAssets using either #ges_asset_request_async or
+ * #ges_asset_request_async. All the #GESAssets are cached and thus any asset that has already
+ * been created can be requested again without overhead.
+ */
+
+#include "ges.h"
+#include "ges-internal.h"
+
+#include
+
+enum
+{
+ PROP_0,
+ PROP_TYPE,
+ PROP_ID,
+ PROP_LAST
+};
+
+typedef enum
+{
+ ASSET_NOT_INITIALIZED,
+ ASSET_INITIALIZING, ASSET_INITIALIZED_WITH_ERROR,
+ ASSET_PROXIED,
+ ASSET_INITIALIZED
+} GESAssetState;
+
+static GParamSpec *_properties[PROP_LAST];
+
+struct _GESAssetPrivate
+{
+ gchar *id;
+ GESAssetState state;
+ GType extractable_type;
+
+ /* When a asset is proxied, instanciating it will
+ * return the asset it points to */
+ char *proxied_asset_id;
+
+ /* The error that accured when a asset has been initialized with error */
+ GError *error;
+};
+
+/* Internal structure to help avoid full loading
+ * of one asset several times
+ */
+typedef struct
+{
+ GList *results;
+ GESAsset *asset;
+} GESAssetCacheEntry;
+
+/* Also protect all the entries in the cache */
+static GMutex asset_cache_lock;
+/* We are mapping entries by types and ID, such as:
+ *
+ * {
+ * first_extractable_type_name1 :
+ * {
+ * "some ID": GESAssetCacheEntry,
+ * "some other ID": GESAssetCacheEntry 2
+ * },
+ * second_extractable_type_name :
+ * {
+ * "some ID": GESAssetCacheEntry,
+ * "some other ID": GESAssetCacheEntry 2
+ * }
+ * }
+ *
+ * (The first extractable type is the type of the class that implemented
+ * the GESExtractable interface ie: GESTimelineObject, GESTimeline,
+ * GESFomatter, etc... but not subclasses)
+ *
+ * This is in order to be able to have 2 Asset with the same ID but
+ * different extractable types.
+ **/
+static GHashTable *type_entries_table = NULL;
+#define LOCK_CACHE (g_mutex_lock (&asset_cache_lock))
+#define UNLOCK_CACHE (g_mutex_unlock (&asset_cache_lock))
+
+static gchar *
+_check_and_update_parameters (GType * extractable_type, const gchar * id,
+ GError ** error)
+{
+ gchar *real_id;
+ GType old_type = *extractable_type;
+
+ *extractable_type =
+ ges_extractable_get_real_extractable_type_for_id (*extractable_type, id);
+
+ if (*extractable_type == G_TYPE_NONE) {
+ GST_WARNING ("No way to create a Asset for ID: %s, type: %s", id,
+ g_type_name (old_type));
+
+ if (error && *error == NULL)
+ g_set_error (error, GES_ERROR_DOMAIN, 0,
+ "Wrong ID, can not find any extractable_type");
+ return NULL;
+ }
+
+ real_id = ges_extractable_type_check_id (*extractable_type, id, error);
+ if (real_id == NULL) {
+ GST_WARNING ("Wrong ID %s, can not create asset", id);
+
+ g_free (real_id);
+ if (error && *error == NULL)
+ g_set_error (error, GES_ERROR_DOMAIN, 0, "Wrong ID");
+
+ return NULL;
+ }
+
+ return real_id;
+}
+
+static gboolean
+initable_init (GInitable * initable, GCancellable * cancellable,
+ GError ** error)
+{
+ gboolean ret;
+ GESAsset *asset = GES_ASSET (initable);
+
+ ges_asset_cache_put (gst_object_ref (asset), NULL);
+ ret = ges_asset_cache_set_loaded (asset->priv->extractable_type,
+ asset->priv->id, NULL);
+
+ g_clear_error (error);
+
+ return ret;
+}
+
+static void
+async_initable_init_async (GAsyncInitable * initable, gint io_priority,
+ GCancellable * cancellable, GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+
+ GError *error = NULL;
+ GESAsset *asset = GES_ASSET (initable);
+
+ simple = g_simple_async_result_new (G_OBJECT (asset),
+ callback, user_data, ges_asset_request_async);
+
+ ges_asset_cache_put (asset, simple);
+ switch (GES_ASSET_GET_CLASS (asset)->start_loading (asset, &error)) {
+ case GES_ASSET_LOADING_ERROR:
+ {
+ if (error == NULL)
+ g_set_error (&error, GES_ERROR_DOMAIN, 1,
+ "Could not start loading asset");
+
+ /* FIXME Define error code */
+ ges_asset_cache_set_loaded (asset->priv->extractable_type,
+ asset->priv->id, error);
+ g_error_free (error);
+ return;
+ }
+ case GES_ASSET_LOADING_OK:
+ {
+ ges_asset_cache_set_loaded (asset->priv->extractable_type,
+ asset->priv->id, error);
+ return;
+ }
+ case GES_ASSET_LOADING_ASYNC:
+ /* If Async.... let it go */
+ break;
+ }
+}
+
+static void
+async_initable_iface_init (GAsyncInitableIface * async_initable_iface)
+{
+ async_initable_iface->init_async = async_initable_init_async;
+}
+
+static void
+initable_iface_init (GInitableIface * initable_iface)
+{
+ initable_iface->init = initable_init;
+}
+
+G_DEFINE_TYPE_WITH_CODE (GESAsset, ges_asset, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init)
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)
+ G_IMPLEMENT_INTERFACE (GES_TYPE_META_CONTAINER, NULL));
+
+/* GESAsset virtual methods default implementation */
+static GESAssetLoadingReturn
+ges_asset_start_loading_default (GESAsset * asset, GError ** error)
+{
+ return GES_ASSET_LOADING_OK;
+}
+
+static GESExtractable *
+ges_asset_extract_default (GESAsset * asset, GError ** error)
+{
+ guint n_params;
+ GParameter *params;
+ GESAssetPrivate *priv = asset->priv;
+
+ params = ges_extractable_type_get_parameters_from_id (priv->extractable_type,
+ priv->id, &n_params);
+
+ return g_object_newv (priv->extractable_type, n_params, params);
+}
+
+static gboolean
+ges_asset_request_id_update_default (GESAsset * self, gchar ** proposed_new_id,
+ GError * error)
+{
+ return FALSE;
+}
+
+/* GObject virtual methods implementation */
+static void
+ges_asset_get_property (GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GESAsset *asset = GES_ASSET (object);
+
+ switch (property_id) {
+ case PROP_TYPE:
+ g_value_set_gtype (value, asset->priv->extractable_type);
+ break;
+ case PROP_ID:
+ g_value_set_string (value, asset->priv->id);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_asset_set_property (GObject * object, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GESAsset *asset = GES_ASSET (object);
+
+ switch (property_id) {
+ case PROP_TYPE:
+ asset->priv->extractable_type = g_value_get_gtype (value);
+ ges_extractable_register_metas (asset->priv->extractable_type, asset);
+ break;
+ case PROP_ID:
+ asset->priv->id = g_value_dup_string (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+ges_asset_finalize (GObject * object)
+{
+ GESAssetPrivate *priv = GES_ASSET (object)->priv;
+
+ if (priv->id)
+ g_free (priv->id);
+
+ if (priv->proxied_asset_id)
+ g_free (priv->proxied_asset_id);
+
+ G_OBJECT_CLASS (ges_asset_parent_class)->finalize (object);
+}
+
+void
+ges_asset_class_init (GESAssetClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ g_type_class_add_private (klass, sizeof (GESAssetPrivate));
+
+ object_class->get_property = ges_asset_get_property;
+ object_class->set_property = ges_asset_set_property;
+ object_class->finalize = ges_asset_finalize;
+
+ _properties[PROP_TYPE] =
+ g_param_spec_gtype ("extractable-type", "Extractable type",
+ "The type of the Object that can be extracted out of the asset",
+ G_TYPE_OBJECT, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
+
+ _properties[PROP_ID] =
+ g_param_spec_string ("id", "Identifier",
+ "The unic identifier of the asset", NULL,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
+
+ g_object_class_install_properties (object_class, PROP_LAST, _properties);
+
+ klass->start_loading = ges_asset_start_loading_default;
+ klass->extract = ges_asset_extract_default;
+ klass->request_id_update = ges_asset_request_id_update_default;
+ klass->inform_proxy = NULL;
+}
+
+void
+ges_asset_init (GESAsset * self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ GES_TYPE_ASSET, GESAssetPrivate);
+
+ self->priv->state = ASSET_INITIALIZING;
+ self->priv->proxied_asset_id = NULL;
+}
+
+/* Internal methods */
+
+/* Find the type that implemented the GESExtractable interface */
+static inline const gchar *
+_extractable_type_name (GType type)
+{
+ while (1) {
+ if (g_type_is_a (g_type_parent (type), GES_TYPE_EXTRACTABLE))
+ type = g_type_parent (type);
+ else
+ return g_type_name (type);
+ }
+}
+
+static inline GESAssetCacheEntry *
+_lookup_entry (GType extractable_type, const gchar * id)
+{
+ GHashTable *entries_table;
+
+ entries_table = g_hash_table_lookup (type_entries_table,
+ _extractable_type_name (extractable_type));
+ if (entries_table)
+ return g_hash_table_lookup (entries_table, id);
+
+ return NULL;
+}
+
+static void
+_free_entries (gpointer entry)
+{
+ g_slice_free (GESAssetCacheEntry, entry);
+}
+
+/**
+ * ges_asset_cache_lookup:
+ *
+ * @id String identifier of asset
+ *
+ * Looks for asset with specified id in cache and it's completely loaded.
+ *
+ * Returns: (transfer none): The #GESAsset found or %NULL
+ */
+GESAsset *
+ges_asset_cache_lookup (GType extractable_type, const gchar * id)
+{
+ GESAsset *asset = NULL;
+ GESAssetCacheEntry *entry = NULL;
+
+ g_return_val_if_fail (id, NULL);
+
+ LOCK_CACHE;
+ entry = _lookup_entry (extractable_type, id);
+ if (entry)
+ asset = entry->asset;
+ UNLOCK_CACHE;
+
+ return asset;
+}
+
+static void
+ges_asset_cache_append_result (GType extractable_type,
+ const gchar * id, GSimpleAsyncResult * res)
+{
+ GESAssetCacheEntry *entry = NULL;
+
+ LOCK_CACHE;
+ if ((entry = _lookup_entry (extractable_type, id)))
+ entry->results = g_list_append (entry->results, res);
+ UNLOCK_CACHE;
+}
+
+gboolean
+ges_asset_cache_set_loaded (GType extractable_type, const gchar * id,
+ GError * error)
+{
+ GList *tmp;
+ GESAsset *asset;
+ GESAssetCacheEntry *entry = NULL;
+
+ LOCK_CACHE;
+ if ((entry = _lookup_entry (extractable_type, id)) == NULL) {
+ UNLOCK_CACHE;
+ GST_ERROR ("Calling but type %s ID: %s not in cached, "
+ "something massively screwed", g_type_name (extractable_type), id);
+
+ return FALSE;
+ }
+
+ asset = entry->asset;
+ GST_DEBUG_OBJECT (entry->asset, ": (extractable type: %s) loaded, calling %i "
+ "callback (Error: %s)", g_type_name (asset->priv->extractable_type),
+ g_list_length (entry->results), error ? error->message : "");
+
+ if (error) {
+ GList *results = entry->results;
+
+ asset->priv->state = ASSET_INITIALIZED_WITH_ERROR;
+ asset->priv->error = g_error_copy (error);
+ entry->results = NULL;
+ UNLOCK_CACHE;
+
+ /* In case of error we do not want to emit in idle as we need to recover
+ * if possible */
+ for (tmp = results; tmp; tmp = tmp->next) {
+ g_simple_async_result_set_from_error (G_SIMPLE_ASYNC_RESULT (tmp->data),
+ error);
+ g_simple_async_result_complete (G_SIMPLE_ASYNC_RESULT (tmp->data));
+ }
+
+ g_list_free (results);
+ return TRUE;
+ } else {
+ asset->priv->state = ASSET_INITIALIZED;
+
+ g_list_free_full (entry->results,
+ (GDestroyNotify) g_simple_async_result_complete_in_idle);
+ entry->results = NULL;
+ UNLOCK_CACHE;
+ }
+
+ return TRUE;
+}
+
+void
+ges_asset_cache_put (GESAsset * asset, GSimpleAsyncResult * res)
+{
+ GType extractable_type;
+ const gchar *asset_id;
+ GESAssetCacheEntry *entry;
+
+ /* Needing to work with the cache, taking the lock */
+ asset_id = ges_asset_get_id (asset);
+ extractable_type = asset->priv->extractable_type;
+
+ LOCK_CACHE;
+ if (!(entry = _lookup_entry (extractable_type, asset_id))) {
+ GHashTable *entries_table;
+
+ entries_table = g_hash_table_lookup (type_entries_table,
+ _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_strdup (_extractable_type_name (extractable_type)), entries_table);
+ }
+
+ entry = g_slice_new0 (GESAssetCacheEntry);
+
+ entry->asset = asset;
+ if (res)
+ entry->results = g_list_prepend (entry->results, res);
+ g_hash_table_insert (entries_table, (gpointer) g_strdup (asset_id),
+ (gpointer) entry);
+ } else {
+ if (res) {
+ GST_DEBUG ("%s already in cache, adding result %p", asset_id, res);
+ entry->results = g_list_prepend (entry->results, res);
+ }
+ }
+ UNLOCK_CACHE;
+}
+
+void
+ges_asset_cache_init (void)
+{
+ g_mutex_init (&asset_cache_lock);
+ type_entries_table = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL, (GDestroyNotify) g_hash_table_unref);
+
+ _init_standard_transition_assets ();
+ _init_formatter_assets ();
+}
+
+gboolean
+ges_asset_request_id_update (GESAsset * asset, gchar ** proposed_id,
+ GError * error)
+{
+ return GES_ASSET_GET_CLASS (asset)->request_id_update (asset, proposed_id,
+ error);
+}
+
+gboolean
+ges_asset_set_proxy (GESAsset * asset, const gchar * new_id)
+{
+ GESAssetClass *class;
+ if (g_strcmp0 (asset->priv->id, new_id) == 0) {
+ GST_WARNING_OBJECT (asset, "Trying to proxy to itself (%s),"
+ " NOT possible", new_id);
+
+ return FALSE;
+ }
+
+ if (asset->priv->proxied_asset_id)
+ g_free (asset->priv->proxied_asset_id);
+
+ asset->priv->state = ASSET_PROXIED;
+ asset->priv->proxied_asset_id = g_strdup (new_id);
+
+ class = GES_ASSET_GET_CLASS (asset);
+ if (class->inform_proxy)
+ GES_ASSET_GET_CLASS (asset)->inform_proxy (asset, new_id);
+
+ GST_DEBUG_OBJECT (asset, "Now proxied to %s", new_id);
+ return TRUE;
+}
+
+/* Caution, this method should be used in rare cases (ie: for the project
+ * as we can change its ID from a useless one to a proper URI). In most
+ * cases you want to update the ID creating a proxy
+ */
+void
+ges_asset_set_id (GESAsset * asset, const gchar * id)
+{
+ GHashTable *entries;
+
+ gpointer orig_id = NULL;
+ GESAssetCacheEntry *entry = NULL;
+ GESAssetPrivate *priv = asset->priv;
+
+ if (priv->state != ASSET_INITIALIZED) {
+ GST_WARNING_OBJECT (asset, "Trying to rest ID on an object that is"
+ " not properly loaded");
+ return;
+ }
+
+ if (g_strcmp0 (id, priv->id) == 0) {
+ GST_DEBUG_OBJECT (asset, "ID is already %s", id);
+
+ return;
+ }
+
+ LOCK_CACHE;
+ entries = g_hash_table_lookup (type_entries_table,
+ _extractable_type_name (asset->priv->extractable_type));
+
+ g_hash_table_lookup_extended (entries, priv->id, &orig_id,
+ (gpointer *) & entry);
+
+ g_hash_table_steal (entries, priv->id);
+ g_hash_table_insert (entries, g_strdup (id), entry);
+
+ GST_DEBUG_OBJECT (asset, "Changing id from %s to %s", priv->id, id);
+ g_free (priv->id);
+ g_free (orig_id);
+ priv->id = g_strdup (id);
+ UNLOCK_CACHE;
+}
+
+static GESAsset *
+_unsure_material_for_wrong_id (const gchar * wrong_id, GType extractable_type,
+ GError * error)
+{
+ GESAsset *asset;
+
+ if ((asset = ges_asset_cache_lookup (extractable_type, wrong_id)))
+ return asset;
+
+ /* It is a dummy GESAsset, we just bruteforce its creation */
+ asset = g_object_new (GES_TYPE_ASSET, "id", wrong_id,
+ "extractable-type", extractable_type, NULL);
+
+ ges_asset_cache_put (asset, NULL);
+ ges_asset_cache_set_loaded (extractable_type, wrong_id, error);
+
+ return asset;
+}
+
+/**********************************
+ * *
+ * API implementation *
+ * *
+ **********************************/
+
+/**
+ * ges_asset_get_extractable_type:
+ * @self: The #GESAsset
+ *
+ * Gets the type of object that can be extracted from @self
+ *
+ * Returns: the type of object that can be extracted from @self
+ */
+GType
+ges_asset_get_extractable_type (GESAsset * self)
+{
+ return self->priv->extractable_type;
+}
+
+/**
+ * ges_asset_request:
+ * @extractable_type: The #GType of the object that can be extracted from the new asset.
+ * @id: (allow-none): The Identifier or %NULL
+ * @error: (allow-none): An error to be set in case something wrong happens or %NULL
+ *
+ * Create a #GESAsset in the most simple cases, you should look at the @extractable_type
+ * documentation to see if that constructor can be called for this particular type
+ *
+ * Note that it won't be possible to instantiate the first %GESAsset with
+ * @id depending on the @extractable_type. For example instantiate a
+ * #GESAsset that extract #GESTimelineFileSource needs to be done async
+ * the first time for a specific ID.
+ *
+ * Returns: (transfer full) (allow-none): A reference to the wanted #GESAsset or %NULL
+ */
+GESAsset *
+ges_asset_request (GType extractable_type, const gchar * id, GError ** error)
+{
+ gchar *real_id;
+
+ GError *lerr = NULL;
+ GESAsset *asset = NULL;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+ g_return_val_if_fail (g_type_is_a (extractable_type, G_TYPE_OBJECT), NULL);
+ g_return_val_if_fail (g_type_is_a (extractable_type, GES_TYPE_EXTRACTABLE),
+ NULL);
+
+ real_id = _check_and_update_parameters (&extractable_type, id, &lerr);
+ if (real_id == NULL) {
+ /* We create an asset for that wrong ID so we have a reference that the
+ * user requested it */
+ _unsure_material_for_wrong_id (id, extractable_type, lerr);
+ real_id = g_strdup (id);
+ }
+
+ asset = ges_asset_cache_lookup (extractable_type, real_id);
+ if (asset) {
+ while (TRUE) {
+ switch (asset->priv->state) {
+ case ASSET_INITIALIZED:
+ gst_object_ref (asset);
+ goto done;
+ case ASSET_INITIALIZING:
+ asset = NULL;
+ goto done;
+ case ASSET_PROXIED:
+ asset =
+ ges_asset_cache_lookup (asset->priv->extractable_type,
+ asset->priv->proxied_asset_id);
+ if (asset == NULL) {
+ GST_ERROR ("Asset against a asset we do not"
+ " have in cache, something massively screwed");
+
+ goto done;
+ }
+ break;
+ case ASSET_INITIALIZED_WITH_ERROR:
+ GST_WARNING_OBJECT (asset, "Initialized with error, not returning");
+ if (error)
+ *error = g_error_copy (asset->priv->error);
+ asset = NULL;
+ goto done;
+ default:
+ GST_WARNING ("Case %i not handle, returning", asset->priv->state);
+ goto done;
+ }
+ }
+ } else {
+ GObjectClass *klass;
+ GInitableIface *iface;
+ GType asset_type = ges_extractable_type_get_asset_type (extractable_type);
+
+ klass = g_type_class_ref (asset_type);
+ iface = g_type_interface_peek (klass, G_TYPE_INITABLE);
+
+ if (iface->init) {
+ asset = g_initable_new (asset_type,
+ NULL, NULL, "id", real_id, "extractable-type",
+ extractable_type, NULL);
+ } else {
+ GST_WARNING ("Tried to create an Asset for type %s but no ->init method",
+ g_type_name (extractable_type));
+ }
+ g_type_class_unref (klass);
+ }
+
+done:
+ if (real_id)
+ g_free (real_id);
+
+ GST_DEBUG ("New asset created synchronously: %p", asset);
+ return asset;
+}
+
+/**
+ * ges_asset_request_async:
+ * @extractable_type: The #GType of the object that can be extracted from the
+ * new asset. The class must implement the #GESExtractable interface.
+ * @id: The Identifier of the asset we want to create. This identifier depends of the extractable,
+ * type you want. By default it is the name of the class itself (or %NULL), but for example for a
+ * GESTrackParseLaunchEffect, it will be the pipeline description, for a GESTimelineFileSource it
+ * will be the name of the file, etc... You should refer to the documentation of the #GESExtractable
+ * type you want to create a #GESAsset for.
+ * @cancellable: (allow-none): optional %GCancellable object, %NULL to ignore.
+ * @callback: a #GAsyncReadyCallback to call when the initialization is finished,
+ * Note that the @source of the callback will be the #GESAsset, but you need to
+ * make sure that the asset is properly loaded using the #ges_asset_request_finish
+ * method. This asset can not be used as is.
+ * @user_data: The user data to pass when @callback is called
+ *
+ * Request a new #GESAsset asyncronously, @callback will be called when the materail is
+ * ready to be used or if an error occured.
+ *
+ * Example of request of a GESAsset async:
+ * |[
+ * // The request callback
+ * static void
+ * asset_loaded_cb (GESAsset * source, GAsyncResult * res, gpointer user_data)
+ * {
+ * GESAsset *asset;
+ * GError *error = NULL;
+ *
+ * asset = ges_asset_request_finish (res, &error);
+ * if (asset) {
+ * g_print ("The file: %s is usable as a FileSource",
+ * ges_asset_get_id (asset));
+ * } else {
+ * g_print ("The file: %s is *not* usable as a FileSource because: %s",
+ * ges_asset_get_id (source), error->message);
+ * }
+ *
+ * g_object_unref (mfs);
+ * }
+ *
+ * // The request:
+ * ges_asset_request_async (GES_TYPE_TIMELINE_FILE_SOURCE, some_uri, NULL,
+ * (GAsyncReadyCallback) asset_loaded_cb, user_data);
+ * ]|
+ */
+void
+ges_asset_request_async (GType extractable_type,
+ const gchar * id, GCancellable * cancellable, GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ gchar *real_id;
+ GESAsset *asset;
+ GError *error = NULL;
+
+ g_return_if_fail (g_type_is_a (extractable_type, G_TYPE_OBJECT));
+ g_return_if_fail (g_type_is_a (extractable_type, GES_TYPE_EXTRACTABLE));
+ g_return_if_fail (callback);
+
+ GST_DEBUG ("Creating asset with extractable type %s and ID=%s",
+ g_type_name (extractable_type), id);
+
+ real_id = _check_and_update_parameters (&extractable_type, id, &error);
+ if (error) {
+ _unsure_material_for_wrong_id (id, extractable_type, error);
+ real_id = g_strdup (id);
+ }
+
+ /* Check if we already have a asset for this ID */
+ asset = ges_asset_cache_lookup (extractable_type, real_id);
+ if (asset) {
+ GSimpleAsyncResult *simple = g_simple_async_result_new (G_OBJECT (asset),
+ callback, user_data, ges_asset_request_async);
+
+ /* In the case of proxied asset, we will loop until we find the
+ * last asset of the chain of proxied asset */
+ while (TRUE) {
+ switch (asset->priv->state) {
+ case ASSET_INITIALIZED:
+ gst_object_ref (asset);
+
+ GST_DEBUG_OBJECT (asset, "Asset in cache and initialized, "
+ "using it");
+
+ /* Takes its own references to @asset */
+ g_simple_async_result_complete_in_idle (simple);
+
+ goto done;
+ case ASSET_INITIALIZING:
+ GST_DEBUG_OBJECT (asset, "Asset in cache and but not "
+ "initialized, setting a new callback");
+ ges_asset_cache_append_result (extractable_type, real_id, simple);
+
+ goto done;
+ case ASSET_PROXIED:
+ asset =
+ ges_asset_cache_lookup (asset->priv->extractable_type,
+ asset->priv->proxied_asset_id);
+ if (asset == NULL) {
+ GST_ERROR ("Asset proxied against a asset we do not"
+ " have in cache, something massively screwed");
+
+ goto done;
+ }
+ break;
+ case ASSET_INITIALIZED_WITH_ERROR:
+ g_simple_async_report_gerror_in_idle (G_OBJECT (asset), callback,
+ user_data, error ? error : asset->priv->error);
+
+ if (error)
+ g_error_free (error);
+ goto done;
+ default:
+ GST_WARNING ("Case %i not handle, returning", asset->priv->state);
+ return;
+ }
+ }
+ }
+
+ g_async_initable_new_async (ges_extractable_type_get_asset_type
+ (extractable_type), G_PRIORITY_DEFAULT, cancellable, callback, user_data,
+ "id", real_id, "extractable-type", extractable_type, NULL);
+done:
+ if (real_id)
+ g_free (real_id);
+}
+
+/**
+ * ges_asset_get_id:
+ * @self: The #GESAsset to get ID from
+ *
+ * Gets the ID of a #GESAsset
+ *
+ * Returns: The ID of @self
+ */
+const gchar *
+ges_asset_get_id (GESAsset * self)
+{
+ g_return_val_if_fail (GES_IS_ASSET (self), NULL);
+
+ return self->priv->id;
+}
+
+/**
+ * ges_asset_extract:
+ * @self: The #GESAsset to get extract an object from
+ * @error: (allow-none): An error to be set in case something wrong happens or %NULL
+ *
+ * Extracts a new #GObject from @asset. The type of the object is
+ * defined by the extractable-type of @asset, you can check what
+ * type will be extracted from @asset using
+ * #ges_asset_get_extractable_type
+ *
+ * Returns: (transfer floating) (allow-none): A newly created #GESExtractable
+ */
+GESExtractable *
+ges_asset_extract (GESAsset * self, GError ** error)
+{
+ GESExtractable *extractable;
+
+ g_return_val_if_fail (GES_IS_ASSET (self), NULL);
+ g_return_val_if_fail (GES_ASSET_GET_CLASS (self)->extract, NULL);
+
+ GST_DEBUG_OBJECT (self, "Extracting asset of type %s",
+ g_type_name (self->priv->extractable_type));
+ extractable = GES_ASSET_GET_CLASS (self)->extract (self, error);
+
+ if (extractable == NULL)
+ return NULL;
+
+ ges_extractable_set_asset (extractable, self);
+
+ return extractable;
+}
+
+/**
+ * ges_asset_request_finish:
+ * @res: The #GAsyncResult from which to get the newly created #GESAsset
+ * @error: (out) (allow-none) (transfer full): An error to be set in case
+ * something wrong happens or %NULL
+ *
+ * Finalize the request of an async #GESAsset
+ *
+ * Returns: (transfer full)(allow-none): The #GESAsset previously requested
+ */
+GESAsset *
+ges_asset_request_finish (GAsyncResult * res, GError ** error)
+{
+ GObject *object;
+ GObject *source_object;
+
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
+
+ source_object = g_async_result_get_source_object (res);
+ g_assert (source_object != NULL);
+
+ object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object),
+ res, error);
+
+ g_object_unref (source_object);
+
+ return GES_ASSET (object);
+}
+
+/**
+ * ges_list_assets:
+ * @filter: Type of assets to list, #GES_TYPE_EXTRACTABLE will list
+ * all assets
+ *
+ * List all @asset filtering per filter as defined by @filter.
+ * It copies the asset and thus will not be updated in time.
+ *
+ * Returns: (transfer container) (element-type GESAsset): The list of
+ * #GESAsset the object contains
+ */
+GList *
+ges_list_assets (GType filter)
+{
+ GList *ret = NULL;
+ GESAsset *asset;
+ GHashTableIter iter, types_iter;
+ gpointer key, value, typename, assets;
+
+ 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);
+ while (g_hash_table_iter_next (&types_iter, &typename, &assets)) {
+ if (g_type_is_a (filter, g_type_from_name ((gchar *) typename)) == FALSE)
+ continue;
+
+ g_hash_table_iter_init (&iter, (GHashTable *) assets);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ asset = ((GESAssetCacheEntry *) value)->asset;
+
+ if (g_type_is_a (asset->priv->extractable_type, filter))
+ ret = g_list_append (ret, asset);
+ }
+ }
+ UNLOCK_CACHE;
+
+ return ret;
+}
diff --git a/ges/ges-asset.h b/ges/ges-asset.h
new file mode 100644
index 0000000000..2232e0647a
--- /dev/null
+++ b/ges/ges-asset.h
@@ -0,0 +1,98 @@
+/* GStreamer Editing Services
+ *
+ * Copyright (C) 2012 Thibault Saunier
+ * Copyright (C) 2012 Volodymyr Rudyi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _GES_ASSET_
+#define _GES_ASSET_
+
+#include "ges-internal-enums.h"
+
+#include
+#include
+#include
+#include
+#include
+
+G_BEGIN_DECLS
+#define GES_TYPE_ASSET ges_asset_get_type()
+#define GES_ASSET(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_ASSET, GESAsset))
+#define GES_ASSET_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_ASSET, GESAssetClass))
+#define GES_IS_ASSET(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_ASSET))
+#define GES_IS_ASSET_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_ASSET))
+#define GES_ASSET_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_ASSET, GESAssetClass))
+
+typedef struct _GESAssetPrivate GESAssetPrivate;
+
+GType ges_asset_get_type (void);
+
+struct _GESAsset
+{
+ GObject parent;
+
+ /* */
+ GESAssetPrivate *priv;
+
+ /* Padding for API extension */
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+struct _GESAssetClass
+{
+ GObjectClass parent;
+
+ GESAssetLoadingReturn (*start_loading) (GESAsset *self,
+ GError **error);
+ GESExtractable* (*extract) (GESAsset *self,
+ GError **error);
+ /* Let subclasses know that we proxied an asset */
+ void (*inform_proxy) (GESAsset *self,
+ const gchar *proxy_id);
+ /* Ask subclasses for a new ID for @self when the asset failed loading
+ * This function returns %FALSE when the ID could be updated or %TRUE
+ * otherwize */
+ gboolean (*request_id_update) (GESAsset *self,
+ gchar **proposed_new_id,
+ GError *error) ;
+ gpointer _ges_reserved[GES_PADDING];
+};
+
+GType ges_asset_get_extractable_type (GESAsset * self);
+void ges_asset_request_async (GType extractable_type,
+ const gchar * id,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GESAsset * ges_asset_request (GType extractable_type,
+ const gchar * id,
+ GError **error);
+const gchar * ges_asset_get_id (GESAsset* self);
+GESAsset * ges_asset_request_finish (GAsyncResult *res,
+ GError **error);
+GESExtractable * ges_asset_extract (GESAsset * self,
+ GError **error);
+GList * ges_list_assets (GType filter);
+
+G_END_DECLS
+#endif /* _GES_ASSET */
diff --git a/ges/ges-internal-enums.h b/ges/ges-internal-enums.h
new file mode 100644
index 0000000000..fde0e6d28b
--- /dev/null
+++ b/ges/ges-internal-enums.h
@@ -0,0 +1,31 @@
+/* GStreamer Editing Services
+ *
+ * Copyright (C) 2012 Thibault Saunier
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GES_INTERNAL_ENUMS_H__
+#define __GES_INTERNAL_ENUMS_H__
+
+typedef enum
+{
+ GES_ASSET_LOADING_ERROR,
+ GES_ASSET_LOADING_ASYNC,
+ GES_ASSET_LOADING_OK
+} GESAssetLoadingReturn;
+
+#endif /* __GES_INTERNAL_ENUMS_H__ */
diff --git a/ges/ges-internal.h b/ges/ges-internal.h
index 15ca0587b1..7fc59146a8 100644
--- a/ges/ges-internal.h
+++ b/ges/ges-internal.h
@@ -22,9 +22,15 @@
#define __GES_INTERNAL_H__
#include
+#include
+
#include "ges-timeline.h"
#include "ges-track-object.h"
+#if 0
+#include "ges-asset.h"
+#endif
+
GST_DEBUG_CATEGORY_EXTERN (_ges_debug);
#define GST_CAT_DEFAULT _ges_debug
@@ -59,4 +65,27 @@ timeline_move_object (GESTimeline *timeline, GESTrackObject * object,
gboolean
timeline_context_to_layer (GESTimeline *timeline, gint offset);
+#if 0
+G_GNUC_INTERNAL void
+ges_asset_cache_init (void);
+
+G_GNUC_INTERNAL void
+ges_asset_set_id (GESAsset *asset, const gchar *id);
+
+G_GNUC_INTERNAL void
+ges_asset_cache_put (GESAsset * asset, GSimpleAsyncResult *res);
+
+G_GNUC_INTERNAL gboolean
+ges_asset_cache_set_loaded(GType extractable_type, const gchar * id, GError *error);
+
+GESAsset*
+ges_asset_cache_lookup(GType extractable_type, const gchar * id);
+
+gboolean
+ges_asset_set_proxy (GESAsset *asset, const gchar *new_id);
+
+G_GNUC_INTERNAL gboolean
+ges_asset_request_id_update (GESAsset *asset, gchar **proposed_id,
+ GError *error);
+#endif
#endif /* __GES_INTERNAL_H__ */
diff --git a/ges/ges-types.h b/ges/ges-types.h
index f677d8a8d8..a7bc701eed 100644
--- a/ges/ges-types.h
+++ b/ges/ges-types.h
@@ -140,4 +140,9 @@ typedef struct _GESKeyfileFormatterClass GESKeyfileFormatterClass;
typedef struct _GESPitiviFormatter GESPitiviFormatter;
typedef struct _GESPitiviFormatterClass GESPitiviFormatterClass;
+#if 0
+typedef struct _GESAsset GESAsset;
+typedef struct _GESAssetClass GESAssetClass;
+#endif
+
#endif /* __GES_TYPES_H__ */
diff --git a/ges/ges.h b/ges/ges.h
index 67e8ee1895..86457885d8 100644
--- a/ges/ges.h
+++ b/ges/ges.h
@@ -45,6 +45,9 @@
#include
#include
#include
+#if 0
+#include
+#endif
#include
#include
@@ -79,6 +82,8 @@ gboolean ges_init (void);
void ges_version (guint * major, guint * minor, guint * micro,
guint * nano);
+#define GES_ERROR_DOMAIN g_quark_from_static_string("GES")
+
G_END_DECLS
#endif /* __GES_H__ */