1 /* GStreamer Editing Services
3 * Copyright (C) 2012-2015 Thibault Saunier <thibault.saunier@collabora.com>
4 * Copyright (C) 2012 Volodymyr Rudyi <vladimir.rudoy@gmail.com>
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.
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.
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.
24 * @short_description: Represents usable resources inside the GStreamer Editing Services
26 * The Assets in the GStreamer Editing Services represent the resources
27 * that can be used. You can create assets for any type that implements the #GESExtractable
28 * interface, for example #GESClips, #GESFormatter, and #GESTrackElement do implement it.
29 * This means that assets will represent for example a #GESUriClips, #GESBaseEffect etc,
30 * and then you can extract objects of those types with the appropriate parameters from the asset
31 * using the #ges_asset_extract method:
34 * GESAsset *effect_asset;
37 * // You create an asset for an effect
38 * effect_asset = ges_asset_request (GES_TYPE_EFFECT, "agingtv", NULL);
40 * // And now you can extract an instance of GESEffect from that asset
41 * effect = GES_EFFECT (ges_asset_extract (effect_asset));
45 * In that example, the advantages of having a #GESAsset are that you can know what effects
46 * you are working with and let your user know about the avalaible ones, you can add metadata
47 * to the #GESAsset through the #GESMetaContainer interface and you have a model for your
48 * custom effects. Note that #GESAsset management is making easier thanks to the #GESProject class.
50 * Each asset is represented by a pair of @extractable_type and @id (string). Actually the @extractable_type
51 * is the type that implements the #GESExtractable interface, that means that for example for a #GESUriClip,
52 * the type that implements the #GESExtractable interface is #GESClip.
53 * The identifier represents different things depending on the @extractable_type and you should check
54 * the documentation of each type to know what the ID of #GESAsset actually represents for that type. By default,
55 * we only have one #GESAsset per type, and the @id is the name of the type, but this behaviour is overriden
56 * to be more useful. For example, for GESTransitionClips, the ID is the vtype of the transition
57 * you will extract from it (ie crossfade, box-wipe-rc etc..) For #GESEffect the ID is the
58 * @bin-description property of the extracted objects (ie the gst-launch style description of the bin that
61 * Each and every #GESAsset is cached into GES, and you can query those with the #ges_list_assets function.
62 * Also the system will automatically register #GESAssets for #GESFormatters and #GESTransitionClips
63 * and standard effects (actually not implemented yet) and you can simply query those calling:
65 * GList *formatter_assets, *tmp;
67 * // List all the transitions
68 * formatter_assets = ges_list_assets (GES_TYPE_FORMATTER);
70 * // Print some infos about the formatter GESAsset
71 * for (tmp = formatter_assets; tmp; tmp = tmp->next) {
72 * g_print ("Name of the formatter: %s, file extension it produces: %s",
73 * ges_meta_container_get_string (GES_META_CONTAINER (tmp->data), GES_META_FORMATTER_NAME),
74 * ges_meta_container_get_string (GES_META_CONTAINER (tmp->data), GES_META_FORMATTER_EXTENSION));
77 * g_list_free (transition_assets);
81 * You can request the creation of #GESAssets using either ges_asset_request() or
82 * ges_asset_request_async(). All the #GESAssets are cached and thus any asset that has already
83 * been created can be requested again without overhead.
90 #include "ges-internal.h"
92 #define GLIB_DISABLE_DEPRECATION_WARNINGS
96 GST_DEBUG_CATEGORY_STATIC (ges_asset_debug);
97 #undef GST_CAT_DEFAULT
98 #define GST_CAT_DEFAULT ges_asset_debug
112 ASSET_NOT_INITIALIZED,
113 ASSET_INITIALIZING, ASSET_INITIALIZED_WITH_ERROR,
119 static GParamSpec *_properties[PROP_LAST];
121 struct _GESAssetPrivate
125 GType extractable_type;
127 /* When an asset is proxied, instantiating it will
128 * return the asset it points to */
129 char *proxied_asset_id;
132 GESAsset *proxy_target;
134 /* The error that occurred when an asset has been initialized with error */
138 /* Internal structure to help avoid full loading
139 * of one asset several times
145 } GESAssetCacheEntry;
147 /* Also protect all the entries in the cache */
148 G_LOCK_DEFINE_STATIC (asset_cache_lock);
149 /* We are mapping entries by types and ID, such as:
152 * first_extractable_type_name1 :
154 * "some ID": GESAssetCacheEntry,
155 * "some other ID": GESAssetCacheEntry 2
157 * second_extractable_type_name :
159 * "some ID": GESAssetCacheEntry,
160 * "some other ID": GESAssetCacheEntry 2
164 * (The first extractable type is the type of the class that implemented
165 * the GESExtractable interface ie: GESClip, GESTimeline,
166 * GESFomatter, etc... but not subclasses)
168 * This is in order to be able to have 2 Asset with the same ID but
169 * different extractable types.
171 static GHashTable *type_entries_table = NULL;
172 #define LOCK_CACHE (G_LOCK (asset_cache_lock))
173 #define UNLOCK_CACHE (G_UNLOCK (asset_cache_lock))
176 _check_and_update_parameters (GType * extractable_type, const gchar * id,
180 GType old_type = *extractable_type;
183 ges_extractable_get_real_extractable_type_for_id (*extractable_type, id);
185 if (*extractable_type == G_TYPE_NONE) {
186 GST_WARNING ("No way to create a Asset for ID: %s, type: %s", id,
187 g_type_name (old_type));
189 if (error && *error == NULL)
190 g_set_error (error, GES_ERROR, GES_ERROR_ASSET_WRONG_ID,
191 "Wrong ID, can not find any extractable_type");
195 real_id = ges_extractable_type_check_id (*extractable_type, id, error);
196 if (real_id == NULL) {
197 GST_WARNING ("Wrong ID %s, can not create asset", id);
200 if (error && *error == NULL)
201 g_set_error (error, GES_ERROR, GES_ERROR_ASSET_WRONG_ID, "Wrong ID");
210 start_loading (GESAsset * asset)
212 ges_asset_cache_put (gst_object_ref (asset), NULL);
213 return ges_asset_cache_set_loaded (asset->priv->extractable_type,
214 asset->priv->id, NULL);
218 initable_init (GInitable * initable, GCancellable * cancellable,
221 g_clear_error (error);
223 return start_loading (GES_ASSET (initable));
227 async_initable_init_async (GAsyncInitable * initable, gint io_priority,
228 GCancellable * cancellable, GAsyncReadyCallback callback,
233 GError *error = NULL;
234 GESAsset *asset = GES_ASSET (initable);
236 task = g_task_new (asset, cancellable, callback, user_data);
238 ges_asset_cache_put (g_object_ref (asset), task);
239 switch (GES_ASSET_GET_CLASS (asset)->start_loading (asset, &error)) {
240 case GES_ASSET_LOADING_ERROR:
243 g_set_error (&error, GES_ERROR, GES_ERROR_ASSET_LOADING,
244 "Could not start loading asset");
246 /* FIXME Define error code */
247 ges_asset_cache_set_loaded (asset->priv->extractable_type,
248 asset->priv->id, error);
249 g_error_free (error);
252 case GES_ASSET_LOADING_OK:
254 ges_asset_cache_set_loaded (asset->priv->extractable_type,
255 asset->priv->id, error);
258 case GES_ASSET_LOADING_ASYNC:
259 /* If Async.... let it go */
265 async_initable_iface_init (GAsyncInitableIface * async_initable_iface)
267 async_initable_iface->init_async = async_initable_init_async;
271 initable_iface_init (GInitableIface * initable_iface)
273 initable_iface->init = initable_init;
276 G_DEFINE_TYPE_WITH_CODE (GESAsset, ges_asset, G_TYPE_OBJECT,
277 G_ADD_PRIVATE (GESAsset)
278 G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init)
279 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)
280 G_IMPLEMENT_INTERFACE (GES_TYPE_META_CONTAINER, NULL));
282 /* GESAsset virtual methods default implementation */
283 static GESAssetLoadingReturn
284 ges_asset_start_loading_default (GESAsset * asset, GError ** error)
286 return GES_ASSET_LOADING_OK;
289 static GESExtractable *
290 ges_asset_extract_default (GESAsset * asset, GError ** error)
294 GESAssetPrivate *priv = asset->priv;
295 GESExtractable *n_extractable;
298 params = ges_extractable_type_get_parameters_from_id (priv->extractable_type,
299 priv->id, &n_params);
301 #if GLIB_CHECK_VERSION(2, 53, 1)
307 values = g_malloc0 (sizeof (GValue) * n_params);
308 names = g_malloc0 (sizeof (gchar *) * n_params);
310 for (i = 0; i < n_params; i++) {
311 values[i] = params[i].value;
312 names[i] = params[i].name;
316 GES_EXTRACTABLE (g_object_new_with_properties (priv->extractable_type,
317 n_params, names, values));
322 n_extractable = g_object_newv (priv->extractable_type, n_params, params);
326 g_value_unset (¶ms[n_params].value);
330 return n_extractable;
334 ges_asset_request_id_update_default (GESAsset * self, gchar ** proposed_new_id,
340 /* GObject virtual methods implementation */
342 ges_asset_get_property (GObject * object, guint property_id,
343 GValue * value, GParamSpec * pspec)
345 GESAsset *asset = GES_ASSET (object);
347 switch (property_id) {
349 g_value_set_gtype (value, asset->priv->extractable_type);
352 g_value_set_string (value, asset->priv->id);
355 g_value_set_object (value, ges_asset_get_proxy (asset));
357 case PROP_PROXY_TARGET:
358 g_value_set_object (value, ges_asset_get_proxy_target (asset));
361 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
366 ges_asset_set_property (GObject * object, guint property_id,
367 const GValue * value, GParamSpec * pspec)
369 GESAsset *asset = GES_ASSET (object);
371 switch (property_id) {
373 asset->priv->extractable_type = g_value_get_gtype (value);
374 ges_extractable_register_metas (asset->priv->extractable_type, asset);
377 asset->priv->id = g_value_dup_string (value);
380 ges_asset_set_proxy (asset, g_value_get_object (value));
382 case PROP_PROXY_TARGET:
383 ges_asset_set_proxy (g_value_get_object (value), asset);
386 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
391 ges_asset_finalize (GObject * object)
393 GESAssetPrivate *priv = GES_ASSET (object)->priv;
395 GST_DEBUG_OBJECT (object, "finalizing");
400 if (priv->proxied_asset_id)
401 g_free (priv->proxied_asset_id);
404 g_error_free (priv->error);
407 g_list_free (priv->proxies);
409 G_OBJECT_CLASS (ges_asset_parent_class)->finalize (object);
413 ges_asset_class_init (GESAssetClass * klass)
415 GObjectClass *object_class = G_OBJECT_CLASS (klass);
417 object_class->get_property = ges_asset_get_property;
418 object_class->set_property = ges_asset_set_property;
419 object_class->finalize = ges_asset_finalize;
421 _properties[PROP_TYPE] =
422 g_param_spec_gtype ("extractable-type", "Extractable type",
423 "The type of the Object that can be extracted out of the asset",
424 G_TYPE_OBJECT, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
426 _properties[PROP_ID] =
427 g_param_spec_string ("id", "Identifier",
428 "The unic identifier of the asset", NULL,
429 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
431 _properties[PROP_PROXY] =
432 g_param_spec_object ("proxy", "Proxy",
433 "The asset default proxy.", GES_TYPE_ASSET, G_PARAM_READWRITE);
435 _properties[PROP_PROXY_TARGET] =
436 g_param_spec_object ("proxy-target", "Proxy target",
437 "The target of a proxy asset.", GES_TYPE_ASSET, G_PARAM_READWRITE);
439 g_object_class_install_properties (object_class, PROP_LAST, _properties);
441 klass->start_loading = ges_asset_start_loading_default;
442 klass->extract = ges_asset_extract_default;
443 klass->request_id_update = ges_asset_request_id_update_default;
444 klass->inform_proxy = NULL;
446 GST_DEBUG_CATEGORY_INIT (ges_asset_debug, "ges-asset",
447 GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GES Asset");
451 ges_asset_init (GESAsset * self)
453 self->priv = ges_asset_get_instance_private (self);
455 self->priv->state = ASSET_INITIALIZING;
456 self->priv->proxied_asset_id = NULL;
459 /* Internal methods */
461 /* Find the type that implemented the GESExtractable interface */
462 static inline const gchar *
463 _extractable_type_name (GType type)
466 if (g_type_is_a (g_type_parent (type), GES_TYPE_EXTRACTABLE))
467 type = g_type_parent (type);
469 return g_type_name (type);
473 static inline GESAssetCacheEntry *
474 _lookup_entry (GType extractable_type, const gchar * id)
476 GHashTable *entries_table;
478 entries_table = g_hash_table_lookup (type_entries_table,
479 _extractable_type_name (extractable_type));
481 return g_hash_table_lookup (entries_table, id);
487 _free_entries (gpointer entry)
489 GESAssetCacheEntry *data = (GESAssetCacheEntry *) entry;
491 gst_object_unref (data->asset);
492 g_slice_free (GESAssetCacheEntry, entry);
496 _gtask_return_error (GTask * task, GError * error)
498 g_task_return_error (task, g_error_copy (error));
502 _gtask_return_true (GTask * task, gpointer udata)
504 g_task_return_boolean (task, TRUE);
508 * ges_asset_cache_lookup:
510 * @id String identifier of asset
512 * Looks for asset with specified id in cache and it's completely loaded.
514 * Returns: (transfer none) (nullable): The #GESAsset found or %NULL
517 ges_asset_cache_lookup (GType extractable_type, const gchar * id)
519 GESAsset *asset = NULL;
520 GESAssetCacheEntry *entry = NULL;
522 g_return_val_if_fail (id, NULL);
525 entry = _lookup_entry (extractable_type, id);
527 asset = entry->asset;
534 ges_asset_cache_append_task (GType extractable_type,
535 const gchar * id, GTask * task)
537 GESAssetCacheEntry *entry = NULL;
540 if ((entry = _lookup_entry (extractable_type, id)))
541 entry->results = g_list_append (entry->results, task);
546 ges_asset_cache_set_loaded (GType extractable_type, const gchar * id,
550 GESAssetCacheEntry *entry = NULL;
551 GList *results = NULL;
552 GFunc user_func = NULL;
553 gpointer user_data = NULL;
556 if ((entry = _lookup_entry (extractable_type, id)) == NULL) {
558 GST_ERROR ("Calling but type %s ID: %s not in cached, "
559 "something massively screwed", g_type_name (extractable_type), id);
564 asset = entry->asset;
565 GST_DEBUG_OBJECT (entry->asset, ": (extractable type: %s) loaded, calling %i "
566 "callback (Error: %s)", g_type_name (asset->priv->extractable_type),
567 g_list_length (entry->results), error ? error->message : "");
569 results = entry->results;
570 entry->results = NULL;
573 asset->priv->state = ASSET_INITIALIZED_WITH_ERROR;
574 if (asset->priv->error)
575 g_error_free (asset->priv->error);
576 asset->priv->error = g_error_copy (error);
578 /* In case of error we do not want to emit in idle as we need to recover
580 user_func = (GFunc) _gtask_return_error;
582 GST_DEBUG_OBJECT (asset, "initialized with error");
584 asset->priv->state = ASSET_INITIALIZED;
585 user_func = (GFunc) _gtask_return_true;
586 GST_DEBUG_OBJECT (asset, "initialized");
590 g_list_foreach (results, user_func, user_data);
591 g_list_free_full (results, g_object_unref);
597 ges_asset_cache_put (GESAsset * asset, GTask * task)
599 GType extractable_type;
600 const gchar *asset_id;
601 GESAssetCacheEntry *entry;
603 /* Needing to work with the cache, taking the lock */
604 asset_id = ges_asset_get_id (asset);
605 extractable_type = asset->priv->extractable_type;
608 if (!(entry = _lookup_entry (extractable_type, asset_id))) {
609 GHashTable *entries_table;
611 entries_table = g_hash_table_lookup (type_entries_table,
612 _extractable_type_name (extractable_type));
613 if (entries_table == NULL) {
614 entries_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
617 g_hash_table_insert (type_entries_table,
618 g_strdup (_extractable_type_name (extractable_type)), entries_table);
621 entry = g_slice_new0 (GESAssetCacheEntry);
623 entry->asset = asset;
625 entry->results = g_list_prepend (entry->results, task);
626 g_hash_table_insert (entries_table, (gpointer) g_strdup (asset_id),
630 GST_DEBUG ("%s already in cache, adding result %p", asset_id, task);
631 entry->results = g_list_prepend (entry->results, task);
638 ges_asset_cache_init (void)
640 type_entries_table = g_hash_table_new_full (g_str_hash, g_str_equal,
641 g_free, (GDestroyNotify) g_hash_table_unref);
643 _init_formatter_assets ();
644 _init_standard_transition_assets ();
648 ges_asset_cache_deinit (void)
650 g_hash_table_destroy (type_entries_table);
651 type_entries_table = NULL;
655 ges_asset_request_id_update (GESAsset * asset, gchar ** proposed_id,
658 g_return_val_if_fail (GES_IS_ASSET (asset), FALSE);
660 return GES_ASSET_GET_CLASS (asset)->request_id_update (asset, proposed_id,
665 ges_asset_try_proxy (GESAsset * asset, const gchar * new_id)
667 GESAssetClass *class;
669 g_return_val_if_fail (GES_IS_ASSET (asset), FALSE);
671 if (g_strcmp0 (asset->priv->id, new_id) == 0) {
672 GST_WARNING_OBJECT (asset, "Trying to proxy to itself (%s),"
673 " NOT possible", new_id);
676 } else if (g_strcmp0 (asset->priv->proxied_asset_id, new_id) == 0) {
677 GST_WARNING_OBJECT (asset,
678 "Trying to proxy to same currently set proxy: %s -- %s",
679 asset->priv->proxied_asset_id, new_id);
684 g_free (asset->priv->proxied_asset_id);
685 asset->priv->state = ASSET_PROXIED;
686 asset->priv->proxied_asset_id = g_strdup (new_id);
688 class = GES_ASSET_GET_CLASS (asset);
689 if (class->inform_proxy)
690 GES_ASSET_GET_CLASS (asset)->inform_proxy (asset, new_id);
692 GST_DEBUG_OBJECT (asset, "Trying to proxy to %s", new_id);
698 _lookup_proxied_asset (const gchar * id, GESAssetCacheEntry * entry,
699 const gchar * asset_id)
701 return !g_strcmp0 (asset_id, entry->asset->priv->proxied_asset_id);
705 * ges_asset_set_proxy:
706 * @asset: The #GESAsset to set proxy on
707 * @proxy: (allow-none): The #GESAsset that should be used as default proxy for @asset or
708 * %NULL if you want to use the currently set proxy. Note that an asset can proxy one and only
711 * A proxying asset is an asset that can substitue the real @asset. For example if you
712 * have a full HD #GESUriClipAsset you might want to set a lower resolution (HD version
713 * of the same file) as proxy. Note that when an asset is proxied, calling
714 * #ges_asset_request will actually return the proxy asset.
716 * Returns: %TRUE if @proxy has been set on @asset, %FALSE otherwise.
719 ges_asset_set_proxy (GESAsset * asset, GESAsset * proxy)
721 g_return_val_if_fail (asset == NULL || GES_IS_ASSET (asset), FALSE);
722 g_return_val_if_fail (proxy == NULL || GES_IS_ASSET (proxy), FALSE);
723 g_return_val_if_fail (asset != proxy, FALSE);
726 if (asset->priv->error) {
727 GST_ERROR_OBJECT (asset,
728 "Proxy was loaded with error (%s), it should not be 'unproxied'",
729 asset->priv->error->message);
734 if (asset->priv->proxies) {
735 GESAsset *old_proxy = GES_ASSET (asset->priv->proxies->data);
737 old_proxy->priv->proxy_target = NULL;
738 g_object_notify_by_pspec (G_OBJECT (old_proxy),
739 _properties[PROP_PROXY_TARGET]);
742 GST_DEBUG_OBJECT (asset, "%s not proxied anymore", asset->priv->id);
743 asset->priv->state = ASSET_INITIALIZED;
744 g_object_notify_by_pspec (G_OBJECT (asset), _properties[PROP_PROXY]);
750 GHashTable *entries_table;
751 GESAssetCacheEntry *entry;
753 entries_table = g_hash_table_lookup (type_entries_table,
754 _extractable_type_name (proxy->priv->extractable_type));
755 entry = g_hash_table_find (entries_table, (GHRFunc) _lookup_proxied_asset,
756 (gpointer) ges_asset_get_id (proxy));
759 GST_DEBUG_OBJECT (asset, "Not proxying any asset");
763 asset = entry->asset;
764 while (asset->priv->proxies)
765 asset = asset->priv->proxies->data;
768 if (proxy->priv->proxy_target) {
769 GST_ERROR_OBJECT (asset,
770 "Trying to use %s as a proxy, but it is already proxying %s",
771 proxy->priv->id, proxy->priv->proxy_target->priv->id);
776 if (g_list_find (proxy->priv->proxies, asset)) {
777 GST_ERROR_OBJECT (asset, "Trying to setup a circular proxying dependency!");
782 if (g_list_find (asset->priv->proxies, proxy)) {
783 GST_INFO_OBJECT (asset,
784 "%" GST_PTR_FORMAT " already marked as proxy, moving to first", proxy);
785 GES_ASSET (asset->priv->proxies->data)->priv->proxy_target = NULL;
786 asset->priv->proxies = g_list_remove (asset->priv->proxies, proxy);
789 GST_INFO ("%s is now proxied by %s", asset->priv->id, proxy->priv->id);
790 asset->priv->proxies = g_list_prepend (asset->priv->proxies, proxy);
791 proxy->priv->proxy_target = asset;
792 g_object_notify_by_pspec (G_OBJECT (proxy), _properties[PROP_PROXY_TARGET]);
794 asset->priv->state = ASSET_PROXIED;
795 g_object_notify_by_pspec (G_OBJECT (asset), _properties[PROP_PROXY]);
802 * @asset: The #GESAsset to stop proxying with @proxy
803 * @proxy: The #GESAsset to stop considering as a proxy for @asset
805 * Removes @proxy from the list of known proxies for @asset.
806 * If @proxy was the current proxy for @asset, stop using it.
808 * Returns: %TRUE if @proxy was a known proxy for @asset, %FALSE otherwise.
811 ges_asset_unproxy (GESAsset * asset, GESAsset * proxy)
813 g_return_val_if_fail (GES_IS_ASSET (asset), FALSE);
814 g_return_val_if_fail (GES_IS_ASSET (proxy), FALSE);
815 g_return_val_if_fail (asset != proxy, FALSE);
817 if (!g_list_find (asset->priv->proxies, proxy)) {
818 GST_INFO_OBJECT (asset, "%s is not a proxy.", proxy->priv->id);
823 if (asset->priv->proxies->data == proxy)
824 ges_asset_set_proxy (asset, NULL);
826 asset->priv->proxies = g_list_remove (asset->priv->proxies, proxy);
832 * ges_asset_list_proxies:
833 * @asset: The #GESAsset to get proxies from
835 * Returns: (element-type GESAsset) (transfer none): The list of proxies @asset has. Note that the default asset to be
836 * used is always the first in that list.
839 ges_asset_list_proxies (GESAsset * asset)
841 g_return_val_if_fail (GES_IS_ASSET (asset), NULL);
843 return asset->priv->proxies;
847 * ges_asset_get_proxy:
848 * @asset: The #GESAsset to get currenlty used proxy
850 * Returns: (transfer none) (nullable): The proxy in use for @asset
853 ges_asset_get_proxy (GESAsset * asset)
855 g_return_val_if_fail (GES_IS_ASSET (asset), NULL);
857 if (asset->priv->state == ASSET_PROXIED && asset->priv->proxies) {
858 return asset->priv->proxies->data;
865 * ges_asset_get_proxy_target:
866 * @proxy: The #GESAsset from which to get the the asset it proxies.
868 * Returns: (transfer none) (nullable): The #GESAsset that is proxied by @proxy
871 ges_asset_get_proxy_target (GESAsset * proxy)
873 g_return_val_if_fail (GES_IS_ASSET (proxy), NULL);
875 return proxy->priv->proxy_target;
878 /* Caution, this method should be used in rare cases (ie: for the project
879 * as we can change its ID from a useless one to a proper URI). In most
880 * cases you want to update the ID creating a proxy
883 ges_asset_set_id (GESAsset * asset, const gchar * id)
887 gpointer orig_id = NULL;
888 GESAssetCacheEntry *entry = NULL;
889 GESAssetPrivate *priv = NULL;
891 g_return_if_fail (GES_IS_ASSET (asset));
895 if (priv->state != ASSET_INITIALIZED) {
896 GST_WARNING_OBJECT (asset, "Trying to rest ID on an object that is"
897 " not properly loaded");
901 if (g_strcmp0 (id, priv->id) == 0) {
902 GST_DEBUG_OBJECT (asset, "ID is already %s", id);
908 entries = g_hash_table_lookup (type_entries_table,
909 _extractable_type_name (asset->priv->extractable_type));
911 g_return_if_fail (g_hash_table_lookup_extended (entries, priv->id, &orig_id,
912 (gpointer *) & entry));
914 g_hash_table_steal (entries, priv->id);
915 g_hash_table_insert (entries, g_strdup (id), entry);
917 GST_DEBUG_OBJECT (asset, "Changing id from %s to %s", priv->id, id);
920 priv->id = g_strdup (id);
925 _unsure_material_for_wrong_id (const gchar * wrong_id, GType extractable_type,
930 if ((asset = ges_asset_cache_lookup (extractable_type, wrong_id)))
933 /* It is a dummy GESAsset, we just bruteforce its creation */
934 asset = g_object_new (GES_TYPE_ASSET, "id", wrong_id,
935 "extractable-type", extractable_type, NULL);
937 ges_asset_cache_put (asset, NULL);
938 ges_asset_cache_set_loaded (extractable_type, wrong_id, error);
943 /**********************************
945 * API implementation *
947 **********************************/
950 * ges_asset_get_extractable_type:
951 * @self: The #GESAsset
953 * Gets the type of object that can be extracted from @self
955 * Returns: the type of object that can be extracted from @self
958 ges_asset_get_extractable_type (GESAsset * self)
960 g_return_val_if_fail (GES_IS_ASSET (self), G_TYPE_INVALID);
962 return self->priv->extractable_type;
967 * @extractable_type: The #GType of the object that can be extracted from the new asset.
968 * @id: (allow-none): The Identifier or %NULL
969 * @error: (allow-none): An error to be set in case something wrong happens or %NULL
971 * Create a #GESAsset in the most simple cases, you should look at the @extractable_type
972 * documentation to see if that constructor can be called for this particular type
974 * As it is recommanded not to instanciate assets for GESUriClip synchronously,
975 * it will not work with this method, but you can instead use the specific
976 * #ges_uri_clip_asset_request_sync method if you really want to.
978 * Returns: (transfer full) (allow-none): A reference to the wanted #GESAsset or %NULL
981 ges_asset_request (GType extractable_type, const gchar * id, GError ** error)
986 GESAsset *asset = NULL;
988 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
989 g_return_val_if_fail (g_type_is_a (extractable_type, G_TYPE_OBJECT), NULL);
990 g_return_val_if_fail (g_type_is_a (extractable_type, GES_TYPE_EXTRACTABLE),
993 real_id = _check_and_update_parameters (&extractable_type, id, &lerr);
994 if (real_id == NULL) {
995 /* We create an asset for that wrong ID so we have a reference that the
996 * user requested it */
997 _unsure_material_for_wrong_id (id, extractable_type, lerr);
998 real_id = g_strdup (id);
1001 g_error_free (lerr);
1003 asset = ges_asset_cache_lookup (extractable_type, real_id);
1006 switch (asset->priv->state) {
1007 case ASSET_INITIALIZED:
1008 gst_object_ref (asset);
1010 case ASSET_INITIALIZING:
1014 if (asset->priv->proxies == NULL) {
1015 GST_ERROR ("Proxied against an asset we do not"
1016 " have in cache, something massively screwed");
1021 asset = asset->priv->proxies->data;
1022 while (ges_asset_get_proxy (asset))
1023 asset = ges_asset_get_proxy (asset);
1026 case ASSET_NEEDS_RELOAD:
1027 GST_DEBUG_OBJECT (asset, "Asset in cache and needs reload");
1028 start_loading (asset);
1031 case ASSET_INITIALIZED_WITH_ERROR:
1032 GST_WARNING_OBJECT (asset, "Initialized with error, not returning");
1033 if (asset->priv->error && error)
1034 *error = g_error_copy (asset->priv->error);
1038 GST_WARNING ("Case %i not handle, returning", asset->priv->state);
1043 GObjectClass *klass;
1044 GInitableIface *iface;
1045 GType asset_type = ges_extractable_type_get_asset_type (extractable_type);
1047 klass = g_type_class_ref (asset_type);
1048 iface = g_type_interface_peek (klass, G_TYPE_INITABLE);
1051 asset = g_initable_new (asset_type,
1052 NULL, NULL, "id", real_id, "extractable-type",
1053 extractable_type, NULL);
1055 GST_WARNING ("Tried to create an Asset for type %s but no ->init method",
1056 g_type_name (extractable_type));
1058 g_type_class_unref (klass);
1065 GST_DEBUG ("New asset created synchronously: %p", asset);
1070 * ges_asset_request_async:
1071 * @extractable_type: The #GType of the object that can be extracted from the
1072 * new asset. The class must implement the #GESExtractable interface.
1073 * @id: The Identifier of the asset we want to create. This identifier depends of the extractable,
1074 * type you want. By default it is the name of the class itself (or %NULL), but for example for a
1075 * GESEffect, it will be the pipeline description, for a GESUriClip it
1076 * will be the name of the file, etc... You should refer to the documentation of the #GESExtractable
1077 * type you want to create a #GESAsset for.
1078 * @cancellable: (allow-none): optional %GCancellable object, %NULL to ignore.
1079 * @callback: a #GAsyncReadyCallback to call when the initialization is finished,
1080 * Note that the @source of the callback will be the #GESAsset, but you need to
1081 * make sure that the asset is properly loaded using the #ges_asset_request_finish
1082 * method. This asset can not be used as is.
1083 * @user_data: The user data to pass when @callback is called
1085 * The @callback will be called from a running #GMainLoop which is iterating a #GMainContext.
1086 * Note that, users should ensure the #GMainContext, since this method will notify
1087 * @callback from the thread which was associated with a thread default
1088 * #GMainContext at calling ges_init().
1089 * For example, if a user wants non-default #GMainContext to be associated
1090 * with @callback, ges_init() must be called after g_main_context_push_thread_default ()
1091 * with custom #GMainContext.
1093 * Request a new #GESAsset asyncronously, @callback will be called when the materail is
1094 * ready to be used or if an error occured.
1096 * Example of request of a GESAsset async:
1098 * // The request callback
1100 * asset_loaded_cb (GESAsset * source, GAsyncResult * res, gpointer user_data)
1103 * GError *error = NULL;
1105 * asset = ges_asset_request_finish (res, &error);
1107 * g_print ("The file: %s is usable as a FileSource",
1108 * ges_asset_get_id (asset));
1110 * g_print ("The file: %s is *not* usable as a FileSource because: %s",
1111 * ges_asset_get_id (source), error->message);
1114 * gst_object_unref (mfs);
1118 * ges_asset_request_async (GES_TYPE_URI_CLIP, some_uri, NULL,
1119 * (GAsyncReadyCallback) asset_loaded_cb, user_data);
1123 ges_asset_request_async (GType extractable_type,
1124 const gchar * id, GCancellable * cancellable, GAsyncReadyCallback callback,
1129 GError *error = NULL;
1132 g_return_if_fail (g_type_is_a (extractable_type, G_TYPE_OBJECT));
1133 g_return_if_fail (g_type_is_a (extractable_type, GES_TYPE_EXTRACTABLE));
1134 g_return_if_fail (callback);
1136 GST_DEBUG ("Creating asset with extractable type %s and ID=%s",
1137 g_type_name (extractable_type), id);
1139 real_id = _check_and_update_parameters (&extractable_type, id, &error);
1141 _unsure_material_for_wrong_id (id, extractable_type, error);
1142 real_id = g_strdup (id);
1145 /* Check if we already have an asset for this ID */
1146 asset = ges_asset_cache_lookup (extractable_type, real_id);
1148 task = g_task_new (asset, NULL, callback, user_data);
1150 /* In the case of proxied asset, we will loop until we find the
1151 * last asset of the chain of proxied asset */
1153 switch (asset->priv->state) {
1154 case ASSET_INITIALIZED:
1155 gst_object_ref (asset);
1157 GST_DEBUG_OBJECT (asset, "Asset in cache and initialized, "
1160 /* Takes its own references to @asset */
1161 g_task_return_boolean (task, TRUE);
1164 case ASSET_INITIALIZING:
1165 GST_DEBUG_OBJECT (asset, "Asset in cache and but not "
1166 "initialized, setting a new callback");
1167 ges_asset_cache_append_task (extractable_type, real_id, task);
1171 case ASSET_PROXIED:{
1172 GESAsset *target = ges_asset_get_proxy (asset);
1174 if (target == NULL) {
1175 GST_ERROR ("Asset %s proxied against an asset (%s) we do not"
1176 " have in cache, something massively screwed",
1177 asset->priv->id, asset->priv->proxied_asset_id);
1184 case ASSET_NEEDS_RELOAD:
1185 GST_DEBUG_OBJECT (asset, "Asset in cache and needs reload");
1186 ges_asset_cache_append_task (extractable_type, real_id, task);
1187 GES_ASSET_GET_CLASS (asset)->start_loading (asset, &error);
1190 case ASSET_INITIALIZED_WITH_ERROR:
1191 g_task_return_error (task,
1192 error ? error : g_error_copy (asset->priv->error));
1196 GST_WARNING ("Case %i not handle, returning", asset->priv->state);
1202 g_async_initable_new_async (ges_extractable_type_get_asset_type
1203 (extractable_type), G_PRIORITY_DEFAULT, cancellable, callback, user_data,
1204 "id", real_id, "extractable-type", extractable_type, NULL);
1207 gst_object_unref (task);
1213 * ges_asset_needs_reload
1214 * @extractable_type: The #GType of the object that can be extracted from the
1215 * asset to be reloaded.
1216 * @id: The identifier of the asset to mark as needing reload
1218 * Sets an asset from the internal cache as needing reload. An asset needs reload
1219 * in the case where, for example, we were missing a GstPlugin to use it and that
1220 * plugin has been installed, or, that particular asset content as changed
1221 * meanwhile (in the case of the usage of proxies).
1223 * Once an asset has been set as "needs reload", requesting that asset again
1224 * will lead to it being re discovered, and reloaded as if it was not in the
1227 * Returns: %TRUE if the asset was in the cache and could be set as needing reload,
1231 ges_asset_needs_reload (GType extractable_type, const gchar * id)
1235 GError *error = NULL;
1237 real_id = _check_and_update_parameters (&extractable_type, id, &error);
1239 _unsure_material_for_wrong_id (id, extractable_type, error);
1240 real_id = g_strdup (id);
1243 asset = ges_asset_cache_lookup (extractable_type, real_id);
1250 GST_DEBUG_OBJECT (asset,
1251 "Asset with id %s switch state to ASSET_NEEDS_RELOAD",
1252 ges_asset_get_id (asset));
1253 asset->priv->state = ASSET_NEEDS_RELOAD;
1254 g_clear_error (&asset->priv->error);
1258 GST_DEBUG ("Asset with id %s not found in cache", id);
1264 * @self: The #GESAsset to get ID from
1266 * Gets the ID of a #GESAsset
1268 * Returns: The ID of @self
1271 ges_asset_get_id (GESAsset * self)
1273 g_return_val_if_fail (GES_IS_ASSET (self), NULL);
1275 return self->priv->id;
1279 * ges_asset_extract:
1280 * @self: The #GESAsset to get extract an object from
1281 * @error: (allow-none): An error to be set in case something wrong happens or %NULL
1283 * Extracts a new #GObject from @asset. The type of the object is
1284 * defined by the extractable-type of @asset, you can check what
1285 * type will be extracted from @asset using
1286 * #ges_asset_get_extractable_type
1288 * Returns: (transfer floating) (allow-none): A newly created #GESExtractable
1291 ges_asset_extract (GESAsset * self, GError ** error)
1293 GESExtractable *extractable;
1295 g_return_val_if_fail (GES_IS_ASSET (self), NULL);
1296 g_return_val_if_fail (GES_ASSET_GET_CLASS (self)->extract, NULL);
1298 GST_DEBUG_OBJECT (self, "Extracting asset of type %s",
1299 g_type_name (self->priv->extractable_type));
1300 extractable = GES_ASSET_GET_CLASS (self)->extract (self, error);
1302 if (extractable == NULL)
1305 if (ges_extractable_get_asset (extractable) == NULL)
1306 ges_extractable_set_asset (extractable, self);
1312 * ges_asset_request_finish:
1313 * @res: The #GAsyncResult from which to get the newly created #GESAsset
1314 * @error: (out) (allow-none) (transfer full): An error to be set in case
1315 * something wrong happens or %NULL
1317 * Finalize the request of an async #GESAsset
1319 * Returns: (transfer full)(allow-none): The #GESAsset previously requested
1322 ges_asset_request_finish (GAsyncResult * res, GError ** error)
1325 GObject *source_object;
1327 g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
1329 source_object = g_async_result_get_source_object (res);
1330 g_assert (source_object != NULL);
1332 object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object),
1335 gst_object_unref (source_object);
1337 return GES_ASSET (object);
1342 * @filter: Type of assets to list, #GES_TYPE_EXTRACTABLE will list
1345 * List all @asset filtering per filter as defined by @filter.
1346 * It copies the asset and thus will not be updated in time.
1348 * Returns: (transfer container) (element-type GESAsset): The list of
1349 * #GESAsset the object contains
1352 ges_list_assets (GType filter)
1356 GHashTableIter iter, types_iter;
1357 gpointer key, value, typename, assets;
1359 g_return_val_if_fail (g_type_is_a (filter, GES_TYPE_EXTRACTABLE), NULL);
1362 g_hash_table_iter_init (&types_iter, type_entries_table);
1363 while (g_hash_table_iter_next (&types_iter, &typename, &assets)) {
1364 if (g_type_is_a (filter, g_type_from_name ((gchar *) typename)) == FALSE)
1367 g_hash_table_iter_init (&iter, (GHashTable *) assets);
1368 while (g_hash_table_iter_next (&iter, &key, &value)) {
1369 asset = ((GESAssetCacheEntry *) value)->asset;
1371 if (g_type_is_a (asset->priv->extractable_type, filter))
1372 ret = g_list_append (ret, asset);
1381 * ges_asset_get_error:
1382 * @self: The asset to retrieve the error from
1384 * Returns: (transfer none) (nullable): The #GError of the asset or %NULL if
1385 * the asset was loaded without issue
1390 ges_asset_get_error (GESAsset * self)
1392 g_return_val_if_fail (GES_IS_ASSET (self), NULL);
1394 return self->priv->error;