Implement asset proxying support
authorThibault Saunier <tsaunier@gnome.org>
Fri, 20 Nov 2015 22:33:12 +0000 (23:33 +0100)
committerThibault Saunier <tsaunier@gnome.org>
Thu, 10 Dec 2015 13:48:02 +0000 (14:48 +0100)
API:
  ges_asset_set_proxy
  ges_asset_get_proxy
  ges_asset_list_proxies
  ges_asset_get_proxy_target

Differential Revision: https://phabricator.freedesktop.org/D504

docs/libs/ges-sections.txt
ges/ges-asset.c
ges/ges-asset.h
ges/ges-base-xml-formatter.c
ges/ges-internal.h
ges/ges-project.c
ges/ges-uri-clip.c
ges/ges-xml-formatter.c
tests/check/Makefile.am
tests/check/ges/asset.c

index 73dfffd..95b3ed7 100644 (file)
@@ -1079,6 +1079,10 @@ ges_asset_request_finish
 ges_asset_extract
 ges_asset_get_error
 ges_list_assets
+ges_asset_set_proxy
+ges_asset_list_proxies
+ges_asset_get_proxy_target
+ges_asset_get_proxy
 <SUBSECTION Standard>
 GESAssetPrivate
 GES_ASSET
index 44b9895..9feab55 100644 (file)
@@ -96,6 +96,8 @@ enum
   PROP_0,
   PROP_TYPE,
   PROP_ID,
+  PROP_PROXY,
+  PROP_PROXY_TARGET,
   PROP_LAST
 };
 
@@ -115,11 +117,14 @@ struct _GESAssetPrivate
   GESAssetState state;
   GType extractable_type;
 
-  /* When a asset is proxied, instanciating it will
+  /* When an asset is proxied, instanciating it will
    * return the asset it points to */
   char *proxied_asset_id;
 
-  /* The error that accured when a asset has been initialized with error */
+  GList *proxies;
+  GESAsset *proxy_target;
+
+  /* The error that accured when an asset has been initialized with error */
   GError *error;
 };
 
@@ -314,6 +319,12 @@ ges_asset_get_property (GObject * object, guint property_id,
     case PROP_ID:
       g_value_set_string (value, asset->priv->id);
       break;
+    case PROP_PROXY:
+      g_value_set_object (value, ges_asset_get_proxy (asset));
+      break;
+    case PROP_PROXY_TARGET:
+      g_value_set_object (value, ges_asset_get_proxy_target (asset));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
   }
@@ -333,6 +344,12 @@ ges_asset_set_property (GObject * object, guint property_id,
     case PROP_ID:
       asset->priv->id = g_value_dup_string (value);
       break;
+    case PROP_PROXY:
+      ges_asset_set_proxy (asset, g_value_get_object (value));
+      break;
+    case PROP_PROXY_TARGET:
+      ges_asset_set_proxy (g_value_get_object (value), asset);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
   }
@@ -377,6 +394,14 @@ ges_asset_class_init (GESAssetClass * klass)
       "The unic identifier of the asset", NULL,
       G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
 
+  _properties[PROP_PROXY] =
+      g_param_spec_object ("proxy", "Proxy",
+      "The asset default proxy.", GES_TYPE_ASSET, G_PARAM_READWRITE);
+
+  _properties[PROP_PROXY_TARGET] =
+      g_param_spec_object ("proxy-target", "Proxy target",
+      "The target of a proxy asset.", GES_TYPE_ASSET, G_PARAM_READWRITE);
+
   g_object_class_install_properties (object_class, PROP_LAST, _properties);
 
   klass->start_loading = ges_asset_start_loading_default;
@@ -593,9 +618,10 @@ ges_asset_request_id_update (GESAsset * asset, gchar ** proposed_id,
 }
 
 gboolean
