ges: Port to gst_print*
[platform/upstream/gst-editing-services.git] / ges / ges-asset.c
1 /* GStreamer Editing Services
2  *
3  * Copyright (C) 2012-2015 Thibault Saunier <thibault.saunier@collabora.com>
4  * Copyright (C) 2012 Volodymyr Rudyi <vladimir.rudoy@gmail.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21 /**
22  * SECTION: gesasset
23  * @title: GESAsset
24  * @short_description: Represents usable resources inside the GStreamer
25  * Editing Services
26  *
27  * A #GESAsset in the GStreamer Editing Services represents a resources
28  * that can be used. In particular, any class that implements the
29  * #GESExtractable interface may have some associated assets with a
30  * corresponding #GESAsset:extractable-type, from which its objects can be
31  * extracted using ges_asset_extract(). Some examples would be
32  * #GESClip, #GESFormatter and #GESTrackElement.
33  *
34  * All assets that are created within GES are stored in a cache; one per
35  * each #GESAsset:id and #GESAsset:extractable-type pair. These assets can
36  * be fetched, and initialized if they do not yet exist in the cache,
37  * using ges_asset_request().
38  *
39  * ``` c
40  * GESAsset *effect_asset;
41  * GESEffect *effect;
42  *
43  * // You create an asset for an effect
44  * effect_asset = ges_asset_request (GES_TYPE_EFFECT, "agingtv", NULL);
45  *
46  * // And now you can extract an instance of GESEffect from that asset
47  * effect = GES_EFFECT (ges_asset_extract (effect_asset));
48  *
49  * ```
50  *
51  * The advantage of using assets, rather than simply creating the object
52  * directly, is that the currently loaded resources can be listed with
53  * ges_list_assets() and displayed to an end user. For example, to show
54  * which media files have been loaded, and a standard list of effects. In
55  * fact, the GES library already creates assets for #GESTransitionClip and
56  * #GESFormatter, which you can use to list all the available transition
57  * types and supported formats.
58  *
59  * The other advantage is that #GESAsset implements #GESMetaContainer, so
60  * metadata can be set on the asset, with some subclasses automatically
61  * creating this metadata on initiation.
62  *
63  * For example, to display information about the supported formats, you
64  * could do the following:
65  * |[
66  *    GList *formatter_assets, *tmp;
67  *
68  *    //  List all  the transitions
69  *    formatter_assets = ges_list_assets (GES_TYPE_FORMATTER);
70  *
71  *    // Print some infos about the formatter GESAsset
72  *    for (tmp = formatter_assets; tmp; tmp = tmp->next) {
73  *      gst_print ("Name of the formatter: %s, file extension it produces: %s",
74  *        ges_meta_container_get_string (
75  *          GES_META_CONTAINER (tmp->data), GES_META_FORMATTER_NAME),
76  *        ges_meta_container_get_string (
77  *          GES_META_CONTAINER (tmp->data), GES_META_FORMATTER_EXTENSION));
78  *    }
79  *
80  *    g_list_free (transition_assets);
81  *
82  * ]|
83  *
84  * ## ID
85  *
86  * Each asset is uniquely defined in the cache by its
87  * #GESAsset:extractable-type and #GESAsset:id. Depending on the
88  * #GESAsset:extractable-type, the #GESAsset:id can be used to parametrise
89  * the creation of the object upon extraction. By default, a class that
90  * implements #GESExtractable will only have a single associated asset,
91  * with an #GESAsset:id set to the type name of its objects. However, this
92  * is overwritten by some implementations, which allow a class to have
93  * multiple associated assets. For example, for #GESTransitionClip the
94  * #GESAsset:id will be a nickname of the #GESTransitionClip:vtype. You
95  * should check the documentation for each extractable type to see if they
96  * differ from the default.
97  *
98  * Moreover, each #GESAsset:extractable-type may also associate itself
99  * with a specific asset subclass. In such cases, when their asset is
100  * requested, an asset of this subclass will be returned instead.
101  *
102  * ## Managing
103  *
104  * You can use a #GESProject to easily manage the assets of a
105  * #GESTimeline.
106  *
107  * ## Proxies
108  *
109  * Some assets can (temporarily) act as the #GESAsset:proxy of another
110  * asset. When the original asset is requested from the cache, the proxy
111  * will be returned in its place. This can be useful if, say, you want
112  * to substitute a #GESUriClipAsset corresponding to a high resolution
113  * media file with the asset of a lower resolution stand in.
114  *
115  * An asset may even have several proxies, the first of which will act as
116  * its default and be returned on requests, but the others will be ordered
117  * to take its place once it is removed. You can add a proxy to an asset,
118  * or set its default, using ges_asset_set_proxy(), and you can remove
119  * them with ges_asset_unproxy().
120  */
121 #ifdef HAVE_CONFIG_H
122 #include "config.h"
123 #endif
124
125 #include "ges.h"
126 #include "ges-internal.h"
127
128 #define GLIB_DISABLE_DEPRECATION_WARNINGS
129
130 #include <gst/gst.h>
131
132 GST_DEBUG_CATEGORY_STATIC (ges_asset_debug);
133 #undef GST_CAT_DEFAULT
134 #define GST_CAT_DEFAULT ges_asset_debug
135
136 enum
137 {
138   PROP_0,
139   PROP_TYPE,
140   PROP_ID,
141   PROP_PROXY,
142   PROP_PROXY_TARGET,
143   PROP_LAST
144 };
145
146 typedef enum
147 {
148   ASSET_NOT_INITIALIZED,
149   ASSET_INITIALIZING, ASSET_INITIALIZED_WITH_ERROR,
150   ASSET_PROXIED,
151   ASSET_NEEDS_RELOAD,
152   ASSET_INITIALIZED
153 } GESAssetState;
154
155 static GParamSpec *_properties[PROP_LAST];
156
157 struct _GESAssetPrivate
158 {
159   gchar *id;
160   GESAssetState state;
161   GType extractable_type;
162
163   /* used internally by try_proxy to pre-set a proxy whilst an asset is
164    * still loading. It can be used later to set the proxy for the asset
165    * once it has finished loading */
166   char *proxied_asset_id;
167
168   /* actual list of proxies */
169   GList *proxies;
170   /* the asset whose proxies list we belong to */
171   GESAsset *proxy_target;
172
173   /* The error that occurred when an asset has been initialized with error */
174   GError *error;
175 };
176
177 /* Internal structure to help avoid full loading
178  * of one asset several times
179  */
180 typedef struct
181 {
182   GList *results;
183   GESAsset *asset;
184 } GESAssetCacheEntry;
185
186 /* We are mapping entries by types and ID, such as:
187  *
188  * {
189  *   first_extractable_type_name1 :
190  *      {
191  *        "some ID": GESAssetCacheEntry,
192  *        "some other ID": GESAssetCacheEntry 2
193  *      },
194  *   second_extractable_type_name :
195  *      {
196  *        "some ID": GESAssetCacheEntry,
197  *        "some other ID": GESAssetCacheEntry 2
198  *      }
199  * }
200  *
201  * (The first extractable type is the type of the class that implemented
202  *  the GESExtractable interface ie: GESClip, GESTimeline,
203  *  GESFomatter, etc... but not subclasses)
204  *
205  * This is in order to be able to have 2 Asset with the same ID but
206  * different extractable types.
207  **/
208 static GHashTable *type_entries_table = NULL;
209 /* Protect all the entries in the cache */
210 static GRecMutex asset_cache_lock;
211 #define LOCK_CACHE   (g_rec_mutex_lock (&asset_cache_lock))
212 #define UNLOCK_CACHE (g_rec_mutex_unlock (&asset_cache_lock))
213
214 static gchar *
215 _check_and_update_parameters (GType * extractable_type, const gchar * id,
216     GError ** error)
217 {
218   gchar *real_id;
219   GType old_type = *extractable_type;
220
221   *extractable_type =
222       ges_extractable_get_real_extractable_type_for_id (*extractable_type, id);
223
224   if (*extractable_type == G_TYPE_NONE) {
225     GST_WARNING ("No way to create a Asset for ID: %s, type: %s", id,
226         g_type_name (old_type));
227
228     if (error && *error == NULL)
229       g_set_error (error, GES_ERROR, GES_ERROR_ASSET_WRONG_ID,
230           "Wrong ID, can not find any extractable_type");
231     return NULL;
232   }
233
234   real_id = ges_extractable_type_check_id (*extractable_type, id, error);
235   if (real_id == NULL) {
236     GST_WARNING ("Wrong ID %s, can not create asset", id);
237
238     g_free (real_id);
239     if (error && *error == NULL)
240       g_set_error (error, GES_ERROR, GES_ERROR_ASSET_WRONG_ID, "Wrong ID");
241
242     return NULL;
243   }
244
245   return real_id;
246 }
247
248 /* FIXME: why are we not accepting a GError ** error argument, which we
249  * could pass to ges_asset_cache_set_loaded ()? Which would allow the
250  * error to be set for the GInitable init method below */
251 static gboolean
252 start_loading (GESAsset * asset)
253 {
254   GInitableIface *iface;
255
256   iface = g_type_interface_peek (GES_ASSET_GET_CLASS (asset), G_TYPE_INITABLE);
257
258   if (!iface->init) {
259     GST_INFO_OBJECT (asset, "Can not start loading sync, as no ->init vmethod");
260     return FALSE;
261   }
262
263   ges_asset_cache_put (gst_object_ref (asset), NULL);
264   return ges_asset_cache_set_loaded (asset->priv->extractable_type,
265       asset->priv->id, NULL);
266 }
267
268 static gboolean
269 initable_init (GInitable * initable, GCancellable * cancellable,
270     GError ** error)
271 {
272   /* FIXME: Is there actually a reason to be freeing the GError that
273    * error points to? */
274   g_clear_error (error);
275
276   return start_loading (GES_ASSET (initable));
277 }
278
279 static void
280 async_initable_init_async (GAsyncInitable * initable, gint io_priority,
281     GCancellable * cancellable, GAsyncReadyCallback callback,
282     gpointer user_data)
283 {
284   GTask *task;
285
286   GError *error = NULL;
287   GESAsset *asset = GES_ASSET (initable);
288
289   task = g_task_new (asset, cancellable, callback, user_data);
290
291   ges_asset_cache_put (gst_object_ref (asset), task);
292   switch (GES_ASSET_GET_CLASS (asset)->start_loading (asset, &error)) {
293     case GES_ASSET_LOADING_ERROR:
294     {
295       if (error == NULL)
296         g_set_error (&error, GES_ERROR, GES_ERROR_ASSET_LOADING,
297             "Could not start loading asset");
298
299       /* FIXME Define error code */
300       ges_asset_cache_set_loaded (asset->priv->extractable_type,
301           asset->priv->id, error);
302       g_error_free (error);
303       return;
304     }
305     case GES_ASSET_LOADING_OK:
306     {
307       ges_asset_cache_set_loaded (asset->priv->extractable_type,
308           asset->priv->id, error);
309       return;
310     }
311     case GES_ASSET_LOADING_ASYNC:
312       /* If Async....  let it go */
313       /* FIXME: how are user subclasses that implement ->start_loading
314        * to return GES_ASSET_LOADING_ASYNC meant to invoke the private
315        * method ges_asset_cache_set_loaded once they finish initializing?
316        */
317       break;
318   }
319 }
320
321 static void
322 async_initable_iface_init (GAsyncInitableIface * async_initable_iface)
323 {
324   async_initable_iface->init_async = async_initable_init_async;
325 }
326
327 static void
328 initable_iface_init (GInitableIface * initable_iface)
329 {
330   initable_iface->init = initable_init;
331 }
332
333 G_DEFINE_TYPE_WITH_CODE (GESAsset, ges_asset, G_TYPE_OBJECT,
334     G_ADD_PRIVATE (GESAsset)
335     G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init)
336     G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)
337     G_IMPLEMENT_INTERFACE (GES_TYPE_META_CONTAINER, NULL));
338
339 /* GESAsset virtual methods default implementation */
340 static GESAssetLoadingReturn
341 ges_asset_start_loading_default (GESAsset * asset, GError ** error)
342 {
343   return GES_ASSET_LOADING_OK;
344 }
345
346 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
347 static GESExtractable *
348 ges_asset_extract_default (GESAsset * asset, GError ** error)
349 {
350   guint n_params;
351   GParameter *params;
352   GESAssetPrivate *priv = asset->priv;
353   GESExtractable *n_extractable;
354   gint i;
355   GValue *values;
356   const gchar **names;
357
358   params = ges_extractable_type_get_parameters_from_id (priv->extractable_type,
359       priv->id, &n_params);
360
361
362   values = g_malloc0 (sizeof (GValue) * n_params);
363   names = g_malloc0 (sizeof (gchar *) * n_params);
364
365   for (i = 0; i < n_params; i++) {
366     values[i] = params[i].value;
367     names[i] = params[i].name;
368   }
369
370   n_extractable =
371       GES_EXTRACTABLE (g_object_new_with_properties (priv->extractable_type,
372           n_params, names, values));
373   g_free (names);
374   g_free (values);
375
376   while (n_params--)
377     g_value_unset (&params[n_params].value);
378
379   g_free (params);
380
381   return n_extractable;
382 }
383
384 G_GNUC_END_IGNORE_DEPRECATIONS;
385
386 static gboolean
387 ges_asset_request_id_update_default (GESAsset * self, gchar ** proposed_new_id,
388     GError * error)
389 {
390   return FALSE;
391 }
392
393 /* GObject virtual methods implementation */
394 static void
395 ges_asset_get_property (GObject * object, guint property_id,
396     GValue * value, GParamSpec * pspec)
397 {
398   GESAsset *asset = GES_ASSET (object);
399
400   switch (property_id) {
401     case PROP_TYPE:
402       g_value_set_gtype (value, asset->priv->extractable_type);
403       break;
404     case PROP_ID:
405       g_value_set_string (value, asset->priv->id);
406       break;
407     case PROP_PROXY:
408       g_value_set_object (value, ges_asset_get_proxy (asset));
409       break;
410     case PROP_PROXY_TARGET:
411       g_value_set_object (value, ges_asset_get_proxy_target (asset));
412       break;
413     default:
414       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
415   }
416 }
417
418 static void
419 ges_asset_set_property (GObject * object, guint property_id,
420     const GValue * value, GParamSpec * pspec)
421 {
422   GESAsset *asset = GES_ASSET (object);
423
424   switch (property_id) {
425     case PROP_TYPE:
426       asset->priv->extractable_type = g_value_get_gtype (value);
427       /* NOTE: we calling this in the setter so metadata is set on the
428        * asset upon initiation, but before it has been loaded. */
429       ges_extractable_register_metas (asset->priv->extractable_type, asset);
430       break;
431     case PROP_ID:
432       asset->priv->id = g_value_dup_string (value);
433       break;
434     case PROP_PROXY:
435       ges_asset_set_proxy (asset, g_value_get_object (value));
436       break;
437     default:
438       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
439   }
440 }
441
442 static void
443 ges_asset_finalize (GObject * object)
444 {
445   GESAssetPrivate *priv = GES_ASSET (object)->priv;
446
447   GST_LOG_OBJECT (object, "finalizing");
448
449   if (priv->id)
450     g_free (priv->id);
451
452   if (priv->proxied_asset_id)
453     g_free (priv->proxied_asset_id);
454
455   if (priv->error)
456     g_error_free (priv->error);
457
458   if (priv->proxies)
459     g_list_free (priv->proxies);
460
461   G_OBJECT_CLASS (ges_asset_parent_class)->finalize (object);
462 }
463
464 void
465 ges_asset_class_init (GESAssetClass * klass)
466 {
467   GObjectClass *object_class = G_OBJECT_CLASS (klass);
468
469   object_class->get_property = ges_asset_get_property;
470   object_class->set_property = ges_asset_set_property;
471   object_class->finalize = ges_asset_finalize;
472
473   /**
474    * GESAsset:extractable-type:
475    *
476    * The #GESExtractable object type that can be extracted from the asset.
477    */
478   _properties[PROP_TYPE] =
479       g_param_spec_gtype ("extractable-type", "Extractable type",
480       "The type of the Object that can be extracted out of the asset",
481       G_TYPE_OBJECT, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
482
483   /**
484    * GESAsset:id:
485    *
486    * The ID of the asset. This should be unique amongst all assets with
487    * the same #GESAsset:extractable-type. Depending on the associated
488    * #GESExtractable implementation, this id may convey some information
489    * about the #GObject that should be extracted. Note that, as such, the
490    * ID will have an expected format, and you can not choose this value
491    * arbitrarily. By default, this will be set to the type name of the
492    * #GESAsset:extractable-type, but you should check the documentation
493    * of the extractable type to see whether they differ from the
494    * default behaviour.
495    */
496   _properties[PROP_ID] =
497       g_param_spec_string ("id", "Identifier",
498       "The unique identifier of the asset", NULL,
499       G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
500
501   /**
502    * GESAsset:proxy:
503    *
504    * The default proxy for this asset, or %NULL if it has no proxy. A
505    * proxy will act as a substitute for the original asset when the
506    * original is requested (see ges_asset_request()).
507    *
508    * Setting this property will not usually remove the existing proxy, but
509    * will replace it as the default (see ges_asset_set_proxy()).
510    */
511   _properties[PROP_PROXY] =
512       g_param_spec_object ("proxy", "Proxy",
513       "The asset default proxy.", GES_TYPE_ASSET, G_PARAM_READWRITE);
514
515   /**
516    * GESAsset:proxy-target:
517    *
518    * The asset that this asset is a proxy for, or %NULL if it is not a
519    * proxy for another asset.
520    *
521    * Note that even if this asset is acting as a proxy for another asset,
522    * but this asset is not the default #GESAsset:proxy, then @proxy-target
523    * will *still* point to this other asset. So you should check the
524    * #GESAsset:proxy property of @target-proxy before assuming it is the
525    * current default proxy for the target.
526    *
527    * Note that the #GObject::notify for this property is emitted after
528    * the #GESAsset:proxy #GObject::notify for the corresponding (if any)
529    * asset it is now the proxy of/no longer the proxy of.
530    */
531   _properties[PROP_PROXY_TARGET] =
532       g_param_spec_object ("proxy-target", "Proxy target",
533       "The target of a proxy asset.", GES_TYPE_ASSET, G_PARAM_READABLE);
534
535   g_object_class_install_properties (object_class, PROP_LAST, _properties);
536
537   klass->start_loading = ges_asset_start_loading_default;
538   klass->extract = ges_asset_extract_default;
539   klass->request_id_update = ges_asset_request_id_update_default;
540   klass->inform_proxy = NULL;
541
542   GST_DEBUG_CATEGORY_INIT (ges_asset_debug, "ges-asset",
543       GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GES Asset");
544 }
545
546 void
547 ges_asset_init (GESAsset * self)
548 {
549   self->priv = ges_asset_get_instance_private (self);
550
551   self->priv->state = ASSET_INITIALIZING;
552   self->priv->proxied_asset_id = NULL;
553 }
554
555 /* Internal methods */
556
557 static inline const gchar *
558 _extractable_type_name (GType type)
559 {
560   /* We can use `ges_asset_request (GES_TYPE_FORMATTER);` */
561   if (g_type_is_a (type, GES_TYPE_FORMATTER))
562     return g_type_name (GES_TYPE_FORMATTER);
563
564   return g_type_name (type);
565 }
566
567 static void
568 ges_asset_cache_init_unlocked (void)
569 {
570   if (type_entries_table)
571     return;
572
573   type_entries_table = g_hash_table_new_full (g_str_hash, g_str_equal,
574       g_free, (GDestroyNotify) g_hash_table_unref);
575
576   _init_formatter_assets ();
577   _init_standard_transition_assets ();
578 }
579
580
581 /* WITH LOCK_CACHE */
582 static GHashTable *
583 _get_type_entries (void)
584 {
585   if (type_entries_table)
586     return type_entries_table;
587
588   ges_asset_cache_init_unlocked ();
589
590   return type_entries_table;
591 }
592
593 /* WITH LOCK_CACHE */
594 static inline GESAssetCacheEntry *
595 _lookup_entry (GType extractable_type, const gchar * id)
596 {
597   GHashTable *entries_table;
598
599   entries_table = g_hash_table_lookup (_get_type_entries (),
600       _extractable_type_name (extractable_type));
601   if (entries_table)
602     return g_hash_table_lookup (entries_table, id);
603
604   return NULL;
605 }
606
607 static void
608 _free_entries (gpointer entry)
609 {
610   GESAssetCacheEntry *data = (GESAssetCacheEntry *) entry;
611   if (data->asset)
612     gst_object_unref (data->asset);
613   g_slice_free (GESAssetCacheEntry, entry);
614 }
615
616 static void
617 _gtask_return_error (GTask * task, GError * error)
618 {
619   g_task_return_error (task, g_error_copy (error));
620 }
621
622 static void
623 _gtask_return_true (GTask * task, gpointer udata)
624 {
625   g_task_return_boolean (task, TRUE);
626 }
627
628 /**
629  * ges_asset_cache_lookup:
630  *
631  * @id String identifier of asset
632  *
633  * Looks for asset with specified id in cache and it's completely loaded.
634  *
635  * Returns: (transfer none) (nullable): The #GESAsset found or %NULL
636  */
637 GESAsset *
638 ges_asset_cache_lookup (GType extractable_type, const gchar * id)
639 {
640   GESAsset *asset = NULL;
641   GESAssetCacheEntry *entry = NULL;
642
643   g_return_val_if_fail (id, NULL);
644
645   LOCK_CACHE;
646   entry = _lookup_entry (extractable_type, id);
647   if (entry)
648     asset = entry->asset;
649   UNLOCK_CACHE;
650
651   return asset;
652 }
653
654 static void
655 ges_asset_cache_append_task (GType extractable_type,
656     const gchar * id, GTask * task)
657 {
658   GESAssetCacheEntry *entry = NULL;
659
660   LOCK_CACHE;
661   if ((entry = _lookup_entry (extractable_type, id)))
662     entry->results = g_list_append (entry->results, task);
663   UNLOCK_CACHE;
664 }
665
666 gboolean
667 ges_asset_cache_set_loaded (GType extractable_type, const gchar * id,
668     GError * error)
669 {
670   GESAsset *asset;
671   GESAssetCacheEntry *entry = NULL;
672   GList *results = NULL;
673   GFunc user_func = NULL;
674   gpointer user_data = NULL;
675
676   LOCK_CACHE;
677   if ((entry = _lookup_entry (extractable_type, id)) == NULL) {
678     UNLOCK_CACHE;
679     GST_ERROR ("Calling but type %s ID: %s not in cached, "
680         "something massively screwed", g_type_name (extractable_type), id);
681
682     return FALSE;
683   }
684
685   asset = entry->asset;
686   GST_DEBUG_OBJECT (entry->asset, ": (extractable type: %s) loaded, calling %i "
687       "callback (Error: %s)", g_type_name (asset->priv->extractable_type),
688       g_list_length (entry->results), error ? error->message : "");
689
690   results = entry->results;
691   entry->results = NULL;
692
693   if (error) {
694     asset->priv->state = ASSET_INITIALIZED_WITH_ERROR;
695     if (asset->priv->error)
696       g_error_free (asset->priv->error);
697     asset->priv->error = g_error_copy (error);
698
699     /* In case of error we do not want to emit in idle as we need to recover
700      * if possible */
701     user_func = (GFunc) _gtask_return_error;
702     user_data = error;
703     GST_DEBUG_OBJECT (asset, "initialized with error");
704   } else {
705     asset->priv->state = ASSET_INITIALIZED;
706     user_func = (GFunc) _gtask_return_true;
707     GST_DEBUG_OBJECT (asset, "initialized");
708   }
709   UNLOCK_CACHE;
710
711   g_list_foreach (results, user_func, user_data);
712   g_list_free_full (results, g_object_unref);
713
714   return TRUE;
715 }
716
717 /* transfer full for both @asset and @task */
718 void
719 ges_asset_cache_put (GESAsset * asset, GTask * task)
720 {
721   GType extractable_type;
722   const gchar *asset_id;
723   GESAssetCacheEntry *entry;
724
725   /* Needing to work with the cache, taking the lock */
726   asset_id = ges_asset_get_id (asset);
727   extractable_type = asset->priv->extractable_type;
728
729   LOCK_CACHE;
730   if (!(entry = _lookup_entry (extractable_type, asset_id))) {
731     GHashTable *entries_table;
732
733     entries_table = g_hash_table_lookup (_get_type_entries (),
734         _extractable_type_name (extractable_type));
735     if (entries_table == NULL) {
736       entries_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
737           _free_entries);
738
739       g_hash_table_insert (_get_type_entries (),
740           g_strdup (_extractable_type_name (extractable_type)), entries_table);
741     }
742
743     entry = g_slice_new0 (GESAssetCacheEntry);
744
745     /* transfer asset to entry */
746     entry->asset = asset;
747     if (task)
748       entry->results = g_list_prepend (entry->results, task);
749     g_hash_table_insert (entries_table, (gpointer) g_strdup (asset_id),
750         (gpointer) entry);
751   } else {
752     /* give up the reference we were given */
753     gst_object_unref (asset);
754     if (task) {
755       GST_DEBUG ("%s already in cache, adding result %p", asset_id, task);
756       entry->results = g_list_prepend (entry->results, task);
757     }
758   }
759   UNLOCK_CACHE;
760 }
761
762 void
763 ges_asset_cache_init (void)
764 {
765   LOCK_CACHE;
766   ges_asset_cache_init_unlocked ();
767   UNLOCK_CACHE;
768 }
769
770 void
771 ges_asset_cache_deinit (void)
772 {
773   _deinit_formatter_assets ();
774
775   LOCK_CACHE;
776   g_hash_table_destroy (type_entries_table);
777   type_entries_table = NULL;
778   UNLOCK_CACHE;
779 }
780
781 gboolean
782 ges_asset_request_id_update (GESAsset * asset, gchar ** proposed_id,
783     GError * error)
784 {
785   g_return_val_if_fail (GES_IS_ASSET (asset), FALSE);
786
787   return GES_ASSET_GET_CLASS (asset)->request_id_update (asset, proposed_id,
788       error);
789 }
790
791 /* pre-set a proxy id whilst the asset is still loading. Once the proxy
792  * is loaded, call ges_asset_finish_proxy (proxy) */
793 gboolean
794 ges_asset_try_proxy (GESAsset * asset, const gchar * new_id)
795 {
796   GESAssetClass *class;
797
798   g_return_val_if_fail (GES_IS_ASSET (asset), FALSE);
799
800   if (g_strcmp0 (asset->priv->id, new_id) == 0) {
801     GST_WARNING_OBJECT (asset, "Trying to proxy to itself (%s),"
802         " NOT possible", new_id);
803
804     return FALSE;
805   } else if (g_strcmp0 (asset->priv->proxied_asset_id, new_id) == 0) {
806     GST_WARNING_OBJECT (asset,
807         "Trying to proxy to same currently set proxy: %s -- %s",
808         asset->priv->proxied_asset_id, new_id);
809
810     return FALSE;
811   }
812
813   g_free (asset->priv->proxied_asset_id);
814   asset->priv->state = ASSET_PROXIED;
815   asset->priv->proxied_asset_id = g_strdup (new_id);
816
817   /* FIXME: inform_proxy is not used consistently. For example, it is
818    * not called in set_proxy. However, it is still used by GESUriAsset.
819    * We should find some other method */
820   class = GES_ASSET_GET_CLASS (asset);
821   if (class->inform_proxy)
822     GES_ASSET_GET_CLASS (asset)->inform_proxy (asset, new_id);
823
824   GST_DEBUG_OBJECT (asset, "Trying to proxy to %s", new_id);
825
826   return TRUE;
827 }
828
829 static gboolean
830 _lookup_proxied_asset (const gchar * id, GESAssetCacheEntry * entry,
831     const gchar * asset_id)
832 {
833   return !g_strcmp0 (asset_id, entry->asset->priv->proxied_asset_id);
834 }
835
836 /* find the assets that called try_proxy for the asset id of @proxy
837  * and set @proxy as their proxy */
838 gboolean
839 ges_asset_finish_proxy (GESAsset * proxy)
840 {
841   GESAsset *proxied_asset;
842   GHashTable *entries_table;
843   GESAssetCacheEntry *entry;
844
845   LOCK_CACHE;
846   entries_table = g_hash_table_lookup (_get_type_entries (),
847       _extractable_type_name (proxy->priv->extractable_type));
848   entry = g_hash_table_find (entries_table, (GHRFunc) _lookup_proxied_asset,
849       (gpointer) ges_asset_get_id (proxy));
850
851   if (!entry) {
852     UNLOCK_CACHE;
853     GST_DEBUG_OBJECT (proxy, "Not proxying any asset %s", proxy->priv->id);
854     return FALSE;
855   }
856
857   proxied_asset = entry->asset;
858   UNLOCK_CACHE;
859
860   /* If the asset with the matching ->proxied_asset_id is already proxied
861    * by another asset, we actually want @proxy to proxy this instead */
862   while (proxied_asset->priv->proxies)
863     proxied_asset = proxied_asset->priv->proxies->data;
864
865   /* unless it is ourselves. I.e. it is already proxied by us */
866   if (proxied_asset == proxy)
867     return FALSE;
868
869   GST_INFO_OBJECT (proxied_asset,
870       "%s Making sure the proxy chain is fully set.",
871       ges_asset_get_id (entry->asset));
872   if (g_strcmp0 (proxied_asset->priv->proxied_asset_id, proxy->priv->id) ||
873       g_strcmp0 (proxied_asset->priv->id, proxy->priv->proxied_asset_id))
874     ges_asset_finish_proxy (proxied_asset);
875   return ges_asset_set_proxy (proxied_asset, proxy);
876 }
877
878 static gboolean
879 _contained_in_proxy_tree (GESAsset * node, GESAsset * search)
880 {
881   GList *tmp;
882   if (node == search)
883     return TRUE;
884   for (tmp = node->priv->proxies; tmp; tmp = tmp->next) {
885     if (_contained_in_proxy_tree (tmp->data, search))
886       return TRUE;
887   }
888   return FALSE;
889 }
890
891 /**
892  * ges_asset_set_proxy:
893  * @asset: The #GESAsset to proxy
894  * @proxy: (allow-none): A new default proxy for @asset
895  *
896  * Sets the #GESAsset:proxy for the asset.
897  *
898  * If @proxy is among the existing proxies of the asset (see
899  * ges_asset_list_proxies()) it will be moved to become the default
900  * proxy. Otherwise, if @proxy is not %NULL, it will be added to the list
901  * of proxies, as the new default. The previous default proxy will become
902  * 'next in line' for if the new one is removed, and so on. As such, this
903  * will **not** actually remove the previous default proxy (use
904  * ges_asset_unproxy() for that).
905  *
906  * Note that an asset can only act as a proxy for one other asset.
907  *
908  * As a special case, if @proxy is %NULL, then this method will actually
909  * remove **all** proxies from the asset.
910  *
911  * Returns: %TRUE if @proxy was successfully set as the default for
912  * @asset.
913  */
914 gboolean
915 ges_asset_set_proxy (GESAsset * asset, GESAsset * proxy)
916 {
917   GESAsset *current_target;
918   g_return_val_if_fail (GES_IS_ASSET (asset), FALSE);
919   g_return_val_if_fail (proxy == NULL || GES_IS_ASSET (proxy), FALSE);
920   g_return_val_if_fail (asset != proxy, FALSE);
921
922   if (!proxy) {
923     GList *tmp, *proxies;
924     if (asset->priv->error) {
925       GST_ERROR_OBJECT (asset,
926           "Asset was loaded with error (%s), it should not be 'unproxied'",
927           asset->priv->error->message);
928
929       return FALSE;
930     }
931
932     GST_DEBUG_OBJECT (asset, "Removing all proxies");
933     proxies = asset->priv->proxies;
934     asset->priv->proxies = NULL;
935
936     for (tmp = proxies; tmp; tmp = tmp->next) {
937       GESAsset *proxy = tmp->data;
938       proxy->priv->proxy_target = NULL;
939     }
940     asset->priv->state = ASSET_INITIALIZED;
941
942     g_object_notify_by_pspec (G_OBJECT (asset), _properties[PROP_PROXY]);
943     for (tmp = proxies; tmp; tmp = tmp->next)
944       g_object_notify_by_pspec (G_OBJECT (tmp->data),
945           _properties[PROP_PROXY_TARGET]);
946
947     g_list_free (proxies);
948     return TRUE;
949   }
950   current_target = proxy->priv->proxy_target;
951
952   if (current_target && current_target != asset) {
953     GST_ERROR_OBJECT (asset,
954         "Trying to use '%s' as a proxy, but it is already proxying '%s'",
955         proxy->priv->id, current_target->priv->id);
956
957     return FALSE;
958   }
959
960   if (_contained_in_proxy_tree (proxy, asset)) {
961     GST_ERROR_OBJECT (asset, "Trying to setup a circular proxying dependency!");
962
963     return FALSE;
964   }
965
966   if (g_list_find (asset->priv->proxies, proxy)) {
967     GST_INFO_OBJECT (asset,
968         "%" GST_PTR_FORMAT " already marked as proxy, moving to first", proxy);
969     asset->priv->proxies = g_list_remove (asset->priv->proxies, proxy);
970   }
971
972   GST_INFO ("%s is now proxied by %s", asset->priv->id, proxy->priv->id);
973   asset->priv->proxies = g_list_prepend (asset->priv->proxies, proxy);
974
975   proxy->priv->proxy_target = asset;
976   asset->priv->state = ASSET_PROXIED;
977
978   g_object_notify_by_pspec (G_OBJECT (asset), _properties[PROP_PROXY]);
979   if (current_target != asset)
980     g_object_notify_by_pspec (G_OBJECT (proxy), _properties[PROP_PROXY_TARGET]);
981
982   /* FIXME: ->inform_proxy is not called. We should figure out what the
983    * purpose of ->inform_proxy should be generically. Currently, it is
984    * only called in ges_asset_try_proxy! */
985
986   return TRUE;
987 }
988
989 /**
990  * ges_asset_unproxy:
991  * @asset: The #GESAsset to no longer proxy with @proxy
992  * @proxy: An existing proxy of @asset
993  *
994  * Removes the proxy from the available list of proxies for the asset. If
995  * the given proxy is the default proxy of the list, then the next proxy
996  * in the available list (see ges_asset_list_proxies()) will become the
997  * default. If there are no other proxies, then the asset will no longer
998  * have a default #GESAsset:proxy.
999  *
1000  * Returns: %TRUE if @proxy was successfully removed from @asset's proxy
1001  * list.
1002  */
1003 gboolean
1004 ges_asset_unproxy (GESAsset * asset, GESAsset * proxy)
1005 {
1006   gboolean removing_default;
1007   gboolean last_proxy;
1008   g_return_val_if_fail (GES_IS_ASSET (asset), FALSE);
1009   g_return_val_if_fail (GES_IS_ASSET (proxy), FALSE);
1010   g_return_val_if_fail (asset != proxy, FALSE);
1011
1012   /* also tests if the list is NULL */
1013   if (!g_list_find (asset->priv->proxies, proxy)) {
1014     GST_INFO_OBJECT (asset, "'%s' is not a proxy.", proxy->priv->id);
1015
1016     return FALSE;
1017   }
1018
1019   last_proxy = (asset->priv->proxies->next == NULL);
1020   if (last_proxy && asset->priv->error) {
1021     GST_ERROR_OBJECT (asset,
1022         "Asset was loaded with error (%s), its last proxy '%s' should "
1023         "not be removed", asset->priv->error->message, proxy->priv->id);
1024     return FALSE;
1025   }
1026
1027   removing_default = (asset->priv->proxies->data == proxy);
1028
1029   asset->priv->proxies = g_list_remove (asset->priv->proxies, proxy);
1030
1031   if (last_proxy)
1032     asset->priv->state = ASSET_INITIALIZED;
1033   proxy->priv->proxy_target = NULL;
1034
1035   if (removing_default)
1036     g_object_notify_by_pspec (G_OBJECT (asset), _properties[PROP_PROXY]);
1037   g_object_notify_by_pspec (G_OBJECT (proxy), _properties[PROP_PROXY_TARGET]);
1038
1039   return TRUE;
1040 }
1041
1042 /**
1043  * ges_asset_list_proxies:
1044  * @asset: A #GESAsset
1045  *
1046  * Get all the proxies that the asset has. The first item of the list will
1047  * be the default #GESAsset:proxy. The second will be the proxy that is
1048  * 'next in line' to be default, and so on.
1049  *
1050  * Returns: (element-type GESAsset) (transfer none): The list of proxies
1051  * that @asset has.
1052  */
1053 GList *
1054 ges_asset_list_proxies (GESAsset * asset)
1055 {
1056   g_return_val_if_fail (GES_IS_ASSET (asset), NULL);
1057
1058   return asset->priv->proxies;
1059 }
1060
1061 /**
1062  * ges_asset_get_proxy:
1063  * @asset: A #GESAsset
1064  *
1065  * Gets the default #GESAsset:proxy of the asset.
1066  *
1067  * Returns: (transfer none) (nullable): The default proxy of @asset.
1068  */
1069 GESAsset *
1070 ges_asset_get_proxy (GESAsset * asset)
1071 {
1072   g_return_val_if_fail (GES_IS_ASSET (asset), NULL);
1073
1074   if (asset->priv->state == ASSET_PROXIED && asset->priv->proxies) {
1075     return asset->priv->proxies->data;
1076   }
1077
1078   return NULL;
1079 }
1080
1081 /**
1082  * ges_asset_get_proxy_target:
1083  * @proxy: A #GESAsset
1084  *
1085  * Gets the #GESAsset:proxy-target of the asset.
1086  *
1087  * Note that the proxy target may have loaded with an error, so you should
1088  * call ges_asset_get_error() on the returned target.
1089  *
1090  * Returns: (transfer none) (nullable): The asset that @proxy is a proxy
1091  * of.
1092  */
1093 GESAsset *
1094 ges_asset_get_proxy_target (GESAsset * proxy)
1095 {
1096   g_return_val_if_fail (GES_IS_ASSET (proxy), NULL);
1097
1098   return proxy->priv->proxy_target;
1099 }
1100
1101 /* Caution, this method should be used in rare cases (ie: for the project
1102  * as we can change its ID from a useless one to a proper URI). In most
1103  * cases you want to update the ID creating a proxy
1104  */
1105 void
1106 ges_asset_set_id (GESAsset * asset, const gchar * id)
1107 {
1108   GHashTable *entries;
1109
1110   gpointer orig_id = NULL;
1111   GESAssetCacheEntry *entry = NULL;
1112   GESAssetPrivate *priv = NULL;
1113
1114   g_return_if_fail (GES_IS_ASSET (asset));
1115
1116   priv = asset->priv;
1117
1118   if (priv->state != ASSET_INITIALIZED) {
1119     GST_WARNING_OBJECT (asset, "Trying to rest ID on an object that is"
1120         " not properly loaded");
1121     return;
1122   }
1123
1124   if (g_strcmp0 (id, priv->id) == 0) {
1125     GST_DEBUG_OBJECT (asset, "ID is already %s", id);
1126
1127     return;
1128   }
1129
1130   LOCK_CACHE;
1131   entries = g_hash_table_lookup (_get_type_entries (),
1132       _extractable_type_name (asset->priv->extractable_type));
1133
1134   g_return_if_fail (g_hash_table_lookup_extended (entries, priv->id, &orig_id,
1135           (gpointer *) & entry));
1136
1137   g_hash_table_steal (entries, priv->id);
1138   g_hash_table_insert (entries, g_strdup (id), entry);
1139
1140   GST_DEBUG_OBJECT (asset, "Changing id from %s to %s", priv->id, id);
1141   g_free (priv->id);
1142   g_free (orig_id);
1143   priv->id = g_strdup (id);
1144   UNLOCK_CACHE;
1145 }
1146
1147 static GESAsset *
1148 _ensure_asset_for_wrong_id (const gchar * wrong_id, GType extractable_type,
1149     GError * error)
1150 {
1151   GESAsset *asset;
1152
1153   if ((asset = ges_asset_cache_lookup (extractable_type, wrong_id)))
1154     return asset;
1155
1156   /* It is a dummy GESAsset, we just bruteforce its creation */
1157   asset = g_object_new (GES_TYPE_ASSET, "id", wrong_id,
1158       "extractable-type", extractable_type, NULL);
1159
1160   /* transfer ownership to the cache */
1161   ges_asset_cache_put (asset, NULL);
1162   ges_asset_cache_set_loaded (extractable_type, wrong_id, error);
1163
1164   return asset;
1165 }
1166
1167 /**********************************
1168  *                                *
1169  *      API implementation        *
1170  *                                *
1171  **********************************/
1172
1173 /**
1174  * ges_asset_get_extractable_type:
1175  * @self: The #GESAsset
1176  *
1177  * Gets the #GESAsset:extractable-type of the asset.
1178  *
1179  * Returns: The extractable type of @self.
1180  */
1181 GType
1182 ges_asset_get_extractable_type (GESAsset * self)
1183 {
1184   g_return_val_if_fail (GES_IS_ASSET (self), G_TYPE_INVALID);
1185
1186   return self->priv->extractable_type;
1187 }
1188
1189 /**
1190  * ges_asset_request:
1191  * @extractable_type: The #GESAsset:extractable-type of the asset
1192  * @id: (allow-none): The #GESAsset:id of the asset
1193  * @error: (allow-none): An error to be set if the requested asset has
1194  * loaded with an error, or %NULL to ignore
1195  *
1196  * Returns an asset with the given properties. If such an asset already
1197  * exists in the cache (it has been previously created in GES), then a
1198  * reference to the existing asset is returned. Otherwise, a newly created
1199  * asset is returned, and also added to the cache.
1200  *
1201  * If the requested asset has been loaded with an error, then @error is
1202  * set, if given, and %NULL will be returned instead.
1203  *
1204  * Note that the given @id may not be exactly the #GESAsset:id that is
1205  * set on the returned asset. For instance, it may be adjusted into a
1206  * standard format. Or, if a #GESExtractable type does not have its
1207  * extraction parametrised, as is the case by default, then the given @id
1208  * may be ignored entirely and the #GESAsset:id set to some standard, in
1209  * which case a %NULL @id can be given.
1210  *
1211  * Similarly, the given @extractable_type may not be exactly the
1212  * #GESAsset:extractable-type that is set on the returned asset. Instead,
1213  * the actual extractable type may correspond to a subclass of the given
1214  * @extractable_type, depending on the given @id.
1215  *
1216  * Moreover, depending on the given @extractable_type, the returned asset
1217  * may belong to a subclass of #GESAsset.
1218  *
1219  * Finally, if the requested asset has a #GESAsset:proxy, then the proxy
1220  * that is found at the end of the chain of proxies is returned (a proxy's
1221  * proxy will take its place, and so on, unless it has no proxy).
1222  *
1223  * Some asset subclasses only support asynchronous construction of its
1224  * assets, such as #GESUriClip. For such assets this method will fail, and
1225  * you should use ges_asset_request_async() instead. In the case of
1226  * #GESUriClip, you can use ges_uri_clip_asset_request_sync() if you only
1227  * want to wait for the request to finish.
1228  *
1229  * Returns: (transfer full) (allow-none): A reference to the requested
1230  * asset, or %NULL if an error occurred.
1231  */
1232 GESAsset *
1233 ges_asset_request (GType extractable_type, const gchar * id, GError ** error)
1234 {
1235   gchar *real_id;
1236
1237   GError *lerr = NULL;
1238   GESAsset *asset = NULL, *proxy;
1239   gboolean proxied = TRUE;
1240
1241   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1242   g_return_val_if_fail (g_type_is_a (extractable_type, G_TYPE_OBJECT), NULL);
1243   g_return_val_if_fail (g_type_is_a (extractable_type, GES_TYPE_EXTRACTABLE),
1244       NULL);
1245
1246   real_id = _check_and_update_parameters (&extractable_type, id, &lerr);
1247   if (real_id == NULL) {
1248     /* We create an asset for that wrong ID so we have a reference that the
1249      * user requested it */
1250     _ensure_asset_for_wrong_id (id, extractable_type, lerr);
1251     real_id = g_strdup (id);
1252   }
1253   if (lerr)
1254     g_error_free (lerr);
1255
1256   /* asset owned by cache */
1257   asset = ges_asset_cache_lookup (extractable_type, real_id);
1258   if (asset) {
1259     while (proxied) {
1260       proxied = FALSE;
1261       switch (asset->priv->state) {
1262         case ASSET_INITIALIZED:
1263           break;
1264         case ASSET_INITIALIZING:
1265           asset = NULL;
1266           break;
1267         case ASSET_PROXIED:
1268           proxy = ges_asset_get_proxy (asset);
1269           if (proxy == NULL) {
1270             GST_ERROR ("Proxied against an asset we do not"
1271                 " have in cache, something massively screwed");
1272             asset = NULL;
1273           } else {
1274             proxied = TRUE;
1275             do {
1276               asset = proxy;
1277             } while ((proxy = ges_asset_get_proxy (asset)));
1278           }
1279           break;
1280         case ASSET_NEEDS_RELOAD:
1281           GST_DEBUG_OBJECT (asset, "Asset in cache and needs reload");
1282           if (!start_loading (asset)) {
1283             GST_ERROR ("Failed to reload the asset for id %s", id);
1284             asset = NULL;
1285           }
1286           break;
1287         case ASSET_INITIALIZED_WITH_ERROR:
1288           GST_WARNING_OBJECT (asset, "Initialized with error, not returning");
1289           if (asset->priv->error && error)
1290             *error = g_error_copy (asset->priv->error);
1291           asset = NULL;
1292           break;
1293         default:
1294           GST_WARNING ("Case %i not handle, returning", asset->priv->state);
1295           asset = NULL;
1296           break;
1297       }
1298     }
1299     if (asset)
1300       gst_object_ref (asset);
1301   } else {
1302     GObjectClass *klass;
1303     GInitableIface *iface;
1304     GType asset_type = ges_extractable_type_get_asset_type (extractable_type);
1305
1306     klass = g_type_class_ref (asset_type);
1307     iface = g_type_interface_peek (klass, G_TYPE_INITABLE);
1308
1309     if (iface->init) {
1310       /* FIXME: allow the error to be set, which GInitable is designed
1311        * for! */
1312       asset = g_initable_new (asset_type,
1313           NULL, NULL, "id", real_id, "extractable-type",
1314           extractable_type, NULL);
1315     } else {
1316       GST_WARNING ("Tried to create an Asset for type %s but no ->init method",
1317           g_type_name (extractable_type));
1318     }
1319     g_type_class_unref (klass);
1320   }
1321
1322   if (real_id)
1323     g_free (real_id);
1324
1325   GST_DEBUG ("New asset created synchronously: %p", asset);
1326   return asset;
1327 }
1328
1329 /**
1330  * ges_asset_request_async:
1331  * @extractable_type: The #GESAsset:extractable-type of the asset
1332  * @id: (allow-none): The #GESAsset:id of the asset
1333  * @cancellable: (allow-none): An object to allow cancellation of the
1334  * asset request, or %NULL to ignore
1335  * @callback: A function to call when the initialization is finished
1336  * @user_data: Data to be passed to @callback
1337  *
1338  * Requests an asset with the given properties asynchronously (see
1339  * ges_asset_request()). When the asset has been initialized or fetched
1340  * from the cache, the given callback function will be called. The
1341  * asset can then be retrieved in the callback using the
1342  * ges_asset_request_finish() method on the given #GAsyncResult.
1343  *
1344  * Note that the source object passed to the callback will be the
1345  * #GESAsset corresponding to the request, but it may not have loaded
1346  * correctly and therefore can not be used as is. Instead,
1347  * ges_asset_request_finish() should be used to fetch a usable asset, or
1348  * indicate that an error occurred in the asset's creation.
1349  *
1350  * Note that the callback will be called in the #GMainLoop running under
1351  * the same #GMainContext that ges_init() was called in. So, if you wish
1352  * the callback to be invoked outside the default #GMainContext, you can
1353  * call g_main_context_push_thread_default() in a new thread before
1354  * calling ges_init().
1355  *
1356  * Example of an asynchronous asset request:
1357  * ``` c
1358  * // The request callback
1359  * static void
1360  * asset_loaded_cb (GESAsset * source, GAsyncResult * res, gpointer user_data)
1361  * {
1362  *   GESAsset *asset;
1363  *   GError *error = NULL;
1364  *
1365  *   asset = ges_asset_request_finish (res, &error);
1366  *   if (asset) {
1367  *    gst_print ("The file: %s is usable as a GESUriClip",
1368  *        ges_asset_get_id (asset));
1369  *   } else {
1370  *    gst_print ("The file: %s is *not* usable as a GESUriClip because: %s",
1371  *        ges_asset_get_id (source), error->message);
1372  *   }
1373  *
1374  *   gst_object_unref (asset);
1375  * }
1376  *
1377  * // The request:
1378  * ges_asset_request_async (GES_TYPE_URI_CLIP, some_uri, NULL,
1379  *    (GAsyncReadyCallback) asset_loaded_cb, user_data);
1380  * ```
1381  */
1382 void
1383 ges_asset_request_async (GType extractable_type,
1384     const gchar * id, GCancellable * cancellable, GAsyncReadyCallback callback,
1385     gpointer user_data)
1386 {
1387   gchar *real_id;
1388   GESAsset *asset;
1389   GError *error = NULL;
1390   GTask *task = NULL;
1391
1392   g_return_if_fail (g_type_is_a (extractable_type, G_TYPE_OBJECT));
1393   g_return_if_fail (g_type_is_a (extractable_type, GES_TYPE_EXTRACTABLE));
1394   g_return_if_fail (callback);
1395
1396   GST_DEBUG ("Creating asset with extractable type %s and ID=%s",
1397       g_type_name (extractable_type), id);
1398
1399   real_id = _check_and_update_parameters (&extractable_type, id, &error);
1400   if (error) {
1401     _ensure_asset_for_wrong_id (id, extractable_type, error);
1402     real_id = g_strdup (id);
1403   }
1404
1405   /* Check if we already have an asset for this ID */
1406   asset = ges_asset_cache_lookup (extractable_type, real_id);
1407   if (asset) {
1408     task = g_task_new (asset, NULL, callback, user_data);
1409
1410     /* In the case of proxied asset, we will loop until we find the
1411      * last asset of the chain of proxied asset */
1412     while (TRUE) {
1413       switch (asset->priv->state) {
1414         case ASSET_INITIALIZED:
1415           GST_DEBUG_OBJECT (asset, "Asset in cache and initialized, "
1416               "using it");
1417
1418           /* Takes its own references to @asset */
1419           g_task_return_boolean (task, TRUE);
1420
1421           goto done;
1422         case ASSET_INITIALIZING:
1423           GST_DEBUG_OBJECT (asset, "Asset in cache and but not "
1424               "initialized, setting a new callback");
1425           ges_asset_cache_append_task (extractable_type, real_id, task);
1426           task = NULL;
1427
1428           goto done;
1429         case ASSET_PROXIED:{
1430           GESAsset *target = ges_asset_get_proxy (asset);
1431
1432           if (target == NULL) {
1433             GST_ERROR ("Asset %s proxied against an asset (%s) we do not"
1434                 " have in cache, something massively screwed",
1435                 asset->priv->id, asset->priv->proxied_asset_id);
1436
1437             goto done;
1438           }
1439           asset = target;
1440           break;
1441         }
1442         case ASSET_NEEDS_RELOAD:
1443           GST_DEBUG_OBJECT (asset, "Asset in cache and needs reload");
1444           ges_asset_cache_append_task (extractable_type, real_id, task);
1445           task = NULL;
1446           GES_ASSET_GET_CLASS (asset)->start_loading (asset, &error);
1447
1448           goto done;
1449         case ASSET_INITIALIZED_WITH_ERROR:
1450           g_task_return_error (task,
1451               error ? g_error_copy (error) : g_error_copy (asset->priv->error));
1452
1453           g_clear_error (&error);
1454
1455           goto done;
1456         default:
1457           GST_WARNING ("Case %i not handle, returning", asset->priv->state);
1458           return;
1459       }
1460     }
1461   }
1462
1463   g_async_initable_new_async (ges_extractable_type_get_asset_type
1464       (extractable_type), G_PRIORITY_DEFAULT, cancellable, callback, user_data,
1465       "id", real_id, "extractable-type", extractable_type, NULL);
1466 done:
1467   if (task)
1468     gst_object_unref (task);
1469   if (real_id)
1470     g_free (real_id);
1471 }
1472
1473 /**
1474  * ges_asset_needs_reload
1475  * @extractable_type: The #GESAsset:extractable-type of the asset that
1476  * needs reloading
1477  * @id: (allow-none): The #GESAsset:id of the asset asset that needs
1478  * reloading
1479  *
1480  * Indicate that an existing #GESAsset in the cache should be reloaded
1481  * upon the next request. This can be used when some condition has
1482  * changed, which may require that an existing asset should be updated.
1483  * For example, if an external resource has changed or now become
1484  * available.
1485  *
1486  * Note, the asset is not immediately changed, but will only actually
1487  * reload on the next call to ges_asset_request() or
1488  * ges_asset_request_async().
1489  *
1490  * Returns: %TRUE if the specified asset exists in the cache and could be
1491  * marked for reloading.
1492  */
1493 gboolean
1494 ges_asset_needs_reload (GType extractable_type, const gchar * id)
1495 {
1496   gchar *real_id;
1497   GESAsset *asset;
1498   GError *error = NULL;
1499
1500   g_return_val_if_fail (g_type_is_a (extractable_type, GES_TYPE_EXTRACTABLE),
1501       FALSE);
1502
1503   real_id = _check_and_update_parameters (&extractable_type, id, &error);
1504   if (error) {
1505     _ensure_asset_for_wrong_id (id, extractable_type, error);
1506     real_id = g_strdup (id);
1507   }
1508
1509   asset = ges_asset_cache_lookup (extractable_type, real_id);
1510
1511   if (real_id) {
1512     g_free (real_id);
1513   }
1514
1515   if (asset) {
1516     GST_DEBUG_OBJECT (asset,
1517         "Asset with id %s switch state to ASSET_NEEDS_RELOAD",
1518         ges_asset_get_id (asset));
1519     asset->priv->state = ASSET_NEEDS_RELOAD;
1520     g_clear_error (&asset->priv->error);
1521     return TRUE;
1522   }
1523
1524   GST_DEBUG ("Asset with id %s not found in cache", id);
1525   return FALSE;
1526 }
1527
1528 /**
1529  * ges_asset_get_id:
1530  * @self: A #GESAsset
1531  *
1532  * Gets the #GESAsset:id of the asset.
1533  *
1534  * Returns: The ID of @self.
1535  */
1536 const gchar *
1537 ges_asset_get_id (GESAsset * self)
1538 {
1539   g_return_val_if_fail (GES_IS_ASSET (self), NULL);
1540
1541   return self->priv->id;
1542 }
1543
1544 /**
1545  * ges_asset_extract:
1546  * @self: The #GESAsset to extract an object from
1547  * @error: (allow-none): An error to be set in case something goes wrong,
1548  * or %NULL to ignore
1549  *
1550  * Extracts a new #GESAsset:extractable-type object from the asset. The
1551  * #GESAsset:id of the asset may determine the properties and state of the
1552  * newly created object.
1553  *
1554  * Returns: (transfer floating): A newly created object, or %NULL if an
1555  * error occurred.
1556  */
1557 GESExtractable *
1558 ges_asset_extract (GESAsset * self, GError ** error)
1559 {
1560   GESExtractable *extractable;
1561
1562   g_return_val_if_fail (GES_IS_ASSET (self), NULL);
1563   g_return_val_if_fail (GES_ASSET_GET_CLASS (self)->extract, NULL);
1564
1565   GST_DEBUG_OBJECT (self, "Extracting asset of type %s",
1566       g_type_name (self->priv->extractable_type));
1567   extractable = GES_ASSET_GET_CLASS (self)->extract (self, error);
1568
1569   if (extractable == NULL)
1570     return NULL;
1571
1572   if (ges_extractable_get_asset (extractable) == NULL)
1573     ges_extractable_set_asset (extractable, self);
1574
1575   return extractable;
1576 }
1577
1578 /**
1579  * ges_asset_request_finish:
1580  * @res: The task result to fetch the asset from
1581  * @error: (out) (allow-none) (transfer full): An error to be set in case
1582  * something goes wrong, or %NULL to ignore
1583  *
1584  * Fetches an asset requested by ges_asset_request_async(), which
1585  * finalises the request.
1586  *
1587  * Returns: (transfer full): The requested asset, or %NULL if an error
1588  * occurred.
1589  */
1590 GESAsset *
1591 ges_asset_request_finish (GAsyncResult * res, GError ** error)
1592 {
1593   GObject *object;
1594   GObject *source_object;
1595
1596   g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
1597
1598   source_object = g_async_result_get_source_object (res);
1599   g_assert (source_object != NULL);
1600
1601   object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object),
1602       res, error);
1603
1604   gst_object_unref (source_object);
1605
1606   return GES_ASSET (object);
1607 }
1608
1609 /**
1610  * ges_list_assets:
1611  * @filter: The type of object that can be extracted from the asset
1612  *
1613  * List all the assets in the current cache whose
1614  * #GESAsset:extractable-type are of the given type (including
1615  * subclasses).
1616  *
1617  * Note that, since only a #GESExtractable can be extracted from an asset,
1618  * using `GES_TYPE_EXTRACTABLE` as @filter will return all the assets in
1619  * the current cache.
1620  *
1621  * Returns: (transfer container) (element-type GESAsset): A list of all
1622  * #GESAsset-s currently in the cache whose #GESAsset:extractable-type is
1623  * of the @filter type.
1624  */
1625 GList *
1626 ges_list_assets (GType filter)
1627 {
1628   GList *ret = NULL;
1629   GESAsset *asset;
1630   GHashTableIter iter, types_iter;
1631   gpointer key, value, typename, assets;
1632
1633   g_return_val_if_fail (g_type_is_a (filter, GES_TYPE_EXTRACTABLE), NULL);
1634
1635   LOCK_CACHE;
1636   g_hash_table_iter_init (&types_iter, _get_type_entries ());
1637   while (g_hash_table_iter_next (&types_iter, &typename, &assets)) {
1638     if (g_type_is_a (filter, g_type_from_name ((gchar *) typename)) == FALSE)
1639       continue;
1640
1641     g_hash_table_iter_init (&iter, (GHashTable *) assets);
1642     while (g_hash_table_iter_next (&iter, &key, &value)) {
1643       asset = ((GESAssetCacheEntry *) value)->asset;
1644
1645       if (g_type_is_a (asset->priv->extractable_type, filter))
1646         ret = g_list_append (ret, asset);
1647     }
1648   }
1649   UNLOCK_CACHE;
1650
1651   return ret;
1652 }
1653
1654 /**
1655  * ges_asset_get_error:
1656  * @self: A #GESAsset
1657  *
1658  * Retrieve the error that was set on the asset when it was loaded.
1659  *
1660  * Returns: (transfer none) (nullable): The error set on @asset, or
1661  * %NULL if no error occurred when @asset was loaded.
1662  *
1663  * Since: 1.8
1664  */
1665 GError *
1666 ges_asset_get_error (GESAsset * self)
1667 {
1668   g_return_val_if_fail (GES_IS_ASSET (self), NULL);
1669
1670   return self->priv->error;
1671 }