-ges_asset_set_proxy (GESAsset * asset, const gchar * new_id)
+ges_asset_try_proxy (GESAsset * asset, const gchar * new_id)
 {
   GESAssetClass *class;
+
   if (g_strcmp0 (asset->priv->id, new_id) == 0) {
     GST_WARNING_OBJECT (asset, "Trying to proxy to itself (%s),"
         " NOT possible", new_id);
@@ -609,9 +635,7 @@ ges_asset_set_proxy (GESAsset * asset, const gchar * new_id)
     return FALSE;
   }
 
-  if (asset->priv->proxied_asset_id)
-    g_free (asset->priv->proxied_asset_id);
-
+  g_free (asset->priv->proxied_asset_id);
   asset->priv->state = ASSET_PROXIED;
   asset->priv->proxied_asset_id = g_strdup (new_id);
 
@@ -619,10 +643,160 @@ ges_asset_set_proxy (GESAsset * asset, const gchar * new_id)
   if (class->inform_proxy)
     GES_ASSET_GET_CLASS (asset)->inform_proxy (asset, new_id);
 
-  GST_DEBUG_OBJECT (asset, "Now proxied to %s", new_id);
+  GST_DEBUG_OBJECT (asset, "Trying to proxy to %s", new_id);
+
+  return TRUE;
+}
+
+static gboolean
+_lookup_proxied_asset (const gchar * id, GESAssetCacheEntry * entry,
+    const gchar * asset_id)
+{
+  return !g_strcmp0 (asset_id, entry->asset->priv->proxied_asset_id);
+}
+
+/**
+ * ges_asset_set_proxy:
+ * @asset: The #GESAsset to set proxy on
+ * @proxy: (allow-none): The #GESAsset that should be used as default proxy for @asset or
+ * %NULL if you want to use the currently set proxy. Note that an asset can proxy one and only
+ * one other asset.
+ *
+ * A proxying asset is an asset that can substitue the real @asset. For example if you
+ * have a full HD #GESUriClipAsset you might want to set a lower resolution (HD version
+ * of the same file) as proxy. Note that when an asset is proxied, calling
+ * #ges_asset_request will actually return the proxy asset.
+ *
+ * Returns: The list of proxies @asset has. Note that the default asset to be
+ * used is always the first in that list.
+ */
+gboolean
+ges_asset_set_proxy (GESAsset * asset, GESAsset * proxy)
+{
+  g_return_val_if_fail (asset == NULL || GES_IS_ASSET (asset), FALSE);
+  g_return_val_if_fail (proxy == NULL || GES_IS_ASSET (proxy), FALSE);
+  g_return_val_if_fail (asset != proxy, FALSE);
+
+  if (!proxy) {
+    if (asset->priv->error) {
+      GST_ERROR_OBJECT (asset,
+          "Proxy was loaded with error (%s), it should not be 'unproxied'",
+          asset->priv->error->message);
+
+      return FALSE;
+    }
+
+    if (asset->priv->proxies) {
+      GESAsset *old_proxy = GES_ASSET (asset->priv->proxies->data);
+
+      GES_ASSET (asset->priv->proxies->data)->priv->proxy_target = NULL;
+      g_object_notify_by_pspec (G_OBJECT (old_proxy),
+          _properties[PROP_PROXY_TARGET]);
+    }
+
+    GST_DEBUG_OBJECT (asset, "%s not proxied anymore", asset->priv->id);
+    asset->priv->state = ASSET_INITIALIZED;
+    g_object_notify_by_pspec (G_OBJECT (asset), _properties[PROP_PROXY]);
+
+    return TRUE;
+  }
+
+  if (asset == NULL) {
+    GHashTable *entries_table;
+    GESAssetCacheEntry *entry;
+
+    entries_table = g_hash_table_lookup (type_entries_table,
+        _extractable_type_name (proxy->priv->extractable_type));
+    entry = g_hash_table_find (entries_table, (GHRFunc) _lookup_proxied_asset,
+        (gpointer) ges_asset_get_id (proxy));
+
+    if (!entry) {
+      GST_DEBUG_OBJECT (asset, "Not proxying any asset");
+      return FALSE;
+    }
+
+    asset = entry->asset;
+    while (asset->priv->proxies)
+      asset = asset->priv->proxies->data;
+  }
+
+  if (proxy->priv->proxy_target) {
+    GST_ERROR_OBJECT (asset,
+        "Trying to use %s as a proxy, but it is already proxying %s",
+        proxy->priv->id, proxy->priv->proxy_target->priv->id);
+  }
+
+  if (g_list_find (proxy->priv->proxies, asset)) {
+    GST_ERROR_OBJECT (asset, "Trying to setup a circular proxying dependency!");
+
+    return FALSE;
+  }
+
+  if (g_list_find (asset->priv->proxies, proxy)) {
+    GST_INFO_OBJECT (asset,
+        "%" GST_PTR_FORMAT " already marked as proxy, moving to first", proxy);
+    GES_ASSET (asset->priv->proxies->data)->priv->proxy_target = NULL;
+    asset->priv->proxies = g_list_remove (asset->priv->proxies, proxy);
+  }
+
+  GST_INFO ("%s is now proxied by %s", asset->priv->id, proxy->priv->id);
+  asset->priv->proxies = g_list_prepend (asset->priv->proxies, proxy);
+  proxy->priv->proxy_target = asset;
+  g_object_notify_by_pspec (G_OBJECT (proxy), _properties[PROP_PROXY_TARGET]);
+
+  asset->priv->state = ASSET_PROXIED;
+  g_object_notify_by_pspec (G_OBJECT (asset), _properties[PROP_PROXY]);
+
   return TRUE;
 }
 
+/**
+ * ges_asset_list_proxies:
+ * @asset: The #GESAsset to get proxies from
+ *
+ * Returns: The list of proxies @asset has. Note that the default asset to be
+ * used is always the first in that list.
+ */
+GList *
+ges_asset_list_proxies (GESAsset * asset)
+{
+  g_return_val_if_fail (GES_IS_ASSET (asset), NULL);
+
+  return asset->priv->proxies;
+}
+
+/**
+ * ges_asset_get_proxy:
+ * @asset: The #GESAsset to get currenlty used proxy
+ *
+ * Returns: (transfer none): The proxy in use for @asset
+ */
+GESAsset *
+ges_asset_get_proxy (GESAsset * asset)
+{
+  g_return_val_if_fail (GES_IS_ASSET (asset), NULL);
+
+  if (asset->priv->state == ASSET_PROXIED && asset->priv->proxies) {
+    return asset->priv->proxies->data;
+  }
+
+  return NULL;
+}
+
+/**
+ * ges_asset_get_proxy_target:
+ * @proxy: The #GESAsset from which to get the the asset it proxies.
+ *
+ * Returns: (transfer none): The #GESAsset that is proxied by @proxy
+ */
+GESAsset *
+ges_asset_get_proxy_target (GESAsset * proxy)
+{
+  g_return_val_if_fail (GES_IS_ASSET (proxy), NULL);
+
+  return proxy->priv->proxy_target;
+}
+
 /* Caution, this method should be used in rare cases (ie: for the project
  * as we can change its ID from a useless one to a proper URI). In most
  * cases you want to update the ID creating a proxy
@@ -751,19 +925,21 @@ ges_asset_request (GType extractable_type, const gchar * id, GError ** error)
           asset = NULL;
           goto done;
         case ASSET_PROXIED:
-          asset =
-              ges_asset_cache_lookup (asset->priv->extractable_type,
-              asset->priv->proxied_asset_id);
-          if (asset == NULL) {
-            GST_ERROR ("Asset against a asset we do not"
+          if (asset->priv->proxies == NULL) {
+            GST_ERROR ("Proxied against an asset we do not"
                 " have in cache, something massively screwed");
 
             goto done;
           }
+
+          asset = asset->priv->proxies->data;
+          while (ges_asset_get_proxy (asset))
+            asset = ges_asset_get_proxy (asset);
+
           break;
         case ASSET_INITIALIZED_WITH_ERROR:
           GST_WARNING_OBJECT (asset, "Initialized with error, not returning");
-          if (asset->priv->error)
+          if (asset->priv->error && error)
             *error = g_error_copy (asset->priv->error);
           asset = NULL;
           goto done;
@@ -866,7 +1042,7 @@ ges_asset_request_async (GType extractable_type,
     real_id = g_strdup (id);
   }
 
-  /* Check if we already have a asset for this ID */
+  /* Check if we already have an asset for this ID */
   asset = ges_asset_cache_lookup (extractable_type, real_id);
   if (asset) {
     GTask *task = g_task_new (asset, NULL, callback, user_data);
@@ -892,12 +1068,12 @@ ges_asset_request_async (GType extractable_type,
 
           goto done;
         case ASSET_PROXIED:
-          asset =
-              ges_asset_cache_lookup (asset->priv->extractable_type,
-              asset->priv->proxied_asset_id);
+          asset = ges_asset_get_proxy (asset);
+
           if (asset == NULL) {
-            GST_ERROR ("Asset proxied against a asset we do not"
-                " have in cache, something massively screwed");
+            GST_ERROR ("Asset %s proxied against an asset (%s) we do not"
+                " have in cache, something massively screwed",
+                asset->priv->id, asset->priv->proxied_asset_id);
 
             goto done;
           }
index 337df20..64a12c2 100644 (file)
@@ -74,6 +74,10 @@ struct _GESAssetClass
   /* Let subclasses know that we proxied an asset */
   void                     (*inform_proxy)      (GESAsset *self,
                                                  const gchar *proxy_id);
+
+  void                     (*proxied)      (GESAsset *self,
+                                            GESAsset *proxy);
+
   /* Ask subclasses for a new ID for @self when the asset failed loading
    * This function returns %FALSE when the ID could be updated or %TRUE
    * otherwize */
@@ -100,5 +104,11 @@ GESExtractable * ges_asset_extract   (GESAsset * self,
                                       GError **error);
 GList * ges_list_assets              (GType filter);
 
+
+gboolean ges_asset_set_proxy         (GESAsset *asset, GESAsset *proxy);
+GList * ges_asset_list_proxies       (GESAsset *asset);
+GESAsset * ges_asset_get_proxy_target(GESAsset *proxy);
+GESAsset * ges_asset_get_proxy       (GESAsset *asset);
+
 G_END_DECLS
 #endif /* _GES_ASSET */
index bf176d8..106aab1 100644 (file)
@@ -100,6 +100,7 @@ typedef struct PendingAsset
   GESFormatter *formatter;
   gchar *metadatas;
   GstStructure *properties;
+  gchar *proxy_id;
 } PendingAsset;
 
 struct _GESBaseXmlFormatterPrivate
@@ -467,6 +468,7 @@ _add_all_groups (GESFormatter * self)
 static void
 _loading_done (GESFormatter * self)
 {
+  GList *assets, *tmp;
   GESBaseXmlFormatterPrivate *priv = GES_BASE_XML_FORMATTER (self)->priv;
 
   _add_all_groups (self);
@@ -478,6 +480,13 @@ _loading_done (GESFormatter * self)
   ges_timeline_set_auto_transition (self->timeline,
       priv->timeline_auto_transition);
 
+  /* Go over all assets and make sure that all proxies we were 'trying' to set are finally
+   * properly set */
+  assets = ges_project_list_assets (self->project, GES_TYPE_EXTRACTABLE);
+  for (tmp = assets; tmp; tmp = tmp->next) {
+    ges_asset_set_proxy (NULL, tmp->data);
+  }
+
   g_hash_table_foreach (priv->layers, (GHFunc) _set_auto_transition, NULL);
   ges_project_set_loaded (self->project, self);
 }
@@ -623,8 +632,8 @@ _free_pending_clip (GESBaseXmlFormatterPrivate * priv, PendingClip * pend)
 static void
 _free_pending_asset (GESBaseXmlFormatterPrivate * priv, PendingAsset * passet)
 {
-  if (passet->metadatas)
-    g_free (passet->metadatas);
+  g_free (passet->metadatas);
+  g_free (passet->proxy_id);
   if (passet->properties)
     gst_structure_free (passet->properties);
 
@@ -722,6 +731,13 @@ new_asset_cb (GESAsset * source, GAsyncResult * res, PendingAsset * passet)
     goto done;
   }
 
+  if (passet->proxy_id) {
+    /* We set the URI to be used as a proxy,
+     * this will finally be set as the proxy when we
+     * are done loading all assets */
+    ges_asset_try_proxy (asset, passet->proxy_id);
+  }
+
   /* now that we have the GESAsset, we create the GESClips */
   pendings = g_hash_table_lookup (priv->assetid_pendingclips, id);
   GST_DEBUG_OBJECT (self, "Asset created with ID %s, now creating pending "
@@ -825,7 +841,7 @@ _create_profile (GESBaseXmlFormatter * self,
 void
 ges_base_xml_formatter_add_asset (GESBaseXmlFormatter * self,
     const gchar * id, GType extractable_type, GstStructure * properties,
-    const gchar * metadatas, GError ** error)
+    const gchar * metadatas, const gchar * proxy_id, GError ** error)
 {
   PendingAsset *passet;
   GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
@@ -835,6 +851,7 @@ ges_base_xml_formatter_add_asset (GESBaseXmlFormatter * self,
 
   passet = g_slice_new0 (PendingAsset);
   passet->metadatas = g_strdup (metadatas);
+  passet->proxy_id = g_strdup (proxy_id);
   passet->formatter = gst_object_ref (self);
   if (properties)
     passet->properties = gst_structure_copy (properties);
index 1e19eb1..cde566b 100644 (file)
@@ -143,7 +143,7 @@ GESAsset*
 ges_asset_cache_lookup(GType extractable_type, const gchar * id);
 
 gboolean
-ges_asset_set_proxy (GESAsset *asset, const gchar *new_id);
+ges_asset_try_proxy (GESAsset *asset, const gchar *new_id);
 
 G_GNUC_INTERNAL gboolean
 ges_asset_request_id_update (GESAsset *asset, gchar **proposed_id,
@@ -224,6 +224,7 @@ G_GNUC_INTERNAL void ges_base_xml_formatter_add_asset        (GESBaseXmlFormatte
                                                                  GType extractable_type,
                                                                  GstStructure *properties,
                                                                  const gchar *metadatas,
+                                                                 const gchar *proxy_id,
                                                                  GError **error);
 G_GNUC_INTERNAL void ges_base_xml_formatter_add_layer           (GESBaseXmlFormatter *self,
                                                                  GType extractable_type,
index 72619af..15025f6 100644 (file)
@@ -595,7 +595,7 @@ ges_project_try_updating_id (GESProject * project, GESAsset * asset,
 
   if (new_id) {
     GST_DEBUG_OBJECT (project, "new id found: %s", new_id);
-    if (!ges_asset_set_proxy (asset, new_id)) {
+    if (!ges_asset_try_proxy (asset, new_id)) {
       g_free (new_id);
       new_id = NULL;
     }
@@ -633,6 +633,7 @@ new_asset_cb (GESAsset * source, GAsyncResult * res, GESProject * project)
     return;
   }
 
+  ges_asset_set_proxy (NULL, asset);
   ges_project_add_asset (project, asset);
   if (asset)
     gst_object_unref (asset);
@@ -766,6 +767,10 @@ ges_project_create_asset_sync (GESProject * project, const gchar * id,
     if (asset) {
       retry = FALSE;
 
+      if ((!g_hash_table_lookup (project->priv->assets,
+                  ges_asset_get_id (asset))))
+        g_signal_emit (project, _signals[ASSET_LOADING_SIGNAL], 0, asset);
+
       if (possible_id) {
         g_free (possible_id);
         ges_uri_assets_validate_uri (id);
@@ -776,6 +781,9 @@ ges_project_create_asset_sync (GESProject * project, const gchar * id,
       GESAsset *tmpasset;
 
       tmpasset = ges_asset_cache_lookup (extractable_type, id);
+      g_signal_emit (project, _signals[ASSET_LOADING_SIGNAL], 0, tmpasset);
+      g_signal_emit (project, _signals[ERROR_LOADING_ASSET], 0, *error, id,
+          extractable_type);
       possible_id = ges_project_try_updating_id (project, tmpasset, *error);
 
       if (possible_id == NULL)
@@ -788,6 +796,9 @@ ges_project_create_asset_sync (GESProject * project, const gchar * id,
     }
   }
 
+  if (!ges_asset_get_proxy_target (asset))
+    ges_asset_set_proxy (NULL, asset);
+
   ges_project_add_asset (project, asset);
 
   return asset;
index 652d1c6..0069227 100644 (file)
@@ -37,6 +37,7 @@
 #include "ges-image-source.h"
 #include "ges-audio-test-source.h"
 #include "ges-multi-file-source.h"
+#include "ges-layer.h"
 
 static void ges_extractable_interface_init (GESExtractableInterface * iface);
 
@@ -260,13 +261,19 @@ extractable_get_id (GESExtractable * self)
   return g_strdup (GES_URI_CLIP (self)->priv->uri);
 }
 
-static void
+static gboolean
 extractable_set_asset (GESExtractable * self, GESAsset * asset)
 {
+  gboolean res = TRUE;
   GESUriClip *uriclip = GES_URI_CLIP (self);
-  GESUriClipAsset *filesource_asset = GES_URI_CLIP_ASSET (asset);
+  GESUriClipAsset *filesource_asset;
   GESClip *clip = GES_CLIP (self);
+  GESLayer *layer = ges_clip_get_layer (clip);
+  GList *tmp;
 
+  g_return_val_if_fail (GES_IS_URI_CLIP_ASSET (asset), FALSE);
+
+  filesource_asset = GES_URI_CLIP_ASSET (asset);
   if (GST_CLOCK_TIME_IS_VALID (GES_TIMELINE_ELEMENT_DURATION (clip)) == FALSE)
     _set_duration0 (GES_TIMELINE_ELEMENT (uriclip),
         ges_uri_clip_asset_get_duration (filesource_asset));
@@ -277,13 +284,35 @@ extractable_set_asset (GESExtractable * self, GESAsset * asset)
       ges_uri_clip_asset_is_image (filesource_asset));
 
   if (ges_clip_get_supported_formats (clip) == GES_TRACK_TYPE_UNKNOWN) {
-
     ges_clip_set_supported_formats (clip,
-        ges_clip_asset_get_supported_formats
-        (GES_CLIP_ASSET (filesource_asset)));
+        ges_clip_asset_get_supported_formats (GES_CLIP_ASSET
+            (filesource_asset)));
   }
 
   GES_TIMELINE_ELEMENT (uriclip)->asset = asset;
+
+  if (layer) {
+    for (tmp = GES_CONTAINER_CHILDREN (self); tmp; tmp = tmp->next) {
+      if (GES_IS_SOURCE (tmp->data)) {
+        GESTrack *track = ges_track_element_get_track (tmp->data);
+
+        ges_track_remove_element (track, tmp->data);
+      }
+    }
+
+    gst_object_ref (clip);
+    ges_layer_remove_clip (layer, clip);
+    res = ges_layer_add_clip (layer, clip);
+    gst_object_unref (clip);
+    gst_object_unref (layer);
+  }
+
+  if (res) {
+    g_free (uriclip->priv->uri);
+    uriclip->priv->uri = g_strdup (ges_asset_get_id (asset));
+  }
+
+  return res;
 }
 
 static void
@@ -293,7 +322,9 @@ ges_extractable_interface_init (GESExtractableInterface * iface)
   iface->check_id = (GESExtractableCheckId) extractable_check_id;
   iface->get_parameters_from_id = extractable_get_parameters_from_id;
   iface->get_id = extractable_get_id;
-  iface->set_asset = extractable_set_asset;
+  iface->get_id = extractable_get_id;
+  iface->can_update_asset = TRUE;
+  iface->set_asset_full = extractable_set_asset;
 }
 
 static void
index 07588f6..b040d77 100644 (file)
@@ -32,8 +32,8 @@
 G_DEFINE_TYPE (GESXmlFormatter, ges_xml_formatter, GES_TYPE_BASE_XML_FORMATTER);
 
 #define API_VERSION 0
-#define MINOR_VERSION 2
-#define VERSION 0.2
+#define MINOR_VERSION 3
+#define VERSION 0.3
 
 #define COLLECT_STR_OPT (G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL)
 
@@ -272,14 +272,15 @@ _parse_asset (GMarkupParseContext * context, const gchar * element_name,
 {
   GType extractable_type;
   const gchar *id, *extractable_type_name, *metadatas = NULL, *properties =
-      NULL;
+      NULL, *proxy_id = NULL;
 
   if (!g_markup_collect_attributes (element_name, attribute_names,
           attribute_values, error, G_MARKUP_COLLECT_STRING, "id", &id,
           G_MARKUP_COLLECT_STRING, "extractable-type-name",
           &extractable_type_name,
           COLLECT_STR_OPT, "properties", &properties,
-          COLLECT_STR_OPT, "metadatas", &metadatas, G_MARKUP_COLLECT_INVALID))
+          COLLECT_STR_OPT, "metadatas", &metadatas,
+          COLLECT_STR_OPT, "proxy-id", &proxy_id, G_MARKUP_COLLECT_INVALID))
     return;
 
   extractable_type = g_type_from_name (extractable_type_name);
@@ -299,7 +300,7 @@ _parse_asset (GMarkupParseContext * context, const gchar * element_name,
       props = gst_structure_from_string (properties, NULL);
 
     ges_base_xml_formatter_add_asset (GES_BASE_XML_FORMATTER (self), id,
-        extractable_type, props, metadatas, error);
+        extractable_type, props, metadatas, proxy_id, error);
     if (props)
       gst_structure_free (props);
   }
@@ -910,10 +911,10 @@ _serialize_properties (GObject * object, const gchar * fieldname, ...)
 }
 
 static inline void
-_save_assets (GString * str, GESProject * project)
+_save_assets (GESXmlFormatter * self, GString * str, GESProject * project)
 {
   char *properties, *metas;
-  GESAsset *asset;
+  GESAsset *asset, *proxy;
   GList *assets, *tmp;
 
   assets = ges_project_list_assets (project, GES_TYPE_EXTRACTABLE);
@@ -923,10 +924,27 @@ _save_assets (GString * str, GESProject * project)
     metas = ges_meta_container_metas_to_string (GES_META_CONTAINER (asset));
     append_escaped (str,
         g_markup_printf_escaped
-        ("      <asset id='%s' extractable-type-name='%s' properties='%s' metadatas='%s' />\n",
+        ("      <asset id='%s' extractable-type-name='%s' properties='%s' metadatas='%s' ",
             ges_asset_get_id (asset),
             g_type_name (ges_asset_get_extractable_type (asset)), properties,
             metas));
+
+    /*TODO Save the whole list of proxies */
+    proxy = ges_asset_get_proxy (asset);
+    if (proxy) {
+      append_escaped (str, g_markup_printf_escaped (" proxy-id='%s' ",
+              ges_asset_get_id (proxy)));
+
+      if (!g_list_find (assets, proxy)) {
+        assets = g_list_append (assets, gst_object_ref (proxy));
+
+        if (!tmp->next)
+          tmp->next = g_list_last (assets);
+      }
+
+      self->priv->min_version = MAX (self->priv->min_version, 3);
+    }
+    g_string_append (str, "/>\n");
     g_free (properties);
     g_free (metas);
   }
@@ -1433,6 +1451,7 @@ _save (GESFormatter * formatter, GESTimeline * timeline, GError ** error)
 
   gchar *projstr = NULL, *version;
   gchar *properties = NULL, *metas = NULL;
+  GESXmlFormatter *self = GES_XML_FORMATTER (formatter);
   GESXmlFormatterPrivate *priv;
 
 
@@ -1455,10 +1474,10 @@ _save (GESFormatter * formatter, GESTimeline * timeline, GError ** error)
   g_string_append (str, "    </encoding-profiles>\n");
 
   g_string_append (str, "    <ressources>\n");
-  _save_assets (str, project);
+  _save_assets (self, str, project);
   g_string_append (str, "    </ressources>\n");
 
-  _save_timeline (GES_XML_FORMATTER (formatter), str, timeline);
+  _save_timeline (self, str, timeline);
   g_string_append (str, "</project>\n</ges>");
 
   projstr = g_strdup_printf ("<ges version='%i.%i'>\n", API_VERSION,
index 039291f..2229105 100644 (file)
@@ -35,6 +35,7 @@ SUPPRESSIONS = $(top_srcdir)/common/gst.supp # $(srcdir)/gst-plugins-bad.supp
 clean-local: clean-local-check
 
 check_PROGRAMS = \
+       ges/asset       \
        ges/backgroundsource\
        ges/basic       \
        ges/layer       \
index 00caca1..f760aaa 100644 (file)
@@ -1,6 +1,7 @@
 /* GStreamer Editing Services
  *
  * Copyright (C) 2012 Volodymyr Rudyi <vladimir.rudoy@gmail.com>
+ * Copyright (C) 2015 Thibault Saunier <tsaunier@gnome.org>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -18,8 +19,9 @@
  * Boston, MA 02111-1307, USA.
  */
 
-#include "../../../ges/ges-internal.h"
+#include "test-utils.h"
 #undef GST_CAT_DEFAULT
+#include "../../../ges/ges-internal.h"
 #include <ges/ges.h>
 #include <gst/check/gstcheck.h>
 
@@ -53,7 +55,7 @@ GST_START_TEST (test_basic)
 
 GST_END_TEST;
 
-GST_START_TEST (test_change_asset)
+GST_START_TEST (test_transition_change_asset)
 {
   gchar *id;
   GESAsset *a;
@@ -96,6 +98,43 @@ GST_START_TEST (test_change_asset)
 
 GST_END_TEST;
 
+GST_START_TEST (test_uri_clip_change_asset)
+{
+  GESAsset *asset, *asset1;
+  GESExtractable *extractable;
+  GESLayer *layer = ges_layer_new ();
+  gchar *uri = ges_test_file_uri ("audio_video.ogg");
+  gchar *uri1 = ges_test_file_uri ("audio_only.ogg");
+  GESTimeline *timeline = ges_timeline_new_audio_video ();
+
+  ges_timeline_add_layer (timeline, layer);
+
+  gst_init (NULL, NULL);
+  ges_init ();
+
+  asset = GES_ASSET (ges_uri_clip_asset_request_sync (uri, NULL));
+
+  fail_unless (GES_IS_ASSET (asset));
+  fail_unless_equals_string (ges_asset_get_id (asset), uri);
+
+  extractable = GES_EXTRACTABLE (ges_layer_add_asset (layer,
+          asset, 0, 0, GST_CLOCK_TIME_NONE, GES_TRACK_TYPE_UNKNOWN));
+  fail_unless (ges_extractable_get_asset (extractable) == asset);
+  gst_object_unref (asset);
+
+  /* Now try to set the a and see if the vtype is properly updated */
+  asset1 = GES_ASSET (ges_uri_clip_asset_request_sync (uri1, NULL));
+  fail_unless_equals_int (g_list_length (GES_CONTAINER_CHILDREN (extractable)),
+      2);
+  fail_unless (ges_extractable_set_asset (extractable, asset1));
+  fail_unless_equals_int (g_list_length (GES_CONTAINER_CHILDREN (extractable)),
+      1);
+
+  gst_object_unref (extractable);
+}
+
+GST_END_TEST;
+
 GST_START_TEST (test_list_asset)
 {
   GList *assets;
@@ -128,7 +167,8 @@ GST_START_TEST (test_proxy_asset)
   nothing = ges_asset_cache_lookup (GES_TYPE_EFFECT, "nothing");
   fail_unless (nothing != NULL);
 
-  fail_unless (ges_asset_set_proxy (nothing, "identity"));
+  fail_unless (ges_asset_try_proxy (nothing, "identity"));
+  fail_unless (ges_asset_set_proxy (NULL, identity));
 
   nothing_at_all = ges_asset_request (GES_TYPE_EFFECT, "nothing_at_all", NULL);
   fail_if (nothing_at_all);
@@ -137,7 +177,13 @@ GST_START_TEST (test_proxy_asset)
   fail_unless (nothing_at_all != NULL);
 
   /* Now we proxy nothing_at_all to nothing which is itself proxied to identity */
-  fail_unless (ges_asset_set_proxy (nothing_at_all, "nothing"));
+  fail_unless (ges_asset_try_proxy (nothing_at_all, "nothing"));
+  fail_unless (ges_asset_set_proxy (NULL, nothing));
+  fail_unless_equals_int (g_list_length (ges_asset_list_proxies
+          (nothing_at_all)), 1);
+
+  fail_unless_equals_pointer (ges_asset_get_proxy_target (nothing),
+      nothing_at_all);
 
   /* If we request nothing_at_all we should get the good proxied identity */
   nothing_at_all = ges_asset_request (GES_TYPE_EFFECT, "nothing_at_all", NULL);
@@ -161,7 +207,8 @@ ges_suite (void)
   /* Must be first until we implement deinit */
   tcase_add_test (tc_chain, test_list_asset);
   tcase_add_test (tc_chain, test_basic);
-  tcase_add_test (tc_chain, test_change_asset);
+  tcase_add_test (tc_chain, test_transition_change_asset);
+  tcase_add_test (tc_chain, test_uri_clip_change_asset);
   tcase_add_test (tc_chain, test_proxy_asset);
 
   return s;